Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/bhnd/siba/siba.c
Show First 20 Lines • Show All 597 Lines • ▼ Show 20 Lines | siba_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask) | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
if ((r = dinfo->cfg_res[0]) == NULL) | if ((r = dinfo->cfg_res[0]) == NULL) | ||||
return (ENODEV); | return (ENODEV); | ||||
/* Mask and set TMSTATELOW core flag bits */ | /* Mask and set TMSTATELOW core flag bits */ | ||||
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 | ||||
siba_is_hw_suspended(device_t dev, device_t child) | siba_is_hw_suspended(device_t dev, device_t child) | ||||
{ | { | ||||
uint32_t ts_low; | uint32_t ts_low; | ||||
uint16_t ioctl; | uint16_t ioctl; | ||||
int error; | int error; | ||||
/* Fetch target state */ | /* Fetch target state */ | ||||
error = bhnd_read_config(child, SIBA_CFG0_TMSTATELOW, &ts_low, 4); | error = bhnd_read_config(child, SIBA_CFG0_TMSTATELOW, &ts_low, 4); | ||||
if (error) { | if (error) { | ||||
device_printf(child, "error reading HW reset state: %d\n", | device_printf(child, "error reading HW reset state: %d\n", | ||||
error); | error); | ||||
return (true); | return (true); | ||||
} | } | ||||
/* Is core held in RESET? */ | /* Is core held in RESET? */ | ||||
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)) | ||||
return (true); | return (true); | ||||
return (false); | return (false); | ||||
} | } | ||||
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) | ||||
return (EINVAL); | return (EINVAL); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
/* Can't suspend the core without access to the CFG0 registers */ | /* Can't suspend the core without access to the CFG0 registers */ | ||||
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) | ||||
return (EINVAL); | return (EINVAL); | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
dinfo = device_get_ivars(child); | dinfo = device_get_ivars(child); | ||||
/* Can't suspend the core without access to the CFG0 registers */ | /* Can't suspend the core without access to the CFG0 registers */ | ||||
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); | ||||
/* If this is an initiator core, we need to reject initiator | /* If this is an initiator core, we need to reject initiator | ||||
* 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 | ||||
* automatically clearing clkctl | * automatically clearing clkctl | ||||
*/ | */ | ||||
SIBA_LOCK(sc); | SIBA_LOCK(sc); | ||||
if (dinfo->pmu_state == SIBA_PMU_PWRCTL) { | if (dinfo->pmu_state == SIBA_PMU_PWRCTL) { | ||||
error = bhnd_pwrctl_request_clock(dinfo->pmu.pwrctl, child, | error = bhnd_pwrctl_request_clock(dinfo->pmu.pwrctl, child, | ||||
▲ Show 20 Lines • Show All 684 Lines • Show Last 20 Lines |