Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157209625
D8009.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
69 KB
Referenced Files
None
Subscribers
None
D8009.diff
View Options
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 <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 @@
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 <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 @@
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 <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_ */
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 <sys/types.h>
+#include <dev/bhnd/bhnd.h>
+
#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 <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 @@
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 <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 @@
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 <bsd.kmod.mk>
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 <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, May 20, 8:56 AM (21 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33341932
Default Alt Text
D8009.diff (69 KB)
Attached To
Mode
D8009: Implement common bhnd(4) API for IOST/IOCTL register access and core reset
Attached
Detach File
Event Timeline
Log In to Comment