From patchwork Fri Jun 19 23:08:41 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Hunter, Jon" X-Patchwork-Id: 31494 X-Patchwork-Delegate: khilman@deeprootsystems.com Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n5JN8hhi022305 for ; Fri, 19 Jun 2009 23:08:43 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753961AbZFSXIi (ORCPT ); Fri, 19 Jun 2009 19:08:38 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754219AbZFSXIi (ORCPT ); Fri, 19 Jun 2009 19:08:38 -0400 Received: from bear.ext.ti.com ([192.94.94.41]:40730 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753961AbZFSXIi (ORCPT ); Fri, 19 Jun 2009 19:08:38 -0400 Received: from dlep35.itg.ti.com ([157.170.170.118]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n5JN8ZHW012253 for ; Fri, 19 Jun 2009 18:08:40 -0500 Received: from legion.dal.design.ti.com (localhost [127.0.0.1]) by dlep35.itg.ti.com (8.13.7/8.13.7) with ESMTP id n5JN8Zpn021298; Fri, 19 Jun 2009 18:08:35 -0500 (CDT) Received: from a0741266-laptop (a0741266-laptop.am.dhcp.ti.com [192.157.144.138]) by legion.dal.design.ti.com (8.11.7p1+Sun/8.11.7) with ESMTP id n5JN8Y922320; Fri, 19 Jun 2009 18:08:34 -0500 (CDT) Received: by a0741266-laptop (Postfix, from userid 1000) id 1EB5F51E98; Fri, 19 Jun 2009 18:08:42 -0500 (CDT) From: Jon Hunter To: Cc: Jon Hunter Subject: [PATCH][RFC] OMAP3: PM: Prevent hang in prcm_interrupt_handler Date: Fri, 19 Jun 2009 18:08:41 -0500 Message-Id: <1245452921-24142-1-git-send-email-jon-hunter@ti.com> X-Mailer: git-send-email 1.6.0.4 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Jon Hunter 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 --- arch/arm/mach-omap2/pm34xx.c | 169 +++++++++++++++++++++++------------------- 1 files changed, 94 insertions(+), 75 deletions(-) diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 7a4a525..0545262 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -200,91 +200,110 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) u32 wkst, irqstatus_mpu; u32 fclk, iclk; - /* WKUP */ - wkst = prm_read_mod_reg(WKUP_MOD, PM_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); - } - - /* 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); - } + do { + /* WKUP */ + wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST); + if (wkst) { + iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN); + fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN); + while (wkst) { + 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); + wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST); + } + cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN); + cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN); + } - /* 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); - } + /* 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); + while (wkst) { + 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); + wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1); + } + 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); + while (wkst) { + 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); + wkst = prm_read_mod_reg(CORE_MOD, + OMAP3430ES2_PM_WKST3); + } + cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3); + cm_write_mod_reg(fclk, CORE_MOD, + OMAP3430ES2_CM_FCLKEN3); + } - if (omap_rev() > OMAP3430_REV_ES1_0) { - /* USBHOST */ - wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST); + /* PER */ + wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST); if (wkst) { - iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, + iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN); + fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN); + while (wkst) { + 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); + wkst = prm_read_mod_reg(OMAP3430_PER_MOD, + PM_WKST); + } + cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN); + cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN); + } + + 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, + 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, + while (wkst) { + 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); + wkst = prm_read_mod_reg( + OMAP3430ES2_USBHOST_MOD, + PM_WKST); + } + cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD, CM_ICLKEN); - cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD, + cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD, CM_FCLKEN); + } } - } - 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; }