|
@@ -51,97 +51,112 @@ static void (*_omap_sram_idle)(u32 *addr, int save_state);
|
|
|
|
|
|
static struct powerdomain *mpu_pwrdm;
|
|
static struct powerdomain *mpu_pwrdm;
|
|
|
|
|
|
-/* PRCM Interrupt Handler for wakeups */
|
|
|
|
-static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
|
|
|
|
|
|
+/*
|
|
|
|
+ * PRCM Interrupt Handler Helper Function
|
|
|
|
+ *
|
|
|
|
+ * The purpose of this function is to clear any wake-up events latched
|
|
|
|
+ * in the PRCM PM_WKST_x registers. It is possible that a wake-up event
|
|
|
|
+ * may occur whilst attempting to clear a PM_WKST_x register and thus
|
|
|
|
+ * set another bit in this register. A while loop is used to ensure
|
|
|
|
+ * that any peripheral wake-up events occurring while attempting to
|
|
|
|
+ * clear the PM_WKST_x are detected and cleared.
|
|
|
|
+ */
|
|
|
|
+static int prcm_clear_mod_irqs(s16 module, u8 regs)
|
|
{
|
|
{
|
|
- u32 wkst, irqstatus_mpu;
|
|
|
|
- u32 fclk, iclk;
|
|
|
|
-
|
|
|
|
- /* WKUP */
|
|
|
|
- wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
|
|
|
|
|
|
+ u32 wkst, fclk, iclk, clken;
|
|
|
|
+ u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1;
|
|
|
|
+ u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1;
|
|
|
|
+ u16 iclk_off = (regs == 3) ? CM_ICLKEN3 : CM_ICLKEN1;
|
|
|
|
+ u16 grpsel_off = (regs == 3) ?
|
|
|
|
+ OMAP3430ES2_PM_MPUGRPSEL3 : OMAP3430_PM_MPUGRPSEL;
|
|
|
|
+ int c = 0;
|
|
|
|
+
|
|
|
|
+ wkst = prm_read_mod_reg(module, wkst_off);
|
|
|
|
+ wkst &= prm_read_mod_reg(module, grpsel_off);
|
|
if (wkst) {
|
|
if (wkst) {
|
|
- iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN);
|
|
|
|
- fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN);
|
|
|
|
- cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN);
|
|
|
|
- cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN);
|
|
|
|
- prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST);
|
|
|
|
- while (prm_read_mod_reg(WKUP_MOD, PM_WKST))
|
|
|
|
- cpu_relax();
|
|
|
|
- cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN);
|
|
|
|
- cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN);
|
|
|
|
|
|
+ iclk = cm_read_mod_reg(module, iclk_off);
|
|
|
|
+ fclk = cm_read_mod_reg(module, fclk_off);
|
|
|
|
+ while (wkst) {
|
|
|
|
+ clken = wkst;
|
|
|
|
+ cm_set_mod_reg_bits(clken, module, iclk_off);
|
|
|
|
+ /*
|
|
|
|
+ * For USBHOST, we don't know whether HOST1 or
|
|
|
|
+ * HOST2 woke us up, so enable both f-clocks
|
|
|
|
+ */
|
|
|
|
+ if (module == OMAP3430ES2_USBHOST_MOD)
|
|
|
|
+ clken |= 1 << OMAP3430ES2_EN_USBHOST2_SHIFT;
|
|
|
|
+ cm_set_mod_reg_bits(clken, module, fclk_off);
|
|
|
|
+ prm_write_mod_reg(wkst, module, wkst_off);
|
|
|
|
+ wkst = prm_read_mod_reg(module, wkst_off);
|
|
|
|
+ c++;
|
|
|
|
+ }
|
|
|
|
+ cm_write_mod_reg(iclk, module, iclk_off);
|
|
|
|
+ cm_write_mod_reg(fclk, module, fclk_off);
|
|
}
|
|
}
|
|
|
|
|
|
- /* CORE */
|
|
|
|
- wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
|
|
|
|
- if (wkst) {
|
|
|
|
- iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
|
|
|
|
- fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
|
|
|
|
- cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN1);
|
|
|
|
- cm_set_mod_reg_bits(wkst, CORE_MOD, CM_FCLKEN1);
|
|
|
|
- prm_write_mod_reg(wkst, CORE_MOD, PM_WKST1);
|
|
|
|
- while (prm_read_mod_reg(CORE_MOD, PM_WKST1))
|
|
|
|
- cpu_relax();
|
|
|
|
- cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
|
|
|
|
- cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
|
|
|
|
- }
|
|
|
|
- wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
|
|
|
|
- if (wkst) {
|
|
|
|
- iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
|
|
|
|
- fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
|
|
|
|
- cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
|
|
|
|
- cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
|
|
|
|
- prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
|
|
|
|
- while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3))
|
|
|
|
- cpu_relax();
|
|
|
|
- cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
|
|
|
|
- cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
|
|
|
|
- }
|
|
|
|
|
|
+ return c;
|
|
|
|
+}
|
|
|
|
|
|
- /* PER */
|
|
|
|
- wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
|
|
|
|
- if (wkst) {
|
|
|
|
- iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
|
|
|
|
- fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
|
|
|
|
- cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN);
|
|
|
|
- cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN);
|
|
|
|
- prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST);
|
|
|
|
- while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST))
|
|
|
|
- cpu_relax();
|
|
|
|
- cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
|
|
|
|
- cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
|
|
|
|
- }
|
|
|
|
|
|
+static int _prcm_int_handle_wakeup(void)
|
|
|
|
+{
|
|
|
|
+ int c;
|
|
|
|
|
|
|
|
+ c = prcm_clear_mod_irqs(WKUP_MOD, 1);
|
|
|
|
+ c += prcm_clear_mod_irqs(CORE_MOD, 1);
|
|
|
|
+ c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
|
|
if (omap_rev() > OMAP3430_REV_ES1_0) {
|
|
if (omap_rev() > OMAP3430_REV_ES1_0) {
|
|
- /* USBHOST */
|
|
|
|
- wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
|
|
|
|
- if (wkst) {
|
|
|
|
- iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- CM_ICLKEN);
|
|
|
|
- fclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- CM_FCLKEN);
|
|
|
|
- cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- CM_ICLKEN);
|
|
|
|
- cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- CM_FCLKEN);
|
|
|
|
- prm_write_mod_reg(wkst, OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- PM_WKST);
|
|
|
|
- while (prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- PM_WKST))
|
|
|
|
- cpu_relax();
|
|
|
|
- cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- CM_ICLKEN);
|
|
|
|
- cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
|
|
|
|
- CM_FCLKEN);
|
|
|
|
- }
|
|
|
|
|
|
+ c += prcm_clear_mod_irqs(CORE_MOD, 3);
|
|
|
|
+ c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
|
|
}
|
|
}
|
|
|
|
|
|
- irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
|
|
|
|
- OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
|
|
|
- prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
|
|
|
|
- OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
|
|
|
|
|
+ return c;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * PRCM Interrupt Handler
|
|
|
|
+ *
|
|
|
|
+ * The PRM_IRQSTATUS_MPU register indicates if there are any pending
|
|
|
|
+ * interrupts from the PRCM for the MPU. These bits must be cleared in
|
|
|
|
+ * order to clear the PRCM interrupt. The PRCM interrupt handler is
|
|
|
|
+ * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear
|
|
|
|
+ * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU
|
|
|
|
+ * register indicates that a wake-up event is pending for the MPU and
|
|
|
|
+ * this bit can only be cleared if the all the wake-up events latched
|
|
|
|
+ * in the various PM_WKST_x registers have been cleared. The interrupt
|
|
|
|
+ * handler is implemented using a do-while loop so that if a wake-up
|
|
|
|
+ * event occurred during the processing of the prcm interrupt handler
|
|
|
|
+ * (setting a bit in the corresponding PM_WKST_x register and thus
|
|
|
|
+ * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register)
|
|
|
|
+ * this would be handled.
|
|
|
|
+ */
|
|
|
|
+static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
|
|
|
|
+{
|
|
|
|
+ u32 irqstatus_mpu;
|
|
|
|
+ int c = 0;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
|
|
|
|
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
|
|
|
+
|
|
|
|
+ if (irqstatus_mpu & (OMAP3430_WKUP_ST | OMAP3430_IO_ST)) {
|
|
|
|
+ c = _prcm_int_handle_wakeup();
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Is the MPU PRCM interrupt handler racing with the
|
|
|
|
+ * IVA2 PRCM interrupt handler ?
|
|
|
|
+ */
|
|
|
|
+ WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
|
|
|
|
+ "but no wakeup sources are marked\n");
|
|
|
|
+ } else {
|
|
|
|
+ /* XXX we need to expand our PRCM interrupt handler */
|
|
|
|
+ WARN(1, "prcm: WARNING: PRCM interrupt received, but "
|
|
|
|
+ "no code to handle it (%08x)\n", irqstatus_mpu);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
|
|
|
|
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
|
|
|
|
|
- while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET))
|
|
|
|
- cpu_relax();
|
|
|
|
|
|
+ } while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET));
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
@@ -624,6 +639,16 @@ static void __init prcm_setup_regs(void)
|
|
prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
|
|
prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
|
|
OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
|
|
OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
|
|
|
|
|
|
|
|
+ /* Enable GPIO wakeups in PER */
|
|
|
|
+ prm_write_mod_reg(OMAP3430_EN_GPIO2 | OMAP3430_EN_GPIO3 |
|
|
|
|
+ OMAP3430_EN_GPIO4 | OMAP3430_EN_GPIO5 |
|
|
|
|
+ OMAP3430_EN_GPIO6, OMAP3430_PER_MOD, PM_WKEN);
|
|
|
|
+ /* and allow them to wake up MPU */
|
|
|
|
+ prm_write_mod_reg(OMAP3430_GRPSEL_GPIO2 | OMAP3430_EN_GPIO3 |
|
|
|
|
+ OMAP3430_GRPSEL_GPIO4 | OMAP3430_EN_GPIO5 |
|
|
|
|
+ OMAP3430_GRPSEL_GPIO6,
|
|
|
|
+ OMAP3430_PER_MOD, OMAP3430_PM_MPUGRPSEL);
|
|
|
|
+
|
|
/* Don't attach IVA interrupts */
|
|
/* Don't attach IVA interrupts */
|
|
prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL);
|
|
prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL);
|
|
prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1);
|
|
prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1);
|