From 724019b0137acf2ea43e5ca854798851f5ebf51f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 1 Jul 2011 22:54:00 +0200 Subject: OMAP2+: hwmod: Fix smart-standby + wakeup support The commit 86009eb326afde34ffdc5648cd344aa86b8d58d4 was adding the wakeup support for new OMAP4 IPs. This support is incomplete for busmaster IPs that need as well to use smart-standby with wakeup. This new standbymode is suported on HSI and USB_HOST_FS for the moment. Add the new MSTANDBY_SMART_WKUP flag to mark the IPs that support this capability. Enable this new mode when applicable in _enable_wakeup, _disable_wakeup, _enable_sysc and _idle_sysc. The omap_hwmod_44xx_data.c will have to be updated to add this new flag. Signed-off-by: Benoit Cousson Signed-off-by: Djamil Elaidi Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 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 293fa6cd50e1..384d3c3ec36d 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -391,7 +391,8 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) if (!oh->class->sysc || !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || - (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP))) + (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || + (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP))) return -EINVAL; if (!oh->class->sysc->sysc_fields) { @@ -405,6 +406,8 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); /* XXX test pwrdm_get_wken for this hwmod's subsystem */ @@ -426,7 +429,8 @@ static int _disable_wakeup(struct omap_hwmod *oh, u32 *v) if (!oh->class->sysc || !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || - (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP))) + (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || + (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP))) return -EINVAL; if (!oh->class->sysc->sysc_fields) { @@ -440,6 +444,8 @@ static int _disable_wakeup(struct omap_hwmod *oh, u32 *v) if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART, v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); /* XXX test pwrdm_get_wken for this hwmod's subsystem */ @@ -781,8 +787,16 @@ static void _enable_sysc(struct omap_hwmod *oh) } if (sf & SYSC_HAS_MIDLEMODE) { - idlemode = (oh->flags & HWMOD_SWSUP_MSTANDBY) ? - HWMOD_IDLEMODE_NO : HWMOD_IDLEMODE_SMART; + if (oh->flags & HWMOD_SWSUP_MSTANDBY) { + idlemode = HWMOD_IDLEMODE_NO; + } else { + if (sf & SYSC_HAS_ENAWAKEUP) + _enable_wakeup(oh, &v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + } _set_master_standbymode(oh, idlemode, &v); } @@ -840,8 +854,16 @@ static void _idle_sysc(struct omap_hwmod *oh) } if (sf & SYSC_HAS_MIDLEMODE) { - idlemode = (oh->flags & HWMOD_SWSUP_MSTANDBY) ? - HWMOD_IDLEMODE_FORCE : HWMOD_IDLEMODE_SMART; + if (oh->flags & HWMOD_SWSUP_MSTANDBY) { + idlemode = HWMOD_IDLEMODE_FORCE; + } else { + if (sf & SYSC_HAS_ENAWAKEUP) + _enable_wakeup(oh, &v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + } _set_master_standbymode(oh, idlemode, &v); } -- cgit From 6481c73c22613660a5b791d2b4d0faf60508d731 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Fri, 1 Jul 2011 22:54:02 +0200 Subject: OMAP2+: hwmod: Enable module in shutdown to access sysconfig When calling the shutdown, the module may be already in idle. Accessing the sysconfig register will then lead to a crash. In that case, re-enable the module in order to allow the access to the sysconfig register. Signed-off-by: Miguel Vadillo Signed-off-by: Benoit Cousson Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 5 ++++- 1 file changed, 4 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 384d3c3ec36d..cbc2a8a4ce39 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1396,8 +1396,11 @@ static int _shutdown(struct omap_hwmod *oh) } } - if (oh->class->sysc) + if (oh->class->sysc) { + if (oh->_state == _HWMOD_STATE_IDLE) + _enable(oh); _shutdown_sysc(oh); + } /* * If an IP contains only one HW reset line, then assert it -- cgit From 1fe741139be5acfe3758b53cdbf0b5e3d26db3fe Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 1 Jul 2011 22:54:03 +0200 Subject: OMAP2+: hwmod: Do not write the enawakeup bit if SYSC_HAS_ENAWAKEUP is not set The Type 2 type of IPs will not have any enawakeup bit in their sysconfig. Writing to that bit will instead trigger a softreset. Check the flag to write this bit only if the module supports it. Reported-by: Miguel Vadillo Signed-off-by: Benoit Cousson Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 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 cbc2a8a4ce39..3800084a1e7a 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -387,8 +387,6 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, */ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) { - u32 wakeup_mask; - if (!oh->class->sysc || !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || @@ -400,9 +398,8 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) return -EINVAL; } - wakeup_mask = (0x1 << oh->class->sysc->sysc_fields->enwkup_shift); - - *v |= wakeup_mask; + if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) + *v |= 0x1 << oh->class->sysc->sysc_fields->enwkup_shift; if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); @@ -425,8 +422,6 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) */ static int _disable_wakeup(struct omap_hwmod *oh, u32 *v) { - u32 wakeup_mask; - if (!oh->class->sysc || !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || @@ -438,9 +433,8 @@ static int _disable_wakeup(struct omap_hwmod *oh, u32 *v) return -EINVAL; } - wakeup_mask = (0x1 << oh->class->sysc->sysc_fields->enwkup_shift); - - *v &= ~wakeup_mask; + if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) + *v &= ~(0x1 << oh->class->sysc->sysc_fields->enwkup_shift); if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART, v); -- cgit From d24bcaa3fa711f7dd9c4aacf3c58083cf666418f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 1 Jul 2011 22:54:04 +0200 Subject: OMAP2+: hwmod: Remove _populate_mpu_rt_base warning It is perfectly valid for some hwmod to not have any register target address for sysconfig. This is especially true for interconnect hwmods. Remove the warning. Signed-off-by: Benoit Cousson Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 3 --- 1 file changed, 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 3800084a1e7a..f4014179d601 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1704,9 +1704,6 @@ static int __init _populate_mpu_rt_base(struct omap_hwmod *oh, void *data) return 0; oh->_mpu_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index); - if (!oh->_mpu_rt_va) - pr_warning("omap_hwmod: %s found no _mpu_rt_va for %s\n", - __func__, oh->name); return 0; } -- cgit From 31f62866c578b3d47ef7810b336e9e193b90167f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 1 Jul 2011 22:54:05 +0200 Subject: OMAP2+: hwmod: Fix the HW reset management The HW reset must be de-assert after the clocks are enabled but before waiting for the target to be ready. Otherwise the reset might not work properly since the clock is not running to proceed the reset. De-assert the reset after _enable_clocks and before _wait_target_ready. Re-assert it only when the clocks are disabled. Signed-off-by: Benoit Cousson Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 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 f4014179d601..df91bb1dc972 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1250,15 +1250,6 @@ static int _enable(struct omap_hwmod *oh) pr_debug("omap_hwmod: %s: enabling\n", oh->name); - /* - * If an IP contains only one HW reset line, then de-assert it in order - * to allow to enable the clocks. Otherwise the PRCM will return - * Intransition status, and the init will failed. - */ - if ((oh->_state == _HWMOD_STATE_INITIALIZED || - oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) - _deassert_hardreset(oh, oh->rst_lines[0].name); - /* Mux pins for device runtime if populated */ if (oh->mux && (!oh->mux->enabled || ((oh->_state == _HWMOD_STATE_IDLE) && @@ -1268,6 +1259,15 @@ static int _enable(struct omap_hwmod *oh) _add_initiator_dep(oh, mpu_oh); _enable_clocks(oh); + /* + * If an IP contains only one HW reset line, then de-assert it in order + * to allow the module state transition. Otherwise the PRCM will return + * Intransition status, and the init will failed. + */ + if ((oh->_state == _HWMOD_STATE_INITIALIZED || + 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) { oh->_state = _HWMOD_STATE_ENABLED; @@ -1396,13 +1396,6 @@ static int _shutdown(struct omap_hwmod *oh) _shutdown_sysc(oh); } - /* - * If an IP contains only one HW reset line, then assert it - * before disabling the clocks and shutting down the IP. - */ - if (oh->rst_lines_cnt == 1) - _assert_hardreset(oh, oh->rst_lines[0].name); - /* clocks and deps are already disabled in idle */ if (oh->_state == _HWMOD_STATE_ENABLED) { _del_initiator_dep(oh, mpu_oh); @@ -1411,6 +1404,13 @@ static int _shutdown(struct omap_hwmod *oh) } /* XXX Should this code also force-disable the optional clocks? */ + /* + * If an IP contains only one HW reset line, then assert it + * after disabling the clocks and before shutting down the IP. + */ + if (oh->rst_lines_cnt == 1) + _assert_hardreset(oh, oh->rst_lines[0].name); + /* Mux pins to safe mode or use populated off mode values */ if (oh->mux) omap_hwmod_mux(oh->mux, _HWMOD_STATE_DISABLED); -- cgit From 6652271a2556c086c04658dce16de2947e849ffd Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 1 Jul 2011 22:54:06 +0200 Subject: OMAP: hwmod: Add warnings if enable failed Change the debug into warning to check what IPs are failing. Signed-off-by: Benoit Cousson Cc: Rajendra Nayak Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 2 ++ 1 file changed, 2 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 df91bb1dc972..64e983046217 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -944,6 +944,8 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) if (!ret) oh->_state = _HWMOD_STATE_CLKS_INITED; + else + pr_warning("omap_hwmod: %s: cannot _init_clocks\n", oh->name); return ret; } -- cgit From 34617e2a4d331fdd8172077d8c70a0421fc136e6 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 1 Jul 2011 22:54:07 +0200 Subject: OMAP: hwmod: Move pr_debug to improve the readability Move the pr_debug at the top of the function to trace the entry even if the first test is failing. That help understanding that we entered the function but failed in it. Move the _enable last part out of the test to reduce indentation and improve readability. Signed-off-by: Benoit Cousson Cc: Paul Walmsley Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 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 64e983046217..e530bcbdebf4 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1242,6 +1242,8 @@ static int _enable(struct omap_hwmod *oh) { int r; + pr_debug("omap_hwmod: %s: enabling\n", oh->name); + if (oh->_state != _HWMOD_STATE_INITIALIZED && oh->_state != _HWMOD_STATE_IDLE && oh->_state != _HWMOD_STATE_DISABLED) { @@ -1250,8 +1252,6 @@ static int _enable(struct omap_hwmod *oh) return -EINVAL; } - pr_debug("omap_hwmod: %s: enabling\n", oh->name); - /* Mux pins for device runtime if populated */ if (oh->mux && (!oh->mux->enabled || ((oh->_state == _HWMOD_STATE_IDLE) && @@ -1271,19 +1271,21 @@ static int _enable(struct omap_hwmod *oh) _deassert_hardreset(oh, oh->rst_lines[0].name); r = _wait_target_ready(oh); - if (!r) { - 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); + if (r) { pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", oh->name, r); + _disable_clocks(oh); + + return r; + } + + 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); } return r; @@ -1299,14 +1301,14 @@ static int _enable(struct omap_hwmod *oh) */ static int _idle(struct omap_hwmod *oh) { + pr_debug("omap_hwmod: %s: idling\n", oh->name); + if (oh->_state != _HWMOD_STATE_ENABLED) { WARN(1, "omap_hwmod: %s: idle state can only be entered from " "enabled state\n", oh->name); return -EINVAL; } - pr_debug("omap_hwmod: %s: idling\n", oh->name); - if (oh->class->sysc) _idle_sysc(oh); _del_initiator_dep(oh, mpu_oh); -- cgit From 78183f3fdf76f422431a81852468be01b36db325 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 9 Jul 2011 19:14:05 -0600 Subject: omap_hwmod: use a null structure record to terminate omap_hwmod_addr_space arrays Previously, struct omap_hwmod_addr_space arrays were unterminated; and users of these arrays used the ARRAY_SIZE() macro to determine the length of the array. However, ARRAY_SIZE() only works when the array is in the same scope as the macro user. So far this hasn't been a problem. However, to reduce duplicated data, a subsequent patch will move common data to a separate, shared file. When this is done, ARRAY_SIZE() will no longer be usable. This patch removes ARRAY_SIZE() usage for struct omap_hwmod_addr_space arrays and uses a null structure member as the array terminator instead. Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 45 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 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 293fa6cd50e1..77094d75367f 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -2,6 +2,7 @@ * omap_hwmod implementation for OMAP2/3/4 * * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. * * Paul Walmsley, BenoƮt Cousson, Kevin Hilman * @@ -677,6 +678,29 @@ static void _disable_optional_clocks(struct omap_hwmod *oh) } } +/** + * _count_ocp_if_addr_spaces - count the number of address space entries for @oh + * @oh: struct omap_hwmod *oh + * + * Count and return the number of address space ranges associated with + * the hwmod @oh. Used to allocate struct resource data. Returns 0 + * if @oh is NULL. + */ +static int _count_ocp_if_addr_spaces(struct omap_hwmod_ocp_if *os) +{ + struct omap_hwmod_addr_space *mem; + int i = 0; + + if (!os || !os->addr) + return 0; + + do { + mem = &os->addr[i++]; + } while (mem->pa_start != mem->pa_end); + + return i; +} + /** * _find_mpu_port_index - find hwmod OCP slave port ID intended for MPU use * @oh: struct omap_hwmod * @@ -722,8 +746,7 @@ static void __iomem * __init _find_mpu_rt_base(struct omap_hwmod *oh, u8 index) { struct omap_hwmod_ocp_if *os; struct omap_hwmod_addr_space *mem; - int i; - int found = 0; + int i = 0, found = 0; void __iomem *va_start; if (!oh || oh->slaves_cnt == 0) @@ -731,12 +754,14 @@ static void __iomem * __init _find_mpu_rt_base(struct omap_hwmod *oh, u8 index) os = oh->slaves[index]; - for (i = 0, mem = os->addr; i < os->addr_cnt; i++, mem++) { - if (mem->flags & ADDR_TYPE_RT) { + if (!os->addr) + return NULL; + + do { + mem = &os->addr[i++]; + if (mem->flags & ADDR_TYPE_RT) found = 1; - break; - } - } + } while (!found && mem->pa_start != mem->pa_end); if (found) { va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start); @@ -1942,7 +1967,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh) ret = oh->mpu_irqs_cnt + oh->sdma_reqs_cnt; for (i = 0; i < oh->slaves_cnt; i++) - ret += oh->slaves[i]->addr_cnt; + ret += _count_ocp_if_addr_spaces(oh->slaves[i]); return ret; } @@ -1982,10 +2007,12 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) for (i = 0; i < oh->slaves_cnt; i++) { struct omap_hwmod_ocp_if *os; + int addr_cnt; os = oh->slaves[i]; + addr_cnt = _count_ocp_if_addr_spaces(os); - for (j = 0; j < os->addr_cnt; j++) { + for (j = 0; j < addr_cnt; j++) { (res + r)->name = (os->addr + j)->name; (res + r)->start = (os->addr + j)->pa_start; (res + r)->end = (os->addr + j)->pa_end; -- cgit From 212738a4499d278254ed6fdb400e3b4be4cb1de2 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 9 Jul 2011 19:14:06 -0600 Subject: omap_hwmod: use a terminator record with omap_hwmod_mpu_irqs arrays Previously, struct omap_hwmod_mpu_irqs arrays were unterminated; and users of these arrays used the ARRAY_SIZE() macro to determine the length of the array. However, ARRAY_SIZE() only works when the array is in the same scope as the macro user. So far this hasn't been a problem. However, to reduce duplicated data, a subsequent patch will move common data to a separate, shared file. When this is done, ARRAY_SIZE() will no longer be usable. This patch removes ARRAY_SIZE() usage for struct omap_hwmod_mpu_irqs arrays and uses a sentinel value (irq == -1) as the array terminator instead. Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 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 77094d75367f..21e3eb8e83c1 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -678,6 +678,29 @@ static void _disable_optional_clocks(struct omap_hwmod *oh) } } +/** + * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh + * @oh: struct omap_hwmod *oh + * + * Count and return the number of MPU IRQs associated with the hwmod + * @oh. Used to allocate struct resource data. Returns 0 if @oh is + * NULL. + */ +static int _count_mpu_irqs(struct omap_hwmod *oh) +{ + struct omap_hwmod_irq_info *ohii; + int i = 0; + + if (!oh || !oh->mpu_irqs) + return 0; + + do { + ohii = &oh->mpu_irqs[i++]; + } while (ohii->irq != -1); + + return i; +} + /** * _count_ocp_if_addr_spaces - count the number of address space entries for @oh * @oh: struct omap_hwmod *oh @@ -1964,7 +1987,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh) { int ret, i; - ret = oh->mpu_irqs_cnt + oh->sdma_reqs_cnt; + ret = _count_mpu_irqs(oh) + oh->sdma_reqs_cnt; for (i = 0; i < oh->slaves_cnt; i++) ret += _count_ocp_if_addr_spaces(oh->slaves[i]); @@ -1984,12 +2007,13 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh) */ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) { - int i, j; + int i, j, mpu_irqs_cnt; int r = 0; /* For each IRQ, DMA, memory area, fill in array.*/ - for (i = 0; i < oh->mpu_irqs_cnt; i++) { + mpu_irqs_cnt = _count_mpu_irqs(oh); + for (i = 0; i < mpu_irqs_cnt; i++) { (res + r)->name = (oh->mpu_irqs + i)->name; (res + r)->start = (oh->mpu_irqs + i)->irq; (res + r)->end = (oh->mpu_irqs + i)->irq; -- cgit From bc6149587b309e3231e5ac7138b84197813e17ec Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 9 Jul 2011 19:14:07 -0600 Subject: omap_hwmod: use a terminator record with omap_hwmod_dma_info arrays Previously, struct omap_hwmod_dma_info arrays were unterminated; and users of these arrays used the ARRAY_SIZE() macro to determine the length of the array. However, ARRAY_SIZE() only works when the array is in the same scope as the macro user. So far this hasn't been a problem. However, to reduce duplicated data, a subsequent patch will move common data to a separate, shared file. When this is done, ARRAY_SIZE() will no longer be usable. This patch removes ARRAY_SIZE() usage for struct omap_hwmod_dma_info arrays and uses a sentinel value (irq == -1) as the array terminator instead. Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 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 21e3eb8e83c1..d1a8bdefea3f 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -701,6 +701,29 @@ static int _count_mpu_irqs(struct omap_hwmod *oh) return i; } +/** + * _count_sdma_reqs - count the number of SDMA request lines associated with @oh + * @oh: struct omap_hwmod *oh + * + * Count and return the number of SDMA request lines associated with + * the hwmod @oh. Used to allocate struct resource data. Returns 0 + * if @oh is NULL. + */ +static int _count_sdma_reqs(struct omap_hwmod *oh) +{ + struct omap_hwmod_dma_info *ohdi; + int i = 0; + + if (!oh || !oh->sdma_reqs) + return 0; + + do { + ohdi = &oh->sdma_reqs[i++]; + } while (ohdi->dma_req != -1); + + return i; +} + /** * _count_ocp_if_addr_spaces - count the number of address space entries for @oh * @oh: struct omap_hwmod *oh @@ -1987,7 +2010,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh) { int ret, i; - ret = _count_mpu_irqs(oh) + oh->sdma_reqs_cnt; + ret = _count_mpu_irqs(oh) + _count_sdma_reqs(oh); for (i = 0; i < oh->slaves_cnt; i++) ret += _count_ocp_if_addr_spaces(oh->slaves[i]); @@ -2007,7 +2030,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh) */ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) { - int i, j, mpu_irqs_cnt; + int i, j, mpu_irqs_cnt, sdma_reqs_cnt; int r = 0; /* For each IRQ, DMA, memory area, fill in array.*/ @@ -2021,7 +2044,8 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) r++; } - for (i = 0; i < oh->sdma_reqs_cnt; i++) { + sdma_reqs_cnt = _count_sdma_reqs(oh); + for (i = 0; i < sdma_reqs_cnt; i++) { (res + r)->name = (oh->sdma_reqs + i)->name; (res + r)->start = (oh->sdma_reqs + i)->dma_req; (res + r)->end = (oh->sdma_reqs + i)->dma_req; -- cgit From 6d3c55fd4f0f94a9455d30df9414ddb0f755f402 Mon Sep 17 00:00:00 2001 From: "Avinash.H.M" Date: Sun, 10 Jul 2011 05:27:16 -0600 Subject: OMAP: hwmod: fix the i2c-reset timeout during bootup The sequence of _ocp_softreset doesn't work for i2c. The i2c module has a special sequence to reset the module. The sequence is - Disable the I2C. - Write to SOFTRESET bit. - Enable the I2C. - Poll on the RESETDONE bit. The sequence is implemented as a function and the i2c_class is updated with the correct 'reset' pointer. omap_hwmod_softreset function is implemented which triggers the softreset by writing into sysconfig register. On following this sequence, i2c module resets properly and timeouts are not seen. Cc: Rajendra Nayak Cc: Paul Walmsley Cc: Benoit Cousson Cc: Kevin Hilman Signed-off-by: Avinash.H.M [paul@pwsan.com: combined this patch with a patch to remove HWMOD_INIT_NO_RESET from the 44xx hwmod flags; change register offset conditional code to use the IP block revision; minor code cleanup] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 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 7d242c9e2a2c..02b6016393a8 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1655,6 +1655,33 @@ void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs) __raw_writel(v, oh->_mpu_rt_va + reg_offs); } +/** + * omap_hwmod_softreset - reset a module via SYSCONFIG.SOFTRESET bit + * @oh: struct omap_hwmod * + * + * This is a public function exposed to drivers. Some drivers may need to do + * some settings before and after resetting the device. Those drivers after + * doing the necessary settings could use this function to start a reset by + * setting the SYSCONFIG.SOFTRESET bit. + */ +int omap_hwmod_softreset(struct omap_hwmod *oh) +{ + u32 v; + int ret; + + if (!oh || !(oh->_sysc_cache)) + return -EINVAL; + + v = oh->_sysc_cache; + ret = _set_softreset(oh, &v); + if (ret) + goto error; + _write_sysconfig(v, oh); + +error: + return ret; +} + /** * omap_hwmod_set_slave_idlemode - set the hwmod's OCP slave idlemode * @oh: struct omap_hwmod * -- cgit 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 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 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 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 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 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