// SPDX-License-Identifier: GPL-2.0-only /* * OMAP WakeupGen Source file * * OMAP WakeupGen is the interrupt controller extension used along * with ARM GIC to wake the CPU out from low power states on * external interrupts. It is responsible for generating wakeup * event from the incoming interrupts and enable bits. It is * implemented in MPU always ON power domain. During normal operation, * WakeupGen delivers external interrupts directly to the GIC. * * Copyright (C) 2011 Texas Instruments, Inc. * Santosh Shilimkar <santosh.shilimkar@ti.com>
*/
/* * The sys_nirq pins bypass peripheral modules and are wired directly * to MPUSS wakeupgen. They get automatically inverted for GIC.
*/ staticint wakeupgen_irq_set_type(struct irq_data *d, unsignedint type)
{ bool inverted = false;
switch (type) { case IRQ_TYPE_LEVEL_LOW:
type &= ~IRQ_TYPE_LEVEL_MASK;
type |= IRQ_TYPE_LEVEL_HIGH;
inverted = true; break; case IRQ_TYPE_EDGE_FALLING:
type &= ~IRQ_TYPE_EDGE_BOTH;
type |= IRQ_TYPE_EDGE_RISING;
inverted = true; break; default: break;
}
if (inverted && d->hwirq != SYS_NIRQ1_EXT_SYS_IRQ_1 &&
d->hwirq != SYS_NIRQ2_EXT_SYS_IRQ_2)
pr_warn("wakeupgen: irq%li polarity inverted in dts\n",
d->hwirq);
for (i = 0; i < irq_banks; i++)
wakeupgen_writel(reg, i, cpu);
}
/* * Mask or unmask all interrupts on given CPU. * 0 = Mask all interrupts on the 'cpu' * 1 = Unmask all interrupts on the 'cpu' * Ensure that the initial mask is maintained. This is faster than * iterating through GIC registers to arrive at the correct masks.
*/ staticvoid wakeupgen_irqmask_all(unsignedint cpu, unsignedint set)
{ unsignedlong flags;
#ifdef CONFIG_CPU_PM staticinlinevoid omap4_irq_save_context(void)
{
u32 i, val;
if (omap_rev() == OMAP4430_REV_ES1_0) return;
for (i = 0; i < irq_banks; i++) { /* Save the CPUx interrupt mask for IRQ 0 to 127 */
val = wakeupgen_readl(i, 0);
sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i);
val = wakeupgen_readl(i, 1);
sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i);
/* * Disable the secure interrupts for CPUx. The restore * code blindly restores secure and non-secure interrupt * masks from SAR RAM. Secure interrupts are not suppose * to be enabled from HLOS. So overwrite the SAR location * so that the secure interrupt remains disabled.
*/
sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
}
/* Save AuxBoot* registers */
val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
writel_relaxed(val, sar_base + AUXCOREBOOT0_OFFSET);
val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_1);
writel_relaxed(val, sar_base + AUXCOREBOOT1_OFFSET);
/* Save SyncReq generation logic */
val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_MASK);
writel_relaxed(val, sar_base + PTMSYNCREQ_MASK_OFFSET);
val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_EN);
writel_relaxed(val, sar_base + PTMSYNCREQ_EN_OFFSET);
/* Set the Backup Bit Mask status */
val = readl_relaxed(sar_base + SAR_BACKUP_STATUS_OFFSET);
val |= SAR_BACKUP_STATUS_WAKEUPGEN;
writel_relaxed(val, sar_base + SAR_BACKUP_STATUS_OFFSET);
}
staticinlinevoid omap5_irq_save_context(void)
{
u32 i, val;
for (i = 0; i < irq_banks; i++) { /* Save the CPUx interrupt mask for IRQ 0 to 159 */
val = wakeupgen_readl(i, 0);
sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU0, i);
val = wakeupgen_readl(i, 1);
sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU1, i);
sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
}
/* Save AuxBoot* registers */
val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT0_OFFSET);
val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT1_OFFSET);
/* Set the Backup Bit Mask status */
val = readl_relaxed(sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET);
val |= SAR_BACKUP_STATUS_WAKEUPGEN;
writel_relaxed(val, sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET);
for (i = 0; i < irq_banks; i++) {
wakeupgen_context[i] = wakeupgen_readl(i, 0);
wakeupgen_writel(0, i, CPU0_ID);
}
}
/* * Save WakeupGen interrupt context in SAR BANK3. Restore is done by * ROM code. WakeupGen IP is integrated along with GIC to manage the * interrupt wakeups from CPU low power states. It manages * masking/unmasking of Shared peripheral interrupts(SPI). So the * interrupt enable/disable control should be in sync and consistent * at WakeupGen and GIC so that interrupts are not lost.
*/ staticvoid irq_save_context(void)
{ /* DRA7 has no SAR to save */ if (soc_is_dra7xx()) return;
if (wakeupgen_ops && wakeupgen_ops->save_context)
wakeupgen_ops->save_context();
}
/* * Clear WakeupGen SAR backup status.
*/ staticvoid irq_sar_clear(void)
{
u32 val;
u32 offset = SAR_BACKUP_STATUS_OFFSET; /* DRA7 has no SAR to save */ if (soc_is_dra7xx()) return;
if (soc_is_omap54xx())
offset = OMAP5_SAR_BACKUP_STATUS_OFFSET;
val = readl_relaxed(sar_base + offset);
val &= ~SAR_BACKUP_STATUS_WAKEUPGEN;
writel_relaxed(val, sar_base + offset);
}
for (i = 0; i < irq_banks; i++)
wakeupgen_writel(wakeupgen_context[i], i, CPU0_ID);
}
staticvoid irq_restore_context(void)
{ if (wakeupgen_ops && wakeupgen_ops->restore_context)
wakeupgen_ops->restore_context();
}
/* * Save GIC and Wakeupgen interrupt context using secure API * for HS/EMU devices.
*/ staticvoid irq_save_secure_context(void)
{
u32 ret;
ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX,
FLAG_START_CRITICAL,
0, 0, 0, 0, 0); if (ret != API_HAL_RET_VALUE_OK)
pr_err("GIC and Wakeupgen context save failed\n");
}
/* Define ops for context save and restore for each SoC */ staticstruct omap_wakeupgen_ops omap4_wakeupgen_ops = {
.save_context = omap4_irq_save_context,
.restore_context = irq_sar_clear,
};
staticvoid __init irq_pm_init(void)
{ /* FIXME: Remove this when MPU OSWR support is added */ if (!IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE))
cpu_pm_register_notifier(&irq_notifier_block);
} #else staticvoid __init irq_pm_init(void)
{} #endif
if (fwspec->param_count != 3) return -EINVAL; /* Not GIC compliant */ if (fwspec->param[0] != 0) return -EINVAL; /* No PPI should point to this domain */
hwirq = fwspec->param[1]; if (hwirq >= MAX_IRQS) return -EINVAL; /* Can't deal with this */
for (i = 0; i < nr_irqs; i++)
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&wakeupgen_chip, NULL);
/* Clear all IRQ bitmasks at wakeupGen level */ for (i = 0; i < irq_banks; i++) {
wakeupgen_writel(0, i, CPU0_ID); if (!soc_is_am43xx())
wakeupgen_writel(0, i, CPU1_ID);
}
/* * FIXME: Add support to set_smp_affinity() once the core * GIC code has necessary hooks in place.
*/
/* Associate all the IRQs to boot CPU like GIC init does. */ for (i = 0; i < max_irqs; i++)
irq_target_cpu[i] = boot_cpu;
/* * Enables OMAP5 ES2 PM Mode using ES2_PM_MODE in AMBA_IF_MODE * 0x0: ES1 behavior, CPU cores would enter and exit OFF mode together. * 0x1: ES2 behavior, CPU cores are allowed to enter/exit OFF mode * independently. * This needs to be set one time thanks to always ON domain. * * We do not support ES1 behavior anymore. OMAP5 is assumed to be * ES2.0, and the same is applicable for DRA7.
*/ if (soc_is_omap54xx() || soc_is_dra7xx()) {
val = __raw_readl(wakeupgen_base + OMAP_AMBA_IF_MODE);
val |= BIT(5);
omap_smc1(OMAP5_MON_AMBA_IF_INDEX, val);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.