Index: head/sys/dev/bhnd/bcma/bcma.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma.c +++ head/sys/dev/bhnd/bcma/bcma.c @@ -39,14 +39,14 @@ #include -#include "bcmavar.h" +#include #include "bcma_dmp.h" #include "bcma_eromreg.h" #include "bcma_eromvar.h" -#include +#include "bcmavar.h" /* RID used when allocating EROM table */ #define BCMA_EROM_RID 0 @@ -91,6 +91,44 @@ return (bhnd_generic_detach(dev)); } +static device_t +bcma_add_child(device_t dev, u_int order, const char *name, int unit) +{ + struct bcma_devinfo *dinfo; + device_t child; + + child = device_add_child_ordered(dev, order, name, unit); + if (child == NULL) + return (NULL); + + if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) { + device_delete_child(dev, child); + return (NULL); + } + + device_set_ivars(child, dinfo); + + return (child); +} + +static void +bcma_child_deleted(device_t dev, device_t child) +{ + struct bhnd_softc *sc; + struct bcma_devinfo *dinfo; + + sc = device_get_softc(dev); + + /* Call required bhnd(4) implementation */ + bhnd_generic_child_deleted(dev, child); + + /* Free bcma device info */ + if ((dinfo = device_get_ivars(child)) != NULL) + bcma_free_dinfo(dev, dinfo); + + device_set_ivars(child, NULL); +} + static int bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { @@ -125,6 +163,9 @@ case BHND_IVAR_CORE_UNIT: *result = ci->unit; return (0); + case BHND_IVAR_PMU_INFO: + *result = (uintptr_t) dinfo->pmu_info; + return (0); default: return (ENOENT); } @@ -133,6 +174,10 @@ static int bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { + struct bcma_devinfo *dinfo; + + dinfo = device_get_ivars(child); + switch (index) { case BHND_IVAR_VENDOR: case BHND_IVAR_DEVICE: @@ -143,6 +188,9 @@ case BHND_IVAR_CORE_INDEX: case BHND_IVAR_CORE_UNIT: return (EINVAL); + case BHND_IVAR_PMU_INFO: + dinfo->pmu_info = (struct bhnd_core_pmu_info *) value; + return (0); default: return (ENOENT); } @@ -156,136 +204,262 @@ } static int -bcma_reset_core(device_t dev, device_t child, uint16_t flags) +bcma_read_iost(device_t dev, device_t child, uint16_t *iost) { - struct bcma_devinfo *dinfo; + uint32_t value; + int error; + + if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4))) + return (error); + + /* Return only the bottom 16 bits */ + *iost = (value & BCMA_DMP_IOST_MASK); + return (0); +} + +static int +bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl) +{ + uint32_t value; + int error; + + if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4))) + return (error); + + /* Return only the bottom 16 bits */ + *ioctl = (value & BCMA_DMP_IOCTRL_MASK); + return (0); +} + +static int +bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask) +{ + struct bcma_devinfo *dinfo; + struct bhnd_resource *r; + uint32_t ioctl; if (device_get_parent(child) != dev) - BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); + return (EINVAL); dinfo = device_get_ivars(child); - - /* Can't reset the core without access to the agent registers */ - if (dinfo->res_agent == NULL) + if ((r = dinfo->res_agent) == NULL) return (ENODEV); - /* Start reset */ - bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, BHND_RESET_CF_ENABLE); - bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF); - DELAY(10); + /* Write new value */ + ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL); + ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask); + ioctl |= (value & mask); - /* Disable clock */ - bhnd_bus_write_4(dinfo->res_agent, BHND_CF, flags); - bhnd_bus_read_4(dinfo->res_agent, BHND_CF); - DELAY(10); + bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl); - /* Enable clocks & force clock gating */ - bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | - BHND_CF_FGC | flags); - bhnd_bus_read_4(dinfo->res_agent, BHND_CF); + /* Perform read-back and wait for completion */ + bhnd_bus_read_4(r, BCMA_DMP_IOCTRL); DELAY(10); - /* Complete reset */ - bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, 0); - bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF); - DELAY(10); + return (0); +} - /* Release force clock gating */ - bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | flags); - bhnd_bus_read_4(dinfo->res_agent, BHND_CF); - DELAY(10); +static bool +bcma_is_hw_suspended(device_t dev, device_t child) +{ + uint32_t rst; + uint16_t ioctl; + int error; + + /* Is core held in RESET? */ + error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4); + if (error) { + device_printf(child, "error reading HW reset state: %d\n", + error); + return (true); + } + + if (rst & BMCA_DMP_RC_RESET) + return (true); + + /* Is core clocked? */ + error = bhnd_read_ioctl(child, &ioctl); + if (error) { + device_printf(child, "error reading HW ioctl register: %d\n", + error); + return (true); + } + + if (!(ioctl & BHND_IOCTL_CLK_EN)) + return (true); + + return (false); +} + +static int +bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl) +{ + struct bcma_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + struct bhnd_resource *r; + int error; + + if (device_get_parent(child) != dev) + return (EINVAL); + + dinfo = device_get_ivars(child); + pm = dinfo->pmu_info; + + /* We require exclusive control over BHND_IOCTL_CLK_EN and + * BHND_IOCTL_CLK_FORCE. */ + if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE)) + return (EINVAL); + + /* Can't suspend the core without access to the agent registers */ + if ((r = dinfo->res_agent) == NULL) + return (ENODEV); + + /* Place core into known RESET state */ + if ((error = BHND_BUS_SUSPEND_HW(dev, child))) + return (error); + + /* + * Leaving the core in reset: + * - Set the caller's IOCTL flags + * - Enable clocks + * - Force clock distribution to ensure propagation throughout the + * core. + */ + error = bhnd_write_ioctl(child, + ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE, UINT16_MAX); + if (error) + return (error); + + /* Bring the core out of reset */ + if ((error = bcma_dmp_write_reset(child, dinfo, 0x0))) + return (error); + + /* Disable forced clock gating (leaving clock enabled) */ + error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE); + if (error) + return (error); return (0); } static int -bcma_suspend_core(device_t dev, device_t child) +bcma_suspend_hw(device_t dev, device_t child) { - struct bcma_devinfo *dinfo; + struct bcma_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + struct bhnd_resource *r; + uint32_t rst; + int error; if (device_get_parent(child) != dev) - BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); + return (EINVAL); dinfo = device_get_ivars(child); + pm = dinfo->pmu_info; /* Can't suspend the core without access to the agent registers */ - if (dinfo->res_agent == NULL) + if ((r = dinfo->res_agent) == NULL) return (ENODEV); - // TODO - perform suspend + /* Wait for any pending reset operations to clear */ + if ((error = bcma_dmp_wait_reset(child, dinfo))) + return (error); - return (ENXIO); + /* Already in reset? */ + rst = bhnd_bus_read_4(r, BCMA_DMP_RESETCTRL); + if (rst & BMCA_DMP_RC_RESET) + return (0); + + /* Put core into reset */ + if ((error = bcma_dmp_write_reset(child, dinfo, BMCA_DMP_RC_RESET))) + return (error); + + /* Clear core flags */ + if ((error = bhnd_write_ioctl(child, 0x0, UINT16_MAX))) + return (error); + + /* Inform PMU that all outstanding request state should be discarded */ + if (pm != NULL) { + if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm))) + return (error); + } + + return (0); } -static uint32_t -bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) +static int +bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value, + u_int width) { struct bcma_devinfo *dinfo; struct bhnd_resource *r; /* Must be a directly attached child core */ if (device_get_parent(child) != dev) - return (UINT32_MAX); + return (EINVAL); /* Fetch the agent registers */ dinfo = device_get_ivars(child); if ((r = dinfo->res_agent) == NULL) - return (UINT32_MAX); + return (ENODEV); /* Verify bounds */ if (offset > rman_get_size(r->res)) - return (UINT32_MAX); + return (EFAULT); if (rman_get_size(r->res) - offset < width) - return (UINT32_MAX); + return (EFAULT); switch (width) { case 1: - return (bhnd_bus_read_1(r, offset)); + *((uint8_t *)value) = bhnd_bus_read_1(r, offset); + return (0); case 2: - return (bhnd_bus_read_2(r, offset)); + *((uint16_t *)value) = bhnd_bus_read_2(r, offset); + return (0); case 4: - return (bhnd_bus_read_4(r, offset)); + *((uint32_t *)value) = bhnd_bus_read_4(r, offset); + return (0); default: - return (UINT32_MAX); + return (EINVAL); } } -static void -bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, - u_int width) +static int +bcma_write_config(device_t dev, device_t child, bus_size_t offset, + const void *value, u_int width) { struct bcma_devinfo *dinfo; struct bhnd_resource *r; /* Must be a directly attached child core */ if (device_get_parent(child) != dev) - return; + return (EINVAL); /* Fetch the agent registers */ dinfo = device_get_ivars(child); if ((r = dinfo->res_agent) == NULL) - return; + return (ENODEV); /* Verify bounds */ if (offset > rman_get_size(r->res)) - return; + return (EFAULT); if (rman_get_size(r->res) - offset < width) - return; + return (EFAULT); switch (width) { case 1: - bhnd_bus_write_1(r, offset, val); - break; + bhnd_bus_write_1(r, offset, *(const uint8_t *)value); + return (0); case 2: - bhnd_bus_write_2(r, offset, val); - break; + bhnd_bus_write_2(r, offset, *(const uint16_t *)value); + return (0); case 4: - bhnd_bus_write_4(r, offset, val); - break; + bhnd_bus_write_4(r, offset, *(const uint32_t *)value); + return (0); default: - break; + return (EINVAL); } } @@ -501,19 +675,6 @@ return (0); } -static struct bhnd_devinfo * -bcma_alloc_bhnd_dinfo(device_t dev) -{ - struct bcma_devinfo *dinfo = bcma_alloc_dinfo(dev); - return ((struct bhnd_devinfo *)dinfo); -} - -static void -bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo) -{ - bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo); -} - /** * Scan the device enumeration ROM table, adding all valid discovered cores to * the bus. @@ -607,16 +768,20 @@ DEVMETHOD(device_detach, bcma_detach), /* Bus interface */ + DEVMETHOD(bus_add_child, bcma_add_child), + DEVMETHOD(bus_child_deleted, bcma_child_deleted), DEVMETHOD(bus_read_ivar, bcma_read_ivar), DEVMETHOD(bus_write_ivar, bcma_write_ivar), DEVMETHOD(bus_get_resource_list, bcma_get_resource_list), /* BHND interface */ DEVMETHOD(bhnd_bus_get_erom_class, bcma_get_erom_class), - DEVMETHOD(bhnd_bus_alloc_devinfo, bcma_alloc_bhnd_dinfo), - DEVMETHOD(bhnd_bus_free_devinfo, bcma_free_bhnd_dinfo), - DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core), - DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core), + DEVMETHOD(bhnd_bus_read_ioctl, bcma_read_ioctl), + DEVMETHOD(bhnd_bus_write_ioctl, bcma_write_ioctl), + DEVMETHOD(bhnd_bus_read_iost, bcma_read_iost), + DEVMETHOD(bhnd_bus_is_hw_suspended, bcma_is_hw_suspended), + DEVMETHOD(bhnd_bus_reset_hw, bcma_reset_hw), + DEVMETHOD(bhnd_bus_suspend_hw, bcma_suspend_hw), DEVMETHOD(bhnd_bus_read_config, bcma_read_config), DEVMETHOD(bhnd_bus_write_config, bcma_write_config), DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count), Index: head/sys/dev/bhnd/bcma/bcma_dmp.h =================================================================== --- head/sys/dev/bhnd/bcma/bcma_dmp.h +++ head/sys/dev/bhnd/bcma/bcma_dmp.h @@ -245,8 +245,14 @@ #define BCMA_DMP_OOBSEL_6_SHIFT BCMA_DMP_OOBSEL_2_SHIFT #define BCMA_DMP_OOBSEL_7_SHIFT BCMA_DMP_OOBSEL_3_SHIFT +/* ioctrl */ +#define BCMA_DMP_IOCTRL_MASK 0x0000FFFF + +/* iostatus */ +#define BCMA_DMP_IOST_MASK 0x0000FFFF + /* resetctrl */ -#define BMCA_DMP_RC_RESET 1 +#define BMCA_DMP_RC_RESET 0x00000001 /* config */ #define BCMA_DMP_CFG_OOB 0x00000020 Index: head/sys/dev/bhnd/bcma/bcma_subr.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_subr.c +++ head/sys/dev/bhnd/bcma/bcma_subr.c @@ -41,6 +41,8 @@ #include +#include "bcma_dmp.h" + #include "bcmavar.h" /* Return the resource ID for a device's agent register allocation */ @@ -368,3 +370,62 @@ free(sport, M_BHND); } + +/** + * Given a bcma(4) child's device info, spin waiting for the device's DMP + * resetstatus register to clear. + * + * @param child The bcma(4) child device. + * @param dinfo The @p child device info. + * + * @retval 0 success + * @retval ENODEV if @p dinfo does not map an agent register resource. + * @retval ETIMEDOUT if timeout occurs + */ +int +bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo) +{ + uint32_t rst; + + if (dinfo->res_agent == NULL) + return (ENODEV); + + /* 300us should be long enough, but there are references to this + * requiring up to 10ms when performing reset of an 80211 core + * after a MAC PSM microcode watchdog event. */ + for (int i = 0; i < 10000; i += 10) { + rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETSTATUS); + if (rst == 0) + return (0); + + DELAY(10); + } + + device_printf(child, "BCMA_DMP_RESETSTATUS timeout\n"); + return (ETIMEDOUT); +} + +/** + * Set the bcma(4) child's DMP resetctrl register value, and then wait + * for all backplane operations to complete. + * + * @param child The bcma(4) child device. + * @param dinfo The @p child device info. + * @param value The new ioctrl value to set. + * + * @retval 0 success + * @retval ENODEV if @p dinfo does not map an agent register resource. + * @retval ETIMEDOUT if timeout occurs waiting for reset completion + */ +int +bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value) +{ + if (dinfo->res_agent == NULL) + return (ENODEV); + + bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value); + bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */ + DELAY(10); + + return (bcma_dmp_wait_reset(child, dinfo)); +} Index: head/sys/dev/bhnd/bcma/bcmavar.h =================================================================== --- head/sys/dev/bhnd/bcma/bcmavar.h +++ head/sys/dev/bhnd/bcma/bcmavar.h @@ -99,6 +99,11 @@ struct bcma_sport *bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type); void bcma_free_sport(struct bcma_sport *sport); +int bcma_dmp_wait_reset(device_t child, + struct bcma_devinfo *dinfo); +int bcma_dmp_write_reset(device_t child, + struct bcma_devinfo *dinfo, uint32_t value); + /** BCMA master port descriptor */ struct bcma_mport { bcma_pid_t mp_num; /**< AXI port identifier (bus-unique) */ @@ -150,14 +155,14 @@ * BCMA per-device info */ struct bcma_devinfo { - struct bhnd_devinfo bhnd_dinfo; /**< superclass device info. */ + struct resource_list resources; /**< Slave port memory regions. */ + struct bcma_corecfg *corecfg; /**< IP core/block config */ - struct resource_list resources; /**< Slave port memory regions. */ - struct bcma_corecfg *corecfg; /**< IP core/block config */ + struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not + * all bcma(4) cores have or require an agent. */ + int rid_agent; /**< Agent resource ID, or -1 */ - struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not - * all bcma(4) cores have or require an agent. */ - int rid_agent; /**< Agent resource ID, or -1 */ + struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */ }; Index: head/sys/dev/bhnd/bhnd.h =================================================================== --- head/sys/dev/bhnd/bhnd.h +++ head/sys/dev/bhnd/bhnd.h @@ -46,6 +46,8 @@ #include "nvram/bhnd_nvram.h" +struct bhnd_core_pmu_info; + extern devclass_t bhnd_devclass; extern devclass_t bhnd_hostb_devclass; extern devclass_t bhnd_nvram_devclass; @@ -67,6 +69,7 @@ BHND_IVAR_CORE_UNIT, /**< Bus-assigned core unit number, assigned sequentially (starting at 0) for each vendor/device pair. */ + BHND_IVAR_PMU_INFO, /**< Internal bus-managed PMU state */ }; /** @@ -99,6 +102,39 @@ }; + +/** + * Per-core IOCTL flags common to all bhnd(4) cores. + */ +enum { + BHND_IOCTL_BIST = 0x8000, /**< Initiate a built-in self-test (BIST). Must be cleared + after BIST results are read via BHND_IOST_BIST_* */ + BHND_IOCTL_PME = 0x4000, /**< Enable posting of power management events by the core. */ + BHND_IOCTL_CFLAGS = 0x3FFC, /**< Reserved for core-specific ioctl flags. */ + BHND_IOCTL_CLK_FORCE = 0x0002, /**< Force disable of clock gating, resulting in all clocks + being distributed within the core. Should be set when + asserting/deasserting reset to ensure the reset signal + fully propagates to the entire core. */ + BHND_IOCTL_CLK_EN = 0x0001, /**< If cleared, the core clock will be disabled. Should be + set during normal operation, and cleared when the core is + held in reset. */ +}; + +/** + * Per-core IOST flags common to all bhnd(4) cores. + */ +enum { + BHND_IOST_BIST_DONE = 0x8000, /**< Set upon BIST completion (see BHND_IOCTL_BIST), and cleared + if 0 is written to BHND_IOCTL_BIST. */ + BHND_IOST_BIST_FAIL = 0x4000, /**< Set upon detection of a BIST error; the value is unspecified + if BIST has not completed and BHND_IOST_BIST_DONE is not set. */ + BHND_IOST_CLK = 0x2000, /**< Set if the core has requested that gated clocks be enabled, or + cleared otherwise. The value is undefined if a core does not + support clock gating. */ + BHND_IOST_DMA64 = 0x1000, /**< Set if this core supports 64-bit DMA */ + BHND_IOST_CFLAGS = 0x0FFC, /**< Reserved for core-specific status flags. */ +}; + /* * Simplified accessors for bhnd device ivars */ @@ -113,6 +149,7 @@ BHND_ACCESSOR(device_name, DEVICE_NAME, const char *); BHND_ACCESSOR(core_index, CORE_INDEX, u_int); BHND_ACCESSOR(core_unit, CORE_UNIT, int); +BHND_ACCESSOR(pmu_info, PMU_INFO, struct bhnd_core_pmu_info *); #undef BHND_ACCESSOR @@ -451,6 +488,119 @@ return (BHND_BUS_GET_CHIPID(device_get_parent(dev), dev)); }; + +/** + * Read the current value of a bhnd(4) device's per-core I/O control register. + * + * @param dev The bhnd bus child device to be queried. + * @param[out] ioctl On success, the I/O control register value. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval ENODEV If agent/config space for @p child is unavailable. + * @retval non-zero If reading the IOCTL register otherwise fails, a regular + * unix error code will be returned. + */ +static inline int +bhnd_read_ioctl(device_t dev, uint16_t *ioctl) +{ + return (BHND_BUS_READ_IOCTL(device_get_parent(dev), dev, ioctl)); +} + +/** + * Write @p value and @p mask to a bhnd(4) device's per-core I/O control + * register. + * + * @param dev The bhnd bus child device for which the IOCTL register will be + * written. + * @param value The value to be written (see BHND_IOCTL_*). + * @param mask Only the bits defined by @p mask will be updated from @p value. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval ENODEV If agent/config space for @p child is unavailable. + * @retval non-zero If writing the IOCTL register otherwise fails, a regular + * unix error code will be returned. + */ +static inline int +bhnd_write_ioctl(device_t dev, uint16_t value, uint16_t mask) +{ + return (BHND_BUS_WRITE_IOCTL(device_get_parent(dev), dev, value, mask)); +} + +/** + * Read the current value of a bhnd(4) device's per-core I/O status register. + * + * @param dev The bhnd bus child device to be queried. + * @param[out] iost On success, the I/O status register value. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval ENODEV If agent/config space for @p child is unavailable. + * @retval non-zero If reading the IOST register otherwise fails, a regular + * unix error code will be returned. + */ +static inline int +bhnd_read_iost(device_t dev, uint16_t *iost) +{ + return (BHND_BUS_READ_IOST(device_get_parent(dev), dev, iost)); +} + +/** + * Return true if the given bhnd device's hardware is currently held + * in a RESET state or otherwise not clocked (BHND_IOCTL_CLK_EN). + * + * @param dev The device to query. + * + * @retval true If @p dev is held in RESET or not clocked (BHND_IOCTL_CLK_EN), + * or an error occured determining @p dev's hardware state. + * @retval false If @p dev is clocked and is not held in RESET. + */ +static inline bool +bhnd_is_hw_suspended(device_t dev) +{ + return (BHND_BUS_IS_HW_SUSPENDED(device_get_parent(dev), dev)); +} + +/** + * Place the bhnd(4) device's hardware into a reset state, and then bring the + * hardware out of reset with BHND_IOCTL_CLK_EN and @p ioctl flags set. + * + * Any clock or resource PMU requests previously made by @p dev will be + * invalidated. + * + * @param dev The device to be reset. + * @param ioctl Device-specific core ioctl flags to be supplied on reset + * (see BHND_IOCTL_*). + * + * @retval 0 success + * @retval non-zero error + */ +static inline int +bhnd_reset_hw(device_t dev, uint16_t ioctl) +{ + return (BHND_BUS_RESET_HW(device_get_parent(dev), dev, ioctl)); +} + +/** + * Suspend @p child's hardware in a low-power reset state. + * + * Any clock or resource PMU requests previously made by @p dev will be + * invalidated. + * + * The hardware may be brought out of reset via bhnd_reset_hw(). + * + * @param dev The device to be suspended. + * + * @retval 0 success + * @retval non-zero error + */ +static inline int +bhnd_suspend_hw(device_t dev) +{ + return (BHND_BUS_SUSPEND_HW(device_get_parent(dev), dev)); +} + /** * If supported by the chipset, return the clock source for the given clock. * @@ -630,17 +780,18 @@ /** * Request that @p clock (or faster) be routed to @p dev. * - * A driver must ask the bhnd bus to allocate clock request state + * @note A driver must ask the bhnd bus to allocate clock request state * via bhnd_alloc_pmu() before it can request clock resources. * - * Request multiplexing is managed by the bus. + * @note Any outstanding PMU clock requests will be discarded upon calling + * BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW(). * * @param dev The bhnd(4) device to which @p clock should be routed. * @param clock The requested clock source. * * @retval 0 success * @retval ENODEV If an unsupported clock was requested. - * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable. + * @retval ENXIO If the PMU has not been initialized or is otherwise unvailable, */ static inline int bhnd_request_clock(device_t dev, bhnd_clock clock) @@ -654,12 +805,13 @@ * This will power any clock sources (e.g. XTAL, PLL, etc) required for * @p clocks and wait until they are ready, discarding any previous * requests by @p dev. - * - * Request multiplexing is managed by the bus. * - * A driver must ask the bhnd bus to allocate clock request state + * @note A driver must ask the bhnd bus to allocate clock request state * via bhnd_alloc_pmu() before it can request clock resources. - * + * + * @note Any outstanding PMU clock requests will be discarded upon calling + * BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW(). + * * @param dev The requesting bhnd(4) device. * @param clocks The clock(s) to be enabled. * @@ -676,9 +828,12 @@ /** * Power up an external PMU-managed resource assigned to @p dev. * - * A driver must ask the bhnd bus to allocate PMU request state + * @note A driver must ask the bhnd bus to allocate PMU request state * via bhnd_alloc_pmu() before it can request PMU resources. * + * @note Any outstanding PMU resource requests will be released upon calling + * bhnd_reset_hw() or bhnd_suspend_hw(). + * * @param dev The requesting bhnd(4) device. * @param rsrc The core-specific external resource identifier. * @@ -711,13 +866,14 @@ return (BHND_BUS_RELEASE_EXT_RSRC(device_get_parent(dev), dev, rsrc)); } - /** * Read @p width bytes at @p offset from the bus-specific agent/config * space of @p dev. * * @param dev The bhnd device for which @p offset should be read. * @param offset The offset to be read. + * @param[out] value On success, the will be set to the @p width value read + * at @p offset. * @param width The size of the access. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. In the case of @@ -725,32 +881,49 @@ * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval EINVAL If @p width is not one of 1, 2, or 4 bytes. + * @retval ENODEV If accessing agent/config space for @p child is unsupported. + * @retval EFAULT If reading @p width at @p offset exceeds the bounds of + * the mapped agent/config space for @p child. */ static inline uint32_t -bhnd_read_config(device_t dev, bus_size_t offset, u_int width) +bhnd_read_config(device_t dev, bus_size_t offset, void *value, u_int width) { return (BHND_BUS_READ_CONFIG(device_get_parent(dev), dev, offset, - width)); + value, width)); } /** - * Read @p width bytes at @p offset from the bus-specific agent/config + * Write @p width bytes at @p offset to the bus-specific agent/config * space of @p dev. * * @param dev The bhnd device for which @p offset should be read. * @param offset The offset to be written. - * @param width The size of the access. Must be 1, 2 or 4 bytes. + * @param value A pointer to the value to be written. + * @param width The size of @p value. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. In the case of * bcma(4), this method provides access to the first agent port of @p child. * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval EINVAL If @p width is not one of 1, 2, or 4 bytes. + * @retval ENODEV If accessing agent/config space for @p child is unsupported. + * @retval EFAULT If reading @p width at @p offset exceeds the bounds of + * the mapped agent/config space for @p child. */ -static inline void -bhnd_write_config(device_t dev, bus_size_t offset, uint32_t val, u_int width) +static inline int +bhnd_write_config(device_t dev, bus_size_t offset, const void *value, + u_int width) { - BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset, val, width); + return (BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset, + value, width)); } /** Index: head/sys/dev/bhnd/bhnd.c =================================================================== --- head/sys/dev/bhnd/bhnd.c +++ head/sys/dev/bhnd/bhnd.c @@ -631,7 +631,6 @@ struct bhnd_softc *sc; struct bhnd_resource *br; struct chipc_caps *ccaps; - struct bhnd_devinfo *dinfo; struct bhnd_core_pmu_info *pm; struct resource_list *rl; struct resource_list_entry *rle; @@ -644,7 +643,7 @@ GIANT_REQUIRED; /* for newbus */ sc = device_get_softc(dev); - dinfo = device_get_ivars(child); + pm = bhnd_get_pmu_info(child); pmu_regs = BHND_CLK_CTL_ST; if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) { @@ -660,7 +659,7 @@ } /* already allocated? */ - if (dinfo->pmu_info != NULL) { + if (pm != NULL) { panic("duplicate PMU allocation for %s", device_get_nameunit(child)); } @@ -728,7 +727,7 @@ br->res = rle->res; br->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0); - pm = malloc(sizeof(*dinfo->pmu_info), M_BHND, M_NOWAIT); + pm = malloc(sizeof(*pm), M_BHND, M_NOWAIT); if (pm == NULL) { free(br, M_BHND); return (ENOMEM); @@ -738,7 +737,7 @@ pm->pm_res = br; pm->pm_regs = pmu_regs; - dinfo->pmu_info = pm; + bhnd_set_pmu_info(child, pm); return (0); } @@ -749,14 +748,13 @@ bhnd_generic_release_pmu(device_t dev, device_t child) { struct bhnd_softc *sc; - struct bhnd_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; device_t pmu; int error; GIANT_REQUIRED; /* for newbus */ sc = device_get_softc(dev); - dinfo = device_get_ivars(child); if ((pmu = bhnd_find_pmu(sc)) == NULL) { device_printf(sc->dev, @@ -765,16 +763,17 @@ } /* dispatch release request */ - if (dinfo->pmu_info == NULL) + pm = bhnd_get_pmu_info(child); + if (pm == NULL) panic("pmu over-release for %s", device_get_nameunit(child)); - if ((error = BHND_PMU_CORE_RELEASE(pmu, dinfo->pmu_info))) + if ((error = BHND_PMU_CORE_RELEASE(pmu, pm))) return (error); /* free PMU info */ - free(dinfo->pmu_info->pm_res, M_BHND); - free(dinfo->pmu_info, M_BHND); - dinfo->pmu_info = NULL; + bhnd_set_pmu_info(child, NULL); + free(pm->pm_res, M_BHND); + free(pm, M_BHND); return (0); } @@ -786,13 +785,11 @@ bhnd_generic_request_clock(device_t dev, device_t child, bhnd_clock clock) { struct bhnd_softc *sc; - struct bhnd_devinfo *dinfo; struct bhnd_core_pmu_info *pm; sc = device_get_softc(dev); - dinfo = device_get_ivars(child); - if ((pm = dinfo->pmu_info) == NULL) + if ((pm = bhnd_get_pmu_info(child)) == NULL) panic("no active PMU request state"); /* dispatch request to PMU */ @@ -806,13 +803,11 @@ bhnd_generic_enable_clocks(device_t dev, device_t child, uint32_t clocks) { struct bhnd_softc *sc; - struct bhnd_devinfo *dinfo; struct bhnd_core_pmu_info *pm; sc = device_get_softc(dev); - dinfo = device_get_ivars(child); - if ((pm = dinfo->pmu_info) == NULL) + if ((pm = bhnd_get_pmu_info(child)) == NULL) panic("no active PMU request state"); /* dispatch request to PMU */ @@ -826,13 +821,11 @@ bhnd_generic_request_ext_rsrc(device_t dev, device_t child, u_int rsrc) { struct bhnd_softc *sc; - struct bhnd_devinfo *dinfo; struct bhnd_core_pmu_info *pm; sc = device_get_softc(dev); - dinfo = device_get_ivars(child); - if ((pm = dinfo->pmu_info) == NULL) + if ((pm = bhnd_get_pmu_info(child)) == NULL) panic("no active PMU request state"); /* dispatch request to PMU */ @@ -846,13 +839,11 @@ bhnd_generic_release_ext_rsrc(device_t dev, device_t child, u_int rsrc) { struct bhnd_softc *sc; - struct bhnd_devinfo *dinfo; struct bhnd_core_pmu_info *pm; sc = device_get_softc(dev); - dinfo = device_get_ivars(child); - if ((pm = dinfo->pmu_info) == NULL) + if ((pm = bhnd_get_pmu_info(child)) == NULL) panic("no active PMU request state"); /* dispatch request to PMU */ @@ -1035,43 +1026,6 @@ } /** - * Default bhnd(4) bus driver implementation of BUS_ADD_CHILD(). - * - * This implementation manages internal bhnd(4) state, and must be called - * by subclassing drivers. - */ -device_t -bhnd_generic_add_child(device_t dev, u_int order, const char *name, int unit) -{ - struct bhnd_devinfo *dinfo; - device_t child; - - child = device_add_child_ordered(dev, order, name, unit); - if (child == NULL) - return (NULL); - - if ((dinfo = BHND_BUS_ALLOC_DEVINFO(dev)) == NULL) { - device_delete_child(dev, child); - return (NULL); - } - - device_set_ivars(child, dinfo); - - return (child); -} - -/** - * Default bhnd(4) bus driver implementation of BHND_BUS_CHILD_ADDED(). - * - * This implementation manages internal bhnd(4) state, and must be called - * by subclassing drivers. - */ -void -bhnd_generic_child_added(device_t dev, device_t child) -{ -} - -/** * Default bhnd(4) bus driver implementation of BUS_CHILD_DELETED(). * * This implementation manages internal bhnd(4) state, and must be called @@ -1081,21 +1035,16 @@ bhnd_generic_child_deleted(device_t dev, device_t child) { struct bhnd_softc *sc; - struct bhnd_devinfo *dinfo; sc = device_get_softc(dev); /* Free device info */ - if ((dinfo = device_get_ivars(child)) != NULL) { - if (dinfo->pmu_info != NULL) { - /* Releasing PMU requests automatically would be nice, - * but we can't reference per-core PMU register - * resource after driver detach */ - panic("%s leaked device pmu state\n", - device_get_nameunit(child)); - } - - BHND_BUS_FREE_DEVINFO(dev, dinfo); + if (bhnd_get_pmu_info(child) != NULL) { + /* Releasing PMU requests automatically would be nice, + * but we can't reference per-core PMU register + * resource after driver detach */ + panic("%s leaked device pmu state\n", + device_get_nameunit(child)); } /* Clean up platform device references */ @@ -1228,7 +1177,6 @@ /* Bus interface */ DEVMETHOD(bus_new_pass, bhnd_new_pass), - DEVMETHOD(bus_add_child, bhnd_generic_add_child), DEVMETHOD(bus_child_deleted, bhnd_generic_child_deleted), DEVMETHOD(bus_probe_nomatch, bhnd_generic_probe_nomatch), DEVMETHOD(bus_print_child, bhnd_generic_print_child), @@ -1269,7 +1217,6 @@ DEVMETHOD(bhnd_bus_request_ext_rsrc, bhnd_generic_request_ext_rsrc), DEVMETHOD(bhnd_bus_release_ext_rsrc, bhnd_generic_release_ext_rsrc), - DEVMETHOD(bhnd_bus_child_added, bhnd_generic_child_added), DEVMETHOD(bhnd_bus_is_region_valid, bhnd_generic_is_region_valid), DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_generic_get_nvram_var), Index: head/sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_bus_if.m +++ head/sys/dev/bhnd/bhnd_bus_if.m @@ -63,6 +63,46 @@ panic("bhnd_bus_get_chipid unimplemented"); } + static int + bhnd_bus_null_read_ioctl(device_t dev, device_t child, uint16_t *ioctl) + { + panic("bhnd_bus_read_ioctl unimplemented"); + } + + + static int + bhnd_bus_null_write_ioctl(device_t dev, device_t child, uint16_t value, + uint16_t mask) + { + panic("bhnd_bus_write_ioctl unimplemented"); + } + + + static int + bhnd_bus_null_read_iost(device_t dev, device_t child, uint16_t *iost) + { + panic("bhnd_bus_read_iost unimplemented"); + } + + static bool + bhnd_bus_null_is_hw_suspended(device_t dev, device_t child) + { + panic("bhnd_bus_is_hw_suspended unimplemented"); + } + + static int + bhnd_bus_null_reset_hw(device_t dev, device_t child, uint16_t ioctl) + { + panic("bhnd_bus_reset_hw unimplemented"); + } + + + static int + bhnd_bus_null_suspend_hw(device_t dev, device_t child) + { + panic("bhnd_bus_suspend_hw unimplemented"); + } + static bhnd_attach_type bhnd_bus_null_get_attach_type(device_t dev, device_t child) { @@ -161,16 +201,16 @@ panic("bhnd_bus_release_ext_rsrc unimplemented"); } - static uint32_t + static int bhnd_bus_null_read_config(device_t dev, device_t child, - bus_size_t offset, u_int width) + bus_size_t offset, void *value, u_int width) { panic("bhnd_bus_null_read_config unimplemented"); } static void bhnd_bus_null_write_config(device_t dev, device_t child, - bus_size_t offset, uint32_t val, u_int width) + bus_size_t offset, void *value, u_int width) { panic("bhnd_bus_null_write_config unimplemented"); } @@ -344,32 +384,6 @@ } DEFAULT bhnd_bus_null_read_board_info; /** - * Allocate and zero-initialize a buffer suitably sized and aligned for a - * bhnd_devinfo structure. - * - * @param dev The bhnd bus device. - * - * @retval non-NULL success - * @retval NULL allocation failed - */ -METHOD struct bhnd_devinfo * alloc_devinfo { - device_t dev; -}; - -/** - * Release memory previously allocated for @p devinfo. - * - * @param dev The bhnd bus device. - * @param dinfo A devinfo buffer previously allocated via - * BHND_BUS_ALLOC_DEVINFO(). - */ -METHOD void free_devinfo { - device_t dev; - struct bhnd_devinfo *dinfo; -}; - - -/** * Return the number of interrupts to be assigned to @p child via * BHND_BUS_ASSIGN_INTR(). * @@ -455,34 +469,123 @@ } DEFAULT bhnd_bus_null_child_added; /** - * Reset the device's hardware core. + * Read the current value of @p child's I/O control register. * - * @param dev The parent of @p child. + * @param dev The bhnd bus parent of @p child. + * @param child The bhnd device for which the I/O control register should be + * read. + * @param[out] ioctl On success, the I/O control register value. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval ENODEV If agent/config space for @p child is unavailable. + * @retval non-zero If reading the IOCTL register otherwise fails, a regular + * unix error code will be returned. + */ +METHOD int read_ioctl { + device_t dev; + device_t child; + uint16_t *ioctl; +} DEFAULT bhnd_bus_null_read_ioctl; + +/** + * Write @p value with @p mask to @p child's I/O control register. + * + * @param dev The bhnd bus parent of @p child. + * @param child The bhnd device for which the I/O control register should + * be updated. + * @param value The value to be written (see also BHND_IOCTL_*). + * @param mask Only the bits defined by @p mask will be updated from @p value. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval ENODEV If agent/config space for @p child is unavailable. + * @retval non-zero If writing the IOCTL register otherwise fails, a regular + * unix error code will be returned. + */ +METHOD int write_ioctl { + device_t dev; + device_t child; + uint16_t value; + uint16_t mask; +} DEFAULT bhnd_bus_null_write_ioctl; + +/** + * Read the current value of @p child's I/O status register. + * + * @param dev The bhnd bus parent of @p child. + * @param child The bhnd device for which the I/O status register should be + * read. + * @param[out] iost On success, the I/O status register value. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval ENODEV If agent/config space for @p child is unavailable. + * @retval non-zero If reading the IOST register otherwise fails, a regular + * unix error code will be returned. + */ +METHOD int read_iost { + device_t dev; + device_t child; + uint16_t *iost; +} DEFAULT bhnd_bus_null_read_iost; + + +/** + * Return true if the given bhnd device's hardware is currently held + * in a RESET state or otherwise not clocked (BHND_IOCTL_CLK_EN). + * + * @param dev The bhnd bus parent of @p child. + * @param child The device to query. + * + * @retval true If @p child is held in RESET or not clocked (BHND_IOCTL_CLK_EN), + * or an error occured determining @p child's hardware state. + * @retval false If @p child is clocked and is not held in RESET. + */ +METHOD bool is_hw_suspended { + device_t dev; + device_t child; +} DEFAULT bhnd_bus_null_is_hw_suspended; + +/** + * Place the bhnd(4) device's hardware into a reset state, and then bring the + * hardware out of reset with BHND_IOCTL_CLK_EN and @p ioctl flags set. + * + * Any clock or resource PMU requests previously made by @p child will be + * invalidated. + * + * @param dev The bhnd bus parent of @p child. * @param child The device to be reset. - * @param flags Device-specific core flags to be supplied on reset. + * @param ioctl Device-specific core ioctl flags to be supplied on reset + * (see BHND_IOCTL_*). * * @retval 0 success * @retval non-zero error */ -METHOD int reset_core { +METHOD int reset_hw { device_t dev; device_t child; - uint16_t flags; -} + uint16_t ioctl; +} DEFAULT bhnd_bus_null_reset_hw; /** - * Suspend a device hardware core. + * Suspend @p child's hardware in a low-power reset state. * - * @param dev The parent of @p child. - * @param child The device to be reset. + * Any clock or resource PMU requests previously made by @p dev will be + * invalidated. + * + * The hardware may be brought out of reset via bhnd_reset_hw(). + * + * @param dev The bhnd bus parent of @P child. + * @param dev The device to be suspended. * * @retval 0 success * @retval non-zero error */ -METHOD int suspend_core { +METHOD int suspend_hw { device_t dev; device_t child; -} +} DEFAULT bhnd_bus_null_suspend_hw; /** * If supported by the chipset, return the clock source for the given clock. @@ -578,10 +681,11 @@ /** * Request that @p clock (or faster) be routed to @p child. * - * A driver must ask the bhnd bus to allocate PMU request state + * @note A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request clock resources. - * - * Request multiplexing is managed by the bus. + * + * @note Any outstanding PMU clock requests will be discarded upon calling + * BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW(). * * @param dev The parent of @p child. * @param child The bhnd device requesting @p clock. @@ -604,11 +708,12 @@ * @p clocks and wait until they are ready, discarding any previous * requests by @p child. * - * Request multiplexing is managed by the bus. - * - * A driver must ask the bhnd bus to allocate PMU request state + * @note A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request clock resources. * + * @note Any outstanding PMU clock requests will be discarded upon calling + * BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW(). + * * @param dev The parent of @p child. * @param child The bhnd device requesting @p clock. * @param clock The requested clock source. @@ -626,9 +731,12 @@ /** * Power up an external PMU-managed resource assigned to @p child. * - * A driver must ask the bhnd bus to allocate PMU request state + * @note A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request PMU resources. * + * @note Any outstanding PMU resource requests will be released upon calling + * BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW(). + * * @param dev The parent of @p child. * @param child The bhnd device requesting @p rsrc. * @param rsrc The core-specific external resource identifier. @@ -646,7 +754,7 @@ /** * Power down an external PMU-managed resource assigned to @p child. * - * A driver must ask the bhnd bus to allocate PMU request state + * @note A driver must ask the bhnd bus to allocate PMU request state * via BHND_BUS_ALLOC_PMU() before it can request PMU resources. * * @param dev The parent of @p child. @@ -670,6 +778,7 @@ * @param dev The parent of @p child. * @param child The bhnd device for which @p offset should be read. * @param offset The offset to be read. + * @param[out] value On success, the bytes read at @p offset. * @param width The size of the access. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. On a bcma(4) bus, this @@ -678,11 +787,19 @@ * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval EINVAL If @p width is not one of 1, 2, or 4 bytes. + * @retval ENODEV If accessing agent/config space for @p child is unsupported. + * @retval EFAULT If reading @p width at @p offset exceeds the bounds of + * the mapped agent/config space for @p child. */ -METHOD uint32_t read_config { +METHOD int read_config { device_t dev; device_t child; bus_size_t offset; + void *value; u_int width; } DEFAULT bhnd_bus_null_read_config; @@ -693,19 +810,27 @@ * @param dev The parent of @p child. * @param child The bhnd device for which @p offset should be read. * @param offset The offset to be written. - * @param width The size of the access. Must be 1, 2 or 4 bytes. + * @param value A pointer to the value to be written. + * @param width The size of @p value. Must be 1, 2 or 4 bytes. * * The exact behavior of this method is bus-specific. In the case of * bcma(4), this method provides access to the first agent port of @p child. * * @note Device drivers should only use this API for functionality * that is not available via another bhnd(4) function. + * + * @retval 0 success + * @retval EINVAL If @p child is not a direct child of @p dev. + * @retval EINVAL If @p width is not one of 1, 2, or 4 bytes. + * @retval ENODEV If accessing agent/config space for @p child is unsupported. + * @retval EFAULT If reading @p width at @p offset exceeds the bounds of + * the mapped agent/config space for @p child. */ -METHOD void write_config { +METHOD int write_config { device_t dev; device_t child; bus_size_t offset; - uint32_t val; + const void *value; u_int width; } DEFAULT bhnd_bus_null_write_config; Index: head/sys/dev/bhnd/bhnd_core.h =================================================================== --- head/sys/dev/bhnd/bhnd_core.h +++ head/sys/dev/bhnd/bhnd_core.h @@ -1,49 +0,0 @@ -/*- - * Copyright (c) 2015 Landon Fuller - * Copyright (c) 2010 Broadcom Corporation - * - * This file is derived from the hndsoc.h header distributed with - * Broadcom's initial brcm80211 Linux driver release, as - * contributed to the Linux staging repository. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * $FreeBSD$ - */ - -#ifndef _BHND_BHND_CORE_H_ -#define _BHND_BHND_CORE_H_ - -/* Common core control flags */ -#define BHND_CF 0x0408 -#define BHND_CF_BIST_EN 0x8000 /**< built-in self test */ -#define BHND_CF_PME_EN 0x4000 /**< ??? */ -#define BHND_CF_CORE_BITS 0x3ffc /**< core specific flag mask */ -#define BHND_CF_FGC 0x0002 /**< force clock gating */ -#define BHND_CF_CLOCK_EN 0x0001 /**< enable clock */ - -/* Common core status flags */ -#define BHND_SF 0x0500 -#define BHND_SF_BIST_DONE 0x8000 /**< ??? */ -#define BHND_SF_BIST_ERROR 0x4000 /**< ??? */ -#define BHND_SF_GATED_CLK 0x2000 /**< clock gated */ -#define BHND_SF_DMA64 0x1000 /**< supports 64-bit DMA */ -#define BHND_SF_CORE_BITS 0x0fff /**< core-specific status mask */ - -/*Reset core control flags */ -#define BHND_RESET_CF 0x0800 -#define BHND_RESET_CF_ENABLE 0x0001 - -#define BHND_RESET_SF 0x0804 - -#endif /* _BHND_BHND_CORE_H_ */ Index: head/sys/dev/bhnd/bhndvar.h =================================================================== --- head/sys/dev/bhnd/bhndvar.h +++ head/sys/dev/bhnd/bhndvar.h @@ -45,8 +45,6 @@ MALLOC_DECLARE(M_BHND); DECLARE_CLASS(bhnd_driver); -struct bhnd_core_pmu_info; - int bhnd_generic_attach(device_t dev); int bhnd_generic_detach(device_t dev); int bhnd_generic_shutdown(device_t dev); @@ -74,9 +72,6 @@ void bhnd_generic_probe_nomatch(device_t dev, device_t child); -device_t bhnd_generic_add_child(device_t dev, u_int order, - const char *name, int unit); -void bhnd_generic_child_added(device_t dev, device_t child); void bhnd_generic_child_deleted(device_t dev, device_t child); int bhnd_generic_suspend_child(device_t dev, @@ -88,15 +83,6 @@ device_t child, const char *name, void *buf, size_t *size, bhnd_nvram_type type); - -/** - * bhnd per-device info. Must be first member of all subclass - * devinfo structures. - */ -struct bhnd_devinfo { - struct bhnd_core_pmu_info *pmu_info; /**< PMU info, or NULL */ -}; - /** * bhnd driver instance state. Must be first member of all subclass * softc structures. Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu.h @@ -34,6 +34,8 @@ #include +#include + #include "bhnd_pmu_if.h" /** Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu.c @@ -80,6 +80,10 @@ .rd_chipst = bhnd_pmu_read_chipst }; +#define BPMU_ASSERT_CLKCTL_AVAIL(_pinfo) \ + KASSERT(!bhnd_is_hw_suspended((_pinfo)->pm_dev), \ + ("reading clkctl on suspended core will trigger system livelock")) + #define BPMU_CLKCTL_READ_4(_pinfo) \ bhnd_bus_read_4((_pinfo)->pm_res, (_pinfo)->pm_regs) @@ -304,6 +308,8 @@ uint32_t avail; uint32_t req; + BPMU_ASSERT_CLKCTL_AVAIL(pinfo); + sc = device_get_softc(dev); avail = 0x0; @@ -351,6 +357,8 @@ uint32_t avail; uint32_t req; + BPMU_ASSERT_CLKCTL_AVAIL(pinfo); + sc = device_get_softc(dev); avail = 0x0; @@ -404,6 +412,8 @@ uint32_t req; uint32_t avail; + BPMU_ASSERT_CLKCTL_AVAIL(pinfo); + sc = device_get_softc(dev); if (rsrc > BHND_CCS_ERSRC_MAX) @@ -433,6 +443,8 @@ struct bhnd_pmu_softc *sc; uint32_t mask; + BPMU_ASSERT_CLKCTL_AVAIL(pinfo); + sc = device_get_softc(dev); if (rsrc > BHND_CCS_ERSRC_MAX) @@ -455,6 +467,11 @@ sc = device_get_softc(dev); + /* On PMU-equipped hardware, clkctl is cleared on RESET (and + * attempting to access it will trigger a system livelock). */ + if (bhnd_is_hw_suspended(pinfo->pm_dev)) + return (0); + BPMU_LOCK(sc); /* Clear all FORCE, AREQ, and ERSRC flags */ Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h @@ -122,7 +122,7 @@ bool bhnd_pmu_is_otp_powered(struct bhnd_pmu_softc *sc); uint32_t bhnd_pmu_measure_alpclk(struct bhnd_pmu_softc *sc); -void bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, +int bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable); uint32_t bhnd_pmu_waitforclk_on_backplane(struct bhnd_pmu_softc *sc, Index: head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c =================================================================== --- head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c +++ head/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c @@ -3363,14 +3363,18 @@ } } -void +int bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable) { - uint32_t oobsel; - uint32_t rsrcs; + uint32_t oobsel; + uint32_t rsrcs; + int error; - if (bhnd_get_device(d11core) != BHND_COREID_D11) - panic("bhnd_pmu_radio_enable() called on non-D11 core"); + if (bhnd_get_device(d11core) != BHND_COREID_D11) { + device_printf(sc->dev, + "bhnd_pmu_radio_enable() called on non-D11 core"); + return (EINVAL); + } switch (sc->cid.chip_id) { case BHND_CHIPID_BCM4325: @@ -3389,9 +3393,13 @@ BHND_PMU_AND_4(sc, BHND_PMU_MIN_RES_MASK, ~rsrcs); } - break; + return (0); + case BHND_CHIPID_BCM4319: - oobsel = bhnd_read_config(d11core, BCMA_DMP_OOBSELOUTB74, 4); + error = bhnd_read_config(d11core, BCMA_DMP_OOBSELOUTB74, + &oobsel, 4); + if (error) + return (error); if (enable) { oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, @@ -3405,9 +3413,11 @@ BCMA_DMP_OOBSEL_6); } - bhnd_write_config(d11core, BCMA_DMP_OOBSELOUTB74, oobsel, 4); - break; + return (bhnd_write_config(d11core, BCMA_DMP_OOBSELOUTB74, + &oobsel, 4)); } + + return (0); } /* Wait for a particular clock level to be on the backplane */ Index: head/sys/dev/bhnd/cores/usb/bhnd_usb.c =================================================================== --- head/sys/dev/bhnd/cores/usb/bhnd_usb.c +++ head/sys/dev/bhnd/cores/usb/bhnd_usb.c @@ -44,7 +44,6 @@ #include #include -#include #include @@ -103,7 +102,7 @@ sc = device_get_softc(dev); - BHND_BUS_RESET_CORE(device_get_parent(dev), dev, 0); + bhnd_reset_hw(dev, 0); /* * Allocate the resources which the parent bus has already Index: head/sys/dev/bhnd/siba/siba.c =================================================================== --- head/sys/dev/bhnd/siba/siba.c +++ head/sys/dev/bhnd/siba/siba.c @@ -40,6 +40,7 @@ #include #include +#include #include "sibareg.h" #include "sibavar.h" @@ -134,6 +135,9 @@ case BHND_IVAR_CORE_UNIT: *result = cfg->unit; return (0); + case BHND_IVAR_PMU_INFO: + *result = (uintptr_t) dinfo->pmu_info; + return (0); default: return (ENOENT); } @@ -142,6 +146,10 @@ static int siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { + struct siba_devinfo *dinfo; + + dinfo = device_get_ivars(child); + switch (index) { case BHND_IVAR_VENDOR: case BHND_IVAR_DEVICE: @@ -152,6 +160,9 @@ case BHND_IVAR_CORE_INDEX: case BHND_IVAR_CORE_UNIT: return (EINVAL); + case BHND_IVAR_PMU_INFO: + dinfo->pmu_info = (struct bhnd_core_pmu_info *) value; + return (0); default: return (ENOENT); } @@ -165,104 +176,320 @@ } static int -siba_reset_core(device_t dev, device_t child, uint16_t flags) +siba_read_iost(device_t dev, device_t child, uint16_t *iost) { - struct siba_devinfo *dinfo; + uint32_t tmhigh; + int error; + + error = bhnd_read_config(child, SIBA_CFG0_TMSTATEHIGH, &tmhigh, 4); + if (error) + return (error); + + *iost = (SIBA_REG_GET(tmhigh, TMH_SISF)); + return (0); +} + +static int +siba_read_ioctl(device_t dev, device_t child, uint16_t *ioctl) +{ + uint32_t ts_low; + int error; + + if ((error = bhnd_read_config(child, SIBA_CFG0_TMSTATELOW, &ts_low, 4))) + return (error); + + *ioctl = (SIBA_REG_GET(ts_low, TML_SICF)); + return (0); +} + +static int +siba_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask) +{ + struct siba_devinfo *dinfo; + struct bhnd_resource *r; + uint32_t ts_low, ts_mask; if (device_get_parent(child) != dev) - BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); + return (EINVAL); + /* Fetch CFG0 mapping */ dinfo = device_get_ivars(child); + if ((r = dinfo->cfg[0]) == NULL) + return (ENODEV); - /* Can't reset the core without access to the CFG0 registers */ - if (dinfo->cfg[0] == NULL) + /* Mask and set TMSTATELOW core flag bits */ + 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)); +} + +static bool +siba_is_hw_suspended(device_t dev, device_t child) +{ + uint32_t ts_low; + uint16_t ioctl; + int error; + + /* Fetch target state */ + error = bhnd_read_config(child, SIBA_CFG0_TMSTATELOW, &ts_low, 4); + if (error) { + device_printf(child, "error reading HW reset state: %d\n", + error); + return (true); + } + + /* Is core held in RESET? */ + if (ts_low & SIBA_TML_RESET) + return (true); + + /* Is core clocked? */ + ioctl = SIBA_REG_GET(ts_low, TML_SICF); + if (!(ioctl & BHND_IOCTL_CLK_EN)) + return (true); + + return (false); +} + +static int +siba_reset_hw(device_t dev, device_t child, uint16_t ioctl) +{ + struct siba_devinfo *dinfo; + struct bhnd_resource *r; + uint32_t ts_low, imstate; + int error; + + if (device_get_parent(child) != dev) + return (EINVAL); + + dinfo = device_get_ivars(child); + + /* Can't suspend the core without access to the CFG0 registers */ + if ((r = dinfo->cfg[0]) == NULL) return (ENODEV); - // TODO - perform reset + /* We require exclusive control over BHND_IOCTL_CLK_EN and + * BHND_IOCTL_CLK_FORCE. */ + if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE)) + return (EINVAL); + + /* Place core into known RESET state */ + if ((error = BHND_BUS_SUSPEND_HW(dev, child))) + 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); + + /* 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); + } + + /* 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); + } + + /* 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); - return (ENXIO); + /* 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); + + return (0); } static int -siba_suspend_core(device_t dev, device_t child) +siba_suspend_hw(device_t dev, device_t child) { - struct siba_devinfo *dinfo; + struct siba_devinfo *dinfo; + struct bhnd_core_pmu_info *pm; + struct bhnd_resource *r; + uint32_t idl, ts_low; + uint16_t ioctl; + int error; if (device_get_parent(child) != dev) - BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); + return (EINVAL); dinfo = device_get_ivars(child); + pm = dinfo->pmu_info; /* Can't suspend the core without access to the CFG0 registers */ - if (dinfo->cfg[0] == NULL) + if ((r = dinfo->cfg[0]) == NULL) return (ENODEV); - // TODO - perform suspend + /* 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)); + + 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)); + } + + /* Reject any further target backplane transactions */ + error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, + SIBA_TML_REJ, SIBA_TML_REJ); + if (error) + return (error); + + /* If this is an initiator core, we need to reject initiator + * 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, + SIBA_IM_RJ, SIBA_IM_RJ); + if (error) + return (error); + } - return (ENXIO); + /* 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; + + error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW, + ts_low, ts_low); + 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); + } + + /* Core is now in RESET, with clocks disabled and REJ not asserted. + * + * We lastly need to inform the PMU, releasing any outstanding per-core + * PMU requests */ + if (pm != NULL) { + if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm))) + return (error); + } + + return (0); } -static uint32_t -siba_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) +static int +siba_read_config(device_t dev, device_t child, bus_size_t offset, void *value, + u_int width) { struct siba_devinfo *dinfo; rman_res_t r_size; /* Must be directly attached */ if (device_get_parent(child) != dev) - return (UINT32_MAX); + return (EINVAL); /* CFG0 registers must be available */ dinfo = device_get_ivars(child); if (dinfo->cfg[0] == NULL) - return (UINT32_MAX); + return (ENODEV); /* Offset must fall within CFG0 */ r_size = rman_get_size(dinfo->cfg[0]->res); if (r_size < offset || r_size - offset < width) - return (UINT32_MAX); + return (EFAULT); switch (width) { case 1: - return (bhnd_bus_read_1(dinfo->cfg[0], offset)); + *((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg[0], offset); + return (0); case 2: - return (bhnd_bus_read_2(dinfo->cfg[0], offset)); + *((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg[0], offset); + return (0); case 4: - return (bhnd_bus_read_4(dinfo->cfg[0], offset)); + *((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg[0], offset); + return (0); + default: + return (EINVAL); } - - /* Unsuported */ - return (UINT32_MAX); } -static void -siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, - u_int width) +static int +siba_write_config(device_t dev, device_t child, bus_size_t offset, + const void *value, u_int width) { struct siba_devinfo *dinfo; + struct bhnd_resource *r; rman_res_t r_size; /* Must be directly attached */ if (device_get_parent(child) != dev) - return; + return (EINVAL); /* CFG0 registers must be available */ dinfo = device_get_ivars(child); - if (dinfo->cfg[0] == NULL) - return; + if ((r = dinfo->cfg[0]) == NULL) + return (ENODEV); /* Offset must fall within CFG0 */ - r_size = rman_get_size(dinfo->cfg[0]->res); + r_size = rman_get_size(r->res); if (r_size < offset || r_size - offset < width) - return; + return (EFAULT); switch (width) { case 1: - bhnd_bus_write_1(dinfo->cfg[0], offset, val); + bhnd_bus_write_1(r, offset, *(const uint8_t *)value); + return (0); case 2: - bhnd_bus_write_2(dinfo->cfg[0], offset, val); + bhnd_bus_write_2(r, offset, *(const uint8_t *)value); + return (0); case 4: - bhnd_bus_write_4(dinfo->cfg[0], offset, val); + bhnd_bus_write_4(r, offset, *(const uint8_t *)value); + return (0); + default: + return (EINVAL); } } @@ -545,18 +772,42 @@ return (0); } - -static struct bhnd_devinfo * -siba_alloc_bhnd_dinfo(device_t dev) +static device_t +siba_add_child(device_t dev, u_int order, const char *name, int unit) { - struct siba_devinfo *dinfo = siba_alloc_dinfo(dev); - return ((struct bhnd_devinfo *)dinfo); + struct siba_devinfo *dinfo; + device_t child; + + child = device_add_child_ordered(dev, order, name, unit); + if (child == NULL) + return (NULL); + + if ((dinfo = siba_alloc_dinfo(dev)) == NULL) { + device_delete_child(dev, child); + return (NULL); + } + + device_set_ivars(child, dinfo); + + return (child); } static void -siba_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo) +siba_child_deleted(device_t dev, device_t child) { - siba_free_dinfo(dev, (struct siba_devinfo *)dinfo); + struct bhnd_softc *sc; + struct siba_devinfo *dinfo; + + sc = device_get_softc(dev); + + /* Call required bhnd(4) implementation */ + bhnd_generic_child_deleted(dev, child); + + /* Free siba device info */ + if ((dinfo = device_get_ivars(child)) != NULL) + siba_free_dinfo(dev, dinfo); + + device_set_ivars(child, NULL); } /** @@ -687,16 +938,20 @@ DEVMETHOD(device_suspend, siba_suspend), /* Bus interface */ + DEVMETHOD(bus_add_child, siba_add_child), + DEVMETHOD(bus_child_deleted, siba_child_deleted), DEVMETHOD(bus_read_ivar, siba_read_ivar), DEVMETHOD(bus_write_ivar, siba_write_ivar), DEVMETHOD(bus_get_resource_list, siba_get_resource_list), /* BHND interface */ DEVMETHOD(bhnd_bus_get_erom_class, siba_get_erom_class), - DEVMETHOD(bhnd_bus_alloc_devinfo, siba_alloc_bhnd_dinfo), - DEVMETHOD(bhnd_bus_free_devinfo, siba_free_bhnd_dinfo), - DEVMETHOD(bhnd_bus_reset_core, siba_reset_core), - DEVMETHOD(bhnd_bus_suspend_core, siba_suspend_core), + DEVMETHOD(bhnd_bus_read_ioctl, siba_read_ioctl), + DEVMETHOD(bhnd_bus_write_ioctl, siba_write_ioctl), + DEVMETHOD(bhnd_bus_read_iost, siba_read_iost), + DEVMETHOD(bhnd_bus_is_hw_suspended, siba_is_hw_suspended), + DEVMETHOD(bhnd_bus_reset_hw, siba_reset_hw), + DEVMETHOD(bhnd_bus_suspend_hw, siba_suspend_hw), DEVMETHOD(bhnd_bus_read_config, siba_read_config), DEVMETHOD(bhnd_bus_write_config, siba_write_config), DEVMETHOD(bhnd_bus_get_port_count, siba_get_port_count), Index: head/sys/dev/bhnd/siba/siba_subr.c =================================================================== --- head/sys/dev/bhnd/siba/siba_subr.c +++ head/sys/dev/bhnd/siba/siba_subr.c @@ -467,3 +467,85 @@ return (0); } + +/** + * Write @p value to @p dev's CFG0 target/initiator state register and + * wait 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 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 +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[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); + + /* 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 + * @p dev's SIBA_CFG0_TMSTATEHIGH register. + * + * @param dev The siba(4) child device to wait on. + * @param dinfo The @p dev's device info + * + * @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. + */ +int +siba_wait_target_busy(device_t dev, struct siba_devinfo *dinfo, int usec) +{ + struct bhnd_resource *r; + uint32_t ts_high; + + if ((r = dinfo->cfg[0]) == NULL) + return (ENODEV); + + for (int i = 0; i < usec; i += 10) { + ts_high = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH); + if (!(ts_high & SIBA_TMH_BUSY)) + return (0); + + DELAY(10); + } + + device_printf(dev, "SIBA_TMH_BUSY wait timeout\n"); + return (ETIMEDOUT); +} Index: head/sys/dev/bhnd/siba/sibareg.h =================================================================== --- head/sys/dev/bhnd/siba/sibareg.h +++ head/sys/dev/bhnd/siba/sibareg.h @@ -146,16 +146,16 @@ #define SIBA_TML_REJ_MASK 0x0006 /* reject field */ #define SIBA_TML_REJ 0x0002 /* reject */ #define SIBA_TML_TMPREJ 0x0004 /* temporary reject, for error recovery */ - -#define SIBA_TML_SICF_SHIFT 16 /* Shift to locate the SI control flags in sbtml */ +#define SIBA_TML_SICF_MASK 0xFFFF0000 /* core IOCTL flags */ +#define SIBA_TML_SICF_SHIFT 16 /* sbtmstatehigh */ #define SIBA_TMH_SERR 0x0001 /* serror */ #define SIBA_TMH_INT 0x0002 /* interrupt */ #define SIBA_TMH_BUSY 0x0004 /* busy */ #define SIBA_TMH_TO 0x0020 /* timeout (sonics >= 2.3) */ - -#define SIBA_TMH_SISF_SHIFT 16 /* Shift to locate the SI status flags in sbtmh */ +#define SIBA_TMH_SISF_MASK 0xFFFF0000 /* core IOST flags */ +#define SIBA_TMH_SISF_SHIFT 16 /* sbbwa0 */ #define SIBA_BWA_TAB0_MASK 0xffff /* lookup table 0 */ Index: head/sys/dev/bhnd/siba/sibavar.h =================================================================== --- head/sys/dev/bhnd/siba/sibavar.h +++ head/sys/dev/bhnd/siba/sibavar.h @@ -95,6 +95,12 @@ int siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size); +int 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); + /* Sonics configuration register blocks */ #define SIBA_CFG_NUM_2_2 1 /**< sonics <= 2.2 maps SIBA_CFG0. */ @@ -147,14 +153,13 @@ * siba(4) per-device info */ struct siba_devinfo { - struct bhnd_devinfo bhnd_dinfo; /**< superclass device info. */ - - struct resource_list resources; /**< per-core memory regions. */ - struct siba_core_id core_id; /**< core identification info */ - struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */ - - struct bhnd_resource *cfg[SIBA_MAX_CFG]; /**< SIBA_CFG_* registers */ - int cfg_rid[SIBA_MAX_CFG]; /**< SIBA_CFG_* resource IDs */ + struct resource_list resources; /**< per-core memory regions. */ + struct siba_core_id core_id; /**< core identification info */ + struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */ + + struct bhnd_resource *cfg[SIBA_MAX_CFG]; /**< SIBA_CFG_* registers */ + int cfg_rid[SIBA_MAX_CFG]; /**< SIBA_CFG_* resource IDs */ + struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */ }; Index: head/sys/modules/bhnd/bcma/Makefile =================================================================== --- head/sys/modules/bhnd/bcma/Makefile +++ head/sys/modules/bhnd/bcma/Makefile @@ -6,6 +6,6 @@ SRCS= bcma.c bcma_subr.c bcma_erom.c SRCS+= device_if.h bus_if.h -SRCS+= bhnd_bus_if.h bhnd_erom_if.h +SRCS+= bhnd_bus_if.h bhnd_erom_if.h bhnd_pmu_if.h .include Index: head/sys/modules/bhnd/siba/Makefile =================================================================== --- head/sys/modules/bhnd/siba/Makefile +++ head/sys/modules/bhnd/siba/Makefile @@ -7,6 +7,6 @@ siba_erom.c SRCS+= device_if.h bus_if.h -SRCS+= bhnd_bus_if.h bhnd_erom_if.h +SRCS+= bhnd_bus_if.h bhnd_erom_if.h bhnd_pmu_if.h .include