From patchwork Mon Jun 27 15:47:12 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benoit Cousson X-Patchwork-Id: 921372 X-Patchwork-Delegate: b-cousson@ti.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.4) with ESMTP id p5RFmjav025021 for ; Mon, 27 Jun 2011 15:48:46 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751929Ab1F0Prl (ORCPT ); Mon, 27 Jun 2011 11:47:41 -0400 Received: from devils.ext.ti.com ([198.47.26.153]:38115 "EHLO devils.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751650Ab1F0Prj (ORCPT ); Mon, 27 Jun 2011 11:47:39 -0400 Received: from dlep34.itg.ti.com ([157.170.170.115]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id p5RFlba0001236 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Mon, 27 Jun 2011 10:47:37 -0500 Received: from dlep26.itg.ti.com (smtp-le.itg.ti.com [157.170.170.27]) by dlep34.itg.ti.com (8.13.7/8.13.8) with ESMTP id p5RFlbcP008096; Mon, 27 Jun 2011 10:47:37 -0500 (CDT) Received: from dlee74.ent.ti.com (localhost [127.0.0.1]) by dlep26.itg.ti.com (8.13.8/8.13.8) with ESMTP id p5RFlbWp019126; Mon, 27 Jun 2011 10:47:37 -0500 (CDT) Received: from dlelxv22.itg.ti.com (172.17.1.197) by dlee74.ent.ti.com (157.170.170.8) with Microsoft SMTP Server id 8.3.106.1; Mon, 27 Jun 2011 10:47:37 -0500 Received: from localhost.localdomain (lncpu04.tif.ti.com [137.167.102.15]) by dlelxv22.itg.ti.com (8.13.8/8.13.8) with ESMTP id p5RFlN3I024273; Mon, 27 Jun 2011 10:47:36 -0500 From: Benoit Cousson To: , CC: santosh.shilimkar@ti.com, linux-omap@vger.kernel.org, Benoit Cousson Subject: [PATCH v2 04/13] OMAP: hwmod: Wait the idle status to be disabled Date: Mon, 27 Jun 2011 17:47:12 +0200 Message-ID: <1309189641-25010-5-git-send-email-b-cousson@ti.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1309189641-25010-1-git-send-email-b-cousson@ti.com> References: <1309189641-25010-1-git-send-email-b-cousson@ti.com> MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Mon, 27 Jun 2011 15:48:46 +0000 (UTC) It is mandatory to wait for a module to be in disabled state before potentially disabling source clock or re-asserting a reset. omap_hwmod_idle and omap_hwmod_shutdown does not wait for the module to be fully idle. Add a cm_xxx accessor to wait the clkctrl idle status to be disabled. Fix hwmod_[idle|shutdown] to use this API. Based on Rajendra's initial patch. Please note that most interconnects hwmod will return one timeout because it is impossible for them to be in idle since the processor is accessing the registers though the interconnect. Signed-off-by: Benoit Cousson Signed-off-by: Rajendra Nayak Cc: Paul Walmsley --- arch/arm/mach-omap2/cminst44xx.c | 25 +++++++++++++++++++++++ arch/arm/mach-omap2/cminst44xx.h | 1 + arch/arm/mach-omap2/omap_hwmod.c | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-omap2/cminst44xx.c b/arch/arm/mach-omap2/cminst44xx.c index 1df740e..fa44ff5 100644 --- a/arch/arm/mach-omap2/cminst44xx.c +++ b/arch/arm/mach-omap2/cminst44xx.c @@ -244,3 +244,28 @@ int omap4_cm_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs) return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; } +/** + * omap4_cm_wait_module_idle - wait for a module to be in 'disabled' + * state + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * Wait for the module IDLEST to be disabled. Some PRCM transition, + * like reset assertion or parent clock de-activation must wait the + * module to be fully disabled. + */ +int omap4_cm_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs) +{ + int i = 0; + + if (!clkctrl_offs) + return 0; + + omap_test_timeout( + _clkctrl_idlest(part, inst, cdoffs, clkctrl_offs) == 0x3, + MAX_MODULE_READY_TIME, i); + + return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; +} diff --git a/arch/arm/mach-omap2/cminst44xx.h b/arch/arm/mach-omap2/cminst44xx.h index 9d39c70..4c5da7d 100644 --- a/arch/arm/mach-omap2/cminst44xx.h +++ b/arch/arm/mach-omap2/cminst44xx.h @@ -18,6 +18,7 @@ extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs); extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs); extern int omap4_cm_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs); +extern int omap4_cm_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs); /* * In an ideal world, we would not export these low-level functions, diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index ea1c976..adbd4b8 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1029,6 +1029,36 @@ static int _wait_target_ready(struct omap_hwmod *oh) } /** + * _wait_target_disable - wait for a module to be disabled + * @oh: struct omap_hwmod * + * + * Wait for a module @oh to leave slave idle. Returns 0 if the module + * does not have an IDLEST bit or if the module successfully leaves + * slave idle; otherwise, pass along the return value of the + * appropriate *_cm_wait_module_ready() function. + */ +static int _wait_target_disable(struct omap_hwmod *oh) +{ + if (!oh) + return -EINVAL; + + if (oh->_int_flags & _HWMOD_NO_MPU_PORT) + return 0; + + if (oh->flags & HWMOD_NO_IDLEST) + return 0; + + /* TODO: For now just handle OMAP4+ */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return 0; + + return omap4_cm_wait_module_idle(oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->clkdm->clkdm_offs, + oh->prcm.omap4.clkctrl_offs); +} + +/** * _lookup_hardreset - fill register bit info for this hwmod/reset line * @oh: struct omap_hwmod * * @name: name of the reset line in the context of this hwmod @@ -1335,6 +1365,8 @@ static int _enable(struct omap_hwmod *oh) */ static int _idle(struct omap_hwmod *oh) { + int ret; + if (oh->_state != _HWMOD_STATE_ENABLED) { WARN(1, "omap_hwmod: %s: idle state can only be entered from " "enabled state\n", oh->name); @@ -1347,6 +1379,10 @@ static int _idle(struct omap_hwmod *oh) _idle_sysc(oh); _del_initiator_dep(oh, mpu_oh); _disable_clocks(oh); + ret = _wait_target_disable(oh); + if (ret) + pr_debug("omap_hwmod: %s: _wait_target_disable failed\n", + oh->name); /* Mux pins for device idle if populated */ if (oh->mux && oh->mux->pads_dynamic) @@ -1439,6 +1475,10 @@ static int _shutdown(struct omap_hwmod *oh) _del_initiator_dep(oh, mpu_oh); /* XXX what about the other system initiators here? dma, dsp */ _disable_clocks(oh); + ret = _wait_target_disable(oh); + if (ret) + pr_debug("omap_hwmod: %s: _wait_target_disable failed\n", + oh->name); } /* XXX Should this code also force-disable the optional clocks? */