From 6ae769973adf1325115d0dfe3fec17e26cbacd81 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Sun, 10 Jul 2011 05:56:30 -0600 Subject: OMAP2+: hwmod: Init clkdm field at boot time At boot time, lookup the clkdm_name to get the clkdm structure pointer for further usage. Signed-off-by: Benoit Cousson Cc: Paul Walmsley Cc: Rajendra Nayak Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 02b6016393a8..1f6f47f1d82a 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -990,9 +990,40 @@ static struct omap_hwmod *_lookup(const char *name) return oh; } +/** + * _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod + * @oh: struct omap_hwmod * + * + * Convert a clockdomain name stored in a struct omap_hwmod into a + * clockdomain pointer, and save it into the struct omap_hwmod. + * return -EINVAL if clkdm_name does not exist or if the lookup failed. + */ +static int _init_clkdm(struct omap_hwmod *oh) +{ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return 0; + + if (!oh->clkdm_name) { + pr_warning("omap_hwmod: %s: no clkdm_name\n", oh->name); + return -EINVAL; + } + + oh->clkdm = clkdm_lookup(oh->clkdm_name); + if (!oh->clkdm) { + pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n", + oh->name, oh->clkdm_name); + return -EINVAL; + } + + pr_debug("omap_hwmod: %s: associated to clkdm %s\n", + oh->name, oh->clkdm_name); + + return 0; +} /** - * _init_clocks - clk_get() all clocks associated with this hwmod + * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as + * well the clockdomain. * @oh: struct omap_hwmod * * @data: not used; pass NULL * @@ -1012,6 +1043,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) ret |= _init_main_clk(oh); ret |= _init_interface_clks(oh); ret |= _init_opt_clks(oh); + ret |= _init_clkdm(oh); if (!ret) oh->_state = _HWMOD_STATE_CLKS_INITED; -- cgit v1.2.3 From d0f0631ddc61026dca71b5b679803000d70fde50 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Sun, 10 Jul 2011 05:56:30 -0600 Subject: OMAP4: hwmod: Replace CLKCTRL absolute address with offset macros The CLKCTRL register was accessed using an absolute address. The usage of hardcoded macros to calculate virtual address from physical one should be avoided as much as possible. The usage of a offset will allow future improvement like migration from the current architecture code toward a module driver. Update cm_xxx accessor, move definition to the proper header file and update copyrights. Signed-off-by: Benoit Cousson Cc: Paul Walmsley Cc: Rajendra Nayak Cc: Todd Poynor [paul@pwsan.com: renamed 'omap4_cm_' fns to 'omap4_cminst_'; removed empty fn prototype section from cm44xx.h; incorporated comments from Todd; documented some functions] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 1f6f47f1d82a..00241ea5bf09 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -146,7 +146,7 @@ #include #include "cm2xxx_3xxx.h" -#include "cm44xx.h" +#include "cminst44xx.h" #include "prm2xxx_3xxx.h" #include "prm44xx.h" #include "mux.h" @@ -1060,7 +1060,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) * 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. + * appropriate *_cm*_wait_module_ready() function. */ static int _wait_target_ready(struct omap_hwmod *oh) { @@ -1087,7 +1087,13 @@ static int _wait_target_ready(struct omap_hwmod *oh) oh->prcm.omap2.idlest_reg_id, oh->prcm.omap2.idlest_idle_bit); } else if (cpu_is_omap44xx()) { - ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg); + if (!oh->clkdm) + return -EINVAL; + + ret = omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->clkdm->clkdm_offs, + oh->prcm.omap4.clkctrl_offs); } else { BUG(); }; -- cgit v1.2.3 From 11b10341bd12c87a8409c69cdcd7ee898400842f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Sun, 10 Jul 2011 05:56:30 -0600 Subject: OMAP: hwmod: Wait the idle status to be disabled 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 Cc: Todd Poynor [paul@pwsan.com: move cpu_is_*() tests to the top of _wait_target_disable(); incorporate some feedback from Todd] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 00241ea5bf09..d21f49b87646 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1101,6 +1101,36 @@ static int _wait_target_ready(struct omap_hwmod *oh) return ret; } +/** + * _wait_target_disable - wait for a module to be disabled + * @oh: struct omap_hwmod * + * + * Wait for a module @oh to enter slave idle. Returns 0 if the module + * does not have an IDLEST bit or if the module successfully enters + * slave idle; otherwise, pass along the return value of the + * appropriate *_cm*_wait_module_idle() function. + */ +static int _wait_target_disable(struct omap_hwmod *oh) +{ + /* TODO: For now just handle OMAP4+ */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return 0; + + if (!oh) + return -EINVAL; + + if (oh->_int_flags & _HWMOD_NO_MPU_PORT) + return 0; + + if (oh->flags & HWMOD_NO_IDLEST) + return 0; + + return omap4_cminst_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 * @@ -1410,6 +1440,8 @@ static int _enable(struct omap_hwmod *oh) */ static int _idle(struct omap_hwmod *oh) { + int ret; + pr_debug("omap_hwmod: %s: idling\n", oh->name); if (oh->_state != _HWMOD_STATE_ENABLED) { @@ -1422,6 +1454,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_warn("omap_hwmod: %s: _wait_target_disable failed\n", + oh->name); /* Mux pins for device idle if populated */ if (oh->mux && oh->mux->pads_dynamic) @@ -1514,6 +1550,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_warn("omap_hwmod: %s: _wait_target_disable failed\n", + oh->name); } /* XXX Should this code also force-disable the optional clocks? */ -- cgit v1.2.3 From eaac329dfa6d3a4025242bf34d33aa3cb9df9f9f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Sun, 10 Jul 2011 05:56:31 -0600 Subject: OMAP4: hwmod: Replace RSTCTRL absolute address with offset macros The RSTCTRL register was accessed using an absolute address. The usage of hardcoded macros to calculate virtual address from physical one should be avoided as much as possible. The usage of an offset will allow future improvement like migration from the current architecture code toward a module driver. Update prm_xxx accessors, move definition to the proper header file and update copyrights. Change the s16 register offset parameter to u16. Signed-off-by: Benoit Cousson Cc: Paul Walmsley Cc: Rajendra Nayak [paul@pwsan.com: use '_prminst_' in function names that are part of the prminst44xx.c file] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index d21f49b87646..a0f7d313e69f 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -149,6 +149,7 @@ #include "cminst44xx.h" #include "prm2xxx_3xxx.h" #include "prm44xx.h" +#include "prminst44xx.h" #include "mux.h" /* Maximum microseconds to wait for OMAP module to softreset */ @@ -1187,8 +1188,10 @@ static int _assert_hardreset(struct omap_hwmod *oh, const char *name) return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs, ohri.rst_shift); else if (cpu_is_omap44xx()) - return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg, - ohri.rst_shift); + return omap4_prminst_assert_hardreset(ohri.rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); else return -EINVAL; } @@ -1223,8 +1226,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) if (ohri.st_shift) pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", oh->name, name); - ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg, - ohri.rst_shift); + ret = omap4_prminst_deassert_hardreset(ohri.rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); } else { return -EINVAL; } @@ -1259,8 +1264,10 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs, ohri.st_shift); } else if (cpu_is_omap44xx()) { - return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg, - ohri.rst_shift); + return omap4_prminst_is_hardreset_asserted(ohri.rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); } else { return -EINVAL; } -- cgit v1.2.3 From 45c38252d76a96e6e0e05f982ca44096191a8eea Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Sun, 10 Jul 2011 05:56:33 -0600 Subject: OMAP4: hwmod: Introduce the module control in hwmod control Take advantage of the explicit modulemode control to fix the way parents clocks are managed. A module must be disabled before any parents are disabled. That programming model was not possible with the previous implementation that was considering a modulemode as a leaf clock node managed by the clock fmwk. This was leading to bad crash upon disable when the parent clock was gated before the module completed its transition to idle. Signed-off-by: Benoit Cousson Cc: Paul Walmsley Cc: Rajendra Nayak Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 63 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index a0f7d313e69f..4424fee5cd5a 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -679,6 +679,56 @@ static void _disable_optional_clocks(struct omap_hwmod *oh) } } +/** + * _enable_module - enable CLKCTRL modulemode on OMAP4 + * @oh: struct omap_hwmod * + * + * Enables the PRCM module mode related to the hwmod @oh. + * No return value. + */ +static void _enable_module(struct omap_hwmod *oh) +{ + /* The module mode does not exist prior OMAP4 */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return; + + if (!oh->clkdm || !oh->prcm.omap4.modulemode) + return; + + pr_debug("omap_hwmod: %s: _enable_module: %d\n", + oh->name, oh->prcm.omap4.modulemode); + + omap4_cminst_module_enable(oh->prcm.omap4.modulemode, + oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->clkdm->clkdm_offs, + oh->prcm.omap4.clkctrl_offs); +} + +/** + * _disable_module - enable CLKCTRL modulemode on OMAP4 + * @oh: struct omap_hwmod * + * + * Disable the PRCM module mode related to the hwmod @oh. + * No return value. + */ +static void _disable_module(struct omap_hwmod *oh) +{ + /* The module mode does not exist prior OMAP4 */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return; + + if (!oh->clkdm || !oh->prcm.omap4.modulemode) + return; + + pr_debug("omap_hwmod: %s: _disable_module\n", oh->name); + + omap4_cminst_module_disable(oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->clkdm->clkdm_offs, + oh->prcm.omap4.clkctrl_offs); +} + /** * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh * @oh: struct omap_hwmod *oh @@ -1424,6 +1474,7 @@ static int _enable(struct omap_hwmod *oh) return r; } + _enable_module(oh); oh->_state = _HWMOD_STATE_ENABLED; @@ -1460,11 +1511,18 @@ static int _idle(struct omap_hwmod *oh) if (oh->class->sysc) _idle_sysc(oh); _del_initiator_dep(oh, mpu_oh); - _disable_clocks(oh); + _disable_module(oh); ret = _wait_target_disable(oh); if (ret) pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", oh->name); + /* + * The module must be in idle mode before disabling any parents + * clocks. Otherwise, the parent clock might be disabled before + * the module transition is done, and thus will prevent the + * transition to complete properly. + */ + _disable_clocks(oh); /* Mux pins for device idle if populated */ if (oh->mux && oh->mux->pads_dynamic) @@ -1556,11 +1614,12 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->_state == _HWMOD_STATE_ENABLED) { _del_initiator_dep(oh, mpu_oh); /* XXX what about the other system initiators here? dma, dsp */ - _disable_clocks(oh); + _disable_module(oh); ret = _wait_target_disable(oh); if (ret) pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", oh->name); + _disable_clocks(oh); } /* XXX Should this code also force-disable the optional clocks? */ -- cgit v1.2.3 From 665d001338b494d6d62810aa99b4c0fa1a0884b9 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Sun, 10 Jul 2011 05:57:07 -0600 Subject: OMAP2+: hwmod: Follow the recommended PRCM module enable sequence On OMAP4, the PRCM recommended sequence for enabling a module after power-on-reset is: -1- Force clkdm to SW_WKUP -2- Enabling the clocks -3- Configure desired module mode to "enable" or "auto" -4- Wait for the desired module idle status to be FUNC -5- Program clkdm in HW_AUTO(if supported) This sequence applies to all older OMAPs' as well, however since they use autodeps, it makes sure that no clkdm is in IDLE, and hence not requiring a force SW_WKUP when a module is being enabled. OMAP4 does not need to support autodeps, because of the dyanamic dependency feature, wherein the HW takes care of waking up a clockdomain from idle and hence the module, whenever an interconnect access happens to the given module. Implementing the sequence for OMAP4 requires the clockdomain handling that is currently done in clock framework to be done as part of hwmod framework since the step -4- above to "Wait for the desired module idle status to be FUNC" is done as part of hwmod framework. Signed-off-by: Rajendra Nayak [b-cousson@ti.com: Adapt it to the new clkdm hwmod attribute and API] Signed-off-by: Benoit Cousson Cc: Paul Walmsley [paul@pwsan.com: dropped mach-omap2/clock.c changes; modified to only call the clockdomain code if oh->clkdm is set; disable clock->clockdomain interaction on OMAP4] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 70 ++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 20 deletions(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 4424fee5cd5a..84cc0bdda3ae 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1437,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh) static int _enable(struct omap_hwmod *oh) { int r; + int hwsup = 0; pr_debug("omap_hwmod: %s: enabling\n", oh->name); @@ -1448,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh) return -EINVAL; } - /* Mux pins for device runtime if populated */ - if (oh->mux && (!oh->mux->enabled || - ((oh->_state == _HWMOD_STATE_IDLE) && - oh->mux->pads_dynamic))) - omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED); - - _add_initiator_dep(oh, mpu_oh); - _enable_clocks(oh); /* * If an IP contains only one HW reset line, then de-assert it in order @@ -1466,23 +1459,56 @@ static int _enable(struct omap_hwmod *oh) oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) _deassert_hardreset(oh, oh->rst_lines[0].name); - r = _wait_target_ready(oh); - if (r) { - pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", - oh->name, r); - _disable_clocks(oh); + /* Mux pins for device runtime if populated */ + if (oh->mux && (!oh->mux->enabled || + ((oh->_state == _HWMOD_STATE_IDLE) && + oh->mux->pads_dynamic))) + omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED); + + _add_initiator_dep(oh, mpu_oh); - return r; + if (oh->clkdm) { + /* + * A clockdomain must be in SW_SUP before enabling + * completely the module. The clockdomain can be set + * in HW_AUTO only when the module become ready. + */ + hwsup = clkdm_in_hwsup(oh->clkdm); + r = clkdm_hwmod_enable(oh->clkdm, oh); + if (r) { + WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", + oh->name, oh->clkdm->name, r); + return r; + } } + + _enable_clocks(oh); _enable_module(oh); - oh->_state = _HWMOD_STATE_ENABLED; + r = _wait_target_ready(oh); + if (!r) { + /* + * Set the clockdomain to HW_AUTO only if the target is ready, + * assuming that the previous state was HW_AUTO + */ + if (oh->clkdm && hwsup) + clkdm_allow_idle(oh->clkdm); - /* Access the sysconfig only if the target is ready */ - if (oh->class->sysc) { - if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) - _update_sysc_cache(oh); - _enable_sysc(oh); + oh->_state = _HWMOD_STATE_ENABLED; + + /* Access the sysconfig only if the target is ready */ + if (oh->class->sysc) { + if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) + _update_sysc_cache(oh); + _enable_sysc(oh); + } + } else { + _disable_clocks(oh); + pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", + oh->name, r); + + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); } return r; @@ -1523,6 +1549,8 @@ static int _idle(struct omap_hwmod *oh) * transition to complete properly. */ _disable_clocks(oh); + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); /* Mux pins for device idle if populated */ if (oh->mux && oh->mux->pads_dynamic) @@ -1620,6 +1648,8 @@ static int _shutdown(struct omap_hwmod *oh) pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", oh->name); _disable_clocks(oh); + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); } /* XXX Should this code also force-disable the optional clocks? */ -- cgit v1.2.3