From 77da2d910a17e1e6a7e949578723d5aab58568d5 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Sat, 27 Jun 2009 00:07:25 -0500 Subject: OMAP3: PM: Prevent hang in prcm_interrupt_handler There are two scenarios where a race condition could result in a hang in the prcm_interrupt handler. These are: 1). Waiting for PRM_IRQSTATUS_MPU register to clear. Bit 0 of the PRM_IRQSTATUS_MPU register indicates that a wake-up event is pending for the MPU. This bit can only be cleared if the all the wake-up events latched in the various PM_WKST_x registers have been cleared. If a wake-up event occurred during the processing of the prcm interrupt handler, after the corresponding PM_WKST_x register was checked but before the PRM_IRQSTATUS_MPU was cleared, then the CPU would be stuck forever waiting for bit 0 in PRM_IRQSTATUS_MPU to be cleared. 2). Waiting for the PM_WKST_x register to clear. Some power domains have more than one wake-up source. The PM_WKST_x registers indicate the source of a wake-up event and need to be cleared after a wake-up event occurs. When the PM_WKST_x registers are read and before they are cleared, it is possible that another wake-up event could occur causing another bit to be set in one of the PM_WKST_x registers. If this did occur after reading a PM_WKST_x register then the CPU would miss this event and get stuck forever in a loop waiting for that PM_WKST_x register to clear. This patch address the above race conditions that would result in a hang. Signed-off-by: Jon Hunter Reviewed-by: Paul Walmsley Signed-off-by: Kevin Hilman diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 0ff5a6c..1e7aae2 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -51,97 +51,74 @@ static void (*_omap_sram_idle)(u32 *addr, int save_state); 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 void prcm_clear_mod_irqs(s16 module, u8 regs) { - u32 wkst, irqstatus_mpu; - u32 fclk, iclk; + u32 wkst, fclk, iclk; + 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; - /* WKUP */ - wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST); + wkst = prm_read_mod_reg(module, wkst_off); 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); - } - - /* 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); - } - - /* 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); + iclk = cm_read_mod_reg(module, iclk_off); + fclk = cm_read_mod_reg(module, fclk_off); + while (wkst) { + cm_set_mod_reg_bits(wkst, module, iclk_off); + cm_set_mod_reg_bits(wkst, module, fclk_off); + prm_write_mod_reg(wkst, module, wkst_off); + wkst = prm_read_mod_reg(module, wkst_off); + } + cm_write_mod_reg(iclk, module, iclk_off); + cm_write_mod_reg(fclk, module, fclk_off); } +} - 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); +/* + * 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; + + do { + prcm_clear_mod_irqs(WKUP_MOD, 1); + prcm_clear_mod_irqs(CORE_MOD, 1); + prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1); + if (omap_rev() > OMAP3430_REV_ES1_0) { + prcm_clear_mod_irqs(CORE_MOD, 3); + 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); + 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); - 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; } -- cgit v0.10.2 From 5d80597801ff0d7e6b184504c04e9c1b3b61d16d Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 22 Jul 2009 10:18:07 -0700 Subject: OMAP3: PM: PRCM interrupt: check MPUGRPSEL register PM_WKST register contents should be ANDed with the contents of the MPUGRPSEL registers. Otherwise the MPU PRCM interrupt handler could wind up clearing wakeup events meant for the IVA PRCM interrupt handler. A future revision to this code should be to read a cached version of MPUGRPSEL from the powerdomain code, since PRM reads are relatively slow. Updated after Jon Hunter's PRCM IRQ change by Kevin Hilman Signed-off-by: Paul Walmsley Signed-off-by: Kevin Hilman diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 1e7aae2..0e7bd8e 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -67,8 +67,11 @@ static void prcm_clear_mod_irqs(s16 module, u8 regs) 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; wkst = prm_read_mod_reg(module, wkst_off); + wkst &= prm_read_mod_reg(module, grpsel_off); if (wkst) { iclk = cm_read_mod_reg(module, iclk_off); fclk = cm_read_mod_reg(module, fclk_off); -- cgit v0.10.2 From 8cb0ac999f253212bb01cd3c0d686489ec5911ad Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 22 Jul 2009 10:29:02 -0700 Subject: OMAP3: PM: PRCM interrupt: only handle selected PRCM interrupts Clearing wakeup sources is now only done when the PRM indicates a wakeup source interrupt. Since we don't handle any other types of PRCM interrupts right now, warn if we get any other type of PRCM interrupt. Either code needs to be added to the PRCM interrupt handler to react to these, or these other interrupts should be masked off at init. Updated after Jon Hunter's PRCM IRQ rework by Kevin Hilman. Signed-off-by: Paul Walmsley Signed-off-by: Kevin Hilman diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 0e7bd8e..d9440a1 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -61,7 +61,7 @@ static struct powerdomain *mpu_pwrdm; * that any peripheral wake-up events occurring while attempting to * clear the PM_WKST_x are detected and cleared. */ -static void prcm_clear_mod_irqs(s16 module, u8 regs) +static int prcm_clear_mod_irqs(s16 module, u8 regs) { u32 wkst, fclk, iclk; u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; @@ -69,6 +69,7 @@ static void prcm_clear_mod_irqs(s16 module, u8 regs) 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); @@ -80,10 +81,28 @@ static void prcm_clear_mod_irqs(s16 module, u8 regs) cm_set_mod_reg_bits(wkst, 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); } + + return c; +} + +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) { + c += prcm_clear_mod_irqs(CORE_MOD, 3); + c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1); + } + + return c; } /* @@ -106,18 +125,27 @@ static void prcm_clear_mod_irqs(s16 module, u8 regs) static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) { u32 irqstatus_mpu; + int c = 0; do { - prcm_clear_mod_irqs(WKUP_MOD, 1); - prcm_clear_mod_irqs(CORE_MOD, 1); - prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1); - if (omap_rev() > OMAP3430_REV_ES1_0) { - prcm_clear_mod_irqs(CORE_MOD, 3); - prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1); - } - 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); -- cgit v0.10.2 From 71a807757394205cdb1465d68a4f0be50fd6f04b Mon Sep 17 00:00:00 2001 From: Vikram Pandita Date: Fri, 17 Jul 2009 19:33:09 -0500 Subject: OMAP3: PM: USBHOST: clear wakeup events on both hosts USBHOST module has 2 fclocks (for HOST1 and HOST2), only one iclock and only a single bit in the WKST register to indicate a wakeup event. Because of the single WKST bit, we cannot know whether a wakeup event was on HOST1 or HOST2, so enable both fclocks before clearing the wakeup event to ensure both hosts can properly clear the event. Signed-off-by: Vikram Pandita Signed-off-by: Kevin Hilman diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index d9440a1..cf6ea9f 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -63,7 +63,7 @@ static struct powerdomain *mpu_pwrdm; */ static int prcm_clear_mod_irqs(s16 module, u8 regs) { - u32 wkst, fclk, iclk; + 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; @@ -77,8 +77,15 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) iclk = cm_read_mod_reg(module, iclk_off); fclk = cm_read_mod_reg(module, fclk_off); while (wkst) { - cm_set_mod_reg_bits(wkst, module, iclk_off); - cm_set_mod_reg_bits(wkst, module, fclk_off); + 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++; -- cgit v0.10.2 From eb350f74ebff9573641c5fb689fb071b695ef35b Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Thu, 10 Sep 2009 15:53:08 +0000 Subject: OMAP3: PM: Enable GPIO module-level wakeups Currently, only GPIOs in the wakeup domain (GPIOs in bank 0) are enabled as wakups. This patch also enables GPIOs in the PER powerdomain (banks 2-6) to be used as possible wakeup sources. In addition, this patch ensures that all GPIO wakeups can wakeup the MPU using the PM_MPUGRPSEL_ registers. NOTE: this doesn't enable the individual GPIOs as wakeups, this simply enables the per-bank wakeups at the powerdomain level. This problem was discovered by Mike Chan when preventing the CORE powerdomain from going into retention/off. When CORE was allowed to hit retention, GPIO wakeups via IO pad were working fine, but when CORE remained on, GPIO module-level wakeups were not working properly. To test, prevent CORE from going inactive/retention/off, thus preventing the IO chain from being armed: # echo 3 > /debug/pm_debug/core_pwrdm/suspend This ensures that GPIO wakeups happen via module-level wakeups and not via IO pad. Tested on 3430SDP using the touchscreen GPIO (gpio 2, in WKUP) Tested on Zoom2 using the QUART interrup GPIO (gpio 102, in PER) Also, c.f. OMAP PM wiki for troubleshooting GPIO wakeup issues: http://elinux.org/OMAP_Power_Management Reported-by: Mike Chan Signed-off-by: Kevin Hilman diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index cf6ea9f..378c2f6 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -639,6 +639,16 @@ static void __init prcm_setup_regs(void) prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN, 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 */ prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL); prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1); -- cgit v0.10.2 From ee894b18e064447f86019af38a90ccb091880942 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 1 Oct 2009 10:01:55 +0300 Subject: OMAP3: PM: introduce a new powerdomain walk helper The 'pwrdm_for_each()' function walks powerdomains with a spinlock locked, so the the callbacks cannot do anything which may sleep. This patch introduces a 'pwrdm_for_each_nolock()' helper which does the same, but without the spinlock locked. This fixes the following lockdep warning: [ 0.000000] WARNING: at kernel/lockdep.c:2460 lockdep_trace_alloc+0xac/0xec() [ 0.000000] Modules linked in: (unwind_backtrace+0x0/0xdc) from [] (warn_slowpath_common+0x48/0x60) (warn_slowpath_common+0x48/0x60) from [] (lockdep_trace_alloc+0xac/0xec) (lockdep_trace_alloc+0xac/0xec) from [] (kmem_cache_alloc+0x1c/0xd0) (kmem_cache_alloc+0x1c/0xd0) from [] (d_alloc+0x1c/0x1a4) (d_alloc+0x1c/0x1a4) from [] (__lookup_hash+0xd8/0x118) (__lookup_hash+0xd8/0x118) from [] (lookup_one_len+0x84/0x94) (lookup_one_len+0x84/0x94) from [] (debugfs_create_file+0x8c/0x20c) (debugfs_create_file+0x8c/0x20c) from [] (debugfs_create_dir+0x1c/0x20) (debugfs_create_dir+0x1c/0x20) from [] (pwrdms_setup+0x60/0x90) (pwrdms_setup+0x60/0x90) from [] (pwrdm_for_each+0x30/0x80) (pwrdm_for_each+0x30/0x80) from [] (pm_dbg_init+0x7c/0x14c) (pm_dbg_init+0x7c/0x14c) from [] (do_one_initcall+0x5c/0x1b8) (do_one_initcall+0x5c/0x1b8) from [] (kernel_init+0x90/0x10c) (kernel_init+0x90/0x10c) from [] (kernel_thread_exit+0x0/0x8) Signed-off-by: Artem Bityutskiy Signed-off-by: Kevin Hilman diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index 1b4c160..2fc4d6a 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c @@ -541,7 +541,7 @@ static int __init pm_dbg_init(void) printk(KERN_ERR "%s: only OMAP3 supported\n", __func__); return -ENODEV; } - + d = debugfs_create_dir("pm_debug", NULL); if (IS_ERR(d)) return PTR_ERR(d); @@ -551,7 +551,7 @@ static int __init pm_dbg_init(void) (void) debugfs_create_file("time", S_IRUGO, d, (void *)DEBUG_FILE_TIMERS, &debug_fops); - pwrdm_for_each(pwrdms_setup, (void *)d); + pwrdm_for_each_nolock(pwrdms_setup, (void *)d); pm_dbg_dir = debugfs_create_dir("registers", d); if (IS_ERR(pm_dbg_dir)) diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 2594cbf..f00289a 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -273,35 +273,50 @@ struct powerdomain *pwrdm_lookup(const char *name) } /** - * pwrdm_for_each - call function on each registered clockdomain + * pwrdm_for_each_nolock - call function on each registered clockdomain * @fn: callback function * * * Call the supplied function for each registered powerdomain. The * callback function can return anything but 0 to bail out early from - * the iterator. The callback function is called with the pwrdm_rwlock - * held for reading, so no powerdomain structure manipulation - * functions should be called from the callback, although hardware - * powerdomain control functions are fine. Returns the last return - * value of the callback function, which should be 0 for success or - * anything else to indicate failure; or -EINVAL if the function - * pointer is null. + * the iterator. Returns the last return value of the callback function, which + * should be 0 for success or anything else to indicate failure; or -EINVAL if + * the function pointer is null. */ -int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), - void *user) +int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user) { struct powerdomain *temp_pwrdm; - unsigned long flags; int ret = 0; if (!fn) return -EINVAL; - read_lock_irqsave(&pwrdm_rwlock, flags); list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { ret = (*fn)(temp_pwrdm, user); if (ret) break; } + + return ret; +} + +/** + * pwrdm_for_each - call function on each registered clockdomain + * @fn: callback function * + * + * This function is the same as 'pwrdm_for_each_nolock()', but keeps the + * &pwrdm_rwlock locked for reading, so no powerdomain structure manipulation + * functions should be called from the callback, although hardware powerdomain + * control functions are fine. + */ +int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user) +{ + unsigned long flags; + int ret; + + read_lock_irqsave(&pwrdm_rwlock, flags); + ret = pwrdm_for_each_nolock(fn, user); read_unlock_irqrestore(&pwrdm_rwlock, flags); return ret; diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h b/arch/arm/plat-omap/include/mach/powerdomain.h index 6271d85..fa64614 100644 --- a/arch/arm/plat-omap/include/mach/powerdomain.h +++ b/arch/arm/plat-omap/include/mach/powerdomain.h @@ -135,6 +135,8 @@ struct powerdomain *pwrdm_lookup(const char *name); int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), void *user); +int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user); int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); -- cgit v0.10.2