aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/bhnd
diff options
context:
space:
mode:
authorLandon J. Fuller <landonf@FreeBSD.org>2016-09-24 04:08:16 +0000
committerLandon J. Fuller <landonf@FreeBSD.org>2016-09-24 04:08:16 +0000
commit8a03f98a8bbbfe45211330f7634c93321ba34813 (patch)
tree26f9c2cd3d03476686c6c460d7036eeda18fea71 /sys/dev/bhnd
parent6f87599bdfdc61c350dafa9523dc953a4cef146a (diff)
downloadsrc-8a03f98a8bbbfe45211330f7634c93321ba34813.tar.gz
src-8a03f98a8bbbfe45211330f7634c93321ba34813.zip
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
Notes
Notes: svn path=/head/; revision=306287
Diffstat (limited to 'sys/dev/bhnd')
-rw-r--r--sys/dev/bhnd/bcma/bcma.c315
-rw-r--r--sys/dev/bhnd/bcma/bcma_dmp.h8
-rw-r--r--sys/dev/bhnd/bcma/bcma_subr.c61
-rw-r--r--sys/dev/bhnd/bcma/bcmavar.h17
-rw-r--r--sys/dev/bhnd/bhnd.c95
-rw-r--r--sys/dev/bhnd/bhnd.h205
-rw-r--r--sys/dev/bhnd/bhnd_bus_if.m229
-rw-r--r--sys/dev/bhnd/bhnd_core.h49
-rw-r--r--sys/dev/bhnd/bhndvar.h14
-rw-r--r--sys/dev/bhnd/cores/pmu/bhnd_pmu.c17
-rw-r--r--sys/dev/bhnd/cores/pmu/bhnd_pmu.h2
-rw-r--r--sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h2
-rw-r--r--sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c28
-rw-r--r--sys/dev/bhnd/cores/usb/bhnd_usb.c3
-rw-r--r--sys/dev/bhnd/siba/siba.c347
-rw-r--r--sys/dev/bhnd/siba/siba_subr.c82
-rw-r--r--sys/dev/bhnd/siba/sibareg.h8
-rw-r--r--sys/dev/bhnd/siba/sibavar.h19
18 files changed, 1145 insertions, 356 deletions
diff --git a/sys/dev/bhnd/bcma/bcma.c b/sys/dev/bhnd/bcma/bcma.c
index 434bbfc57c6d..08808955ba0e 100644
--- a/sys/dev/bhnd/bcma/bcma.c
+++ b/sys/dev/bhnd/bcma/bcma.c
@@ -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,136 +204,262 @@ 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);
+ /* 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);
+
+ /* 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 (ENXIO);
+ 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 @@ 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),
diff --git a/sys/dev/bhnd/bcma/bcma_dmp.h b/sys/dev/bhnd/bcma/bcma_dmp.h
index ae5f8dcb5499..0fd41a7a4f24 100644
--- a/sys/dev/bhnd/bcma/bcma_dmp.h
+++ b/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
diff --git a/sys/dev/bhnd/bcma/bcma_subr.c b/sys/dev/bhnd/bcma/bcma_subr.c
index e6d544cf8485..ff69e5f7e147 100644
--- a/sys/dev/bhnd/bcma/bcma_subr.c
+++ b/sys/dev/bhnd/bcma/bcma_subr.c
@@ -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));
+}
diff --git a/sys/dev/bhnd/bcma/bcmavar.h b/sys/dev/bhnd/bcma/bcmavar.h
index 8d1c08e5d4f2..1cdabcd6a81d 100644
--- a/sys/dev/bhnd/bcma/bcmavar.h
+++ b/sys/dev/bhnd/bcma/bcmavar.h
@@ -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 */
};
diff --git a/sys/dev/bhnd/bhnd.c b/sys/dev/bhnd/bhnd.c
index 63acead4875f..104ca8ecb44a 100644
--- a/sys/dev/bhnd/bhnd.c
+++ b/sys/dev/bhnd/bhnd.c
@@ -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 */
@@ -1035,43 +1026,6 @@ bhnd_child_location_str(device_t dev, device_t child, char *buf,
}
/**
- * 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 @@ 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),
diff --git a/sys/dev/bhnd/bhnd.h b/sys/dev/bhnd/bhnd.h
index ea4d55d19b53..0e4e03a42fc8 100644
--- a/sys/dev/bhnd/bhnd.h
+++ b/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 @@ 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));
}
/**
diff --git a/sys/dev/bhnd/bhnd_bus_if.m b/sys/dev/bhnd/bhnd_bus_if.m
index b6a75c415311..1b2bff6d4159 100644
--- a/sys/dev/bhnd/bhnd_bus_if.m
+++ b/sys/dev/bhnd/bhnd_bus_if.m
@@ -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");
}
@@ -344,32 +384,6 @@ METHOD int read_board_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 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 @@ 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;
diff --git a/sys/dev/bhnd/bhnd_core.h b/sys/dev/bhnd/bhnd_core.h
deleted file mode 100644
index 26506969429a..000000000000
--- a/sys/dev/bhnd/bhnd_core.h
+++ /dev/null
@@ -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_ */
diff --git a/sys/dev/bhnd/bhndvar.h b/sys/dev/bhnd/bhndvar.h
index c5317a04b5b9..2602302a1861 100644
--- a/sys/dev/bhnd/bhndvar.h
+++ b/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 @@ 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.
diff --git a/sys/dev/bhnd/cores/pmu/bhnd_pmu.c b/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
index 0d24392f0da8..6d7e907da4cb 100644
--- a/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
+++ b/sys/dev/bhnd/cores/pmu/bhnd_pmu.c
@@ -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 */
diff --git a/sys/dev/bhnd/cores/pmu/bhnd_pmu.h b/sys/dev/bhnd/cores/pmu/bhnd_pmu.h
index 64f4c8e5c645..01c3ea127e67 100644
--- a/sys/dev/bhnd/cores/pmu/bhnd_pmu.h
+++ b/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"
/**
diff --git a/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h b/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h
index 786c0b871937..e20f65fe7787 100644
--- a/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h
+++ b/sys/dev/bhnd/cores/pmu/bhnd_pmu_private.h
@@ -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,
diff --git a/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c b/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c
index bd10febff274..7744da60222e 100644
--- a/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c
+++ b/sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c
@@ -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 */
diff --git a/sys/dev/bhnd/cores/usb/bhnd_usb.c b/sys/dev/bhnd/cores/usb/bhnd_usb.c
index 263c0cad9b5c..f753e5b0d6cd 100644
--- a/sys/dev/bhnd/cores/usb/bhnd_usb.c
+++ b/sys/dev/bhnd/cores/usb/bhnd_usb.c
@@ -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
diff --git a/sys/dev/bhnd/siba/siba.c b/sys/dev/bhnd/siba/siba.c
index 678940ba872e..7f85a1d8ec89 100644
--- a/sys/dev/bhnd/siba/siba.c
+++ b/sys/dev/bhnd/siba/siba.c
@@ -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,104 +176,320 @@ 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;
+
+ 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);
- return (ENXIO);
+ /* 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);
+
+ /* 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 @@ 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),
diff --git a/sys/dev/bhnd/siba/siba_subr.c b/sys/dev/bhnd/siba/siba_subr.c
index 86a0bbf89607..52a5e019cc38 100644
--- a/sys/dev/bhnd/siba/siba_subr.c
+++ b/sys/dev/bhnd/siba/siba_subr.c
@@ -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);
+}
diff --git a/sys/dev/bhnd/siba/sibareg.h b/sys/dev/bhnd/siba/sibareg.h
index 63a6d5bf28ce..60771f982da2 100644
--- a/sys/dev/bhnd/siba/sibareg.h
+++ b/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 */
diff --git a/sys/dev/bhnd/siba/sibavar.h b/sys/dev/bhnd/siba/sibavar.h
index fc2f26f56847..27364660e8fb 100644
--- a/sys/dev/bhnd/siba/sibavar.h
+++ b/sys/dev/bhnd/siba/sibavar.h
@@ -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 */
};