bhnd(4): Fix bcma/siba core reset behavior
Add missing support for specifying I/O control flags during core reset, and resolve a number of siba(4)-specific reset issues: - Add missing check for target reject flags in siba_is_hw_suspended(). - Remove incorrect wait on SIBA_TMH_BUSY when modifying any target state register; this should only be done when waiting for initiated transactions to clear. - Add missing wait on SIBA_IM_BY when asserting SIBA_IM_RJ. - Overwrite any previously set SIBA_TML_REJ flag when bringing the core out of reset. This fixes a lockup that occured when we brought up a core (after reboot) that had previously been placed into RESET by siba_bwn(4). Approved by: adrian (mentor, implicit) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D13039
This commit is contained in:
parent
05ed3f9063
commit
ac59515b98
@ -266,9 +266,9 @@
|
|||||||
.Fa "device_t dev" "bus_size_t offset" "const void *value" "u_int width"
|
.Fa "device_t dev" "bus_size_t offset" "const void *value" "u_int width"
|
||||||
.Fc
|
.Fc
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn bhnd_reset_hw "device_t dev" "uint16_t ioctl"
|
.Fn bhnd_reset_hw "device_t dev" "uint16_t ioctl" "uint16_t reset_ioctl"
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn bhnd_suspend_hw "device_t dev"
|
.Fn bhnd_suspend_hw "device_t dev" "uint16_t ioctl"
|
||||||
.Ft bool
|
.Ft bool
|
||||||
.Fn bhnd_is_hw_suspended "device_t dev"
|
.Fn bhnd_is_hw_suspended "device_t dev"
|
||||||
.\"
|
.\"
|
||||||
@ -1054,7 +1054,10 @@ function transitions the device
|
|||||||
.Fa dev
|
.Fa dev
|
||||||
to a low power
|
to a low power
|
||||||
.Dq RESET
|
.Dq RESET
|
||||||
state.
|
state, writing
|
||||||
|
.Fa ioctl
|
||||||
|
to the I/O control flags of
|
||||||
|
.Fa dev .
|
||||||
The hardware may be brought out of this state using
|
The hardware may be brought out of this state using
|
||||||
.Fn bhnd_reset_hw .
|
.Fn bhnd_reset_hw .
|
||||||
.Pp
|
.Pp
|
||||||
@ -1062,10 +1065,14 @@ The
|
|||||||
.Fn bhnd_reset_hw
|
.Fn bhnd_reset_hw
|
||||||
function first transitions the device
|
function first transitions the device
|
||||||
.Fa dev
|
.Fa dev
|
||||||
to a low power RESET state, and then brings the device out of RESET, writing
|
to a low power RESET state, writing
|
||||||
|
.Fa ioctl_reset
|
||||||
|
to the I/O control flags
|
||||||
|
of
|
||||||
|
.Fa dev ,
|
||||||
|
and then brings the device out of RESET, writing
|
||||||
.Fa ioctl
|
.Fa ioctl
|
||||||
to the I/O control flags of
|
to the device's I/O control flags.
|
||||||
.Fa dev .
|
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
.Fn bhnd_is_hw_suspended
|
.Fn bhnd_is_hw_suspended
|
||||||
|
@ -296,22 +296,22 @@ bcma_is_hw_suspended(device_t dev, device_t child)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl,
|
||||||
|
uint16_t reset_ioctl)
|
||||||
{
|
{
|
||||||
struct bcma_devinfo *dinfo;
|
struct bcma_devinfo *dinfo;
|
||||||
struct bhnd_core_pmu_info *pm;
|
struct bhnd_resource *r;
|
||||||
struct bhnd_resource *r;
|
uint16_t clkflags;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (device_get_parent(child) != dev)
|
if (device_get_parent(child) != dev)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
dinfo = device_get_ivars(child);
|
dinfo = device_get_ivars(child);
|
||||||
pm = dinfo->pmu_info;
|
|
||||||
|
|
||||||
/* We require exclusive control over BHND_IOCTL_CLK_EN and
|
/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
|
||||||
* BHND_IOCTL_CLK_FORCE. */
|
clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
|
||||||
if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE))
|
if (ioctl & clkflags)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/* Can't suspend the core without access to the agent registers */
|
/* Can't suspend the core without access to the agent registers */
|
||||||
@ -319,7 +319,7 @@ bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
|||||||
return (ENODEV);
|
return (ENODEV);
|
||||||
|
|
||||||
/* Place core into known RESET state */
|
/* Place core into known RESET state */
|
||||||
if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
|
if ((error = bhnd_suspend_hw(child, reset_ioctl)))
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -329,9 +329,7 @@ bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
|||||||
* - Force clock distribution to ensure propagation throughout the
|
* - Force clock distribution to ensure propagation throughout the
|
||||||
* core.
|
* core.
|
||||||
*/
|
*/
|
||||||
error = bhnd_write_ioctl(child,
|
if ((error = bhnd_write_ioctl(child, ioctl | clkflags, UINT16_MAX)))
|
||||||
ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE, UINT16_MAX);
|
|
||||||
if (error)
|
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/* Bring the core out of reset */
|
/* Bring the core out of reset */
|
||||||
@ -347,11 +345,11 @@ bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bcma_suspend_hw(device_t dev, device_t child)
|
bcma_suspend_hw(device_t dev, device_t child, uint16_t ioctl)
|
||||||
{
|
{
|
||||||
struct bcma_devinfo *dinfo;
|
struct bcma_devinfo *dinfo;
|
||||||
struct bhnd_resource *r;
|
struct bhnd_resource *r;
|
||||||
uint32_t rst;
|
uint16_t clkflags;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (device_get_parent(child) != dev)
|
if (device_get_parent(child) != dev)
|
||||||
@ -359,6 +357,11 @@ bcma_suspend_hw(device_t dev, device_t child)
|
|||||||
|
|
||||||
dinfo = device_get_ivars(child);
|
dinfo = device_get_ivars(child);
|
||||||
|
|
||||||
|
/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
|
||||||
|
clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
|
||||||
|
if (ioctl & clkflags)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
/* Can't suspend the core without access to the agent registers */
|
/* Can't suspend the core without access to the agent registers */
|
||||||
if ((r = dinfo->res_agent) == NULL)
|
if ((r = dinfo->res_agent) == NULL)
|
||||||
return (ENODEV);
|
return (ENODEV);
|
||||||
@ -367,17 +370,12 @@ bcma_suspend_hw(device_t dev, device_t child)
|
|||||||
if ((error = bcma_dmp_wait_reset(child, dinfo)))
|
if ((error = bcma_dmp_wait_reset(child, dinfo)))
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/* Already in reset? */
|
/* Put core into reset (if not already in reset) */
|
||||||
rst = bhnd_bus_read_4(r, BCMA_DMP_RESETCTRL);
|
|
||||||
if (rst & BCMA_DMP_RC_RESET)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
/* Put core into reset */
|
|
||||||
if ((error = bcma_dmp_write_reset(child, dinfo, BCMA_DMP_RC_RESET)))
|
if ((error = bcma_dmp_write_reset(child, dinfo, BCMA_DMP_RC_RESET)))
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/* Clear core flags */
|
/* Write core flags (and clear CLK_EN/CLK_FORCE) */
|
||||||
if ((error = bhnd_write_ioctl(child, 0x0, UINT16_MAX)))
|
if ((error = bhnd_write_ioctl(child, ioctl, ~clkflags)))
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
@ -594,9 +594,16 @@ bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo)
|
|||||||
int
|
int
|
||||||
bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value)
|
bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value)
|
||||||
{
|
{
|
||||||
|
uint32_t rst;
|
||||||
|
|
||||||
if (dinfo->res_agent == NULL)
|
if (dinfo->res_agent == NULL)
|
||||||
return (ENODEV);
|
return (ENODEV);
|
||||||
|
|
||||||
|
/* Already in requested reset state? */
|
||||||
|
rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL);
|
||||||
|
if (rst == value)
|
||||||
|
return (0);
|
||||||
|
|
||||||
bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value);
|
bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value);
|
||||||
bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */
|
bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */
|
||||||
DELAY(10);
|
DELAY(10);
|
||||||
|
@ -818,23 +818,27 @@ bhnd_is_hw_suspended(device_t dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Place the bhnd(4) device's hardware into a reset state, and then bring the
|
* Place the bhnd(4) device's hardware into a low-power RESET state with
|
||||||
* hardware out of reset with BHND_IOCTL_CLK_EN and @p ioctl flags set.
|
* the @p reset_ioctl I/O control flags set, and then bring the hardware out of
|
||||||
|
* RESET with the @p ioctl I/O control flags set.
|
||||||
*
|
*
|
||||||
* Any clock or resource PMU requests previously made by @p dev will be
|
* Any clock or resource PMU requests previously made by @p child will be
|
||||||
* invalidated.
|
* invalidated.
|
||||||
*
|
*
|
||||||
* @param dev The device to be reset.
|
* @param dev The device to be reset.
|
||||||
* @param ioctl Device-specific core ioctl flags to be supplied on reset
|
* @param ioctl Device-specific I/O control flags to be set when bringing
|
||||||
* (see BHND_IOCTL_*).
|
* the core out of its RESET state (see BHND_IOCTL_*).
|
||||||
|
* @param reset_ioctl Device-specific I/O control flags to be set when placing
|
||||||
|
* the core into its RESET state.
|
||||||
*
|
*
|
||||||
* @retval 0 success
|
* @retval 0 success
|
||||||
* @retval non-zero error
|
* @retval non-zero error
|
||||||
*/
|
*/
|
||||||
static inline int
|
static inline int
|
||||||
bhnd_reset_hw(device_t dev, uint16_t ioctl)
|
bhnd_reset_hw(device_t dev, uint16_t ioctl, uint16_t reset_ioctl)
|
||||||
{
|
{
|
||||||
return (BHND_BUS_RESET_HW(device_get_parent(dev), dev, ioctl));
|
return (BHND_BUS_RESET_HW(device_get_parent(dev), dev, ioctl,
|
||||||
|
reset_ioctl));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -851,9 +855,9 @@ bhnd_reset_hw(device_t dev, uint16_t ioctl)
|
|||||||
* @retval non-zero error
|
* @retval non-zero error
|
||||||
*/
|
*/
|
||||||
static inline int
|
static inline int
|
||||||
bhnd_suspend_hw(device_t dev)
|
bhnd_suspend_hw(device_t dev, uint16_t ioctl)
|
||||||
{
|
{
|
||||||
return (BHND_BUS_SUSPEND_HW(device_get_parent(dev), dev));
|
return (BHND_BUS_SUSPEND_HW(device_get_parent(dev), dev, ioctl));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +96,8 @@ CODE {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bhnd_bus_null_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
bhnd_bus_null_reset_hw(device_t dev, device_t child, uint16_t ioctl,
|
||||||
|
uint16_t reset_ioctl)
|
||||||
{
|
{
|
||||||
panic("bhnd_bus_reset_hw unimplemented");
|
panic("bhnd_bus_reset_hw unimplemented");
|
||||||
}
|
}
|
||||||
@ -624,16 +625,19 @@ METHOD bool is_hw_suspended {
|
|||||||
} DEFAULT bhnd_bus_null_is_hw_suspended;
|
} DEFAULT bhnd_bus_null_is_hw_suspended;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Place the bhnd(4) device's hardware into a reset state, and then bring the
|
* Place the bhnd(4) device's hardware into a low-power RESET state with
|
||||||
* hardware out of reset with BHND_IOCTL_CLK_EN and @p ioctl flags set.
|
* the @p reset_ioctl I/O control flags set, and then bring the hardware out of
|
||||||
|
* RESET with the @p ioctl I/O control flags set.
|
||||||
*
|
*
|
||||||
* Any clock or resource PMU requests previously made by @p child will be
|
* Any clock or resource PMU requests previously made by @p child will be
|
||||||
* invalidated.
|
* invalidated.
|
||||||
*
|
*
|
||||||
* @param dev The bhnd bus parent of @p child.
|
* @param dev The bhnd bus parent of @p child.
|
||||||
* @param child The device to be reset.
|
* @param child The device to be reset.
|
||||||
* @param ioctl Device-specific core ioctl flags to be supplied on reset
|
* @param ioctl Device-specific I/O control flags to be set when bringing
|
||||||
* (see BHND_IOCTL_*).
|
* the core out of its RESET state (see BHND_IOCTL_*).
|
||||||
|
* @param reset_ioctl Device-specific I/O control flags to be set when placing
|
||||||
|
* the core into its RESET state.
|
||||||
*
|
*
|
||||||
* @retval 0 success
|
* @retval 0 success
|
||||||
* @retval non-zero error
|
* @retval non-zero error
|
||||||
@ -642,18 +646,21 @@ METHOD int reset_hw {
|
|||||||
device_t dev;
|
device_t dev;
|
||||||
device_t child;
|
device_t child;
|
||||||
uint16_t ioctl;
|
uint16_t ioctl;
|
||||||
|
uint16_t reset_ioctl;
|
||||||
} DEFAULT bhnd_bus_null_reset_hw;
|
} DEFAULT bhnd_bus_null_reset_hw;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Suspend @p child's hardware in a low-power reset state.
|
* Suspend @p child's hardware in a low-power RESET state.
|
||||||
*
|
*
|
||||||
* Any clock or resource PMU requests previously made by @p dev will be
|
* Any clock or resource PMU requests previously made by @p dev will be
|
||||||
* invalidated.
|
* invalidated.
|
||||||
*
|
*
|
||||||
* The hardware may be brought out of reset via bhnd_reset_hw().
|
* The hardware may be brought out of RESET via bhnd_reset_hw().
|
||||||
*
|
*
|
||||||
* @param dev The bhnd bus parent of @p child.
|
* @param dev The bhnd bus parent of @p child.
|
||||||
* @param dev The device to be suspended.
|
* @param dev The device to be suspended.
|
||||||
|
* @param ioctl Device-specific I/O control flags to be set when placing
|
||||||
|
* the core into its RESET state (see BHND_IOCTL_*).
|
||||||
*
|
*
|
||||||
* @retval 0 success
|
* @retval 0 success
|
||||||
* @retval non-zero error
|
* @retval non-zero error
|
||||||
@ -661,6 +668,7 @@ METHOD int reset_hw {
|
|||||||
METHOD int suspend_hw {
|
METHOD int suspend_hw {
|
||||||
device_t dev;
|
device_t dev;
|
||||||
device_t child;
|
device_t child;
|
||||||
|
uint16_t ioctl;
|
||||||
} DEFAULT bhnd_bus_null_suspend_hw;
|
} DEFAULT bhnd_bus_null_suspend_hw;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +98,7 @@ bhnd_usb_attach(device_t dev)
|
|||||||
|
|
||||||
sc = device_get_softc(dev);
|
sc = device_get_softc(dev);
|
||||||
|
|
||||||
bhnd_reset_hw(dev, 0);
|
bhnd_reset_hw(dev, 0, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate the resources which the parent bus has already
|
* Allocate the resources which the parent bus has already
|
||||||
|
@ -603,8 +603,9 @@ siba_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
|
|||||||
ts_mask = (mask << SIBA_TML_SICF_SHIFT) & SIBA_TML_SICF_MASK;
|
ts_mask = (mask << SIBA_TML_SICF_SHIFT) & SIBA_TML_SICF_MASK;
|
||||||
ts_low = (value << SIBA_TML_SICF_SHIFT) & ts_mask;
|
ts_low = (value << SIBA_TML_SICF_SHIFT) & ts_mask;
|
||||||
|
|
||||||
return (siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||||
ts_low, ts_mask));
|
ts_low, ts_mask);
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -626,6 +627,10 @@ siba_is_hw_suspended(device_t dev, device_t child)
|
|||||||
if (ts_low & SIBA_TML_RESET)
|
if (ts_low & SIBA_TML_RESET)
|
||||||
return (true);
|
return (true);
|
||||||
|
|
||||||
|
/* Is target reject enabled? */
|
||||||
|
if (ts_low & SIBA_TML_REJ_MASK)
|
||||||
|
return (true);
|
||||||
|
|
||||||
/* Is core clocked? */
|
/* Is core clocked? */
|
||||||
ioctl = SIBA_REG_GET(ts_low, TML_SICF);
|
ioctl = SIBA_REG_GET(ts_low, TML_SICF);
|
||||||
if (!(ioctl & BHND_IOCTL_CLK_EN))
|
if (!(ioctl & BHND_IOCTL_CLK_EN))
|
||||||
@ -635,11 +640,13 @@ siba_is_hw_suspended(device_t dev, device_t child)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
siba_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
siba_reset_hw(device_t dev, device_t child, uint16_t ioctl,
|
||||||
|
uint16_t reset_ioctl)
|
||||||
{
|
{
|
||||||
struct siba_devinfo *dinfo;
|
struct siba_devinfo *dinfo;
|
||||||
struct bhnd_resource *r;
|
struct bhnd_resource *r;
|
||||||
uint32_t ts_low, imstate;
|
uint32_t ts_low, imstate;
|
||||||
|
uint16_t clkflags;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (device_get_parent(child) != dev)
|
if (device_get_parent(child) != dev)
|
||||||
@ -651,66 +658,60 @@ siba_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
|||||||
if ((r = dinfo->cfg_res[0]) == NULL)
|
if ((r = dinfo->cfg_res[0]) == NULL)
|
||||||
return (ENODEV);
|
return (ENODEV);
|
||||||
|
|
||||||
/* We require exclusive control over BHND_IOCTL_CLK_EN and
|
/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
|
||||||
* BHND_IOCTL_CLK_FORCE. */
|
clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
|
||||||
if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE))
|
if (ioctl & clkflags)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/* Place core into known RESET state */
|
/* Place core into known RESET state */
|
||||||
if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
|
if ((error = bhnd_suspend_hw(child, reset_ioctl)))
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/* Leaving the core in reset, set the caller's IOCTL flags and
|
/* Set RESET, clear REJ, set the caller's IOCTL flags, and
|
||||||
* enable the core's clocks. */
|
* force clocks to ensure the signal propagates throughout the
|
||||||
ts_low = (ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) <<
|
* core. */
|
||||||
SIBA_TML_SICF_SHIFT;
|
ts_low = SIBA_TML_RESET |
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
(ioctl << SIBA_TML_SICF_SHIFT) |
|
||||||
ts_low, SIBA_TML_SICF_MASK);
|
(BHND_IOCTL_CLK_EN << SIBA_TML_SICF_SHIFT) |
|
||||||
if (error)
|
(BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT);
|
||||||
return (error);
|
|
||||||
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||||
|
ts_low, UINT32_MAX);
|
||||||
|
|
||||||
/* Clear any target errors */
|
/* Clear any target errors */
|
||||||
if (bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH) & SIBA_TMH_SERR) {
|
if (bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH) & SIBA_TMH_SERR) {
|
||||||
error = siba_write_target_state(child, dinfo,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATEHIGH,
|
||||||
SIBA_CFG0_TMSTATEHIGH, 0, SIBA_TMH_SERR);
|
0x0, SIBA_TMH_SERR);
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear any initiator errors */
|
/* Clear any initiator errors */
|
||||||
imstate = bhnd_bus_read_4(r, SIBA_CFG0_IMSTATE);
|
imstate = bhnd_bus_read_4(r, SIBA_CFG0_IMSTATE);
|
||||||
if (imstate & (SIBA_IM_IBE|SIBA_IM_TO)) {
|
if (imstate & (SIBA_IM_IBE|SIBA_IM_TO)) {
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE, 0x0,
|
||||||
0, SIBA_IM_IBE|SIBA_IM_TO);
|
SIBA_IM_IBE|SIBA_IM_TO);
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release from RESET while leaving clocks forced, ensuring the
|
/* Release from RESET while leaving clocks forced, ensuring the
|
||||||
* signal propagates throughout the core */
|
* signal propagates throughout the core */
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, 0x0,
|
||||||
0x0, SIBA_TML_RESET);
|
SIBA_TML_RESET);
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
/* The core should now be active; we can clear the BHND_IOCTL_CLK_FORCE
|
/* The core should now be active; we can clear the BHND_IOCTL_CLK_FORCE
|
||||||
* bit and allow the core to manage clock gating. */
|
* bit and allow the core to manage clock gating. */
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, 0x0,
|
||||||
0x0, (BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT));
|
(BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT));
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
siba_suspend_hw(device_t dev, device_t child)
|
siba_suspend_hw(device_t dev, device_t child, uint16_t ioctl)
|
||||||
{
|
{
|
||||||
struct siba_softc *sc;
|
struct siba_softc *sc;
|
||||||
struct siba_devinfo *dinfo;
|
struct siba_devinfo *dinfo;
|
||||||
struct bhnd_resource *r;
|
struct bhnd_resource *r;
|
||||||
uint32_t idl, ts_low;
|
uint32_t idl, ts_low, ts_mask;
|
||||||
uint16_t ioctl;
|
uint16_t cflags, clkflags;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (device_get_parent(child) != dev)
|
if (device_get_parent(child) != dev)
|
||||||
@ -723,30 +724,37 @@ siba_suspend_hw(device_t dev, device_t child)
|
|||||||
if ((r = dinfo->cfg_res[0]) == NULL)
|
if ((r = dinfo->cfg_res[0]) == NULL)
|
||||||
return (ENODEV);
|
return (ENODEV);
|
||||||
|
|
||||||
|
/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
|
||||||
|
clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
|
||||||
|
if (ioctl & clkflags)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
/* Already in RESET? */
|
/* Already in RESET? */
|
||||||
ts_low = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATELOW);
|
ts_low = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATELOW);
|
||||||
if (ts_low & SIBA_TML_RESET) {
|
if (ts_low & SIBA_TML_RESET)
|
||||||
/* Clear IOCTL flags, ensuring the clock is disabled */
|
return (0);
|
||||||
return (siba_write_target_state(child, dinfo,
|
|
||||||
SIBA_CFG0_TMSTATELOW, 0x0, SIBA_TML_SICF_MASK));
|
|
||||||
|
|
||||||
|
/* If clocks are already disabled, we can place the core directly
|
||||||
|
* into RESET|REJ while setting the caller's IOCTL flags. */
|
||||||
|
cflags = SIBA_REG_GET(ts_low, TML_SICF);
|
||||||
|
if (!(cflags & BHND_IOCTL_CLK_EN)) {
|
||||||
|
ts_low = SIBA_TML_RESET | SIBA_TML_REJ |
|
||||||
|
(ioctl << SIBA_TML_SICF_SHIFT);
|
||||||
|
ts_mask = SIBA_TML_RESET | SIBA_TML_REJ | SIBA_TML_SICF_MASK;
|
||||||
|
|
||||||
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||||
|
ts_low, ts_mask);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If clocks are already disabled, we can put the core directly
|
/* Reject further transactions reaching this core */
|
||||||
* into RESET */
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||||
ioctl = SIBA_REG_GET(ts_low, TML_SICF);
|
|
||||||
if (!(ioctl & BHND_IOCTL_CLK_EN)) {
|
|
||||||
/* Set RESET and clear IOCTL flags */
|
|
||||||
return (siba_write_target_state(child, dinfo,
|
|
||||||
SIBA_CFG0_TMSTATELOW,
|
|
||||||
SIBA_TML_RESET,
|
|
||||||
SIBA_TML_RESET | SIBA_TML_SICF_MASK));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reject any further target backplane transactions */
|
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
|
||||||
SIBA_TML_REJ, SIBA_TML_REJ);
|
SIBA_TML_REJ, SIBA_TML_REJ);
|
||||||
|
|
||||||
|
/* Wait for transaction busy flag to clear for all transactions
|
||||||
|
* initiated by this core */
|
||||||
|
error = siba_wait_target_state(child, dinfo, SIBA_CFG0_TMSTATEHIGH,
|
||||||
|
0x0, SIBA_TMH_BUSY, 100000);
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
@ -754,44 +762,47 @@ siba_suspend_hw(device_t dev, device_t child)
|
|||||||
* transactions too. */
|
* transactions too. */
|
||||||
idl = bhnd_bus_read_4(r, SIBA_CFG0_IDLOW);
|
idl = bhnd_bus_read_4(r, SIBA_CFG0_IDLOW);
|
||||||
if (idl & SIBA_IDL_INIT) {
|
if (idl & SIBA_IDL_INIT) {
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
/* Reject further initiator transactions */
|
||||||
|
siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
||||||
SIBA_IM_RJ, SIBA_IM_RJ);
|
SIBA_IM_RJ, SIBA_IM_RJ);
|
||||||
|
|
||||||
|
/* Wait for initiator busy flag to clear */
|
||||||
|
error = siba_wait_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
||||||
|
0x0, SIBA_IM_BY, 100000);
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Put the core into RESET|REJECT, forcing clocks to ensure the RESET
|
/* Put the core into RESET, set the caller's IOCTL flags, and
|
||||||
* signal propagates throughout the core, leaving REJECT asserted. */
|
* force clocks to ensure the RESET signal propagates throughout the
|
||||||
ts_low = SIBA_TML_RESET;
|
* core. */
|
||||||
ts_low |= (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) <<
|
ts_low = SIBA_TML_RESET |
|
||||||
SIBA_TML_SICF_SHIFT;
|
(ioctl << SIBA_TML_SICF_SHIFT) |
|
||||||
|
(BHND_IOCTL_CLK_EN << SIBA_TML_SICF_SHIFT) |
|
||||||
|
(BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT);
|
||||||
|
ts_mask = SIBA_TML_RESET |
|
||||||
|
SIBA_TML_SICF_MASK;
|
||||||
|
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, ts_low,
|
||||||
ts_low, ts_low);
|
ts_mask);
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/* Give RESET ample time */
|
/* Give RESET ample time */
|
||||||
DELAY(10);
|
DELAY(10);
|
||||||
|
|
||||||
/* Leaving core in reset, disable all clocks, clear REJ flags and
|
|
||||||
* IOCTL state */
|
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
|
||||||
SIBA_TML_RESET,
|
|
||||||
SIBA_TML_RESET | SIBA_TML_REJ | SIBA_TML_SICF_MASK);
|
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
/* Clear previously asserted initiator reject */
|
/* Clear previously asserted initiator reject */
|
||||||
if (idl & SIBA_IDL_INIT) {
|
if (idl & SIBA_IDL_INIT) {
|
||||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE, 0x0,
|
||||||
0, SIBA_IM_RJ);
|
SIBA_IM_RJ);
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disable all clocks, leaving RESET and REJ asserted */
|
||||||
|
siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, 0x0,
|
||||||
|
(BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) << SIBA_TML_SICF_SHIFT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Core is now in RESET, with clocks disabled and REJ not asserted.
|
* Core is now in RESET.
|
||||||
*
|
*
|
||||||
* If the core holds any PWRCTL clock reservations, we need to release
|
* If the core holds any PWRCTL clock reservations, we need to release
|
||||||
* those now. This emulates the standard bhnd(4) PMU behavior of RESET
|
* those now. This emulates the standard bhnd(4) PMU behavior of RESET
|
||||||
|
@ -624,83 +624,73 @@ siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write @p value to @p dev's CFG0 target/initiator state register and
|
* Write @p value to @p dev's CFG0 target/initiator state register, performing
|
||||||
* wait for completion.
|
* required read-back and waiting for completion.
|
||||||
*
|
*
|
||||||
* @param dev The siba(4) child device.
|
* @param dev The siba(4) child device.
|
||||||
* @param reg The state register to write (e.g. SIBA_CFG0_TMSTATELOW,
|
* @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW,
|
||||||
* SIBA_CFG0_IMSTATE)
|
* SIBA_CFG0_IMSTATE)
|
||||||
* @param value The value to write to @p reg.
|
* @param value The value to write to @p reg.
|
||||||
* @param mask The mask of bits to be included from @p value.
|
* @param mask The mask of bits to be included from @p value.
|
||||||
*
|
|
||||||
* @retval 0 success.
|
|
||||||
* @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.
|
|
||||||
* @retval ETIMEDOUT if a timeout occurs prior to SIBA_TMH_BUSY clearing.
|
|
||||||
*/
|
*/
|
||||||
int
|
void
|
||||||
siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,
|
siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,
|
||||||
bus_size_t reg, uint32_t value, uint32_t mask)
|
bus_size_t reg, uint32_t value, uint32_t mask)
|
||||||
{
|
{
|
||||||
struct bhnd_resource *r;
|
struct bhnd_resource *r;
|
||||||
uint32_t rval;
|
uint32_t rval;
|
||||||
|
|
||||||
/* Must have a CFG0 block */
|
r = dinfo->cfg_res[0];
|
||||||
if ((r = dinfo->cfg_res[0]) == NULL)
|
|
||||||
return (ENODEV);
|
|
||||||
|
|
||||||
/* Verify the register offset falls within CFG register block */
|
KASSERT(r != NULL, ("%s missing CFG0 mapping",
|
||||||
if (reg > SIBA_CFG_SIZE-4)
|
device_get_nameunit(dev)));
|
||||||
return (EFAULT);
|
KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx",
|
||||||
|
device_get_nameunit(dev), (uintmax_t)reg));
|
||||||
|
|
||||||
for (int i = 0; i < 300; i += 10) {
|
rval = bhnd_bus_read_4(r, reg);
|
||||||
rval = bhnd_bus_read_4(r, reg);
|
rval &= ~mask;
|
||||||
rval &= ~mask;
|
rval |= (value & mask);
|
||||||
rval |= (value & mask);
|
|
||||||
|
|
||||||
bhnd_bus_write_4(r, reg, rval);
|
bhnd_bus_write_4(r, reg, rval);
|
||||||
bhnd_bus_read_4(r, reg); /* read-back */
|
bhnd_bus_read_4(r, reg); /* read-back */
|
||||||
DELAY(1);
|
DELAY(1);
|
||||||
|
|
||||||
/* If the write has completed, wait for target busy state
|
|
||||||
* to clear */
|
|
||||||
rval = bhnd_bus_read_4(r, reg);
|
|
||||||
if ((rval & mask) == (value & mask))
|
|
||||||
return (siba_wait_target_busy(dev, dinfo, 100000));
|
|
||||||
|
|
||||||
DELAY(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ETIMEDOUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spin for up to @p usec waiting for SIBA_TMH_BUSY to clear in
|
* Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state
|
||||||
* @p dev's SIBA_CFG0_TMSTATEHIGH register.
|
* register value to be equal to @p value after applying @p mask bits to both
|
||||||
|
* values.
|
||||||
*
|
*
|
||||||
* @param dev The siba(4) child device to wait on.
|
* @param dev The siba(4) child device to wait on.
|
||||||
* @param dinfo The @p dev's device info
|
* @param dinfo The @p dev's device info
|
||||||
|
* @param reg The state register to read (e.g. SIBA_CFG0_TMSTATEHIGH,
|
||||||
|
* SIBA_CFG0_IMSTATE)
|
||||||
|
* @param value The value against which @p reg will be compared.
|
||||||
|
* @param mask The mask to be applied when comparing @p value with @p reg.
|
||||||
|
* @param usec The maximum number of microseconds to wait for completion.
|
||||||
*
|
*
|
||||||
* @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.
|
* @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.
|
||||||
* @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.
|
* @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.
|
||||||
* @retval ETIMEDOUT if a timeout occurs prior to SIBA_TMH_BUSY clearing.
|
* @retval ETIMEDOUT if a timeout occurs.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
siba_wait_target_busy(device_t dev, struct siba_devinfo *dinfo, int usec)
|
siba_wait_target_state(device_t dev, struct siba_devinfo *dinfo, bus_size_t reg,
|
||||||
|
uint32_t value, uint32_t mask, u_int usec)
|
||||||
{
|
{
|
||||||
struct bhnd_resource *r;
|
struct bhnd_resource *r;
|
||||||
uint32_t ts_high;
|
uint32_t rval;
|
||||||
|
|
||||||
if ((r = dinfo->cfg_res[0]) == NULL)
|
if ((r = dinfo->cfg_res[0]) == NULL)
|
||||||
return (ENODEV);
|
return (ENODEV);
|
||||||
|
|
||||||
|
value &= mask;
|
||||||
for (int i = 0; i < usec; i += 10) {
|
for (int i = 0; i < usec; i += 10) {
|
||||||
ts_high = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH);
|
rval = bhnd_bus_read_4(r, reg);
|
||||||
if (!(ts_high & SIBA_TMH_BUSY))
|
if ((rval & mask) == value)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
DELAY(10);
|
DELAY(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
device_printf(dev, "SIBA_TMH_BUSY wait timeout\n");
|
|
||||||
return (ETIMEDOUT);
|
return (ETIMEDOUT);
|
||||||
}
|
}
|
||||||
|
@ -117,11 +117,12 @@ u_int siba_admatch_offset(uint8_t addrspace);
|
|||||||
int siba_parse_admatch(uint32_t am, uint32_t *addr,
|
int siba_parse_admatch(uint32_t am, uint32_t *addr,
|
||||||
uint32_t *size);
|
uint32_t *size);
|
||||||
|
|
||||||
int siba_write_target_state(device_t dev,
|
void siba_write_target_state(device_t dev,
|
||||||
struct siba_devinfo *dinfo, bus_size_t reg,
|
struct siba_devinfo *dinfo, bus_size_t reg,
|
||||||
uint32_t value, uint32_t mask);
|
uint32_t value, uint32_t mask);
|
||||||
int siba_wait_target_busy(device_t child,
|
int siba_wait_target_state(device_t dev,
|
||||||
struct siba_devinfo *dinfo, int usec);
|
struct siba_devinfo *dinfo, bus_size_t reg,
|
||||||
|
uint32_t value, uint32_t mask, u_int usec);
|
||||||
|
|
||||||
|
|
||||||
/* Sonics configuration register blocks */
|
/* Sonics configuration register blocks */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user