diff options
| author | Landon J. Fuller <landonf@FreeBSD.org> | 2017-11-27 22:13:30 +0000 |
|---|---|---|
| committer | Landon J. Fuller <landonf@FreeBSD.org> | 2017-11-27 22:13:30 +0000 |
| commit | ac59515b9800732037d8147e96afd9927d3a7ae7 (patch) | |
| tree | 062e2712c6fdbf77b132a4e70b85a34c3be3d597 /sys/dev/bhnd/siba | |
| parent | 05ed3f9063415c85e3d15f11904bac28e285e9f9 (diff) | |
Notes
Diffstat (limited to 'sys/dev/bhnd/siba')
| -rw-r--r-- | sys/dev/bhnd/siba/siba.c | 155 | ||||
| -rw-r--r-- | sys/dev/bhnd/siba/siba_subr.c | 72 | ||||
| -rw-r--r-- | sys/dev/bhnd/siba/sibavar.h | 7 |
3 files changed, 118 insertions, 116 deletions
diff --git a/sys/dev/bhnd/siba/siba.c b/sys/dev/bhnd/siba/siba.c index 000b5aa7c040..16defbaf0c14 100644 --- a/sys/dev/bhnd/siba/siba.c +++ b/sys/dev/bhnd/siba/siba.c @@ -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_low = (value << SIBA_TML_SICF_SHIFT) & ts_mask; - return (siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, - ts_low, ts_mask)); + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, + ts_low, ts_mask); + return (0); } static bool @@ -626,6 +627,10 @@ siba_is_hw_suspended(device_t dev, device_t child) if (ts_low & SIBA_TML_RESET) return (true); + /* Is target reject enabled? */ + if (ts_low & SIBA_TML_REJ_MASK) + return (true); + /* Is core clocked? */ ioctl = SIBA_REG_GET(ts_low, TML_SICF); if (!(ioctl & BHND_IOCTL_CLK_EN)) @@ -635,11 +640,13 @@ siba_is_hw_suspended(device_t dev, device_t child) } 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 bhnd_resource *r; uint32_t ts_low, imstate; + uint16_t clkflags; int error; 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) return (ENODEV); - /* We require exclusive control over BHND_IOCTL_CLK_EN and - * BHND_IOCTL_CLK_FORCE. */ - if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE)) + /* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */ + clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE; + if (ioctl & clkflags) return (EINVAL); /* Place core into known RESET state */ - if ((error = BHND_BUS_SUSPEND_HW(dev, child))) + if ((error = bhnd_suspend_hw(child, reset_ioctl))) return (error); - /* Leaving the core in reset, set the caller's IOCTL flags and - * enable the core's clocks. */ - ts_low = (ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) << - SIBA_TML_SICF_SHIFT; - error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, - ts_low, SIBA_TML_SICF_MASK); - if (error) - return (error); + /* Set RESET, clear REJ, set the caller's IOCTL flags, and + * force clocks to ensure the signal propagates throughout the + * core. */ + ts_low = SIBA_TML_RESET | + (ioctl << SIBA_TML_SICF_SHIFT) | + (BHND_IOCTL_CLK_EN << SIBA_TML_SICF_SHIFT) | + (BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT); + + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, + ts_low, UINT32_MAX); /* Clear any target errors */ if (bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH) & SIBA_TMH_SERR) { - error = siba_write_target_state(child, dinfo, - SIBA_CFG0_TMSTATEHIGH, 0, SIBA_TMH_SERR); - if (error) - return (error); + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATEHIGH, + 0x0, SIBA_TMH_SERR); } /* Clear any initiator errors */ imstate = bhnd_bus_read_4(r, SIBA_CFG0_IMSTATE); if (imstate & (SIBA_IM_IBE|SIBA_IM_TO)) { - error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE, - 0, SIBA_IM_IBE|SIBA_IM_TO); - if (error) - return (error); + siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE, 0x0, + SIBA_IM_IBE|SIBA_IM_TO); } /* Release from RESET while leaving clocks forced, ensuring the * signal propagates throughout the core */ - error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, - 0x0, SIBA_TML_RESET); - if (error) - return (error); + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, 0x0, + SIBA_TML_RESET); /* The core should now be active; we can clear the BHND_IOCTL_CLK_FORCE * bit and allow the core to manage clock gating. */ - error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, - 0x0, (BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT)); - if (error) - return (error); + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, 0x0, + (BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT)); return (0); } 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_devinfo *dinfo; struct bhnd_resource *r; - uint32_t idl, ts_low; - uint16_t ioctl; + uint32_t idl, ts_low, ts_mask; + uint16_t cflags, clkflags; int error; 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) 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? */ ts_low = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATELOW); - if (ts_low & SIBA_TML_RESET) { - /* Clear IOCTL flags, ensuring the clock is disabled */ - return (siba_write_target_state(child, dinfo, - SIBA_CFG0_TMSTATELOW, 0x0, SIBA_TML_SICF_MASK)); - + if (ts_low & SIBA_TML_RESET) return (0); - } - /* If clocks are already disabled, we can put the core directly - * into RESET */ - 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)); + /* 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); } - /* Reject any further target backplane transactions */ - error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, + /* Reject further transactions reaching this core */ + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, 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) return (error); @@ -754,44 +762,47 @@ siba_suspend_hw(device_t dev, device_t child) * transactions too. */ idl = bhnd_bus_read_4(r, SIBA_CFG0_IDLOW); 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); + + /* Wait for initiator busy flag to clear */ + error = siba_wait_target_state(child, dinfo, SIBA_CFG0_IMSTATE, + 0x0, SIBA_IM_BY, 100000); if (error) return (error); } - /* Put the core into RESET|REJECT, forcing clocks to ensure the RESET - * signal propagates throughout the core, leaving REJECT asserted. */ - ts_low = SIBA_TML_RESET; - ts_low |= (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) << - SIBA_TML_SICF_SHIFT; + /* Put the core into RESET, set the caller's IOCTL flags, and + * force clocks to ensure the RESET signal propagates throughout the + * core. */ + ts_low = SIBA_TML_RESET | + (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, - ts_low, ts_low); + siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, ts_low, + ts_mask); if (error) return (error); /* Give RESET ample time */ 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 */ if (idl & SIBA_IDL_INIT) { - error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE, - 0, SIBA_IM_RJ); - if (error) - return (error); + siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE, 0x0, + SIBA_IM_RJ); } + /* 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 * those now. This emulates the standard bhnd(4) PMU behavior of RESET diff --git a/sys/dev/bhnd/siba/siba_subr.c b/sys/dev/bhnd/siba/siba_subr.c index 37c3a335efef..9a854abd14c6 100644 --- a/sys/dev/bhnd/siba/siba_subr.c +++ b/sys/dev/bhnd/siba/siba_subr.c @@ -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 - * wait for completion. + * Write @p value to @p dev's CFG0 target/initiator state register, performing + * required read-back and waiting for completion. * * @param dev The siba(4) child device. - * @param reg The state register to write (e.g. SIBA_CFG0_TMSTATELOW, - * SIBA_CFG0_IMSTATE) + * @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW, + * SIBA_CFG0_IMSTATE) * @param value The value to write to @p reg. * @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, bus_size_t reg, uint32_t value, uint32_t mask) { struct bhnd_resource *r; uint32_t rval; - /* Must have a CFG0 block */ - if ((r = dinfo->cfg_res[0]) == NULL) - return (ENODEV); - - /* Verify the register offset falls within CFG register block */ - if (reg > SIBA_CFG_SIZE-4) - return (EFAULT); - - for (int i = 0; i < 300; i += 10) { - rval = bhnd_bus_read_4(r, reg); - rval &= ~mask; - rval |= (value & mask); - - bhnd_bus_write_4(r, reg, rval); - bhnd_bus_read_4(r, reg); /* read-back */ - DELAY(1); + r = dinfo->cfg_res[0]; - /* 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)); + KASSERT(r != NULL, ("%s missing CFG0 mapping", + device_get_nameunit(dev))); + KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx", + device_get_nameunit(dev), (uintmax_t)reg)); - DELAY(10); - } + rval = bhnd_bus_read_4(r, reg); + rval &= ~mask; + rval |= (value & mask); - return (ETIMEDOUT); + bhnd_bus_write_4(r, reg, rval); + bhnd_bus_read_4(r, reg); /* read-back */ + DELAY(1); } /** - * Spin for up to @p usec waiting for SIBA_TMH_BUSY to clear in - * @p dev's SIBA_CFG0_TMSTATEHIGH register. + * Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state + * 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 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 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 -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; - uint32_t ts_high; + uint32_t rval; if ((r = dinfo->cfg_res[0]) == NULL) return (ENODEV); + value &= mask; for (int i = 0; i < usec; i += 10) { - ts_high = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH); - if (!(ts_high & SIBA_TMH_BUSY)) + rval = bhnd_bus_read_4(r, reg); + if ((rval & mask) == value) return (0); DELAY(10); } - device_printf(dev, "SIBA_TMH_BUSY wait timeout\n"); return (ETIMEDOUT); } diff --git a/sys/dev/bhnd/siba/sibavar.h b/sys/dev/bhnd/siba/sibavar.h index 0e952abb1696..da0a9cb155cc 100644 --- a/sys/dev/bhnd/siba/sibavar.h +++ b/sys/dev/bhnd/siba/sibavar.h @@ -117,11 +117,12 @@ u_int siba_admatch_offset(uint8_t addrspace); int siba_parse_admatch(uint32_t am, uint32_t *addr, 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, uint32_t value, uint32_t mask); -int siba_wait_target_busy(device_t child, - struct siba_devinfo *dinfo, int usec); +int siba_wait_target_state(device_t dev, + struct siba_devinfo *dinfo, bus_size_t reg, + uint32_t value, uint32_t mask, u_int usec); /* Sonics configuration register blocks */ |
