Commit 9af2ebbd authored by Tony Lindgren's avatar Tony Lindgren

Merge branch 'hwmod_2.6.37' of git://git.pwsan.com/linux-2.6 into omap-for-linus

parents 493c32a0 74ff3a68
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# Common support # Common support
obj-y := id.o io.o control.o mux.o devices.o serial.o gpmc.o timer-gp.o pm.o obj-y := id.o io.o control.o mux.o devices.o serial.o gpmc.o timer-gp.o pm.o
omap-2-3-common = irq.o sdrc.o omap-2-3-common = irq.o sdrc.o prm2xxx_3xxx.o
hwmod-common = omap_hwmod.o \ hwmod-common = omap_hwmod.o \
omap_hwmod_common_data.o omap_hwmod_common_data.o
prcm-common = prcm.o powerdomain.o prcm-common = prcm.o powerdomain.o
...@@ -15,7 +15,7 @@ clock-common = clock.o clock_common_data.o \ ...@@ -15,7 +15,7 @@ clock-common = clock.o clock_common_data.o \
obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(prcm-common) $(hwmod-common) obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(prcm-common) $(hwmod-common)
obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(prcm-common) $(hwmod-common) obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(prcm-common) $(hwmod-common)
obj-$(CONFIG_ARCH_OMAP4) += $(prcm-common) $(hwmod-common) obj-$(CONFIG_ARCH_OMAP4) += $(prcm-common) prm44xx.o $(hwmod-common)
obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
......
...@@ -13,10 +13,102 @@ ...@@ -13,10 +13,102 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
* *
* This code manages "OMAP modules" (on-chip devices) and their * Introduction
* integration with Linux device driver and bus code. * ------------
* * One way to view an OMAP SoC is as a collection of largely unrelated
* References: * IP blocks connected by interconnects. The IP blocks include
* devices such as ARM processors, audio serial interfaces, UARTs,
* etc. Some of these devices, like the DSP, are created by TI;
* others, like the SGX, largely originate from external vendors. In
* TI's documentation, on-chip devices are referred to as "OMAP
* modules." Some of these IP blocks are identical across several
* OMAP versions. Others are revised frequently.
*
* These OMAP modules are tied together by various interconnects.
* Most of the address and data flow between modules is via OCP-based
* interconnects such as the L3 and L4 buses; but there are other
* interconnects that distribute the hardware clock tree, handle idle
* and reset signaling, supply power, and connect the modules to
* various pads or balls on the OMAP package.
*
* OMAP hwmod provides a consistent way to describe the on-chip
* hardware blocks and their integration into the rest of the chip.
* This description can be automatically generated from the TI
* hardware database. OMAP hwmod provides a standard, consistent API
* to reset, enable, idle, and disable these hardware blocks. And
* hwmod provides a way for other core code, such as the Linux device
* code or the OMAP power management and address space mapping code,
* to query the hardware database.
*
* Using hwmod
* -----------
* Drivers won't call hwmod functions directly. That is done by the
* omap_device code, and in rare occasions, by custom integration code
* in arch/arm/ *omap*. The omap_device code includes functions to
* build a struct platform_device using omap_hwmod data, and that is
* currently how hwmod data is communicated to drivers and to the
* Linux driver model. Most drivers will call omap_hwmod functions only
* indirectly, via pm_runtime*() functions.
*
* From a layering perspective, here is where the OMAP hwmod code
* fits into the kernel software stack:
*
* +-------------------------------+
* | Device driver code |
* | (e.g., drivers/) |
* +-------------------------------+
* | Linux driver model |
* | (platform_device / |
* | platform_driver data/code) |
* +-------------------------------+
* | OMAP core-driver integration |
* |(arch/arm/mach-omap2/devices.c)|
* +-------------------------------+
* | omap_device code |
* | (../plat-omap/omap_device.c) |
* +-------------------------------+
* ----> | omap_hwmod code/data | <-----
* | (../mach-omap2/omap_hwmod*) |
* +-------------------------------+
* | OMAP clock/PRCM/register fns |
* | (__raw_{read,write}l, clk*) |
* +-------------------------------+
*
* Device drivers should not contain any OMAP-specific code or data in
* them. They should only contain code to operate the IP block that
* the driver is responsible for. This is because these IP blocks can
* also appear in other SoCs, either from TI (such as DaVinci) or from
* other manufacturers; and drivers should be reusable across other
* platforms.
*
* The OMAP hwmod code also will attempt to reset and idle all on-chip
* devices upon boot. The goal here is for the kernel to be
* completely self-reliant and independent from bootloaders. This is
* to ensure a repeatable configuration, both to ensure consistent
* runtime behavior, and to make it easier for others to reproduce
* bugs.
*
* OMAP module activity states
* ---------------------------
* The hwmod code considers modules to be in one of several activity
* states. IP blocks start out in an UNKNOWN state, then once they
* are registered via the hwmod code, proceed to the REGISTERED state.
* Once their clock names are resolved to clock pointers, the module
* enters the CLKS_INITED state; and finally, once the module has been
* reset and the integration registers programmed, the INITIALIZED state
* is entered. The hwmod code will then place the module into either
* the IDLE state to save power, or in the case of a critical system
* module, the ENABLED state.
*
* OMAP core integration code can then call omap_hwmod*() functions
* directly to move the module between the IDLE, ENABLED, and DISABLED
* states, as needed. This is done during both the PM idle loop, and
* in the OMAP core integration code's implementation of the PM runtime
* functions.
*
* References
* ----------
* This is a partial list.
* - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064) * - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064)
* - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090) * - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090)
* - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108) * - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108)
...@@ -50,11 +142,13 @@ ...@@ -50,11 +142,13 @@
#include <plat/powerdomain.h> #include <plat/powerdomain.h>
#include <plat/clock.h> #include <plat/clock.h>
#include <plat/omap_hwmod.h> #include <plat/omap_hwmod.h>
#include <plat/prcm.h>
#include "cm.h" #include "cm.h"
#include "prm.h"
/* Maximum microseconds to wait for OMAP module to reset */ /* Maximum microseconds to wait for OMAP module to softreset */
#define MAX_MODULE_RESET_WAIT 10000 #define MAX_MODULE_SOFTRESET_WAIT 10000
/* Name of the OMAP hwmod for the MPU */ /* Name of the OMAP hwmod for the MPU */
#define MPU_INITIATOR_NAME "mpu" #define MPU_INITIATOR_NAME "mpu"
...@@ -544,6 +638,36 @@ static int _disable_clocks(struct omap_hwmod *oh) ...@@ -544,6 +638,36 @@ static int _disable_clocks(struct omap_hwmod *oh)
return 0; return 0;
} }
static void _enable_optional_clocks(struct omap_hwmod *oh)
{
struct omap_hwmod_opt_clk *oc;
int i;
pr_debug("omap_hwmod: %s: enabling optional clocks\n", oh->name);
for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
if (oc->_clk) {
pr_debug("omap_hwmod: enable %s:%s\n", oc->role,
oc->_clk->name);
clk_enable(oc->_clk);
}
}
static void _disable_optional_clocks(struct omap_hwmod *oh)
{
struct omap_hwmod_opt_clk *oc;
int i;
pr_debug("omap_hwmod: %s: disabling optional clocks\n", oh->name);
for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
if (oc->_clk) {
pr_debug("omap_hwmod: disable %s:%s\n", oc->role,
oc->_clk->name);
clk_disable(oc->_clk);
}
}
/** /**
* _find_mpu_port_index - find hwmod OCP slave port ID intended for MPU use * _find_mpu_port_index - find hwmod OCP slave port ID intended for MPU use
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
...@@ -622,7 +746,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index) ...@@ -622,7 +746,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
} }
/** /**
* _sysc_enable - try to bring a module out of idle via OCP_SYSCONFIG * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* If module is marked as SWSUP_SIDLE, force the module out of slave * If module is marked as SWSUP_SIDLE, force the module out of slave
...@@ -630,7 +754,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index) ...@@ -630,7 +754,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
* as SWSUP_MSUSPEND, force the module out of master standby; * as SWSUP_MSUSPEND, force the module out of master standby;
* otherwise, configure it for smart-standby. No return value. * otherwise, configure it for smart-standby. No return value.
*/ */
static void _sysc_enable(struct omap_hwmod *oh) static void _enable_sysc(struct omap_hwmod *oh)
{ {
u8 idlemode, sf; u8 idlemode, sf;
u32 v; u32 v;
...@@ -659,8 +783,6 @@ static void _sysc_enable(struct omap_hwmod *oh) ...@@ -659,8 +783,6 @@ static void _sysc_enable(struct omap_hwmod *oh)
_set_module_autoidle(oh, idlemode, &v); _set_module_autoidle(oh, idlemode, &v);
} }
/* XXX OCP ENAWAKEUP bit? */
/* /*
* XXX The clock framework should handle this, by * XXX The clock framework should handle this, by
* calling into this code. But this must wait until the * calling into this code. But this must wait until the
...@@ -671,10 +793,14 @@ static void _sysc_enable(struct omap_hwmod *oh) ...@@ -671,10 +793,14 @@ static void _sysc_enable(struct omap_hwmod *oh)
_set_clockactivity(oh, oh->class->sysc->clockact, &v); _set_clockactivity(oh, oh->class->sysc->clockact, &v);
_write_sysconfig(v, oh); _write_sysconfig(v, oh);
/* If slave is in SMARTIDLE, also enable wakeup */
if ((sf & SYSC_HAS_SIDLEMODE) && !(oh->flags & HWMOD_SWSUP_SIDLE))
_enable_wakeup(oh);
} }
/** /**
* _sysc_idle - try to put a module into idle via OCP_SYSCONFIG * _idle_sysc - try to put a module into idle via OCP_SYSCONFIG
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* If module is marked as SWSUP_SIDLE, force the module into slave * If module is marked as SWSUP_SIDLE, force the module into slave
...@@ -682,7 +808,7 @@ static void _sysc_enable(struct omap_hwmod *oh) ...@@ -682,7 +808,7 @@ static void _sysc_enable(struct omap_hwmod *oh)
* as SWSUP_MSUSPEND, force the module into master standby; otherwise, * as SWSUP_MSUSPEND, force the module into master standby; otherwise,
* configure it for smart-standby. No return value. * configure it for smart-standby. No return value.
*/ */
static void _sysc_idle(struct omap_hwmod *oh) static void _idle_sysc(struct omap_hwmod *oh)
{ {
u8 idlemode, sf; u8 idlemode, sf;
u32 v; u32 v;
...@@ -709,13 +835,13 @@ static void _sysc_idle(struct omap_hwmod *oh) ...@@ -709,13 +835,13 @@ static void _sysc_idle(struct omap_hwmod *oh)
} }
/** /**
* _sysc_shutdown - force a module into idle via OCP_SYSCONFIG * _shutdown_sysc - force a module into idle via OCP_SYSCONFIG
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Force the module into slave idle and master suspend. No return * Force the module into slave idle and master suspend. No return
* value. * value.
*/ */
static void _sysc_shutdown(struct omap_hwmod *oh) static void _shutdown_sysc(struct omap_hwmod *oh)
{ {
u32 v; u32 v;
u8 sf; u8 sf;
...@@ -767,10 +893,10 @@ static struct omap_hwmod *_lookup(const char *name) ...@@ -767,10 +893,10 @@ static struct omap_hwmod *_lookup(const char *name)
* @data: not used; pass NULL * @data: not used; pass NULL
* *
* Called by omap_hwmod_late_init() (after omap2_clk_init()). * Called by omap_hwmod_late_init() (after omap2_clk_init()).
* Resolves all clock names embedded in the hwmod. Must be called * Resolves all clock names embedded in the hwmod. Returns -EINVAL if
* with omap_hwmod_mutex held. Returns -EINVAL if the omap_hwmod * the omap_hwmod has not yet been registered or if the clocks have
* has not yet been registered or if the clocks have already been * already been initialized, 0 on success, or a non-zero error on
* initialized, 0 on success, or a non-zero error on failure. * failure.
*/ */
static int _init_clocks(struct omap_hwmod *oh, void *data) static int _init_clocks(struct omap_hwmod *oh, void *data)
{ {
...@@ -833,57 +959,203 @@ static int _wait_target_ready(struct omap_hwmod *oh) ...@@ -833,57 +959,203 @@ static int _wait_target_ready(struct omap_hwmod *oh)
return ret; return ret;
} }
/**
* _lookup_hardreset - return the register bit shift for this hwmod/reset line
* @oh: struct omap_hwmod *
* @name: name of the reset line in the context of this hwmod
*
* Return the bit position of the reset line that match the
* input name. Return -ENOENT if not found.
*/
static u8 _lookup_hardreset(struct omap_hwmod *oh, const char *name)
{
int i;
for (i = 0; i < oh->rst_lines_cnt; i++) {
const char *rst_line = oh->rst_lines[i].name;
if (!strcmp(rst_line, name)) {
u8 shift = oh->rst_lines[i].rst_shift;
pr_debug("omap_hwmod: %s: _lookup_hardreset: %s: %d\n",
oh->name, rst_line, shift);
return shift;
}
}
return -ENOENT;
}
/**
* _assert_hardreset - assert the HW reset line of submodules
* contained in the hwmod module.
* @oh: struct omap_hwmod *
* @name: name of the reset line to lookup and assert
*
* Some IP like dsp, ipu or iva contain processor that require
* an HW reset line to be assert / deassert in order to enable fully
* the IP.
*/
static int _assert_hardreset(struct omap_hwmod *oh, const char *name)
{
u8 shift;
if (!oh)
return -EINVAL;
shift = _lookup_hardreset(oh, name);
if (IS_ERR_VALUE(shift))
return shift;
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs,
shift);
else if (cpu_is_omap44xx())
return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg,
shift);
else
return -EINVAL;
}
/**
* _deassert_hardreset - deassert the HW reset line of submodules contained
* in the hwmod module.
* @oh: struct omap_hwmod *
* @name: name of the reset line to look up and deassert
*
* Some IP like dsp, ipu or iva contain processor that require
* an HW reset line to be assert / deassert in order to enable fully
* the IP.
*/
static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
{
u8 shift;
int r;
if (!oh)
return -EINVAL;
shift = _lookup_hardreset(oh, name);
if (IS_ERR_VALUE(shift))
return shift;
if (cpu_is_omap24xx() || cpu_is_omap34xx())
r = omap2_prm_deassert_hardreset(oh->prcm.omap2.module_offs,
shift);
else if (cpu_is_omap44xx())
r = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg,
shift);
else
return -EINVAL;
if (r == -EBUSY)
pr_warning("omap_hwmod: %s: failed to hardreset\n", oh->name);
return r;
}
/**
* _read_hardreset - read the HW reset line state of submodules
* contained in the hwmod module
* @oh: struct omap_hwmod *
* @name: name of the reset line to look up and read
*
* Return the state of the reset line.
*/
static int _read_hardreset(struct omap_hwmod *oh, const char *name)
{
u8 shift;
if (!oh)
return -EINVAL;
shift = _lookup_hardreset(oh, name);
if (IS_ERR_VALUE(shift))
return shift;
if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs,
shift);
} else if (cpu_is_omap44xx()) {
return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg,
shift);
} else {
return -EINVAL;
}
}
/** /**
* _reset - reset an omap_hwmod * _reset - reset an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit. hwmod must be * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit. hwmod must be
* enabled for this to work. Must be called with omap_hwmod_mutex * enabled for this to work. Returns -EINVAL if the hwmod cannot be
* held. Returns -EINVAL if the hwmod cannot be reset this way or if * reset this way or if the hwmod is in the wrong state, -ETIMEDOUT if
* the hwmod is in the wrong state, -ETIMEDOUT if the module did not * the module did not reset in time, or 0 upon success.
* reset in time, or 0 upon success. *
* In OMAP3 a specific SYSSTATUS register is used to get the reset status.
* Starting in OMAP4, some IPs does not have SYSSTATUS register and instead
* use the SYSCONFIG softreset bit to provide the status.
*
* Note that some IP like McBSP does have a reset control but no reset status.
*/ */
static int _reset(struct omap_hwmod *oh) static int _reset(struct omap_hwmod *oh)
{ {
u32 r, v; u32 v;
int c = 0; int c = 0;
int ret = 0;
if (!oh->class->sysc || if (!oh->class->sysc ||
!(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET) || !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
(oh->class->sysc->sysc_flags & SYSS_MISSING))
return -EINVAL; return -EINVAL;
/* clocks must be on for this operation */ /* clocks must be on for this operation */
if (oh->_state != _HWMOD_STATE_ENABLED) { if (oh->_state != _HWMOD_STATE_ENABLED) {
WARN(1, "omap_hwmod: %s: reset can only be entered from " pr_warning("omap_hwmod: %s: reset can only be entered from "
"enabled state\n", oh->name); "enabled state\n", oh->name);
return -EINVAL; return -EINVAL;
} }
/* For some modules, all optionnal clocks need to be enabled as well */
if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
_enable_optional_clocks(oh);
pr_debug("omap_hwmod: %s: resetting\n", oh->name); pr_debug("omap_hwmod: %s: resetting\n", oh->name);
v = oh->_sysc_cache; v = oh->_sysc_cache;
r = _set_softreset(oh, &v); ret = _set_softreset(oh, &v);
if (r) if (ret)
return r; goto dis_opt_clks;
_write_sysconfig(v, oh); _write_sysconfig(v, oh);
omap_test_timeout((omap_hwmod_readl(oh, oh->class->sysc->syss_offs) & if (oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS)
SYSS_RESETDONE_MASK), omap_test_timeout((omap_hwmod_readl(oh,
MAX_MODULE_RESET_WAIT, c); oh->class->sysc->syss_offs)
& SYSS_RESETDONE_MASK),
if (c == MAX_MODULE_RESET_WAIT) MAX_MODULE_SOFTRESET_WAIT, c);
WARN(1, "omap_hwmod: %s: failed to reset in %d usec\n", else if (oh->class->sysc->sysc_flags & SYSC_HAS_RESET_STATUS)
oh->name, MAX_MODULE_RESET_WAIT); omap_test_timeout(!(omap_hwmod_readl(oh,
oh->class->sysc->sysc_offs)
& SYSC_TYPE2_SOFTRESET_MASK),
MAX_MODULE_SOFTRESET_WAIT, c);
if (c == MAX_MODULE_SOFTRESET_WAIT)
pr_warning("omap_hwmod: %s: softreset failed (waited %d usec)\n",
oh->name, MAX_MODULE_SOFTRESET_WAIT);
else else
pr_debug("omap_hwmod: %s: reset in %d usec\n", oh->name, c); pr_debug("omap_hwmod: %s: softreset in %d usec\n", oh->name, c);
/* /*
* XXX add _HWMOD_STATE_WEDGED for modules that don't come back from * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from
* _wait_target_ready() or _reset() * _wait_target_ready() or _reset()
*/ */
return (c == MAX_MODULE_RESET_WAIT) ? -ETIMEDOUT : 0; ret = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0;
dis_opt_clks:
if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
_disable_optional_clocks(oh);
return ret;
} }
/** /**
...@@ -891,9 +1163,11 @@ static int _reset(struct omap_hwmod *oh) ...@@ -891,9 +1163,11 @@ static int _reset(struct omap_hwmod *oh)
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Enables an omap_hwmod @oh such that the MPU can access the hwmod's * Enables an omap_hwmod @oh such that the MPU can access the hwmod's
* register target. Must be called with omap_hwmod_mutex held. * register target. (This function has a full name --
* Returns -EINVAL if the hwmod is in the wrong state or passes along * _omap_hwmod_enable() rather than simply _enable() -- because it is
* the return value of _wait_target_ready(). * currently required by the pm34xx.c idle loop.) Returns -EINVAL if
* the hwmod is in the wrong state or passes along the return value of
* _wait_target_ready().
*/ */
int _omap_hwmod_enable(struct omap_hwmod *oh) int _omap_hwmod_enable(struct omap_hwmod *oh)
{ {
...@@ -909,6 +1183,15 @@ int _omap_hwmod_enable(struct omap_hwmod *oh) ...@@ -909,6 +1183,15 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
pr_debug("omap_hwmod: %s: enabling\n", oh->name); 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);
/* XXX mux balls */ /* XXX mux balls */
_add_initiator_dep(oh, mpu_oh); _add_initiator_dep(oh, mpu_oh);
...@@ -922,7 +1205,7 @@ int _omap_hwmod_enable(struct omap_hwmod *oh) ...@@ -922,7 +1205,7 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
if (oh->class->sysc) { if (oh->class->sysc) {
if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
_update_sysc_cache(oh); _update_sysc_cache(oh);
_sysc_enable(oh); _enable_sysc(oh);
} }
} else { } else {
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
...@@ -933,12 +1216,14 @@ int _omap_hwmod_enable(struct omap_hwmod *oh) ...@@ -933,12 +1216,14 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
} }
/** /**
* _idle - idle an omap_hwmod * _omap_hwmod_idle - idle an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Idles an omap_hwmod @oh. This should be called once the hwmod has * Idles an omap_hwmod @oh. This should be called once the hwmod has
* no further work. Returns -EINVAL if the hwmod is in the wrong * no further work. (This function has a full name --
* state or returns 0. * _omap_hwmod_idle() rather than simply _idle() -- because it is
* currently required by the pm34xx.c idle loop.) Returns -EINVAL if
* the hwmod is in the wrong state or returns 0.
*/ */
int _omap_hwmod_idle(struct omap_hwmod *oh) int _omap_hwmod_idle(struct omap_hwmod *oh)
{ {
...@@ -951,7 +1236,7 @@ int _omap_hwmod_idle(struct omap_hwmod *oh) ...@@ -951,7 +1236,7 @@ int _omap_hwmod_idle(struct omap_hwmod *oh)
pr_debug("omap_hwmod: %s: idling\n", oh->name); pr_debug("omap_hwmod: %s: idling\n", oh->name);
if (oh->class->sysc) if (oh->class->sysc)
_sysc_idle(oh); _idle_sysc(oh);
_del_initiator_dep(oh, mpu_oh); _del_initiator_dep(oh, mpu_oh);
_disable_clocks(oh); _disable_clocks(oh);
...@@ -981,10 +1266,21 @@ static int _shutdown(struct omap_hwmod *oh) ...@@ -981,10 +1266,21 @@ static int _shutdown(struct omap_hwmod *oh)
pr_debug("omap_hwmod: %s: disabling\n", oh->name); pr_debug("omap_hwmod: %s: disabling\n", oh->name);
if (oh->class->sysc) if (oh->class->sysc)
_sysc_shutdown(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); _del_initiator_dep(oh, mpu_oh);
/* XXX what about the other system initiators here? DMA, tesla, d2d */ /* XXX what about the other system initiators here? dma, dsp */
_disable_clocks(oh); _disable_clocks(oh);
}
/* XXX Should this code also force-disable the optional clocks? */ /* XXX Should this code also force-disable the optional clocks? */
/* XXX mux any associated balls to safe mode */ /* XXX mux any associated balls to safe mode */
...@@ -1000,11 +1296,10 @@ static int _shutdown(struct omap_hwmod *oh) ...@@ -1000,11 +1296,10 @@ static int _shutdown(struct omap_hwmod *oh)
* @skip_setup_idle_p: do not idle hwmods at the end of the fn if 1 * @skip_setup_idle_p: do not idle hwmods at the end of the fn if 1
* *
* Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh * Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh
* OCP_SYSCONFIG register. Must be called with omap_hwmod_mutex held. * OCP_SYSCONFIG register. @skip_setup_idle is intended to be used on
* @skip_setup_idle is intended to be used on a system that will not * a system that will not call omap_hwmod_enable() to enable devices
* call omap_hwmod_enable() to enable devices (e.g., a system without * (e.g., a system without PM runtime). Returns -EINVAL if the hwmod
* PM runtime). Returns -EINVAL if the hwmod is in the wrong state or * is in the wrong state or returns 0.
* returns 0.
*/ */
static int _setup(struct omap_hwmod *oh, void *data) static int _setup(struct omap_hwmod *oh, void *data)
{ {
...@@ -1034,8 +1329,19 @@ static int _setup(struct omap_hwmod *oh, void *data) ...@@ -1034,8 +1329,19 @@ static int _setup(struct omap_hwmod *oh, void *data)
} }
} }
mutex_init(&oh->_mutex);
oh->_state = _HWMOD_STATE_INITIALIZED; oh->_state = _HWMOD_STATE_INITIALIZED;
/*
* In the case of hwmod with hardreset that should not be
* de-assert at boot time, we have to keep the module
* initialized, because we cannot enable it properly with the
* reset asserted. Exit without warning because that behavior is
* expected.
*/
if ((oh->flags & HWMOD_INIT_NO_RESET) && oh->rst_lines_cnt == 1)
return 0;
r = _omap_hwmod_enable(oh); r = _omap_hwmod_enable(oh);
if (r) { if (r) {
pr_warning("omap_hwmod: %s: cannot be enabled (%d)\n", pr_warning("omap_hwmod: %s: cannot be enabled (%d)\n",
...@@ -1044,16 +1350,16 @@ static int _setup(struct omap_hwmod *oh, void *data) ...@@ -1044,16 +1350,16 @@ static int _setup(struct omap_hwmod *oh, void *data)
} }
if (!(oh->flags & HWMOD_INIT_NO_RESET)) { if (!(oh->flags & HWMOD_INIT_NO_RESET)) {
_reset(oh);
/* /*
* XXX Do the OCP_SYSCONFIG bits need to be * OCP_SYSCONFIG bits need to be reprogrammed after a softreset.
* reprogrammed after a reset? If not, then this can * The _omap_hwmod_enable() function should be split to
* be removed. If they do, then probably the * avoid the rewrite of the OCP_SYSCONFIG register.
* _omap_hwmod_enable() function should be split to avoid the
* rewrite of the OCP_SYSCONFIG register.
*/ */
if (oh->class->sysc) { if (oh->class->sysc) {
_update_sysc_cache(oh); _update_sysc_cache(oh);
_sysc_enable(oh); _enable_sysc(oh);
} }
} }
...@@ -1309,7 +1615,7 @@ int omap_hwmod_unregister(struct omap_hwmod *oh) ...@@ -1309,7 +1615,7 @@ int omap_hwmod_unregister(struct omap_hwmod *oh)
* omap_hwmod_enable - enable an omap_hwmod * omap_hwmod_enable - enable an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Enable an omap_hwomd @oh. Intended to be called by omap_device_enable(). * Enable an omap_hwmod @oh. Intended to be called by omap_device_enable().
* Returns -EINVAL on error or passes along the return value from _enable(). * Returns -EINVAL on error or passes along the return value from _enable().
*/ */
int omap_hwmod_enable(struct omap_hwmod *oh) int omap_hwmod_enable(struct omap_hwmod *oh)
...@@ -1319,9 +1625,9 @@ int omap_hwmod_enable(struct omap_hwmod *oh) ...@@ -1319,9 +1625,9 @@ int omap_hwmod_enable(struct omap_hwmod *oh)
if (!oh) if (!oh)
return -EINVAL; return -EINVAL;
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
r = _omap_hwmod_enable(oh); r = _omap_hwmod_enable(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return r; return r;
} }
...@@ -1331,7 +1637,7 @@ int omap_hwmod_enable(struct omap_hwmod *oh) ...@@ -1331,7 +1637,7 @@ int omap_hwmod_enable(struct omap_hwmod *oh)
* omap_hwmod_idle - idle an omap_hwmod * omap_hwmod_idle - idle an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Idle an omap_hwomd @oh. Intended to be called by omap_device_idle(). * Idle an omap_hwmod @oh. Intended to be called by omap_device_idle().
* Returns -EINVAL on error or passes along the return value from _idle(). * Returns -EINVAL on error or passes along the return value from _idle().
*/ */
int omap_hwmod_idle(struct omap_hwmod *oh) int omap_hwmod_idle(struct omap_hwmod *oh)
...@@ -1339,9 +1645,9 @@ int omap_hwmod_idle(struct omap_hwmod *oh) ...@@ -1339,9 +1645,9 @@ int omap_hwmod_idle(struct omap_hwmod *oh)
if (!oh) if (!oh)
return -EINVAL; return -EINVAL;
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
_omap_hwmod_idle(oh); _omap_hwmod_idle(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return 0; return 0;
} }
...@@ -1350,7 +1656,7 @@ int omap_hwmod_idle(struct omap_hwmod *oh) ...@@ -1350,7 +1656,7 @@ int omap_hwmod_idle(struct omap_hwmod *oh)
* omap_hwmod_shutdown - shutdown an omap_hwmod * omap_hwmod_shutdown - shutdown an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Shutdown an omap_hwomd @oh. Intended to be called by * Shutdown an omap_hwmod @oh. Intended to be called by
* omap_device_shutdown(). Returns -EINVAL on error or passes along * omap_device_shutdown(). Returns -EINVAL on error or passes along
* the return value from _shutdown(). * the return value from _shutdown().
*/ */
...@@ -1359,9 +1665,9 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh) ...@@ -1359,9 +1665,9 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh)
if (!oh) if (!oh)
return -EINVAL; return -EINVAL;
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
_shutdown(oh); _shutdown(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return 0; return 0;
} }
...@@ -1374,9 +1680,9 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh) ...@@ -1374,9 +1680,9 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh)
*/ */
int omap_hwmod_enable_clocks(struct omap_hwmod *oh) int omap_hwmod_enable_clocks(struct omap_hwmod *oh)
{ {
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
_enable_clocks(oh); _enable_clocks(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return 0; return 0;
} }
...@@ -1389,9 +1695,9 @@ int omap_hwmod_enable_clocks(struct omap_hwmod *oh) ...@@ -1389,9 +1695,9 @@ int omap_hwmod_enable_clocks(struct omap_hwmod *oh)
*/ */
int omap_hwmod_disable_clocks(struct omap_hwmod *oh) int omap_hwmod_disable_clocks(struct omap_hwmod *oh)
{ {
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
_disable_clocks(oh); _disable_clocks(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return 0; return 0;
} }
...@@ -1430,20 +1736,18 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh) ...@@ -1430,20 +1736,18 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh)
* *
* Under some conditions, a driver may wish to reset the entire device. * Under some conditions, a driver may wish to reset the entire device.
* Called from omap_device code. Returns -EINVAL on error or passes along * Called from omap_device code. Returns -EINVAL on error or passes along
* the return value from _reset()/_enable(). * the return value from _reset().
*/ */
int omap_hwmod_reset(struct omap_hwmod *oh) int omap_hwmod_reset(struct omap_hwmod *oh)
{ {
int r; int r;
if (!oh || !(oh->_state & _HWMOD_STATE_ENABLED)) if (!oh)
return -EINVAL; return -EINVAL;
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
r = _reset(oh); r = _reset(oh);
if (!r) mutex_unlock(&oh->_mutex);
r = _omap_hwmod_enable(oh);
mutex_unlock(&omap_hwmod_mutex);
return r; return r;
} }
...@@ -1468,7 +1772,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh) ...@@ -1468,7 +1772,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh)
{ {
int ret, i; int ret, i;
ret = oh->mpu_irqs_cnt + oh->sdma_chs_cnt; ret = oh->mpu_irqs_cnt + oh->sdma_reqs_cnt;
for (i = 0; i < oh->slaves_cnt; i++) for (i = 0; i < oh->slaves_cnt; i++)
ret += oh->slaves[i]->addr_cnt; ret += oh->slaves[i]->addr_cnt;
...@@ -1501,10 +1805,10 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) ...@@ -1501,10 +1805,10 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)
r++; r++;
} }
for (i = 0; i < oh->sdma_chs_cnt; i++) { for (i = 0; i < oh->sdma_reqs_cnt; i++) {
(res + r)->name = (oh->sdma_chs + i)->name; (res + r)->name = (oh->sdma_reqs + i)->name;
(res + r)->start = (oh->sdma_chs + i)->dma_ch; (res + r)->start = (oh->sdma_reqs + i)->dma_req;
(res + r)->end = (oh->sdma_chs + i)->dma_ch; (res + r)->end = (oh->sdma_reqs + i)->dma_req;
(res + r)->flags = IORESOURCE_DMA; (res + r)->flags = IORESOURCE_DMA;
r++; r++;
} }
...@@ -1644,9 +1948,9 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) ...@@ -1644,9 +1948,9 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
!(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
return -EINVAL; return -EINVAL;
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
_enable_wakeup(oh); _enable_wakeup(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return 0; return 0;
} }
...@@ -1669,13 +1973,91 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) ...@@ -1669,13 +1973,91 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh)
!(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
return -EINVAL; return -EINVAL;
mutex_lock(&omap_hwmod_mutex); mutex_lock(&oh->_mutex);
_disable_wakeup(oh); _disable_wakeup(oh);
mutex_unlock(&omap_hwmod_mutex); mutex_unlock(&oh->_mutex);
return 0; return 0;
} }
/**
* omap_hwmod_assert_hardreset - assert the HW reset line of submodules
* contained in the hwmod module.
* @oh: struct omap_hwmod *
* @name: name of the reset line to lookup and assert
*
* Some IP like dsp, ipu or iva contain processor that require
* an HW reset line to be assert / deassert in order to enable fully
* the IP. Returns -EINVAL if @oh is null or if the operation is not
* yet supported on this OMAP; otherwise, passes along the return value
* from _assert_hardreset().
*/
int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name)
{
int ret;
if (!oh)
return -EINVAL;
mutex_lock(&oh->_mutex);
ret = _assert_hardreset(oh, name);
mutex_unlock(&oh->_mutex);
return ret;
}
/**
* omap_hwmod_deassert_hardreset - deassert the HW reset line of submodules
* contained in the hwmod module.
* @oh: struct omap_hwmod *
* @name: name of the reset line to look up and deassert
*
* Some IP like dsp, ipu or iva contain processor that require
* an HW reset line to be assert / deassert in order to enable fully
* the IP. Returns -EINVAL if @oh is null or if the operation is not
* yet supported on this OMAP; otherwise, passes along the return value
* from _deassert_hardreset().
*/
int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name)
{
int ret;
if (!oh)
return -EINVAL;
mutex_lock(&oh->_mutex);
ret = _deassert_hardreset(oh, name);
mutex_unlock(&oh->_mutex);
return ret;
}
/**
* omap_hwmod_read_hardreset - read the HW reset line state of submodules
* contained in the hwmod module
* @oh: struct omap_hwmod *
* @name: name of the reset line to look up and read
*
* Return the current state of the hwmod @oh's reset line named @name:
* returns -EINVAL upon parameter error or if this operation
* is unsupported on the current OMAP; otherwise, passes along the return
* value from _read_hardreset().
*/
int omap_hwmod_read_hardreset(struct omap_hwmod *oh, const char *name)
{
int ret;
if (!oh)
return -EINVAL;
mutex_lock(&oh->_mutex);
ret = _read_hardreset(oh, name);
mutex_unlock(&oh->_mutex);
return ret;
}
/** /**
* omap_hwmod_for_each_by_class - call @fn for each hwmod of class @classname * omap_hwmod_for_each_by_class - call @fn for each hwmod of class @classname
* @classname: struct omap_hwmod_class name to search for * @classname: struct omap_hwmod_class name to search for
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "cm.h" #include "cm.h"
#include "prm.h" #include "prm.h"
#include "prm-regbits-24xx.h" #include "prm-regbits-24xx.h"
#include "prm-regbits-44xx.h"
static void __iomem *prm_base; static void __iomem *prm_base;
static void __iomem *cm_base; static void __iomem *cm_base;
...@@ -161,8 +162,8 @@ void omap_prcm_arch_reset(char mode, const char *cmd) ...@@ -161,8 +162,8 @@ void omap_prcm_arch_reset(char mode, const char *cmd)
prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, prcm_offs, prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, prcm_offs,
OMAP2_RM_RSTCTRL); OMAP2_RM_RSTCTRL);
if (cpu_is_omap44xx()) if (cpu_is_omap44xx())
prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, prcm_offs, prm_set_mod_reg_bits(OMAP4430_RST_GLOBAL_WARM_SW_MASK,
OMAP4_RM_RSTCTRL); prcm_offs, OMAP4_RM_RSTCTRL);
} }
static inline u32 __omap_prcm_read(void __iomem *base, s16 module, u16 reg) static inline u32 __omap_prcm_read(void __iomem *base, s16 module, u16 reg)
...@@ -215,6 +216,30 @@ u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask) ...@@ -215,6 +216,30 @@ u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask)
return v; return v;
} }
/* Read a PRM register, AND it, and shift the result down to bit 0 */
u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask)
{
u32 v;
v = __raw_readl(reg);
v &= mask;
v >>= __ffs(mask);
return v;
}
/* Read-modify-write a register in a PRM module. Caller must lock */
u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg)
{
u32 v;
v = __raw_readl(reg);
v &= ~mask;
v |= bits;
__raw_writel(v, reg);
return v;
}
/* Read a register in a CM module */ /* Read a register in a CM module */
u32 cm_read_mod_reg(s16 module, u16 idx) u32 cm_read_mod_reg(s16 module, u16 idx)
{ {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* OMAP2/3 Power/Reset Management (PRM) register definitions * OMAP2/3 Power/Reset Management (PRM) register definitions
* *
* Copyright (C) 2007-2009 Texas Instruments, Inc. * Copyright (C) 2007-2009 Texas Instruments, Inc.
* Copyright (C) 2009 Nokia Corporation * Copyright (C) 2010 Nokia Corporation
* *
* Written by Paul Walmsley * Written by Paul Walmsley
* *
...@@ -246,6 +246,15 @@ static inline u32 prm_clear_mod_reg_bits(u32 bits, s16 module, s16 idx) ...@@ -246,6 +246,15 @@ static inline u32 prm_clear_mod_reg_bits(u32 bits, s16 module, s16 idx)
return prm_rmw_mod_reg_bits(bits, 0x0, module, idx); return prm_rmw_mod_reg_bits(bits, 0x0, module, idx);
} }
/* These omap2_ PRM functions apply to both OMAP2 and 3 */
int omap2_prm_is_hardreset_asserted(s16 prm_mod, u8 shift);
int omap2_prm_assert_hardreset(s16 prm_mod, u8 shift);
int omap2_prm_deassert_hardreset(s16 prm_mod, u8 shift);
int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift);
int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift);
int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift);
#endif #endif
/* /*
...@@ -398,4 +407,11 @@ static inline u32 prm_clear_mod_reg_bits(u32 bits, s16 module, s16 idx) ...@@ -398,4 +407,11 @@ static inline u32 prm_clear_mod_reg_bits(u32 bits, s16 module, s16 idx)
#define OMAP_POWERSTATE_MASK (0x3 << 0) #define OMAP_POWERSTATE_MASK (0x3 << 0)
/*
* MAX_MODULE_HARDRESET_WAIT: Maximum microseconds to wait for an OMAP
* submodule to exit hardreset
*/
#define MAX_MODULE_HARDRESET_WAIT 10000
#endif #endif
/*
* OMAP2/3 PRM module functions
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
* Benoît Cousson
* Paul Walmsley
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <plat/common.h>
#include <plat/cpu.h>
#include <plat/prcm.h>
#include "prm.h"
#include "prm-regbits-24xx.h"
#include "prm-regbits-34xx.h"
/**
* omap2_prm_is_hardreset_asserted - read the HW reset line state of
* submodules contained in the hwmod module
* @prm_mod: PRM submodule base (e.g. CORE_MOD)
* @shift: register bit shift corresponding to the reset line to check
*
* Returns 1 if the (sub)module hardreset line is currently asserted,
* 0 if the (sub)module hardreset line is not currently asserted, or
* -EINVAL if called while running on a non-OMAP2/3 chip.
*/
int omap2_prm_is_hardreset_asserted(s16 prm_mod, u8 shift)
{
if (!(cpu_is_omap24xx() || cpu_is_omap34xx()))
return -EINVAL;
return prm_read_mod_bits_shift(prm_mod, OMAP2_RM_RSTCTRL,
(1 << shift));
}
/**
* omap2_prm_assert_hardreset - assert the HW reset line of a submodule
* @prm_mod: PRM submodule base (e.g. CORE_MOD)
* @shift: register bit shift corresponding to the reset line to assert
*
* Some IPs like dsp or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* place the submodule into reset. Returns 0 upon success or -EINVAL
* upon an argument error.
*/
int omap2_prm_assert_hardreset(s16 prm_mod, u8 shift)
{
u32 mask;
if (!(cpu_is_omap24xx() || cpu_is_omap34xx()))
return -EINVAL;
mask = 1 << shift;
prm_rmw_mod_reg_bits(mask, mask, prm_mod, OMAP2_RM_RSTCTRL);
return 0;
}
/**
* omap2_prm_deassert_hardreset - deassert a submodule hardreset line and wait
* @prm_mod: PRM submodule base (e.g. CORE_MOD)
* @shift: register bit shift corresponding to the reset line to deassert
*
* Some IPs like dsp or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* take the submodule out of reset and wait until the PRCM indicates
* that the reset has completed before returning. Returns 0 upon success or
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
* of reset, or -EBUSY if the submodule did not exit reset promptly.
*/
int omap2_prm_deassert_hardreset(s16 prm_mod, u8 shift)
{
u32 mask;
int c;
if (!(cpu_is_omap24xx() || cpu_is_omap34xx()))
return -EINVAL;
mask = 1 << shift;
/* Check the current status to avoid de-asserting the line twice */
if (prm_read_mod_bits_shift(prm_mod, OMAP2_RM_RSTCTRL, mask) == 0)
return -EEXIST;
/* Clear the reset status by writing 1 to the status bit */
prm_rmw_mod_reg_bits(0xffffffff, mask, prm_mod, OMAP2_RM_RSTST);
/* de-assert the reset control line */
prm_rmw_mod_reg_bits(mask, 0, prm_mod, OMAP2_RM_RSTCTRL);
/* wait the status to be set */
omap_test_timeout(prm_read_mod_bits_shift(prm_mod, OMAP2_RM_RSTST,
mask),
MAX_MODULE_HARDRESET_WAIT, c);
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
/*
* OMAP4 PRM module functions
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation
* Benoît Cousson
* Paul Walmsley
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <plat/common.h>
#include <plat/cpu.h>
#include <plat/prcm.h>
#include "prm.h"
#include "prm-regbits-44xx.h"
/*
* Address offset (in bytes) between the reset control and the reset
* status registers: 4 bytes on OMAP4
*/
#define OMAP4_RST_CTRL_ST_OFFSET 4
/**
* omap4_prm_is_hardreset_asserted - read the HW reset line state of
* submodules contained in the hwmod module
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to check
*
* Returns 1 if the (sub)module hardreset line is currently asserted,
* 0 if the (sub)module hardreset line is not currently asserted, or
* -EINVAL upon parameter error.
*/
int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift)
{
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
return omap4_prm_read_bits_shift(rstctrl_reg, (1 << shift));
}
/**
* omap4_prm_assert_hardreset - assert the HW reset line of a submodule
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to assert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* place the submodule into reset. Returns 0 upon success or -EINVAL
* upon an argument error.
*/
int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift)
{
u32 mask;
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
mask = 1 << shift;
omap4_prm_rmw_reg_bits(mask, mask, rstctrl_reg);
return 0;
}
/**
* omap4_prm_deassert_hardreset - deassert a submodule hardreset line and wait
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to deassert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* take the submodule out of reset and wait until the PRCM indicates
* that the reset has completed before returning. Returns 0 upon success or
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
* of reset, or -EBUSY if the submodule did not exit reset promptly.
*/
int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift)
{
u32 mask;
void __iomem *rstst_reg;
int c;
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
rstst_reg = rstctrl_reg + OMAP4_RST_CTRL_ST_OFFSET;
mask = 1 << shift;
/* Check the current status to avoid de-asserting the line twice */
if (omap4_prm_read_bits_shift(rstctrl_reg, mask) == 0)
return -EEXIST;
/* Clear the reset status by writing 1 to the status bit */
omap4_prm_rmw_reg_bits(0xffffffff, mask, rstst_reg);
/* de-assert the reset control line */
omap4_prm_rmw_reg_bits(mask, 0, rstctrl_reg);
/* wait the status to be set */
omap_test_timeout(omap4_prm_read_bits_shift(rstst_reg, mask),
MAX_MODULE_HARDRESET_WAIT, c);
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
...@@ -14,19 +14,16 @@ ...@@ -14,19 +14,16 @@
* *
* These headers and macros are used to define OMAP on-chip module * These headers and macros are used to define OMAP on-chip module
* data and their integration with other OMAP modules and Linux. * data and their integration with other OMAP modules and Linux.
* * Copious documentation and references can also be found in the
* References: * omap_hwmod code, in arch/arm/mach-omap2/omap_hwmod.c (as of this
* - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064) * writing).
* - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090)
* - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108)
* - OMAP4430 Multimedia Device Silicon Revision 1.0 (SWPU140)
* - Open Core Protocol Specification 2.2
* *
* To do: * To do:
* - add interconnect error log structures * - add interconnect error log structures
* - add pinmuxing * - add pinmuxing
* - init_conn_id_bit (CONNID_BIT_VECTOR) * - init_conn_id_bit (CONNID_BIT_VECTOR)
* - implement default hwmod SMS/SDRC flags? * - implement default hwmod SMS/SDRC flags?
* - remove unused fields
* *
*/ */
#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_HWMOD_H #ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_HWMOD_H
...@@ -35,6 +32,7 @@ ...@@ -35,6 +32,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/mutex.h>
#include <plat/cpu.h> #include <plat/cpu.h>
struct omap_device; struct omap_device;
...@@ -96,7 +94,7 @@ struct omap_hwmod_irq_info { ...@@ -96,7 +94,7 @@ struct omap_hwmod_irq_info {
/** /**
* struct omap_hwmod_dma_info - DMA channels used by the hwmod * struct omap_hwmod_dma_info - DMA channels used by the hwmod
* @name: name of the DMA channel (module local name) * @name: name of the DMA channel (module local name)
* @dma_ch: DMA channel ID * @dma_req: DMA request ID
* *
* @name should be something short, e.g., "tx" or "rx". It is for use * @name should be something short, e.g., "tx" or "rx". It is for use
* by platform_get_resource_byname(). It is defined locally to the * by platform_get_resource_byname(). It is defined locally to the
...@@ -104,7 +102,20 @@ struct omap_hwmod_irq_info { ...@@ -104,7 +102,20 @@ struct omap_hwmod_irq_info {
*/ */
struct omap_hwmod_dma_info { struct omap_hwmod_dma_info {
const char *name; const char *name;
u16 dma_ch; u16 dma_req;
};
/**
* struct omap_hwmod_rst_info - IPs reset lines use by hwmod
* @name: name of the reset line (module local name)
* @rst_shift: Offset of the reset bit
*
* @name should be something short, e.g., "cpu0" or "rst". It is defined
* locally to the hwmod.
*/
struct omap_hwmod_rst_info {
const char *name;
u8 rst_shift;
}; };
/** /**
...@@ -237,8 +248,9 @@ struct omap_hwmod_ocp_if { ...@@ -237,8 +248,9 @@ struct omap_hwmod_ocp_if {
#define SYSC_HAS_CLOCKACTIVITY (1 << 4) #define SYSC_HAS_CLOCKACTIVITY (1 << 4)
#define SYSC_HAS_SIDLEMODE (1 << 5) #define SYSC_HAS_SIDLEMODE (1 << 5)
#define SYSC_HAS_MIDLEMODE (1 << 6) #define SYSC_HAS_MIDLEMODE (1 << 6)
#define SYSS_MISSING (1 << 7) #define SYSS_HAS_RESET_STATUS (1 << 7)
#define SYSC_NO_CACHE (1 << 8) /* XXX SW flag, belongs elsewhere */ #define SYSC_NO_CACHE (1 << 8) /* XXX SW flag, belongs elsewhere */
#define SYSC_HAS_RESET_STATUS (1 << 9)
/* omap_hwmod_sysconfig.clockact flags */ /* omap_hwmod_sysconfig.clockact flags */
#define CLOCKACT_TEST_BOTH 0x0 #define CLOCKACT_TEST_BOTH 0x0
...@@ -327,10 +339,12 @@ struct omap_hwmod_omap2_prcm { ...@@ -327,10 +339,12 @@ struct omap_hwmod_omap2_prcm {
/** /**
* struct omap_hwmod_omap4_prcm - OMAP4-specific PRCM data * struct omap_hwmod_omap4_prcm - OMAP4-specific PRCM data
* @clkctrl_reg: PRCM address of the clock control register * @clkctrl_reg: PRCM address of the clock control register
* @rstctrl_reg: adress of the XXX_RSTCTRL register located in the PRM
* @submodule_wkdep_bit: bit shift of the WKDEP range * @submodule_wkdep_bit: bit shift of the WKDEP range
*/ */
struct omap_hwmod_omap4_prcm { struct omap_hwmod_omap4_prcm {
void __iomem *clkctrl_reg; void __iomem *clkctrl_reg;
void __iomem *rstctrl_reg;
u8 submodule_wkdep_bit; u8 submodule_wkdep_bit;
}; };
...@@ -352,6 +366,10 @@ struct omap_hwmod_omap4_prcm { ...@@ -352,6 +366,10 @@ struct omap_hwmod_omap4_prcm {
* HWMOD_SET_DEFAULT_CLOCKACT: program CLOCKACTIVITY bits at startup * HWMOD_SET_DEFAULT_CLOCKACT: program CLOCKACTIVITY bits at startup
* HWMOD_NO_IDLEST : this module does not have idle status - this is the case * HWMOD_NO_IDLEST : this module does not have idle status - this is the case
* only for few initiator modules on OMAP2 & 3. * only for few initiator modules on OMAP2 & 3.
* HWMOD_CONTROL_OPT_CLKS_IN_RESET: Enable all optional clocks during reset.
* This is needed for devices like DSS that require optional clocks enabled
* in order to complete the reset. Optional clocks will be disabled
* again after the reset.
*/ */
#define HWMOD_SWSUP_SIDLE (1 << 0) #define HWMOD_SWSUP_SIDLE (1 << 0)
#define HWMOD_SWSUP_MSTANDBY (1 << 1) #define HWMOD_SWSUP_MSTANDBY (1 << 1)
...@@ -360,6 +378,7 @@ struct omap_hwmod_omap4_prcm { ...@@ -360,6 +378,7 @@ struct omap_hwmod_omap4_prcm {
#define HWMOD_NO_OCP_AUTOIDLE (1 << 4) #define HWMOD_NO_OCP_AUTOIDLE (1 << 4)
#define HWMOD_SET_DEFAULT_CLOCKACT (1 << 5) #define HWMOD_SET_DEFAULT_CLOCKACT (1 << 5)
#define HWMOD_NO_IDLEST (1 << 6) #define HWMOD_NO_IDLEST (1 << 6)
#define HWMOD_CONTROL_OPT_CLKS_IN_RESET (1 << 7)
/* /*
* omap_hwmod._int_flags definitions * omap_hwmod._int_flags definitions
...@@ -410,7 +429,7 @@ struct omap_hwmod_class { ...@@ -410,7 +429,7 @@ struct omap_hwmod_class {
* @class: struct omap_hwmod_class * to the class of this hwmod * @class: struct omap_hwmod_class * to the class of this hwmod
* @od: struct omap_device currently associated with this hwmod (internal use) * @od: struct omap_device currently associated with this hwmod (internal use)
* @mpu_irqs: ptr to an array of MPU IRQs (see also mpu_irqs_cnt) * @mpu_irqs: ptr to an array of MPU IRQs (see also mpu_irqs_cnt)
* @sdma_chs: ptr to an array of SDMA channel IDs (see also sdma_chs_cnt) * @sdma_reqs: ptr to an array of System DMA request IDs (see sdma_reqs_cnt)
* @prcm: PRCM data pertaining to this hwmod * @prcm: PRCM data pertaining to this hwmod
* @main_clk: main clock: OMAP clock name * @main_clk: main clock: OMAP clock name
* @_clk: pointer to the main struct clk (filled in at runtime) * @_clk: pointer to the main struct clk (filled in at runtime)
...@@ -424,7 +443,7 @@ struct omap_hwmod_class { ...@@ -424,7 +443,7 @@ struct omap_hwmod_class {
* @msuspendmux_reg_id: CONTROL_MSUSPENDMUX register ID (1-6) * @msuspendmux_reg_id: CONTROL_MSUSPENDMUX register ID (1-6)
* @msuspendmux_shift: CONTROL_MSUSPENDMUX register bit shift * @msuspendmux_shift: CONTROL_MSUSPENDMUX register bit shift
* @mpu_irqs_cnt: number of @mpu_irqs * @mpu_irqs_cnt: number of @mpu_irqs
* @sdma_chs_cnt: number of @sdma_chs * @sdma_reqs_cnt: number of @sdma_reqs
* @opt_clks_cnt: number of @opt_clks * @opt_clks_cnt: number of @opt_clks
* @master_cnt: number of @master entries * @master_cnt: number of @master entries
* @slaves_cnt: number of @slave entries * @slaves_cnt: number of @slave entries
...@@ -433,6 +452,7 @@ struct omap_hwmod_class { ...@@ -433,6 +452,7 @@ struct omap_hwmod_class {
* @_state: internal-use hwmod state * @_state: internal-use hwmod state
* @flags: hwmod flags (documented below) * @flags: hwmod flags (documented below)
* @omap_chip: OMAP chips this hwmod is present on * @omap_chip: OMAP chips this hwmod is present on
* @_mutex: mutex serializing operations on this hwmod
* @node: list node for hwmod list (internal use) * @node: list node for hwmod list (internal use)
* *
* @main_clk refers to this module's "main clock," which for our * @main_clk refers to this module's "main clock," which for our
...@@ -448,7 +468,8 @@ struct omap_hwmod { ...@@ -448,7 +468,8 @@ struct omap_hwmod {
struct omap_hwmod_class *class; struct omap_hwmod_class *class;
struct omap_device *od; struct omap_device *od;
struct omap_hwmod_irq_info *mpu_irqs; struct omap_hwmod_irq_info *mpu_irqs;
struct omap_hwmod_dma_info *sdma_chs; struct omap_hwmod_dma_info *sdma_reqs;
struct omap_hwmod_rst_info *rst_lines;
union { union {
struct omap_hwmod_omap2_prcm omap2; struct omap_hwmod_omap2_prcm omap2;
struct omap_hwmod_omap4_prcm omap4; struct omap_hwmod_omap4_prcm omap4;
...@@ -461,6 +482,7 @@ struct omap_hwmod { ...@@ -461,6 +482,7 @@ struct omap_hwmod {
void *dev_attr; void *dev_attr;
u32 _sysc_cache; u32 _sysc_cache;
void __iomem *_mpu_rt_va; void __iomem *_mpu_rt_va;
struct mutex _mutex;
struct list_head node; struct list_head node;
u16 flags; u16 flags;
u8 _mpu_port_index; u8 _mpu_port_index;
...@@ -468,7 +490,8 @@ struct omap_hwmod { ...@@ -468,7 +490,8 @@ struct omap_hwmod {
u8 msuspendmux_shift; u8 msuspendmux_shift;
u8 response_lat; u8 response_lat;
u8 mpu_irqs_cnt; u8 mpu_irqs_cnt;
u8 sdma_chs_cnt; u8 sdma_reqs_cnt;
u8 rst_lines_cnt;
u8 opt_clks_cnt; u8 opt_clks_cnt;
u8 masters_cnt; u8 masters_cnt;
u8 slaves_cnt; u8 slaves_cnt;
...@@ -492,6 +515,10 @@ int omap_hwmod_idle(struct omap_hwmod *oh); ...@@ -492,6 +515,10 @@ int omap_hwmod_idle(struct omap_hwmod *oh);
int _omap_hwmod_idle(struct omap_hwmod *oh); int _omap_hwmod_idle(struct omap_hwmod *oh);
int omap_hwmod_shutdown(struct omap_hwmod *oh); int omap_hwmod_shutdown(struct omap_hwmod *oh);
int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name);
int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name);
int omap_hwmod_read_hardreset(struct omap_hwmod *oh, const char *name);
int omap_hwmod_enable_clocks(struct omap_hwmod *oh); int omap_hwmod_enable_clocks(struct omap_hwmod *oh);
int omap_hwmod_disable_clocks(struct omap_hwmod *oh); int omap_hwmod_disable_clocks(struct omap_hwmod *oh);
......
...@@ -38,6 +38,8 @@ u32 prm_read_mod_reg(s16 module, u16 idx); ...@@ -38,6 +38,8 @@ u32 prm_read_mod_reg(s16 module, u16 idx);
void prm_write_mod_reg(u32 val, s16 module, u16 idx); void prm_write_mod_reg(u32 val, s16 module, u16 idx);
u32 prm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, s16 idx); u32 prm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, s16 idx);
u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask); u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask);
u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask);
u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg);
u32 cm_read_mod_reg(s16 module, u16 idx); u32 cm_read_mod_reg(s16 module, u16 idx);
void cm_write_mod_reg(u32 val, s16 module, u16 idx); void cm_write_mod_reg(u32 val, s16 module, u16 idx);
u32 cm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, s16 idx); u32 cm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, s16 idx);
......
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/clk.h>
#include <plat/omap_device.h> #include <plat/omap_device.h>
#include <plat/omap_hwmod.h> #include <plat/omap_hwmod.h>
...@@ -243,6 +244,44 @@ static inline struct omap_device *_find_by_pdev(struct platform_device *pdev) ...@@ -243,6 +244,44 @@ static inline struct omap_device *_find_by_pdev(struct platform_device *pdev)
return container_of(pdev, struct omap_device, pdev); return container_of(pdev, struct omap_device, pdev);
} }
/**
* _add_optional_clock_alias - Add clock alias for hwmod optional clocks
* @od: struct omap_device *od
*
* For every optional clock present per hwmod per omap_device, this function
* adds an entry in the clocks list of the form <dev-id=dev_name, con-id=role>
* if an entry is already present in it with the form <dev-id=NULL, con-id=role>
*
* The function is called from inside omap_device_build_ss(), after
* omap_device_register.
*
* This allows drivers to get a pointer to its optional clocks based on its role
* by calling clk_get(<dev*>, <role>).
*
* No return value.
*/
static void _add_optional_clock_alias(struct omap_device *od,
struct omap_hwmod *oh)
{
int i;
for (i = 0; i < oh->opt_clks_cnt; i++) {
struct omap_hwmod_opt_clk *oc;
int r;
oc = &oh->opt_clks[i];
if (!oc->_clk)
continue;
r = clk_add_alias(oc->role, dev_name(&od->pdev.dev),
(char *)oc->clk, &od->pdev.dev);
if (r)
pr_err("omap_device: %s: clk_add_alias for %s failed\n",
dev_name(&od->pdev.dev), oc->role);
}
}
/* Public functions for use by core code */ /* Public functions for use by core code */
...@@ -421,8 +460,10 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id, ...@@ -421,8 +460,10 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
else else
ret = omap_device_register(od); ret = omap_device_register(od);
for (i = 0; i < oh_cnt; i++) for (i = 0; i < oh_cnt; i++) {
hwmods[i]->od = od; hwmods[i]->od = od;
_add_optional_clock_alias(od, hwmods[i]);
}
if (ret) if (ret)
goto odbs_exit4; goto odbs_exit4;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment