Commit 30e105c0 authored by Paul Walmsley's avatar Paul Walmsley

ARM: OMAP2+: hwmod: revise the IP block reset process

Revise the IP block reset process.  This patch ensures that the
OCP_SYSCONFIG registers are reloaded after a custom reset.  Since
OCP_SYSCONFIG bits are cleared during reset, they should be
reprogrammed unless the IP block is being left in reset.  (The only IP
blocks that are left in reset are IP blocks with hardreset lines and
no custom reset function.)  If the IP block is left in reset, then it
is inaccessible to the MPU, and an access to the OCP_SYSCONFIG
register will cause an abort.

This version incorporates comments from Omar Ramirez Luna
<omar.ramirez@ti.com> to skip the OCP_SYSCONFIG access after asserting
hardreset lines.  This allows the MMU (IOMMU) IP block, which has
both hardreset lines and an OCP_SYSCONFIG register.

Also, ignore _ocp_softreset() errors if the IP block doesn't include a
softreset bit.  This is needed since a subsequent patch will start
taking the return value of the _reset() function seriously.
Signed-off-by: default avatarPaul Walmsley <paul@pwsan.com>
Cc: Benoît Cousson <b-cousson@ti.com>
Cc: Omar Ramirez Luna <omar.ramirez@ti.com>
parent f2f5736c
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* omap_hwmod implementation for OMAP2/3/4 * omap_hwmod implementation for OMAP2/3/4
* *
* Copyright (C) 2009-2011 Nokia Corporation * Copyright (C) 2009-2011 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011-2012 Texas Instruments, Inc.
* *
* Paul Walmsley, Benoît Cousson, Kevin Hilman * Paul Walmsley, Benoît Cousson, Kevin Hilman
* *
...@@ -1382,9 +1382,9 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) ...@@ -1382,9 +1382,9 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
* @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. Returns -EINVAL if the hwmod cannot be * enabled for this to work. Returns -ENOENT if the hwmod cannot be
* reset this way or if the hwmod is in the wrong state, -ETIMEDOUT if * reset this way, -EINVAL if the hwmod is in the wrong state,
* the module did not reset in time, or 0 upon success. * -ETIMEDOUT if 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 do not have SYSSTATUS registers and instead * Starting in OMAP4, some IPs do not have SYSSTATUS registers and instead
...@@ -1401,7 +1401,7 @@ static int _ocp_softreset(struct omap_hwmod *oh) ...@@ -1401,7 +1401,7 @@ static int _ocp_softreset(struct omap_hwmod *oh)
if (!oh->class->sysc || if (!oh->class->sysc ||
!(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
return -EINVAL; return -ENOENT;
/* 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) {
...@@ -1462,37 +1462,60 @@ static int _ocp_softreset(struct omap_hwmod *oh) ...@@ -1462,37 +1462,60 @@ static int _ocp_softreset(struct omap_hwmod *oh)
* _reset - reset an omap_hwmod * _reset - reset an omap_hwmod
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* *
* Resets an omap_hwmod @oh. The default software reset mechanism for * Resets an omap_hwmod @oh. If the module has a custom reset
* most OMAP IP blocks is triggered via the OCP_SYSCONFIG.SOFTRESET * function pointer defined, then call it to reset the IP block, and
* bit. However, some hwmods cannot be reset via this method: some * pass along its return value to the caller. Otherwise, if the IP
* are not targets and therefore have no OCP header registers to * block has an OCP_SYSCONFIG register with a SOFTRESET bitfield
* access; others (like the IVA) have idiosyncratic reset sequences. * associated with it, call a function to reset the IP block via that
* So for these relatively rare cases, custom reset code can be * method, and pass along the return value to the caller. Finally, if
* supplied in the struct omap_hwmod_class .reset function pointer. * the IP block has some hardreset lines associated with it, assert
* Passes along the return value from either _reset() or the custom * all of those, but do _not_ deassert them. (This is because driver
* reset function - these must return -EINVAL if the hwmod cannot be * authors have expressed an apparent requirement to control the
* reset this way or if the hwmod is in the wrong state, -ETIMEDOUT if * deassertion of the hardreset lines themselves.)
* the module did not reset in time, or 0 upon success. *
* 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 _ocp_softreset() 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) static int _reset(struct omap_hwmod *oh)
{ {
int ret; int i, r;
pr_debug("omap_hwmod: %s: resetting\n", oh->name); pr_debug("omap_hwmod: %s: resetting\n", oh->name);
if (oh->class->reset) {
r = oh->class->reset(oh);
} else {
if (oh->rst_lines_cnt > 0) {
for (i = 0; i < oh->rst_lines_cnt; i++)
_assert_hardreset(oh, oh->rst_lines[i].name);
return 0;
} else {
r = _ocp_softreset(oh);
if (r == -ENOENT)
r = 0;
}
}
/* /*
* XXX We're not resetting modules with hardreset lines * OCP_SYSCONFIG bits need to be reprogrammed after a
* automatically here. Should we do this also, or just expect * softreset. The _enable() function should be split to avoid
* those modules to define custom reset functions? * the rewrite of the OCP_SYSCONFIG register.
*/ */
ret = (oh->class->reset) ? oh->class->reset(oh) : _ocp_softreset(oh);
if (oh->class->sysc) { if (oh->class->sysc) {
_update_sysc_cache(oh); _update_sysc_cache(oh);
_enable_sysc(oh); _enable_sysc(oh);
} }
return ret; return r;
} }
/** /**
......
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