Commit bd36179e authored by Paul Walmsley's avatar Paul Walmsley

OMAP2+: hwmod: add support for per-class custom device reset functions

The standard omap_hwmod.c _reset() code relies on an IP block's
OCP_SYSCONFIG.SOFTRESET register bit to reset the IP block.  This
works for most IP blocks on the chip, but unfortunately not all.  For
example, initiator-only IP blocks often don't have any MPU-accessible
OCP-header registers, and therefore the MPU can't write to any
OCP_SYSCONFIG registers in that block.  Other IP blocks, such as the
IVA and I2C, require a specialized reset sequence.

Since we need to be able to reset these IP blocks as well, allow
custom IP block reset functions to be passed into the hwmod code via a
per-hwmod-class reset function pointer, struct omap_hwmod_class.reset.
If .reset is non-null, then the hwmod _reset() code will call the custom
function instead of the standard OCP SOFTRESET-based code.

As part of this change, rename most of the existing _reset() function
code to _ocp_softreset(), to indicate more clearly that it does not work
for all cases.
Signed-off-by: default avatarPaul Walmsley <paul@pwsan.com>
Cc: Benoît Cousson <b-cousson@ti.com>
Cc: Paul Hunt <hunt@ti.com>
Cc: Stanley Liu <stanley_liu@ti.com>
parent 2092e5cc
...@@ -1089,7 +1089,7 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) ...@@ -1089,7 +1089,7 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
} }
/** /**
* _reset - reset an omap_hwmod * _ocp_softreset - reset an omap_hwmod via the OCP_SYSCONFIG bit
* @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
...@@ -1098,12 +1098,13 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) ...@@ -1098,12 +1098,13 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
* the module did not reset in time, or 0 upon success. * the module did not reset in time, or 0 upon success.
* *
* In OMAP3 a specific SYSSTATUS register is used to get the reset status. * 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 * Starting in OMAP4, some IPs do not have SYSSTATUS registers and instead
* use the SYSCONFIG softreset bit to provide the status. * use the SYSCONFIG softreset bit to provide the status.
* *
* Note that some IP like McBSP does have a reset control but no reset status. * Note that some IP like McBSP do have reset control but don't have
* reset status.
*/ */
static int _reset(struct omap_hwmod *oh) static int _ocp_softreset(struct omap_hwmod *oh)
{ {
u32 v; u32 v;
int c = 0; int c = 0;
...@@ -1124,7 +1125,7 @@ static int _reset(struct omap_hwmod *oh) ...@@ -1124,7 +1125,7 @@ static int _reset(struct omap_hwmod *oh)
if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
_enable_optional_clocks(oh); _enable_optional_clocks(oh);
pr_debug("omap_hwmod: %s: resetting\n", oh->name); pr_debug("omap_hwmod: %s: resetting via OCP SOFTRESET\n", oh->name);
v = oh->_sysc_cache; v = oh->_sysc_cache;
ret = _set_softreset(oh, &v); ret = _set_softreset(oh, &v);
...@@ -1163,6 +1164,33 @@ static int _reset(struct omap_hwmod *oh) ...@@ -1163,6 +1164,33 @@ static int _reset(struct omap_hwmod *oh)
return ret; return ret;
} }
/**
* _reset - reset an omap_hwmod
* @oh: struct omap_hwmod *
*
* Resets an omap_hwmod @oh. The default software reset mechanism for
* most OMAP IP blocks is triggered via the OCP_SYSCONFIG.SOFTRESET
* bit. However, some hwmods cannot be reset via this method: some
* are not targets and therefore have no OCP header registers to
* access; others (like the IVA) have idiosyncratic reset sequences.
* So for these relatively rare cases, custom reset code can be
* supplied in the struct omap_hwmod_class .reset function pointer.
* Passes along the return value from either _reset() or the custom
* reset function - these must return -EINVAL if the hwmod cannot be
* reset this way or if the hwmod is in the wrong state, -ETIMEDOUT if
* the module did not reset in time, or 0 upon success.
*/
static int _reset(struct omap_hwmod *oh)
{
int ret;
pr_debug("omap_hwmod: %s: resetting\n", oh->name);
ret = (oh->class->reset) ? oh->class->reset(oh) : _ocp_softreset(oh);
return ret;
}
/** /**
* _omap_hwmod_enable - enable an omap_hwmod * _omap_hwmod_enable - enable an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
......
...@@ -364,7 +364,7 @@ struct omap_hwmod_omap4_prcm { ...@@ -364,7 +364,7 @@ struct omap_hwmod_omap4_prcm {
* when module is enabled, rather than the default, which is to * when module is enabled, rather than the default, which is to
* enable autoidle * enable autoidle
* 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. * HWMOD_CONTROL_OPT_CLKS_IN_RESET: Enable all optional clocks during reset.
* This is needed for devices like DSS that require optional clocks enabled * This is needed for devices like DSS that require optional clocks enabled
...@@ -416,6 +416,7 @@ struct omap_hwmod_omap4_prcm { ...@@ -416,6 +416,7 @@ struct omap_hwmod_omap4_prcm {
* @sysc: device SYSCONFIG/SYSSTATUS register data * @sysc: device SYSCONFIG/SYSSTATUS register data
* @rev: revision of the IP class * @rev: revision of the IP class
* @pre_shutdown: ptr to fn to be executed immediately prior to device shutdown * @pre_shutdown: ptr to fn to be executed immediately prior to device shutdown
* @reset: ptr to fn to be executed in place of the standard hwmod reset fn
* *
* Represent the class of a OMAP hardware "modules" (e.g. timer, * Represent the class of a OMAP hardware "modules" (e.g. timer,
* smartreflex, gpio, uart...) * smartreflex, gpio, uart...)
...@@ -427,12 +428,18 @@ struct omap_hwmod_omap4_prcm { ...@@ -427,12 +428,18 @@ struct omap_hwmod_omap4_prcm {
* or some negative error upon failure. Returning an error will cause * or some negative error upon failure. Returning an error will cause
* omap_hwmod_shutdown() to abort the device shutdown and return an * omap_hwmod_shutdown() to abort the device shutdown and return an
* error. * error.
*
* If @reset is defined, then the function it points to will be
* executed in place of the standard hwmod _reset() code in
* mach-omap2/omap_hwmod.c. This is needed for IP blocks which have
* unusual reset sequences - usually processor IP blocks like the IVA.
*/ */
struct omap_hwmod_class { struct omap_hwmod_class {
const char *name; const char *name;
struct omap_hwmod_class_sysconfig *sysc; struct omap_hwmod_class_sysconfig *sysc;
u32 rev; u32 rev;
int (*pre_shutdown)(struct omap_hwmod *oh); int (*pre_shutdown)(struct omap_hwmod *oh);
int (*reset)(struct omap_hwmod *oh);
}; };
/** /**
......
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