bhnd(4): Implement common API for IOST/IOCTL register access and core reset

- Added bhnd(4) bus APIs for per-core ioctl/iost register access.
- Updated reset/suspend bhnd(4) APIs for compatibility with ioctl/iost
  changes.
- Implemented core reset/suspend support for both bcma(4) and siba(4).
- Implemented explicit release of all outstanding PMU requests at the bus
  level when putting a core into reset.

Approved by:    adrian (mentor, implicit)
Differential Revision:  https://reviews.freebsd.org/D8009
This commit is contained in:
Landon J. Fuller
2016-09-24 04:08:16 +00:00
parent 6f87599bdf
commit 8a03f98a8b
20 changed files with 1184 additions and 395 deletions
+254 -89
View File
@@ -39,14 +39,14 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include "bcmavar.h"
#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
#include "bcma_dmp.h"
#include "bcma_eromreg.h"
#include "bcma_eromvar.h"
#include <dev/bhnd/bhnd_core.h>
#include "bcmavar.h"
/* RID used when allocating EROM table */
#define BCMA_EROM_RID 0
@@ -91,6 +91,44 @@ bcma_detach(device_t dev)
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 @@ bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
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 @@ bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
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 @@ bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
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,103 +204,191 @@ bcma_get_resource_list(device_t dev, device_t child)
}
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);
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);
/* 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);
/* Perform read-back and wait for completion */
bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
DELAY(10);
return (0);
}
static int
bcma_suspend_core(device_t dev, device_t child)
static bool
bcma_is_hw_suspended(device_t dev, device_t child)
{
struct bcma_devinfo *dinfo;
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)
BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
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 (dinfo->res_agent == NULL)
if ((r = dinfo->res_agent) == NULL)
return (ENODEV);
// TODO - perform suspend
/* Place core into known RESET state */
if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
return (error);
return (ENXIO);
/*
* 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 uint32_t
bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width)
static int
bcma_suspend_hw(device_t dev, device_t child)
{
struct bcma_devinfo *dinfo;
struct bhnd_resource *r;
struct bcma_devinfo *dinfo;
struct bhnd_core_pmu_info *pm;
struct bhnd_resource *r;
uint32_t rst;
int error;
/* 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);
pm = dinfo->pmu_info;
/* Can't suspend the core without access to the agent registers */
if ((r = dinfo->res_agent) == NULL)
return (UINT32_MAX);
return (ENODEV);
/* Verify bounds */
if (offset > rman_get_size(r->res))
return (UINT32_MAX);
/* Wait for any pending reset operations to clear */
if ((error = bcma_dmp_wait_reset(child, dinfo)))
return (error);
if (rman_get_size(r->res) - offset < width)
return (UINT32_MAX);
/* Already in reset? */
rst = bhnd_bus_read_4(r, BCMA_DMP_RESETCTRL);
if (rst & BMCA_DMP_RC_RESET)
return (0);
switch (width) {
case 1:
return (bhnd_bus_read_1(r, offset));
case 2:
return (bhnd_bus_read_2(r, offset));
case 4:
return (bhnd_bus_read_4(r, offset));
default:
return (UINT32_MAX);
/* 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 void
bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
static int
bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
u_int width)
{
struct bcma_devinfo *dinfo;
@@ -260,32 +396,70 @@ bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
/* 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;
*((uint8_t *)value) = bhnd_bus_read_1(r, offset);
return (0);
case 2:
bhnd_bus_write_2(r, offset, val);
break;
*((uint16_t *)value) = bhnd_bus_read_2(r, offset);
return (0);
case 4:
bhnd_bus_write_4(r, offset, val);
break;
*((uint32_t *)value) = bhnd_bus_read_4(r, offset);
return (0);
default:
break;
return (EINVAL);
}
}
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 (EINVAL);
/* Fetch the agent registers */
dinfo = device_get_ivars(child);
if ((r = dinfo->res_agent) == NULL)
return (ENODEV);
/* Verify bounds */
if (offset > rman_get_size(r->res))
return (EFAULT);
if (rman_get_size(r->res) - offset < width)
return (EFAULT);
switch (width) {
case 1:
bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
return (0);
case 2:
bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
return (0);
case 4:
bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
return (0);
default:
return (EINVAL);
}
}
@@ -501,19 +675,6 @@ bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec)
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 @@ static device_method_t bcma_methods[] = {
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),
+7 -1
View File
@@ -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
+61
View File
@@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/bhndvar.h>
#include "bcma_dmp.h"
#include "bcmavar.h"
/* Return the resource ID for a device's agent register allocation */
@@ -368,3 +370,62 @@ bcma_free_sport(struct bcma_sport *sport) {
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));
}
+11 -6
View File
@@ -99,6 +99,11 @@ void bcma_free_corecfg(struct bcma_corecfg *corecfg);
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 @@ struct bcma_corecfg {
* 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 */
};
+21 -74
View File
@@ -631,7 +631,6 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
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 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
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 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
}
/* already allocated? */
if (dinfo->pmu_info != NULL) {
if (pm != NULL) {
panic("duplicate PMU allocation for %s",
device_get_nameunit(child));
}
@@ -728,7 +727,7 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
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 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
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 @@ int
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 @@ bhnd_generic_release_pmu(device_t dev, device_t child)
}
/* 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 @@ int
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 @@ int
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 @@ int
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 @@ int
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 */
@@ -1034,43 +1025,6 @@ bhnd_child_location_str(device_t dev, device_t child, char *buf,
return (0);
}
/**
* 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().
*
@@ -1081,21 +1035,16 @@ void
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 @@ static device_method_t bhnd_methods[] = {
/* 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 @@ static device_method_t bhnd_methods[] = {
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),
+189 -16
View File
@@ -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 @@ enum bhnd_device_vars {
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 @@ enum {
};
/**
* 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(vendor_name, VENDOR_NAME, const char *);
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 @@ bhnd_get_chipid(device_t dev) {
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 @@ bhnd_release_pmu(device_t dev)
/**
* 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 @@ bhnd_request_clock(device_t dev, bhnd_clock clock)
* 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 @@ bhnd_enable_clocks(device_t dev, uint32_t clocks)
/**
* 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 @@ bhnd_release_ext_rsrc(device_t dev, u_int rsrc)
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 @@ bhnd_release_ext_rsrc(device_t dev, u_int rsrc)
*
* @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));
}
/**
+178 -53
View File
@@ -63,6 +63,46 @@ CODE {
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 @@ CODE {
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");
}
@@ -343,32 +383,6 @@ METHOD int read_board_info {
struct bhnd_board_info *info;
} 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 @@ METHOD void child_added {
} 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 child The device to be reset.
* @param flags Device-specific core flags to be supplied on reset.
* @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 non-zero error
* @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 reset_core {
METHOD int read_ioctl {
device_t dev;
device_t child;
uint16_t flags;
}
uint16_t *ioctl;
} DEFAULT bhnd_bus_null_read_ioctl;
/**
* Suspend a device hardware core.
* 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 parent of @p child.
* @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 ioctl Device-specific core ioctl flags to be supplied on reset
* (see BHND_IOCTL_*).
*
* @retval 0 success
* @retval non-zero error
*/
METHOD int suspend_core {
METHOD int reset_hw {
device_t dev;
device_t child;
}
uint16_t ioctl;
} DEFAULT bhnd_bus_null_reset_hw;
/**
* 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 bhnd bus parent of @P child.
* @param dev The device to be suspended.
*
* @retval 0 success
* @retval non-zero error
*/
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 @@ METHOD int release_pmu {
/**
* 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 @@ METHOD int request_clock {
* @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 @@ METHOD int enable_clocks {
/**
* 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 @@ METHOD int request_ext_rsrc {
/**
* 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 @@ METHOD int release_ext_rsrc {
* @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 @@ METHOD int release_ext_rsrc {
*
* @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 @@ METHOD uint32_t read_config {
* @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;
-49
View File
@@ -1,49 +0,0 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* 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_ */
-14
View File
@@ -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 @@ int bhnd_generic_print_child(device_t dev,
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 @@ int bhnd_generic_get_nvram_var(device_t dev,
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.
+17
View File
@@ -80,6 +80,10 @@ static const struct bhnd_pmu_io bhnd_pmu_res_io = {
.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 @@ bhnd_pmu_core_req_clock(device_t dev, struct bhnd_core_pmu_info *pinfo,
uint32_t avail;
uint32_t req;
BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
sc = device_get_softc(dev);
avail = 0x0;
@@ -351,6 +357,8 @@ bhnd_pmu_core_en_clocks(device_t dev, struct bhnd_core_pmu_info *pinfo,
uint32_t avail;
uint32_t req;
BPMU_ASSERT_CLKCTL_AVAIL(pinfo);
sc = device_get_softc(dev);
avail = 0x0;
@@ -404,6 +412,8 @@ bhnd_pmu_core_req_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
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 @@ bhnd_pmu_core_release_ext_rsrc(device_t dev, struct bhnd_core_pmu_info *pinfo,
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 @@ bhnd_pmu_core_release(device_t dev, struct bhnd_core_pmu_info *pinfo)
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 */
+2
View File
@@ -34,6 +34,8 @@
#include <sys/types.h>
#include <dev/bhnd/bhnd.h>
#include "bhnd_pmu_if.h"
/**
+1 -1
View File
@@ -122,7 +122,7 @@ void bhnd_pmu_spuravoid(struct bhnd_pmu_softc *sc,
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,
+19 -9
View File
@@ -3363,14 +3363,18 @@ bhnd_pmu_swreg_init(struct bhnd_pmu_softc *sc)
}
}
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_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable)
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 @@ bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable)
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 */
+1 -2
View File
@@ -44,7 +44,6 @@ __FBSDID("$FreeBSD$");
#include <machine/resource.h>
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhnd_core.h>
#include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
@@ -103,7 +102,7 @@ bhnd_usb_attach(device_t dev)
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
+323 -68
View File
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <dev/bhnd/cores/chipc/chipcreg.h>
#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
#include "sibareg.h"
#include "sibavar.h"
@@ -134,6 +135,9 @@ siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
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 @@ siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
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 @@ siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
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,78 +176,252 @@ siba_get_resource_list(device_t dev, device_t child)
}
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;
if (device_get_parent(child) != dev)
BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
error = bhnd_read_config(child, SIBA_CFG0_TMSTATEHIGH, &tmhigh, 4);
if (error)
return (error);
dinfo = device_get_ivars(child);
/* Can't reset the core without access to the CFG0 registers */
if (dinfo->cfg[0] == NULL)
return (ENODEV);
// TODO - perform reset
return (ENXIO);
*iost = (SIBA_REG_GET(tmhigh, TMH_SISF));
return (0);
}
static int
siba_suspend_core(device_t dev, device_t child)
siba_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
{
struct siba_devinfo *dinfo;
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_SUSPEND_CORE(device_get_parent(dev), child);
return (EINVAL);
/* Fetch CFG0 mapping */
dinfo = device_get_ivars(child);
if ((r = dinfo->cfg[0]) == NULL)
return (ENODEV);
/* 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 (dinfo->cfg[0] == NULL)
if ((r = dinfo->cfg[0]) == NULL)
return (ENODEV);
// TODO - perform suspend
/* 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);
return (ENXIO);
}
/* Place core into known RESET state */
if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
return (error);
static uint32_t
siba_read_config(device_t dev, device_t child, bus_size_t offset, u_int width)
{
struct siba_devinfo *dinfo;
rman_res_t r_size;
/* 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);
/* Must be directly attached */
if (device_get_parent(child) != dev)
return (UINT32_MAX);
/* CFG0 registers must be available */
dinfo = device_get_ivars(child);
if (dinfo->cfg[0] == NULL)
return (UINT32_MAX);
/* 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);
switch (width) {
case 1:
return (bhnd_bus_read_1(dinfo->cfg[0], offset));
case 2:
return (bhnd_bus_read_2(dinfo->cfg[0], offset));
case 4:
return (bhnd_bus_read_4(dinfo->cfg[0], offset));
/* 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);
}
/* Unsuported */
return (UINT32_MAX);
/* 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);
/* 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 void
siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
static int
siba_suspend_hw(device_t dev, device_t child)
{
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)
return (EINVAL);
dinfo = device_get_ivars(child);
pm = dinfo->pmu_info;
/* Can't suspend the core without access to the CFG0 registers */
if ((r = dinfo->cfg[0]) == NULL)
return (ENODEV);
/* 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);
}
/* 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 int
siba_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
u_int width)
{
struct siba_devinfo *dinfo;
@@ -244,25 +429,67 @@ siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
/* 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;
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;
return (EFAULT);
switch (width) {
case 1:
bhnd_bus_write_1(dinfo->cfg[0], offset, val);
*((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg[0], offset);
return (0);
case 2:
bhnd_bus_write_2(dinfo->cfg[0], offset, val);
*((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg[0], offset);
return (0);
case 4:
bhnd_bus_write_4(dinfo->cfg[0], offset, val);
*((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg[0], offset);
return (0);
default:
return (EINVAL);
}
}
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 (EINVAL);
/* CFG0 registers must be available */
dinfo = device_get_ivars(child);
if ((r = dinfo->cfg[0]) == NULL)
return (ENODEV);
/* Offset must fall within CFG0 */
r_size = rman_get_size(r->res);
if (r_size < offset || r_size - offset < width)
return (EFAULT);
switch (width) {
case 1:
bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
return (0);
case 2:
bhnd_bus_write_2(r, offset, *(const uint8_t *)value);
return (0);
case 4:
bhnd_bus_write_4(r, offset, *(const uint8_t *)value);
return (0);
default:
return (EINVAL);
}
}
@@ -545,18 +772,42 @@ siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo)
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 @@ static device_method_t siba_methods[] = {
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),
+82
View File
@@ -467,3 +467,85 @@ siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size)
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);
}
+4 -4
View File
@@ -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 */
+12 -7
View File
@@ -95,6 +95,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,
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 @@ struct siba_core_id {
* 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 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_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 */
};
+1 -1
View File
@@ -6,6 +6,6 @@ KMOD= bcma
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 <bsd.kmod.mk>
+1 -1
View File
@@ -7,6 +7,6 @@ SRCS= siba.c siba_subr.c \
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 <bsd.kmod.mk>