bhnd(4): Implement common API for IOST/IOCTL register access and core reset
- Added bhnd(4) bus APIs for per-core ioctl/iost register access. - Updated reset/suspend bhnd(4) APIs for compatibility with ioctl/iost changes. - Implemented core reset/suspend support for both bcma(4) and siba(4). - Implemented explicit release of all outstanding PMU requests at the bus level when putting a core into reset. Approved by: adrian (mentor, implicit) Differential Revision: https://reviews.freebsd.org/D8009
This commit is contained in:
+254
-89
@@ -39,14 +39,14 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include "bcmavar.h"
|
||||
#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
|
||||
|
||||
#include "bcma_dmp.h"
|
||||
|
||||
#include "bcma_eromreg.h"
|
||||
#include "bcma_eromvar.h"
|
||||
|
||||
#include <dev/bhnd/bhnd_core.h>
|
||||
#include "bcmavar.h"
|
||||
|
||||
/* RID used when allocating EROM table */
|
||||
#define BCMA_EROM_RID 0
|
||||
@@ -91,6 +91,44 @@ bcma_detach(device_t dev)
|
||||
return (bhnd_generic_detach(dev));
|
||||
}
|
||||
|
||||
static device_t
|
||||
bcma_add_child(device_t dev, u_int order, const char *name, int unit)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
device_t child;
|
||||
|
||||
child = device_add_child_ordered(dev, order, name, unit);
|
||||
if (child == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) {
|
||||
device_delete_child(dev, child);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
device_set_ivars(child, dinfo);
|
||||
|
||||
return (child);
|
||||
}
|
||||
|
||||
static void
|
||||
bcma_child_deleted(device_t dev, device_t child)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bcma_devinfo *dinfo;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Call required bhnd(4) implementation */
|
||||
bhnd_generic_child_deleted(dev, child);
|
||||
|
||||
/* Free bcma device info */
|
||||
if ((dinfo = device_get_ivars(child)) != NULL)
|
||||
bcma_free_dinfo(dev, dinfo);
|
||||
|
||||
device_set_ivars(child, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
|
||||
{
|
||||
@@ -125,6 +163,9 @@ bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
|
||||
case BHND_IVAR_CORE_UNIT:
|
||||
*result = ci->unit;
|
||||
return (0);
|
||||
case BHND_IVAR_PMU_INFO:
|
||||
*result = (uintptr_t) dinfo->pmu_info;
|
||||
return (0);
|
||||
default:
|
||||
return (ENOENT);
|
||||
}
|
||||
@@ -133,6 +174,10 @@ bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
|
||||
static int
|
||||
bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
switch (index) {
|
||||
case BHND_IVAR_VENDOR:
|
||||
case BHND_IVAR_DEVICE:
|
||||
@@ -143,6 +188,9 @@ bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
|
||||
case BHND_IVAR_CORE_INDEX:
|
||||
case BHND_IVAR_CORE_UNIT:
|
||||
return (EINVAL);
|
||||
case BHND_IVAR_PMU_INFO:
|
||||
dinfo->pmu_info = (struct bhnd_core_pmu_info *) value;
|
||||
return (0);
|
||||
default:
|
||||
return (ENOENT);
|
||||
}
|
||||
@@ -156,103 +204,191 @@ bcma_get_resource_list(device_t dev, device_t child)
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_reset_core(device_t dev, device_t child, uint16_t flags)
|
||||
bcma_read_iost(device_t dev, device_t child, uint16_t *iost)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
uint32_t value;
|
||||
int error;
|
||||
|
||||
if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4)))
|
||||
return (error);
|
||||
|
||||
/* Return only the bottom 16 bits */
|
||||
*iost = (value & BCMA_DMP_IOST_MASK);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
|
||||
{
|
||||
uint32_t value;
|
||||
int error;
|
||||
|
||||
if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4)))
|
||||
return (error);
|
||||
|
||||
/* Return only the bottom 16 bits */
|
||||
*ioctl = (value & BCMA_DMP_IOCTRL_MASK);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
struct bhnd_resource *r;
|
||||
uint32_t ioctl;
|
||||
|
||||
if (device_get_parent(child) != dev)
|
||||
BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
|
||||
return (EINVAL);
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
/* Can't reset the core without access to the agent registers */
|
||||
if (dinfo->res_agent == NULL)
|
||||
if ((r = dinfo->res_agent) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
/* Start reset */
|
||||
bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, BHND_RESET_CF_ENABLE);
|
||||
bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF);
|
||||
DELAY(10);
|
||||
/* Write new value */
|
||||
ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
|
||||
ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask);
|
||||
ioctl |= (value & mask);
|
||||
|
||||
/* Disable clock */
|
||||
bhnd_bus_write_4(dinfo->res_agent, BHND_CF, flags);
|
||||
bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
|
||||
DELAY(10);
|
||||
bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl);
|
||||
|
||||
/* Enable clocks & force clock gating */
|
||||
bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN |
|
||||
BHND_CF_FGC | flags);
|
||||
bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
|
||||
DELAY(10);
|
||||
|
||||
/* Complete reset */
|
||||
bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, 0);
|
||||
bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF);
|
||||
DELAY(10);
|
||||
|
||||
/* Release force clock gating */
|
||||
bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | flags);
|
||||
bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
|
||||
/* Perform read-back and wait for completion */
|
||||
bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
|
||||
DELAY(10);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_suspend_core(device_t dev, device_t child)
|
||||
static bool
|
||||
bcma_is_hw_suspended(device_t dev, device_t child)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
uint32_t rst;
|
||||
uint16_t ioctl;
|
||||
int error;
|
||||
|
||||
/* Is core held in RESET? */
|
||||
error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4);
|
||||
if (error) {
|
||||
device_printf(child, "error reading HW reset state: %d\n",
|
||||
error);
|
||||
return (true);
|
||||
}
|
||||
|
||||
if (rst & BMCA_DMP_RC_RESET)
|
||||
return (true);
|
||||
|
||||
/* Is core clocked? */
|
||||
error = bhnd_read_ioctl(child, &ioctl);
|
||||
if (error) {
|
||||
device_printf(child, "error reading HW ioctl register: %d\n",
|
||||
error);
|
||||
return (true);
|
||||
}
|
||||
|
||||
if (!(ioctl & BHND_IOCTL_CLK_EN))
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
struct bhnd_resource *r;
|
||||
int error;
|
||||
|
||||
if (device_get_parent(child) != dev)
|
||||
BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
|
||||
return (EINVAL);
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
pm = dinfo->pmu_info;
|
||||
|
||||
/* We require exclusive control over BHND_IOCTL_CLK_EN and
|
||||
* BHND_IOCTL_CLK_FORCE. */
|
||||
if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE))
|
||||
return (EINVAL);
|
||||
|
||||
/* Can't suspend the core without access to the agent registers */
|
||||
if (dinfo->res_agent == NULL)
|
||||
if ((r = dinfo->res_agent) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
// TODO - perform suspend
|
||||
/* Place core into known RESET state */
|
||||
if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
|
||||
return (error);
|
||||
|
||||
return (ENXIO);
|
||||
/*
|
||||
* Leaving the core in reset:
|
||||
* - Set the caller's IOCTL flags
|
||||
* - Enable clocks
|
||||
* - Force clock distribution to ensure propagation throughout the
|
||||
* core.
|
||||
*/
|
||||
error = bhnd_write_ioctl(child,
|
||||
ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE, UINT16_MAX);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Bring the core out of reset */
|
||||
if ((error = bcma_dmp_write_reset(child, dinfo, 0x0)))
|
||||
return (error);
|
||||
|
||||
/* Disable forced clock gating (leaving clock enabled) */
|
||||
error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width)
|
||||
static int
|
||||
bcma_suspend_hw(device_t dev, device_t child)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
struct bhnd_resource *r;
|
||||
struct bcma_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
struct bhnd_resource *r;
|
||||
uint32_t rst;
|
||||
int error;
|
||||
|
||||
/* Must be a directly attached child core */
|
||||
if (device_get_parent(child) != dev)
|
||||
return (UINT32_MAX);
|
||||
return (EINVAL);
|
||||
|
||||
/* Fetch the agent registers */
|
||||
dinfo = device_get_ivars(child);
|
||||
pm = dinfo->pmu_info;
|
||||
|
||||
/* Can't suspend the core without access to the agent registers */
|
||||
if ((r = dinfo->res_agent) == NULL)
|
||||
return (UINT32_MAX);
|
||||
return (ENODEV);
|
||||
|
||||
/* Verify bounds */
|
||||
if (offset > rman_get_size(r->res))
|
||||
return (UINT32_MAX);
|
||||
/* Wait for any pending reset operations to clear */
|
||||
if ((error = bcma_dmp_wait_reset(child, dinfo)))
|
||||
return (error);
|
||||
|
||||
if (rman_get_size(r->res) - offset < width)
|
||||
return (UINT32_MAX);
|
||||
/* Already in reset? */
|
||||
rst = bhnd_bus_read_4(r, BCMA_DMP_RESETCTRL);
|
||||
if (rst & BMCA_DMP_RC_RESET)
|
||||
return (0);
|
||||
|
||||
switch (width) {
|
||||
case 1:
|
||||
return (bhnd_bus_read_1(r, offset));
|
||||
case 2:
|
||||
return (bhnd_bus_read_2(r, offset));
|
||||
case 4:
|
||||
return (bhnd_bus_read_4(r, offset));
|
||||
default:
|
||||
return (UINT32_MAX);
|
||||
/* Put core into reset */
|
||||
if ((error = bcma_dmp_write_reset(child, dinfo, BMCA_DMP_RC_RESET)))
|
||||
return (error);
|
||||
|
||||
/* Clear core flags */
|
||||
if ((error = bhnd_write_ioctl(child, 0x0, UINT16_MAX)))
|
||||
return (error);
|
||||
|
||||
/* Inform PMU that all outstanding request state should be discarded */
|
||||
if (pm != NULL) {
|
||||
if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
|
||||
static int
|
||||
bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
|
||||
u_int width)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
@@ -260,32 +396,70 @@ bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
|
||||
|
||||
/* Must be a directly attached child core */
|
||||
if (device_get_parent(child) != dev)
|
||||
return;
|
||||
return (EINVAL);
|
||||
|
||||
/* Fetch the agent registers */
|
||||
dinfo = device_get_ivars(child);
|
||||
if ((r = dinfo->res_agent) == NULL)
|
||||
return;
|
||||
return (ENODEV);
|
||||
|
||||
/* Verify bounds */
|
||||
if (offset > rman_get_size(r->res))
|
||||
return;
|
||||
return (EFAULT);
|
||||
|
||||
if (rman_get_size(r->res) - offset < width)
|
||||
return;
|
||||
return (EFAULT);
|
||||
|
||||
switch (width) {
|
||||
case 1:
|
||||
bhnd_bus_write_1(r, offset, val);
|
||||
break;
|
||||
*((uint8_t *)value) = bhnd_bus_read_1(r, offset);
|
||||
return (0);
|
||||
case 2:
|
||||
bhnd_bus_write_2(r, offset, val);
|
||||
break;
|
||||
*((uint16_t *)value) = bhnd_bus_read_2(r, offset);
|
||||
return (0);
|
||||
case 4:
|
||||
bhnd_bus_write_4(r, offset, val);
|
||||
break;
|
||||
*((uint32_t *)value) = bhnd_bus_read_4(r, offset);
|
||||
return (0);
|
||||
default:
|
||||
break;
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
bcma_write_config(device_t dev, device_t child, bus_size_t offset,
|
||||
const void *value, u_int width)
|
||||
{
|
||||
struct bcma_devinfo *dinfo;
|
||||
struct bhnd_resource *r;
|
||||
|
||||
/* Must be a directly attached child core */
|
||||
if (device_get_parent(child) != dev)
|
||||
return (EINVAL);
|
||||
|
||||
/* Fetch the agent registers */
|
||||
dinfo = device_get_ivars(child);
|
||||
if ((r = dinfo->res_agent) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
/* Verify bounds */
|
||||
if (offset > rman_get_size(r->res))
|
||||
return (EFAULT);
|
||||
|
||||
if (rman_get_size(r->res) - offset < width)
|
||||
return (EFAULT);
|
||||
|
||||
switch (width) {
|
||||
case 1:
|
||||
bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
|
||||
return (0);
|
||||
case 2:
|
||||
bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
|
||||
return (0);
|
||||
case 4:
|
||||
bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
|
||||
return (0);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,19 +675,6 @@ bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct bhnd_devinfo *
|
||||
bcma_alloc_bhnd_dinfo(device_t dev)
|
||||
{
|
||||
struct bcma_devinfo *dinfo = bcma_alloc_dinfo(dev);
|
||||
return ((struct bhnd_devinfo *)dinfo);
|
||||
}
|
||||
|
||||
static void
|
||||
bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
|
||||
{
|
||||
bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the device enumeration ROM table, adding all valid discovered cores to
|
||||
* the bus.
|
||||
@@ -607,16 +768,20 @@ static device_method_t bcma_methods[] = {
|
||||
DEVMETHOD(device_detach, bcma_detach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_add_child, bcma_add_child),
|
||||
DEVMETHOD(bus_child_deleted, bcma_child_deleted),
|
||||
DEVMETHOD(bus_read_ivar, bcma_read_ivar),
|
||||
DEVMETHOD(bus_write_ivar, bcma_write_ivar),
|
||||
DEVMETHOD(bus_get_resource_list, bcma_get_resource_list),
|
||||
|
||||
/* BHND interface */
|
||||
DEVMETHOD(bhnd_bus_get_erom_class, bcma_get_erom_class),
|
||||
DEVMETHOD(bhnd_bus_alloc_devinfo, bcma_alloc_bhnd_dinfo),
|
||||
DEVMETHOD(bhnd_bus_free_devinfo, bcma_free_bhnd_dinfo),
|
||||
DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core),
|
||||
DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core),
|
||||
DEVMETHOD(bhnd_bus_read_ioctl, bcma_read_ioctl),
|
||||
DEVMETHOD(bhnd_bus_write_ioctl, bcma_write_ioctl),
|
||||
DEVMETHOD(bhnd_bus_read_iost, bcma_read_iost),
|
||||
DEVMETHOD(bhnd_bus_is_hw_suspended, bcma_is_hw_suspended),
|
||||
DEVMETHOD(bhnd_bus_reset_hw, bcma_reset_hw),
|
||||
DEVMETHOD(bhnd_bus_suspend_hw, bcma_suspend_hw),
|
||||
DEVMETHOD(bhnd_bus_read_config, bcma_read_config),
|
||||
DEVMETHOD(bhnd_bus_write_config, bcma_write_config),
|
||||
DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -99,6 +99,11 @@ void bcma_free_corecfg(struct bcma_corecfg *corecfg);
|
||||
struct bcma_sport *bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type);
|
||||
void bcma_free_sport(struct bcma_sport *sport);
|
||||
|
||||
int bcma_dmp_wait_reset(device_t child,
|
||||
struct bcma_devinfo *dinfo);
|
||||
int bcma_dmp_write_reset(device_t child,
|
||||
struct bcma_devinfo *dinfo, uint32_t value);
|
||||
|
||||
/** BCMA master port descriptor */
|
||||
struct bcma_mport {
|
||||
bcma_pid_t mp_num; /**< AXI port identifier (bus-unique) */
|
||||
@@ -150,14 +155,14 @@ struct bcma_corecfg {
|
||||
* BCMA per-device info
|
||||
*/
|
||||
struct bcma_devinfo {
|
||||
struct bhnd_devinfo bhnd_dinfo; /**< superclass device info. */
|
||||
struct resource_list resources; /**< Slave port memory regions. */
|
||||
struct bcma_corecfg *corecfg; /**< IP core/block config */
|
||||
|
||||
struct resource_list resources; /**< Slave port memory regions. */
|
||||
struct bcma_corecfg *corecfg; /**< IP core/block config */
|
||||
struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not
|
||||
* all bcma(4) cores have or require an agent. */
|
||||
int rid_agent; /**< Agent resource ID, or -1 */
|
||||
|
||||
struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not
|
||||
* all bcma(4) cores have or require an agent. */
|
||||
int rid_agent; /**< Agent resource ID, or -1 */
|
||||
struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */
|
||||
};
|
||||
|
||||
|
||||
|
||||
+21
-74
@@ -631,7 +631,6 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_resource *br;
|
||||
struct chipc_caps *ccaps;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
struct resource_list *rl;
|
||||
struct resource_list_entry *rle;
|
||||
@@ -644,7 +643,7 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
|
||||
GIANT_REQUIRED; /* for newbus */
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
dinfo = device_get_ivars(child);
|
||||
pm = bhnd_get_pmu_info(child);
|
||||
pmu_regs = BHND_CLK_CTL_ST;
|
||||
|
||||
if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) {
|
||||
@@ -660,7 +659,7 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
|
||||
}
|
||||
|
||||
/* already allocated? */
|
||||
if (dinfo->pmu_info != NULL) {
|
||||
if (pm != NULL) {
|
||||
panic("duplicate PMU allocation for %s",
|
||||
device_get_nameunit(child));
|
||||
}
|
||||
@@ -728,7 +727,7 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
|
||||
br->res = rle->res;
|
||||
br->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0);
|
||||
|
||||
pm = malloc(sizeof(*dinfo->pmu_info), M_BHND, M_NOWAIT);
|
||||
pm = malloc(sizeof(*pm), M_BHND, M_NOWAIT);
|
||||
if (pm == NULL) {
|
||||
free(br, M_BHND);
|
||||
return (ENOMEM);
|
||||
@@ -738,7 +737,7 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
|
||||
pm->pm_res = br;
|
||||
pm->pm_regs = pmu_regs;
|
||||
|
||||
dinfo->pmu_info = pm;
|
||||
bhnd_set_pmu_info(child, pm);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -749,14 +748,13 @@ int
|
||||
bhnd_generic_release_pmu(device_t dev, device_t child)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
device_t pmu;
|
||||
int error;
|
||||
|
||||
GIANT_REQUIRED; /* for newbus */
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
if ((pmu = bhnd_find_pmu(sc)) == NULL) {
|
||||
device_printf(sc->dev,
|
||||
@@ -765,16 +763,17 @@ bhnd_generic_release_pmu(device_t dev, device_t child)
|
||||
}
|
||||
|
||||
/* dispatch release request */
|
||||
if (dinfo->pmu_info == NULL)
|
||||
pm = bhnd_get_pmu_info(child);
|
||||
if (pm == NULL)
|
||||
panic("pmu over-release for %s", device_get_nameunit(child));
|
||||
|
||||
if ((error = BHND_PMU_CORE_RELEASE(pmu, dinfo->pmu_info)))
|
||||
if ((error = BHND_PMU_CORE_RELEASE(pmu, pm)))
|
||||
return (error);
|
||||
|
||||
/* free PMU info */
|
||||
free(dinfo->pmu_info->pm_res, M_BHND);
|
||||
free(dinfo->pmu_info, M_BHND);
|
||||
dinfo->pmu_info = NULL;
|
||||
bhnd_set_pmu_info(child, NULL);
|
||||
free(pm->pm_res, M_BHND);
|
||||
free(pm, M_BHND);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@@ -786,13 +785,11 @@ int
|
||||
bhnd_generic_request_clock(device_t dev, device_t child, bhnd_clock clock)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
if ((pm = dinfo->pmu_info) == NULL)
|
||||
if ((pm = bhnd_get_pmu_info(child)) == NULL)
|
||||
panic("no active PMU request state");
|
||||
|
||||
/* dispatch request to PMU */
|
||||
@@ -806,13 +803,11 @@ int
|
||||
bhnd_generic_enable_clocks(device_t dev, device_t child, uint32_t clocks)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
if ((pm = dinfo->pmu_info) == NULL)
|
||||
if ((pm = bhnd_get_pmu_info(child)) == NULL)
|
||||
panic("no active PMU request state");
|
||||
|
||||
/* dispatch request to PMU */
|
||||
@@ -826,13 +821,11 @@ int
|
||||
bhnd_generic_request_ext_rsrc(device_t dev, device_t child, u_int rsrc)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
if ((pm = dinfo->pmu_info) == NULL)
|
||||
if ((pm = bhnd_get_pmu_info(child)) == NULL)
|
||||
panic("no active PMU request state");
|
||||
|
||||
/* dispatch request to PMU */
|
||||
@@ -846,13 +839,11 @@ int
|
||||
bhnd_generic_release_ext_rsrc(device_t dev, device_t child, u_int rsrc)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
if ((pm = dinfo->pmu_info) == NULL)
|
||||
if ((pm = bhnd_get_pmu_info(child)) == NULL)
|
||||
panic("no active PMU request state");
|
||||
|
||||
/* dispatch request to PMU */
|
||||
@@ -1034,43 +1025,6 @@ bhnd_child_location_str(device_t dev, device_t child, char *buf,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default bhnd(4) bus driver implementation of BUS_ADD_CHILD().
|
||||
*
|
||||
* This implementation manages internal bhnd(4) state, and must be called
|
||||
* by subclassing drivers.
|
||||
*/
|
||||
device_t
|
||||
bhnd_generic_add_child(device_t dev, u_int order, const char *name, int unit)
|
||||
{
|
||||
struct bhnd_devinfo *dinfo;
|
||||
device_t child;
|
||||
|
||||
child = device_add_child_ordered(dev, order, name, unit);
|
||||
if (child == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((dinfo = BHND_BUS_ALLOC_DEVINFO(dev)) == NULL) {
|
||||
device_delete_child(dev, child);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
device_set_ivars(child, dinfo);
|
||||
|
||||
return (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default bhnd(4) bus driver implementation of BHND_BUS_CHILD_ADDED().
|
||||
*
|
||||
* This implementation manages internal bhnd(4) state, and must be called
|
||||
* by subclassing drivers.
|
||||
*/
|
||||
void
|
||||
bhnd_generic_child_added(device_t dev, device_t child)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Default bhnd(4) bus driver implementation of BUS_CHILD_DELETED().
|
||||
*
|
||||
@@ -1081,21 +1035,16 @@ void
|
||||
bhnd_generic_child_deleted(device_t dev, device_t child)
|
||||
{
|
||||
struct bhnd_softc *sc;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Free device info */
|
||||
if ((dinfo = device_get_ivars(child)) != NULL) {
|
||||
if (dinfo->pmu_info != NULL) {
|
||||
/* Releasing PMU requests automatically would be nice,
|
||||
* but we can't reference per-core PMU register
|
||||
* resource after driver detach */
|
||||
panic("%s leaked device pmu state\n",
|
||||
device_get_nameunit(child));
|
||||
}
|
||||
|
||||
BHND_BUS_FREE_DEVINFO(dev, dinfo);
|
||||
if (bhnd_get_pmu_info(child) != NULL) {
|
||||
/* Releasing PMU requests automatically would be nice,
|
||||
* but we can't reference per-core PMU register
|
||||
* resource after driver detach */
|
||||
panic("%s leaked device pmu state\n",
|
||||
device_get_nameunit(child));
|
||||
}
|
||||
|
||||
/* Clean up platform device references */
|
||||
@@ -1228,7 +1177,6 @@ static device_method_t bhnd_methods[] = {
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_new_pass, bhnd_new_pass),
|
||||
DEVMETHOD(bus_add_child, bhnd_generic_add_child),
|
||||
DEVMETHOD(bus_child_deleted, bhnd_generic_child_deleted),
|
||||
DEVMETHOD(bus_probe_nomatch, bhnd_generic_probe_nomatch),
|
||||
DEVMETHOD(bus_print_child, bhnd_generic_print_child),
|
||||
@@ -1269,7 +1217,6 @@ static device_method_t bhnd_methods[] = {
|
||||
DEVMETHOD(bhnd_bus_request_ext_rsrc, bhnd_generic_request_ext_rsrc),
|
||||
DEVMETHOD(bhnd_bus_release_ext_rsrc, bhnd_generic_release_ext_rsrc),
|
||||
|
||||
DEVMETHOD(bhnd_bus_child_added, bhnd_generic_child_added),
|
||||
DEVMETHOD(bhnd_bus_is_region_valid, bhnd_generic_is_region_valid),
|
||||
DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_generic_get_nvram_var),
|
||||
|
||||
|
||||
+189
-16
@@ -46,6 +46,8 @@
|
||||
|
||||
#include "nvram/bhnd_nvram.h"
|
||||
|
||||
struct bhnd_core_pmu_info;
|
||||
|
||||
extern devclass_t bhnd_devclass;
|
||||
extern devclass_t bhnd_hostb_devclass;
|
||||
extern devclass_t bhnd_nvram_devclass;
|
||||
@@ -67,6 +69,7 @@ enum bhnd_device_vars {
|
||||
BHND_IVAR_CORE_UNIT, /**< Bus-assigned core unit number,
|
||||
assigned sequentially (starting at 0) for
|
||||
each vendor/device pair. */
|
||||
BHND_IVAR_PMU_INFO, /**< Internal bus-managed PMU state */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,6 +102,39 @@ enum {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Per-core IOCTL flags common to all bhnd(4) cores.
|
||||
*/
|
||||
enum {
|
||||
BHND_IOCTL_BIST = 0x8000, /**< Initiate a built-in self-test (BIST). Must be cleared
|
||||
after BIST results are read via BHND_IOST_BIST_* */
|
||||
BHND_IOCTL_PME = 0x4000, /**< Enable posting of power management events by the core. */
|
||||
BHND_IOCTL_CFLAGS = 0x3FFC, /**< Reserved for core-specific ioctl flags. */
|
||||
BHND_IOCTL_CLK_FORCE = 0x0002, /**< Force disable of clock gating, resulting in all clocks
|
||||
being distributed within the core. Should be set when
|
||||
asserting/deasserting reset to ensure the reset signal
|
||||
fully propagates to the entire core. */
|
||||
BHND_IOCTL_CLK_EN = 0x0001, /**< If cleared, the core clock will be disabled. Should be
|
||||
set during normal operation, and cleared when the core is
|
||||
held in reset. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Per-core IOST flags common to all bhnd(4) cores.
|
||||
*/
|
||||
enum {
|
||||
BHND_IOST_BIST_DONE = 0x8000, /**< Set upon BIST completion (see BHND_IOCTL_BIST), and cleared
|
||||
if 0 is written to BHND_IOCTL_BIST. */
|
||||
BHND_IOST_BIST_FAIL = 0x4000, /**< Set upon detection of a BIST error; the value is unspecified
|
||||
if BIST has not completed and BHND_IOST_BIST_DONE is not set. */
|
||||
BHND_IOST_CLK = 0x2000, /**< Set if the core has requested that gated clocks be enabled, or
|
||||
cleared otherwise. The value is undefined if a core does not
|
||||
support clock gating. */
|
||||
BHND_IOST_DMA64 = 0x1000, /**< Set if this core supports 64-bit DMA */
|
||||
BHND_IOST_CFLAGS = 0x0FFC, /**< Reserved for core-specific status flags. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Simplified accessors for bhnd device ivars
|
||||
*/
|
||||
@@ -113,6 +149,7 @@ BHND_ACCESSOR(vendor_name, VENDOR_NAME, const char *);
|
||||
BHND_ACCESSOR(device_name, DEVICE_NAME, const char *);
|
||||
BHND_ACCESSOR(core_index, CORE_INDEX, u_int);
|
||||
BHND_ACCESSOR(core_unit, CORE_UNIT, int);
|
||||
BHND_ACCESSOR(pmu_info, PMU_INFO, struct bhnd_core_pmu_info *);
|
||||
|
||||
#undef BHND_ACCESSOR
|
||||
|
||||
@@ -451,6 +488,119 @@ bhnd_get_chipid(device_t dev) {
|
||||
return (BHND_BUS_GET_CHIPID(device_get_parent(dev), dev));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Read the current value of a bhnd(4) device's per-core I/O control register.
|
||||
*
|
||||
* @param dev The bhnd bus child device to be queried.
|
||||
* @param[out] ioctl On success, the I/O control register value.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval ENODEV If agent/config space for @p child is unavailable.
|
||||
* @retval non-zero If reading the IOCTL register otherwise fails, a regular
|
||||
* unix error code will be returned.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_read_ioctl(device_t dev, uint16_t *ioctl)
|
||||
{
|
||||
return (BHND_BUS_READ_IOCTL(device_get_parent(dev), dev, ioctl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write @p value and @p mask to a bhnd(4) device's per-core I/O control
|
||||
* register.
|
||||
*
|
||||
* @param dev The bhnd bus child device for which the IOCTL register will be
|
||||
* written.
|
||||
* @param value The value to be written (see BHND_IOCTL_*).
|
||||
* @param mask Only the bits defined by @p mask will be updated from @p value.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval ENODEV If agent/config space for @p child is unavailable.
|
||||
* @retval non-zero If writing the IOCTL register otherwise fails, a regular
|
||||
* unix error code will be returned.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_write_ioctl(device_t dev, uint16_t value, uint16_t mask)
|
||||
{
|
||||
return (BHND_BUS_WRITE_IOCTL(device_get_parent(dev), dev, value, mask));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current value of a bhnd(4) device's per-core I/O status register.
|
||||
*
|
||||
* @param dev The bhnd bus child device to be queried.
|
||||
* @param[out] iost On success, the I/O status register value.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval ENODEV If agent/config space for @p child is unavailable.
|
||||
* @retval non-zero If reading the IOST register otherwise fails, a regular
|
||||
* unix error code will be returned.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_read_iost(device_t dev, uint16_t *iost)
|
||||
{
|
||||
return (BHND_BUS_READ_IOST(device_get_parent(dev), dev, iost));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given bhnd device's hardware is currently held
|
||||
* in a RESET state or otherwise not clocked (BHND_IOCTL_CLK_EN).
|
||||
*
|
||||
* @param dev The device to query.
|
||||
*
|
||||
* @retval true If @p dev is held in RESET or not clocked (BHND_IOCTL_CLK_EN),
|
||||
* or an error occured determining @p dev's hardware state.
|
||||
* @retval false If @p dev is clocked and is not held in RESET.
|
||||
*/
|
||||
static inline bool
|
||||
bhnd_is_hw_suspended(device_t dev)
|
||||
{
|
||||
return (BHND_BUS_IS_HW_SUSPENDED(device_get_parent(dev), dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the bhnd(4) device's hardware into a reset state, and then bring the
|
||||
* hardware out of reset with BHND_IOCTL_CLK_EN and @p ioctl flags set.
|
||||
*
|
||||
* Any clock or resource PMU requests previously made by @p dev will be
|
||||
* invalidated.
|
||||
*
|
||||
* @param dev The device to be reset.
|
||||
* @param ioctl Device-specific core ioctl flags to be supplied on reset
|
||||
* (see BHND_IOCTL_*).
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero error
|
||||
*/
|
||||
static inline int
|
||||
bhnd_reset_hw(device_t dev, uint16_t ioctl)
|
||||
{
|
||||
return (BHND_BUS_RESET_HW(device_get_parent(dev), dev, ioctl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend @p child's hardware in a low-power reset state.
|
||||
*
|
||||
* Any clock or resource PMU requests previously made by @p dev will be
|
||||
* invalidated.
|
||||
*
|
||||
* The hardware may be brought out of reset via bhnd_reset_hw().
|
||||
*
|
||||
* @param dev The device to be suspended.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero error
|
||||
*/
|
||||
static inline int
|
||||
bhnd_suspend_hw(device_t dev)
|
||||
{
|
||||
return (BHND_BUS_SUSPEND_HW(device_get_parent(dev), dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* If supported by the chipset, return the clock source for the given clock.
|
||||
*
|
||||
@@ -630,17 +780,18 @@ bhnd_release_pmu(device_t dev)
|
||||
/**
|
||||
* Request that @p clock (or faster) be routed to @p dev.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate clock request state
|
||||
* @note A driver must ask the bhnd bus to allocate clock request state
|
||||
* via bhnd_alloc_pmu() before it can request clock resources.
|
||||
*
|
||||
* Request multiplexing is managed by the bus.
|
||||
* @note Any outstanding PMU clock requests will be discarded upon calling
|
||||
* BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW().
|
||||
*
|
||||
* @param dev The bhnd(4) device to which @p clock should be routed.
|
||||
* @param clock The requested clock source.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENODEV If an unsupported clock was requested.
|
||||
* @retval ENXIO If the PMU has not been initialized or is otherwise unvailable.
|
||||
* @retval ENXIO If the PMU has not been initialized or is otherwise unvailable,
|
||||
*/
|
||||
static inline int
|
||||
bhnd_request_clock(device_t dev, bhnd_clock clock)
|
||||
@@ -654,12 +805,13 @@ bhnd_request_clock(device_t dev, bhnd_clock clock)
|
||||
* This will power any clock sources (e.g. XTAL, PLL, etc) required for
|
||||
* @p clocks and wait until they are ready, discarding any previous
|
||||
* requests by @p dev.
|
||||
*
|
||||
* Request multiplexing is managed by the bus.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate clock request state
|
||||
* @note A driver must ask the bhnd bus to allocate clock request state
|
||||
* via bhnd_alloc_pmu() before it can request clock resources.
|
||||
*
|
||||
*
|
||||
* @note Any outstanding PMU clock requests will be discarded upon calling
|
||||
* BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW().
|
||||
*
|
||||
* @param dev The requesting bhnd(4) device.
|
||||
* @param clocks The clock(s) to be enabled.
|
||||
*
|
||||
@@ -676,9 +828,12 @@ bhnd_enable_clocks(device_t dev, uint32_t clocks)
|
||||
/**
|
||||
* Power up an external PMU-managed resource assigned to @p dev.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate PMU request state
|
||||
* @note A driver must ask the bhnd bus to allocate PMU request state
|
||||
* via bhnd_alloc_pmu() before it can request PMU resources.
|
||||
*
|
||||
* @note Any outstanding PMU resource requests will be released upon calling
|
||||
* bhnd_reset_hw() or bhnd_suspend_hw().
|
||||
*
|
||||
* @param dev The requesting bhnd(4) device.
|
||||
* @param rsrc The core-specific external resource identifier.
|
||||
*
|
||||
@@ -711,13 +866,14 @@ bhnd_release_ext_rsrc(device_t dev, u_int rsrc)
|
||||
return (BHND_BUS_RELEASE_EXT_RSRC(device_get_parent(dev), dev, rsrc));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read @p width bytes at @p offset from the bus-specific agent/config
|
||||
* space of @p dev.
|
||||
*
|
||||
* @param dev The bhnd device for which @p offset should be read.
|
||||
* @param offset The offset to be read.
|
||||
* @param[out] value On success, the will be set to the @p width value read
|
||||
* at @p offset.
|
||||
* @param width The size of the access. Must be 1, 2 or 4 bytes.
|
||||
*
|
||||
* The exact behavior of this method is bus-specific. In the case of
|
||||
@@ -725,32 +881,49 @@ bhnd_release_ext_rsrc(device_t dev, u_int rsrc)
|
||||
*
|
||||
* @note Device drivers should only use this API for functionality
|
||||
* that is not available via another bhnd(4) function.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval EINVAL If @p width is not one of 1, 2, or 4 bytes.
|
||||
* @retval ENODEV If accessing agent/config space for @p child is unsupported.
|
||||
* @retval EFAULT If reading @p width at @p offset exceeds the bounds of
|
||||
* the mapped agent/config space for @p child.
|
||||
*/
|
||||
static inline uint32_t
|
||||
bhnd_read_config(device_t dev, bus_size_t offset, u_int width)
|
||||
bhnd_read_config(device_t dev, bus_size_t offset, void *value, u_int width)
|
||||
{
|
||||
return (BHND_BUS_READ_CONFIG(device_get_parent(dev), dev, offset,
|
||||
width));
|
||||
value, width));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read @p width bytes at @p offset from the bus-specific agent/config
|
||||
* Write @p width bytes at @p offset to the bus-specific agent/config
|
||||
* space of @p dev.
|
||||
*
|
||||
* @param dev The bhnd device for which @p offset should be read.
|
||||
* @param offset The offset to be written.
|
||||
* @param width The size of the access. Must be 1, 2 or 4 bytes.
|
||||
* @param value A pointer to the value to be written.
|
||||
* @param width The size of @p value. Must be 1, 2 or 4 bytes.
|
||||
*
|
||||
* The exact behavior of this method is bus-specific. In the case of
|
||||
* bcma(4), this method provides access to the first agent port of @p child.
|
||||
*
|
||||
* @note Device drivers should only use this API for functionality
|
||||
* that is not available via another bhnd(4) function.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval EINVAL If @p width is not one of 1, 2, or 4 bytes.
|
||||
* @retval ENODEV If accessing agent/config space for @p child is unsupported.
|
||||
* @retval EFAULT If reading @p width at @p offset exceeds the bounds of
|
||||
* the mapped agent/config space for @p child.
|
||||
*/
|
||||
static inline void
|
||||
bhnd_write_config(device_t dev, bus_size_t offset, uint32_t val, u_int width)
|
||||
static inline int
|
||||
bhnd_write_config(device_t dev, bus_size_t offset, const void *value,
|
||||
u_int width)
|
||||
{
|
||||
BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset, val, width);
|
||||
return (BHND_BUS_WRITE_CONFIG(device_get_parent(dev), dev, offset,
|
||||
value, width));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+178
-53
@@ -63,6 +63,46 @@ CODE {
|
||||
panic("bhnd_bus_get_chipid unimplemented");
|
||||
}
|
||||
|
||||
static int
|
||||
bhnd_bus_null_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
|
||||
{
|
||||
panic("bhnd_bus_read_ioctl unimplemented");
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bhnd_bus_null_write_ioctl(device_t dev, device_t child, uint16_t value,
|
||||
uint16_t mask)
|
||||
{
|
||||
panic("bhnd_bus_write_ioctl unimplemented");
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bhnd_bus_null_read_iost(device_t dev, device_t child, uint16_t *iost)
|
||||
{
|
||||
panic("bhnd_bus_read_iost unimplemented");
|
||||
}
|
||||
|
||||
static bool
|
||||
bhnd_bus_null_is_hw_suspended(device_t dev, device_t child)
|
||||
{
|
||||
panic("bhnd_bus_is_hw_suspended unimplemented");
|
||||
}
|
||||
|
||||
static int
|
||||
bhnd_bus_null_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
||||
{
|
||||
panic("bhnd_bus_reset_hw unimplemented");
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bhnd_bus_null_suspend_hw(device_t dev, device_t child)
|
||||
{
|
||||
panic("bhnd_bus_suspend_hw unimplemented");
|
||||
}
|
||||
|
||||
static bhnd_attach_type
|
||||
bhnd_bus_null_get_attach_type(device_t dev, device_t child)
|
||||
{
|
||||
@@ -161,16 +201,16 @@ CODE {
|
||||
panic("bhnd_bus_release_ext_rsrc unimplemented");
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
static int
|
||||
bhnd_bus_null_read_config(device_t dev, device_t child,
|
||||
bus_size_t offset, u_int width)
|
||||
bus_size_t offset, void *value, u_int width)
|
||||
{
|
||||
panic("bhnd_bus_null_read_config unimplemented");
|
||||
}
|
||||
|
||||
static void
|
||||
bhnd_bus_null_write_config(device_t dev, device_t child,
|
||||
bus_size_t offset, uint32_t val, u_int width)
|
||||
bus_size_t offset, void *value, u_int width)
|
||||
{
|
||||
panic("bhnd_bus_null_write_config unimplemented");
|
||||
}
|
||||
@@ -343,32 +383,6 @@ METHOD int read_board_info {
|
||||
struct bhnd_board_info *info;
|
||||
} DEFAULT bhnd_bus_null_read_board_info;
|
||||
|
||||
/**
|
||||
* Allocate and zero-initialize a buffer suitably sized and aligned for a
|
||||
* bhnd_devinfo structure.
|
||||
*
|
||||
* @param dev The bhnd bus device.
|
||||
*
|
||||
* @retval non-NULL success
|
||||
* @retval NULL allocation failed
|
||||
*/
|
||||
METHOD struct bhnd_devinfo * alloc_devinfo {
|
||||
device_t dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* Release memory previously allocated for @p devinfo.
|
||||
*
|
||||
* @param dev The bhnd bus device.
|
||||
* @param dinfo A devinfo buffer previously allocated via
|
||||
* BHND_BUS_ALLOC_DEVINFO().
|
||||
*/
|
||||
METHOD void free_devinfo {
|
||||
device_t dev;
|
||||
struct bhnd_devinfo *dinfo;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of interrupts to be assigned to @p child via
|
||||
* BHND_BUS_ASSIGN_INTR().
|
||||
@@ -455,34 +469,123 @@ METHOD void child_added {
|
||||
} DEFAULT bhnd_bus_null_child_added;
|
||||
|
||||
/**
|
||||
* Reset the device's hardware core.
|
||||
* Read the current value of @p child's I/O control register.
|
||||
*
|
||||
* @param dev The parent of @p child.
|
||||
* @param child The device to be reset.
|
||||
* @param flags Device-specific core flags to be supplied on reset.
|
||||
* @param dev The bhnd bus parent of @p child.
|
||||
* @param child The bhnd device for which the I/O control register should be
|
||||
* read.
|
||||
* @param[out] ioctl On success, the I/O control register value.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero error
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval ENODEV If agent/config space for @p child is unavailable.
|
||||
* @retval non-zero If reading the IOCTL register otherwise fails, a regular
|
||||
* unix error code will be returned.
|
||||
*/
|
||||
METHOD int reset_core {
|
||||
METHOD int read_ioctl {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
uint16_t flags;
|
||||
}
|
||||
uint16_t *ioctl;
|
||||
} DEFAULT bhnd_bus_null_read_ioctl;
|
||||
|
||||
/**
|
||||
* Suspend a device hardware core.
|
||||
* Write @p value with @p mask to @p child's I/O control register.
|
||||
*
|
||||
* @param dev The bhnd bus parent of @p child.
|
||||
* @param child The bhnd device for which the I/O control register should
|
||||
* be updated.
|
||||
* @param value The value to be written (see also BHND_IOCTL_*).
|
||||
* @param mask Only the bits defined by @p mask will be updated from @p value.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval ENODEV If agent/config space for @p child is unavailable.
|
||||
* @retval non-zero If writing the IOCTL register otherwise fails, a regular
|
||||
* unix error code will be returned.
|
||||
*/
|
||||
METHOD int write_ioctl {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
uint16_t value;
|
||||
uint16_t mask;
|
||||
} DEFAULT bhnd_bus_null_write_ioctl;
|
||||
|
||||
/**
|
||||
* Read the current value of @p child's I/O status register.
|
||||
*
|
||||
* @param dev The parent of @p child.
|
||||
* @param dev The bhnd bus parent of @p child.
|
||||
* @param child The bhnd device for which the I/O status register should be
|
||||
* read.
|
||||
* @param[out] iost On success, the I/O status register value.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval ENODEV If agent/config space for @p child is unavailable.
|
||||
* @retval non-zero If reading the IOST register otherwise fails, a regular
|
||||
* unix error code will be returned.
|
||||
*/
|
||||
METHOD int read_iost {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
uint16_t *iost;
|
||||
} DEFAULT bhnd_bus_null_read_iost;
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the given bhnd device's hardware is currently held
|
||||
* in a RESET state or otherwise not clocked (BHND_IOCTL_CLK_EN).
|
||||
*
|
||||
* @param dev The bhnd bus parent of @p child.
|
||||
* @param child The device to query.
|
||||
*
|
||||
* @retval true If @p child is held in RESET or not clocked (BHND_IOCTL_CLK_EN),
|
||||
* or an error occured determining @p child's hardware state.
|
||||
* @retval false If @p child is clocked and is not held in RESET.
|
||||
*/
|
||||
METHOD bool is_hw_suspended {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
} DEFAULT bhnd_bus_null_is_hw_suspended;
|
||||
|
||||
/**
|
||||
* Place the bhnd(4) device's hardware into a reset state, and then bring the
|
||||
* hardware out of reset with BHND_IOCTL_CLK_EN and @p ioctl flags set.
|
||||
*
|
||||
* Any clock or resource PMU requests previously made by @p child will be
|
||||
* invalidated.
|
||||
*
|
||||
* @param dev The bhnd bus parent of @p child.
|
||||
* @param child The device to be reset.
|
||||
* @param ioctl Device-specific core ioctl flags to be supplied on reset
|
||||
* (see BHND_IOCTL_*).
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero error
|
||||
*/
|
||||
METHOD int suspend_core {
|
||||
METHOD int reset_hw {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
}
|
||||
uint16_t ioctl;
|
||||
} DEFAULT bhnd_bus_null_reset_hw;
|
||||
|
||||
/**
|
||||
* Suspend @p child's hardware in a low-power reset state.
|
||||
*
|
||||
* Any clock or resource PMU requests previously made by @p dev will be
|
||||
* invalidated.
|
||||
*
|
||||
* The hardware may be brought out of reset via bhnd_reset_hw().
|
||||
*
|
||||
* @param dev The bhnd bus parent of @P child.
|
||||
* @param dev The device to be suspended.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero error
|
||||
*/
|
||||
METHOD int suspend_hw {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
} DEFAULT bhnd_bus_null_suspend_hw;
|
||||
|
||||
/**
|
||||
* If supported by the chipset, return the clock source for the given clock.
|
||||
@@ -578,10 +681,11 @@ METHOD int release_pmu {
|
||||
/**
|
||||
* Request that @p clock (or faster) be routed to @p child.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate PMU request state
|
||||
* @note A driver must ask the bhnd bus to allocate PMU request state
|
||||
* via BHND_BUS_ALLOC_PMU() before it can request clock resources.
|
||||
*
|
||||
* Request multiplexing is managed by the bus.
|
||||
*
|
||||
* @note Any outstanding PMU clock requests will be discarded upon calling
|
||||
* BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW().
|
||||
*
|
||||
* @param dev The parent of @p child.
|
||||
* @param child The bhnd device requesting @p clock.
|
||||
@@ -604,11 +708,12 @@ METHOD int request_clock {
|
||||
* @p clocks and wait until they are ready, discarding any previous
|
||||
* requests by @p child.
|
||||
*
|
||||
* Request multiplexing is managed by the bus.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate PMU request state
|
||||
* @note A driver must ask the bhnd bus to allocate PMU request state
|
||||
* via BHND_BUS_ALLOC_PMU() before it can request clock resources.
|
||||
*
|
||||
* @note Any outstanding PMU clock requests will be discarded upon calling
|
||||
* BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW().
|
||||
*
|
||||
* @param dev The parent of @p child.
|
||||
* @param child The bhnd device requesting @p clock.
|
||||
* @param clock The requested clock source.
|
||||
@@ -626,9 +731,12 @@ METHOD int enable_clocks {
|
||||
/**
|
||||
* Power up an external PMU-managed resource assigned to @p child.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate PMU request state
|
||||
* @note A driver must ask the bhnd bus to allocate PMU request state
|
||||
* via BHND_BUS_ALLOC_PMU() before it can request PMU resources.
|
||||
*
|
||||
* @note Any outstanding PMU resource requests will be released upon calling
|
||||
* BHND_BUS_RESET_HW() or BHND_BUS_SUSPEND_HW().
|
||||
*
|
||||
* @param dev The parent of @p child.
|
||||
* @param child The bhnd device requesting @p rsrc.
|
||||
* @param rsrc The core-specific external resource identifier.
|
||||
@@ -646,7 +754,7 @@ METHOD int request_ext_rsrc {
|
||||
/**
|
||||
* Power down an external PMU-managed resource assigned to @p child.
|
||||
*
|
||||
* A driver must ask the bhnd bus to allocate PMU request state
|
||||
* @note A driver must ask the bhnd bus to allocate PMU request state
|
||||
* via BHND_BUS_ALLOC_PMU() before it can request PMU resources.
|
||||
*
|
||||
* @param dev The parent of @p child.
|
||||
@@ -670,6 +778,7 @@ METHOD int release_ext_rsrc {
|
||||
* @param dev The parent of @p child.
|
||||
* @param child The bhnd device for which @p offset should be read.
|
||||
* @param offset The offset to be read.
|
||||
* @param[out] value On success, the bytes read at @p offset.
|
||||
* @param width The size of the access. Must be 1, 2 or 4 bytes.
|
||||
*
|
||||
* The exact behavior of this method is bus-specific. On a bcma(4) bus, this
|
||||
@@ -678,11 +787,19 @@ METHOD int release_ext_rsrc {
|
||||
*
|
||||
* @note Device drivers should only use this API for functionality
|
||||
* that is not available via another bhnd(4) function.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval EINVAL If @p width is not one of 1, 2, or 4 bytes.
|
||||
* @retval ENODEV If accessing agent/config space for @p child is unsupported.
|
||||
* @retval EFAULT If reading @p width at @p offset exceeds the bounds of
|
||||
* the mapped agent/config space for @p child.
|
||||
*/
|
||||
METHOD uint32_t read_config {
|
||||
METHOD int read_config {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
bus_size_t offset;
|
||||
void *value;
|
||||
u_int width;
|
||||
} DEFAULT bhnd_bus_null_read_config;
|
||||
|
||||
@@ -693,19 +810,27 @@ METHOD uint32_t read_config {
|
||||
* @param dev The parent of @p child.
|
||||
* @param child The bhnd device for which @p offset should be read.
|
||||
* @param offset The offset to be written.
|
||||
* @param width The size of the access. Must be 1, 2 or 4 bytes.
|
||||
* @param value A pointer to the value to be written.
|
||||
* @param width The size of @p value. Must be 1, 2 or 4 bytes.
|
||||
*
|
||||
* The exact behavior of this method is bus-specific. In the case of
|
||||
* bcma(4), this method provides access to the first agent port of @p child.
|
||||
*
|
||||
* @note Device drivers should only use this API for functionality
|
||||
* that is not available via another bhnd(4) function.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EINVAL If @p child is not a direct child of @p dev.
|
||||
* @retval EINVAL If @p width is not one of 1, 2, or 4 bytes.
|
||||
* @retval ENODEV If accessing agent/config space for @p child is unsupported.
|
||||
* @retval EFAULT If reading @p width at @p offset exceeds the bounds of
|
||||
* the mapped agent/config space for @p child.
|
||||
*/
|
||||
METHOD void write_config {
|
||||
METHOD int write_config {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
bus_size_t offset;
|
||||
uint32_t val;
|
||||
const void *value;
|
||||
u_int width;
|
||||
} DEFAULT bhnd_bus_null_write_config;
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
|
||||
#include "bhnd_pmu_if.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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -44,7 +44,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhnd_core.h>
|
||||
|
||||
#include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
|
||||
|
||||
@@ -103,7 +102,7 @@ bhnd_usb_attach(device_t dev)
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
BHND_BUS_RESET_CORE(device_get_parent(dev), dev, 0);
|
||||
bhnd_reset_hw(dev, 0);
|
||||
|
||||
/*
|
||||
* Allocate the resources which the parent bus has already
|
||||
|
||||
+323
-68
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/bhnd/cores/chipc/chipcreg.h>
|
||||
#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
|
||||
|
||||
#include "sibareg.h"
|
||||
#include "sibavar.h"
|
||||
@@ -134,6 +135,9 @@ siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
|
||||
case BHND_IVAR_CORE_UNIT:
|
||||
*result = cfg->unit;
|
||||
return (0);
|
||||
case BHND_IVAR_PMU_INFO:
|
||||
*result = (uintptr_t) dinfo->pmu_info;
|
||||
return (0);
|
||||
default:
|
||||
return (ENOENT);
|
||||
}
|
||||
@@ -142,6 +146,10 @@ siba_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
|
||||
static int
|
||||
siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
switch (index) {
|
||||
case BHND_IVAR_VENDOR:
|
||||
case BHND_IVAR_DEVICE:
|
||||
@@ -152,6 +160,9 @@ siba_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
|
||||
case BHND_IVAR_CORE_INDEX:
|
||||
case BHND_IVAR_CORE_UNIT:
|
||||
return (EINVAL);
|
||||
case BHND_IVAR_PMU_INFO:
|
||||
dinfo->pmu_info = (struct bhnd_core_pmu_info *) value;
|
||||
return (0);
|
||||
default:
|
||||
return (ENOENT);
|
||||
}
|
||||
@@ -165,78 +176,252 @@ siba_get_resource_list(device_t dev, device_t child)
|
||||
}
|
||||
|
||||
static int
|
||||
siba_reset_core(device_t dev, device_t child, uint16_t flags)
|
||||
siba_read_iost(device_t dev, device_t child, uint16_t *iost)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
uint32_t tmhigh;
|
||||
int error;
|
||||
|
||||
if (device_get_parent(child) != dev)
|
||||
BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
|
||||
error = bhnd_read_config(child, SIBA_CFG0_TMSTATEHIGH, &tmhigh, 4);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
/* Can't reset the core without access to the CFG0 registers */
|
||||
if (dinfo->cfg[0] == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
// TODO - perform reset
|
||||
|
||||
return (ENXIO);
|
||||
*iost = (SIBA_REG_GET(tmhigh, TMH_SISF));
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_suspend_core(device_t dev, device_t child)
|
||||
siba_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
uint32_t ts_low;
|
||||
int error;
|
||||
|
||||
if ((error = bhnd_read_config(child, SIBA_CFG0_TMSTATELOW, &ts_low, 4)))
|
||||
return (error);
|
||||
|
||||
*ioctl = (SIBA_REG_GET(ts_low, TML_SICF));
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
struct bhnd_resource *r;
|
||||
uint32_t ts_low, ts_mask;
|
||||
|
||||
if (device_get_parent(child) != dev)
|
||||
BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
|
||||
return (EINVAL);
|
||||
|
||||
/* Fetch CFG0 mapping */
|
||||
dinfo = device_get_ivars(child);
|
||||
if ((r = dinfo->cfg[0]) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
/* Mask and set TMSTATELOW core flag bits */
|
||||
ts_mask = (mask << SIBA_TML_SICF_SHIFT) & SIBA_TML_SICF_MASK;
|
||||
ts_low = (value << SIBA_TML_SICF_SHIFT) & ts_mask;
|
||||
|
||||
return (siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
ts_low, ts_mask));
|
||||
}
|
||||
|
||||
static bool
|
||||
siba_is_hw_suspended(device_t dev, device_t child)
|
||||
{
|
||||
uint32_t ts_low;
|
||||
uint16_t ioctl;
|
||||
int error;
|
||||
|
||||
/* Fetch target state */
|
||||
error = bhnd_read_config(child, SIBA_CFG0_TMSTATELOW, &ts_low, 4);
|
||||
if (error) {
|
||||
device_printf(child, "error reading HW reset state: %d\n",
|
||||
error);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/* Is core held in RESET? */
|
||||
if (ts_low & SIBA_TML_RESET)
|
||||
return (true);
|
||||
|
||||
/* Is core clocked? */
|
||||
ioctl = SIBA_REG_GET(ts_low, TML_SICF);
|
||||
if (!(ioctl & BHND_IOCTL_CLK_EN))
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_reset_hw(device_t dev, device_t child, uint16_t ioctl)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
struct bhnd_resource *r;
|
||||
uint32_t ts_low, imstate;
|
||||
int error;
|
||||
|
||||
if (device_get_parent(child) != dev)
|
||||
return (EINVAL);
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
|
||||
/* Can't suspend the core without access to the CFG0 registers */
|
||||
if (dinfo->cfg[0] == NULL)
|
||||
if ((r = dinfo->cfg[0]) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
// TODO - perform suspend
|
||||
/* We require exclusive control over BHND_IOCTL_CLK_EN and
|
||||
* BHND_IOCTL_CLK_FORCE. */
|
||||
if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE))
|
||||
return (EINVAL);
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
/* Place core into known RESET state */
|
||||
if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
|
||||
return (error);
|
||||
|
||||
static uint32_t
|
||||
siba_read_config(device_t dev, device_t child, bus_size_t offset, u_int width)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
rman_res_t r_size;
|
||||
/* Leaving the core in reset, set the caller's IOCTL flags and
|
||||
* enable the core's clocks. */
|
||||
ts_low = (ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) <<
|
||||
SIBA_TML_SICF_SHIFT;
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
ts_low, SIBA_TML_SICF_MASK);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Must be directly attached */
|
||||
if (device_get_parent(child) != dev)
|
||||
return (UINT32_MAX);
|
||||
|
||||
/* CFG0 registers must be available */
|
||||
dinfo = device_get_ivars(child);
|
||||
if (dinfo->cfg[0] == NULL)
|
||||
return (UINT32_MAX);
|
||||
|
||||
/* Offset must fall within CFG0 */
|
||||
r_size = rman_get_size(dinfo->cfg[0]->res);
|
||||
if (r_size < offset || r_size - offset < width)
|
||||
return (UINT32_MAX);
|
||||
|
||||
switch (width) {
|
||||
case 1:
|
||||
return (bhnd_bus_read_1(dinfo->cfg[0], offset));
|
||||
case 2:
|
||||
return (bhnd_bus_read_2(dinfo->cfg[0], offset));
|
||||
case 4:
|
||||
return (bhnd_bus_read_4(dinfo->cfg[0], offset));
|
||||
/* Clear any target errors */
|
||||
if (bhnd_bus_read_4(r, SIBA_CFG0_TMSTATEHIGH) & SIBA_TMH_SERR) {
|
||||
error = siba_write_target_state(child, dinfo,
|
||||
SIBA_CFG0_TMSTATEHIGH, 0, SIBA_TMH_SERR);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Unsuported */
|
||||
return (UINT32_MAX);
|
||||
|
||||
/* Clear any initiator errors */
|
||||
imstate = bhnd_bus_read_4(r, SIBA_CFG0_IMSTATE);
|
||||
if (imstate & (SIBA_IM_IBE|SIBA_IM_TO)) {
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
||||
0, SIBA_IM_IBE|SIBA_IM_TO);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Release from RESET while leaving clocks forced, ensuring the
|
||||
* signal propagates throughout the core */
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
0x0, SIBA_TML_RESET);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* The core should now be active; we can clear the BHND_IOCTL_CLK_FORCE
|
||||
* bit and allow the core to manage clock gating. */
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
0x0, (BHND_IOCTL_CLK_FORCE << SIBA_TML_SICF_SHIFT));
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
|
||||
static int
|
||||
siba_suspend_hw(device_t dev, device_t child)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
struct bhnd_core_pmu_info *pm;
|
||||
struct bhnd_resource *r;
|
||||
uint32_t idl, ts_low;
|
||||
uint16_t ioctl;
|
||||
int error;
|
||||
|
||||
if (device_get_parent(child) != dev)
|
||||
return (EINVAL);
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
pm = dinfo->pmu_info;
|
||||
|
||||
/* Can't suspend the core without access to the CFG0 registers */
|
||||
if ((r = dinfo->cfg[0]) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
/* Already in RESET? */
|
||||
ts_low = bhnd_bus_read_4(r, SIBA_CFG0_TMSTATELOW);
|
||||
if (ts_low & SIBA_TML_RESET) {
|
||||
/* Clear IOCTL flags, ensuring the clock is disabled */
|
||||
return (siba_write_target_state(child, dinfo,
|
||||
SIBA_CFG0_TMSTATELOW, 0x0, SIBA_TML_SICF_MASK));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* If clocks are already disabled, we can put the core directly
|
||||
* into RESET */
|
||||
ioctl = SIBA_REG_GET(ts_low, TML_SICF);
|
||||
if (!(ioctl & BHND_IOCTL_CLK_EN)) {
|
||||
/* Set RESET and clear IOCTL flags */
|
||||
return (siba_write_target_state(child, dinfo,
|
||||
SIBA_CFG0_TMSTATELOW,
|
||||
SIBA_TML_RESET,
|
||||
SIBA_TML_RESET | SIBA_TML_SICF_MASK));
|
||||
}
|
||||
|
||||
/* Reject any further target backplane transactions */
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
SIBA_TML_REJ, SIBA_TML_REJ);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* If this is an initiator core, we need to reject initiator
|
||||
* transactions too. */
|
||||
idl = bhnd_bus_read_4(r, SIBA_CFG0_IDLOW);
|
||||
if (idl & SIBA_IDL_INIT) {
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
||||
SIBA_IM_RJ, SIBA_IM_RJ);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Put the core into RESET|REJECT, forcing clocks to ensure the RESET
|
||||
* signal propagates throughout the core, leaving REJECT asserted. */
|
||||
ts_low = SIBA_TML_RESET;
|
||||
ts_low |= (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE) <<
|
||||
SIBA_TML_SICF_SHIFT;
|
||||
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
ts_low, ts_low);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Give RESET ample time */
|
||||
DELAY(10);
|
||||
|
||||
/* Leaving core in reset, disable all clocks, clear REJ flags and
|
||||
* IOCTL state */
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_TMSTATELOW,
|
||||
SIBA_TML_RESET,
|
||||
SIBA_TML_RESET | SIBA_TML_REJ | SIBA_TML_SICF_MASK);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Clear previously asserted initiator reject */
|
||||
if (idl & SIBA_IDL_INIT) {
|
||||
error = siba_write_target_state(child, dinfo, SIBA_CFG0_IMSTATE,
|
||||
0, SIBA_IM_RJ);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Core is now in RESET, with clocks disabled and REJ not asserted.
|
||||
*
|
||||
* We lastly need to inform the PMU, releasing any outstanding per-core
|
||||
* PMU requests */
|
||||
if (pm != NULL) {
|
||||
if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
|
||||
u_int width)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
@@ -244,25 +429,67 @@ siba_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
|
||||
|
||||
/* Must be directly attached */
|
||||
if (device_get_parent(child) != dev)
|
||||
return;
|
||||
return (EINVAL);
|
||||
|
||||
/* CFG0 registers must be available */
|
||||
dinfo = device_get_ivars(child);
|
||||
if (dinfo->cfg[0] == NULL)
|
||||
return;
|
||||
return (ENODEV);
|
||||
|
||||
/* Offset must fall within CFG0 */
|
||||
r_size = rman_get_size(dinfo->cfg[0]->res);
|
||||
if (r_size < offset || r_size - offset < width)
|
||||
return;
|
||||
return (EFAULT);
|
||||
|
||||
switch (width) {
|
||||
case 1:
|
||||
bhnd_bus_write_1(dinfo->cfg[0], offset, val);
|
||||
*((uint8_t *)value) = bhnd_bus_read_1(dinfo->cfg[0], offset);
|
||||
return (0);
|
||||
case 2:
|
||||
bhnd_bus_write_2(dinfo->cfg[0], offset, val);
|
||||
*((uint16_t *)value) = bhnd_bus_read_2(dinfo->cfg[0], offset);
|
||||
return (0);
|
||||
case 4:
|
||||
bhnd_bus_write_4(dinfo->cfg[0], offset, val);
|
||||
*((uint32_t *)value) = bhnd_bus_read_4(dinfo->cfg[0], offset);
|
||||
return (0);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
siba_write_config(device_t dev, device_t child, bus_size_t offset,
|
||||
const void *value, u_int width)
|
||||
{
|
||||
struct siba_devinfo *dinfo;
|
||||
struct bhnd_resource *r;
|
||||
rman_res_t r_size;
|
||||
|
||||
/* Must be directly attached */
|
||||
if (device_get_parent(child) != dev)
|
||||
return (EINVAL);
|
||||
|
||||
/* CFG0 registers must be available */
|
||||
dinfo = device_get_ivars(child);
|
||||
if ((r = dinfo->cfg[0]) == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
/* Offset must fall within CFG0 */
|
||||
r_size = rman_get_size(r->res);
|
||||
if (r_size < offset || r_size - offset < width)
|
||||
return (EFAULT);
|
||||
|
||||
switch (width) {
|
||||
case 1:
|
||||
bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
|
||||
return (0);
|
||||
case 2:
|
||||
bhnd_bus_write_2(r, offset, *(const uint8_t *)value);
|
||||
return (0);
|
||||
case 4:
|
||||
bhnd_bus_write_4(r, offset, *(const uint8_t *)value);
|
||||
return (0);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,18 +772,42 @@ siba_map_cfg_resources(device_t dev, struct siba_devinfo *dinfo)
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static struct bhnd_devinfo *
|
||||
siba_alloc_bhnd_dinfo(device_t dev)
|
||||
static device_t
|
||||
siba_add_child(device_t dev, u_int order, const char *name, int unit)
|
||||
{
|
||||
struct siba_devinfo *dinfo = siba_alloc_dinfo(dev);
|
||||
return ((struct bhnd_devinfo *)dinfo);
|
||||
struct siba_devinfo *dinfo;
|
||||
device_t child;
|
||||
|
||||
child = device_add_child_ordered(dev, order, name, unit);
|
||||
if (child == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((dinfo = siba_alloc_dinfo(dev)) == NULL) {
|
||||
device_delete_child(dev, child);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
device_set_ivars(child, dinfo);
|
||||
|
||||
return (child);
|
||||
}
|
||||
|
||||
static void
|
||||
siba_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
|
||||
siba_child_deleted(device_t dev, device_t child)
|
||||
{
|
||||
siba_free_dinfo(dev, (struct siba_devinfo *)dinfo);
|
||||
struct bhnd_softc *sc;
|
||||
struct siba_devinfo *dinfo;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Call required bhnd(4) implementation */
|
||||
bhnd_generic_child_deleted(dev, child);
|
||||
|
||||
/* Free siba device info */
|
||||
if ((dinfo = device_get_ivars(child)) != NULL)
|
||||
siba_free_dinfo(dev, dinfo);
|
||||
|
||||
device_set_ivars(child, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -687,16 +938,20 @@ static device_method_t siba_methods[] = {
|
||||
DEVMETHOD(device_suspend, siba_suspend),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_add_child, siba_add_child),
|
||||
DEVMETHOD(bus_child_deleted, siba_child_deleted),
|
||||
DEVMETHOD(bus_read_ivar, siba_read_ivar),
|
||||
DEVMETHOD(bus_write_ivar, siba_write_ivar),
|
||||
DEVMETHOD(bus_get_resource_list, siba_get_resource_list),
|
||||
|
||||
/* BHND interface */
|
||||
DEVMETHOD(bhnd_bus_get_erom_class, siba_get_erom_class),
|
||||
DEVMETHOD(bhnd_bus_alloc_devinfo, siba_alloc_bhnd_dinfo),
|
||||
DEVMETHOD(bhnd_bus_free_devinfo, siba_free_bhnd_dinfo),
|
||||
DEVMETHOD(bhnd_bus_reset_core, siba_reset_core),
|
||||
DEVMETHOD(bhnd_bus_suspend_core, siba_suspend_core),
|
||||
DEVMETHOD(bhnd_bus_read_ioctl, siba_read_ioctl),
|
||||
DEVMETHOD(bhnd_bus_write_ioctl, siba_write_ioctl),
|
||||
DEVMETHOD(bhnd_bus_read_iost, siba_read_iost),
|
||||
DEVMETHOD(bhnd_bus_is_hw_suspended, siba_is_hw_suspended),
|
||||
DEVMETHOD(bhnd_bus_reset_hw, siba_reset_hw),
|
||||
DEVMETHOD(bhnd_bus_suspend_hw, siba_suspend_hw),
|
||||
DEVMETHOD(bhnd_bus_read_config, siba_read_config),
|
||||
DEVMETHOD(bhnd_bus_write_config, siba_write_config),
|
||||
DEVMETHOD(bhnd_bus_get_port_count, siba_get_port_count),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@ KMOD= bcma
|
||||
SRCS= bcma.c bcma_subr.c bcma_erom.c
|
||||
|
||||
SRCS+= device_if.h bus_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h bhnd_pmu_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
||||
@@ -7,6 +7,6 @@ SRCS= siba.c siba_subr.c \
|
||||
siba_erom.c
|
||||
|
||||
SRCS+= device_if.h bus_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h bhnd_pmu_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
||||
Reference in New Issue
Block a user