siba(4): Adopt bcma-compatible mapping of bhnd(4) port/region identifiers.

Maps Sonics/OCP per-core address spaces to bcma(4)-compatible port/region
identifiers.

This permits the use of common address map identifiers in bhnd device
drivers, independent of the underlying interconnect type.

Approved by:	re (gjb), adrian (mentor)
Differential Revision:	https://reviews.freebsd.org/D6850
This commit is contained in:
Landon J. Fuller 2016-06-16 20:04:33 +00:00
parent 7d1fb1aadc
commit 06018a8e7a
4 changed files with 190 additions and 209 deletions

View File

@ -70,7 +70,6 @@ siba_attach(device_t dev)
for (int i = 0; i < ndevs; i++) {
struct siba_addrspace *addrspace;
struct siba_port *port;
dinfo = device_get_ivars(devs[i]);
@ -79,14 +78,10 @@ siba_attach(device_t dev)
"not be suspended before siba_attach()"));
/* Fetch the core register address space */
port = siba_dinfo_get_port(dinfo, BHND_PORT_DEVICE, 0);
if (port == NULL) {
error = ENXIO;
goto cleanup;
}
addrspace = siba_find_port_addrspace(port, SIBA_ADDRSPACE_CORE);
addrspace = siba_find_addrspace(dinfo, BHND_PORT_DEVICE, 0, 0);
if (addrspace == NULL) {
device_printf(dev,
"missing device registers for core %d\n", i);
error = ENXIO;
goto cleanup;
}
@ -94,7 +89,7 @@ siba_attach(device_t dev)
/*
* Map the per-core configuration blocks
*/
KASSERT(dinfo->core_id.num_cfg_blocks <= SIBA_CFG_NUM_MAX,
KASSERT(dinfo->core_id.num_cfg_blocks <= SIBA_MAX_CFG,
("config block count %u out of range",
dinfo->core_id.num_cfg_blocks));
@ -285,32 +280,25 @@ siba_get_port_count(device_t dev, device_t child, bhnd_port_type type)
type));
dinfo = device_get_ivars(child);
/* We advertise exactly one port of any type */
if (siba_dinfo_get_port(dinfo, type, 0) != NULL)
return (1);
return (0);
return (siba_addrspace_port_count(dinfo));
}
static u_int
siba_get_region_count(device_t dev, device_t child, bhnd_port_type type,
u_int port_num)
u_int port)
{
struct siba_devinfo *dinfo;
struct siba_port *port;
/* delegate non-bus-attached devices to our parent */
if (device_get_parent(child) != dev)
return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
type, port_num));
type, port));
dinfo = device_get_ivars(child);
port = siba_dinfo_get_port(dinfo, type, port_num);
if (port == NULL)
if (!siba_is_port_valid(dinfo, type, port))
return (0);
return (port->sp_num_addrs);
return (siba_addrspace_region_count(dinfo, port));
}
static int
@ -318,7 +306,6 @@ siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
u_int port_num, u_int region_num)
{
struct siba_devinfo *dinfo;
struct siba_port *port;
struct siba_addrspace *addrspace;
/* delegate non-bus-attached devices to our parent */
@ -327,17 +314,11 @@ siba_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
port_type, port_num, region_num));
dinfo = device_get_ivars(child);
port = siba_dinfo_get_port(dinfo, port_type, port_num);
if (port == NULL)
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num);
if (addrspace == NULL)
return (-1);
STAILQ_FOREACH(addrspace, &port->sp_addrs, sa_link) {
if (addrspace->sa_region_num == region_num)
return (addrspace->sa_rid);
}
/* not found */
return (-1);
return (addrspace->sa_rid);
}
static int
@ -345,8 +326,6 @@ siba_decode_port_rid(device_t dev, device_t child, int type, int rid,
bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
{
struct siba_devinfo *dinfo;
struct siba_port *port;
struct siba_addrspace *addrspace;
/* delegate non-bus-attached devices to our parent */
if (device_get_parent(child) != dev)
@ -359,29 +338,17 @@ siba_decode_port_rid(device_t dev, device_t child, int type, int rid,
if (type != SYS_RES_MEMORY)
return (EINVAL);
/* Starting with the most likely device list, search all three port
* lists */
bhnd_port_type types[] = {
BHND_PORT_DEVICE,
BHND_PORT_AGENT,
BHND_PORT_BRIDGE
};
for (int i = 0; i < nitems(types); i++) {
port = siba_dinfo_get_port(dinfo, types[i], 0);
if (port == NULL)
for (int i = 0; i < dinfo->core_id.num_addrspace; i++) {
if (dinfo->addrspace[i].sa_rid != rid)
continue;
STAILQ_FOREACH(addrspace, &port->sp_addrs, sa_link) {
if (addrspace->sa_rid != rid)
continue;
*port_type = port->sp_type;
*port_num = port->sp_num;
*region_num = addrspace->sa_region_num;
}
*port_type = BHND_PORT_DEVICE;
*port_num = siba_addrspace_port(i);
*region_num = siba_addrspace_region(i);
return (0);
}
/* Not found */
return (ENOENT);
}
@ -390,7 +357,6 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
{
struct siba_devinfo *dinfo;
struct siba_port *port;
struct siba_addrspace *addrspace;
/* delegate non-bus-attached devices to our parent */
@ -400,20 +366,13 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
}
dinfo = device_get_ivars(child);
port = siba_dinfo_get_port(dinfo, port_type, port_num);
if (port == NULL)
addrspace = siba_find_addrspace(dinfo, port_type, port_num, region_num);
if (addrspace == NULL)
return (ENOENT);
STAILQ_FOREACH(addrspace, &port->sp_addrs, sa_link) {
if (addrspace->sa_region_num != region_num)
continue;
*addr = addrspace->sa_base;
*size = addrspace->sa_size - addrspace->sa_bus_reserved;
return (0);
}
return (ENOENT);
*addr = addrspace->sa_base;
*size = addrspace->sa_size - addrspace->sa_bus_reserved;
return (0);
}
@ -432,26 +391,21 @@ siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
struct siba_core_id *cid;
uint32_t addr;
uint32_t size;
u_int region_num;
int error;
cid = &di->core_id;
/* Region numbers must be assigned in order, but our siba address
* space IDs may be sparsely allocated; thus, we track
* the region index separately. */
region_num = 0;
/* Register the device address space entries */
for (uint8_t sid = 0; sid < di->core_id.num_addrspace; sid++) {
for (uint8_t i = 0; i < di->core_id.num_addrspace; i++) {
uint32_t adm;
u_int adm_offset;
uint32_t bus_reserved;
/* Determine the register offset */
adm_offset = siba_admatch_offset(sid);
adm_offset = siba_admatch_offset(i);
if (adm_offset == 0) {
device_printf(dev, "addrspace %hhu is unsupported", sid);
device_printf(dev, "addrspace %hhu is unsupported", i);
return (ENODEV);
}
@ -469,17 +423,14 @@ siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
* reserve the Sonics configuration register blocks for the
* use of our bus. */
bus_reserved = 0;
if (sid == SIBA_ADDRSPACE_CORE)
if (i == SIBA_CORE_ADDRSPACE)
bus_reserved = cid->num_cfg_blocks * SIBA_CFG_SIZE;
/* Append the region info */
error = siba_append_dinfo_region(di, BHND_PORT_DEVICE, 0,
region_num, sid, addr, size, bus_reserved);
error = siba_append_dinfo_region(di, i, addr, size,
bus_reserved);
if (error)
return (error);
region_num++;
}
return (0);

View File

@ -105,35 +105,6 @@ siba_parse_core_id(uint32_t idhigh, uint32_t idlow, u_int core_idx, int unit)
};
}
/**
* Initialize new port descriptor.
*
* @param port_num Port number.
* @param port_type Port type.
*/
static void
siba_init_port(struct siba_port *port, bhnd_port_type port_type, u_int port_num)
{
port->sp_num = port_num;
port->sp_type = port_type;
port->sp_num_addrs = 0;
STAILQ_INIT(&port->sp_addrs);
}
/**
* Deallocate all resources associated with the given port descriptor.
*
* @param port Port descriptor to be deallocated.
*/
static void
siba_release_port(struct siba_port *port) {
struct siba_addrspace *as, *as_next;
STAILQ_FOREACH_SAFE(as, &port->sp_addrs, sa_link, as_next) {
free(as, M_BHND);
}
}
/**
* Allocate and initialize new device info structure, copying the
* provided core id.
@ -157,76 +128,153 @@ siba_alloc_dinfo(device_t bus, const struct siba_core_id *core_id)
dinfo->cfg_rid[i] = -1;
}
siba_init_port(&dinfo->device_port, BHND_PORT_DEVICE, 0);
resource_list_init(&dinfo->resources);
return dinfo;
}
/**
* Return the @p dinfo port instance for @p type, or NULL.
* Map an addrspace index to its corresponding bhnd(4) port number.
*
* @param dinfo The siba device info.
* @param type The requested port type.
*
* @retval siba_port If @p port_type and @p port_num are defined on @p dinfo.
* @retval NULL If the requested port is not defined on @p dinfo.
* @param addrspace Address space index.
*/
struct siba_port *
siba_dinfo_get_port(struct siba_devinfo *dinfo, bhnd_port_type port_type,
u_int port_num)
u_int
siba_addrspace_port(u_int addrspace)
{
/* We only define a single port for any given type. */
if (port_num != 0)
return (NULL);
switch (port_type) {
case BHND_PORT_DEVICE:
return (&dinfo->device_port);
case BHND_PORT_BRIDGE:
return (NULL);
case BHND_PORT_AGENT:
return (NULL);
default:
printf("%s: unknown port_type (%d)\n",
__func__,
port_type);
return (NULL);
}
/* The first addrspace is always mapped to device0; the remainder
* are mapped to device1 */
if (addrspace == 0)
return (0);
else
return (1);
}
/**
* Map an addrspace index to its corresponding bhnd(4) region number.
*
* @param addrspace Address space index.
*/
u_int
siba_addrspace_region(u_int addrspace)
{
/* The first addrspace is always mapped to device0.0; the remainder
* are mapped to device1.0 + (n - 1) */
if (addrspace == 0)
return (0);
else
return (addrspace - 1);
}
/**
* Find an address space with @p sid on @p port.
* Return the number of bhnd(4) ports to advertise for the given
* @p dinfo.
*
* @param port The port to search for a matching address space.
* @param sid The siba-assigned address space ID to search for.
* @param dinfo The device info to query.
*/
u_int
siba_addrspace_port_count(struct siba_devinfo *dinfo)
{
/* 0, 1, or 2 ports */
return min(dinfo->core_id.num_addrspace, 2);
}
/**
* Return the number of bhnd(4) regions to advertise on @p port
* given the provided @p num_addrspace address space count.
*
* @param num_addrspace The number of core-mapped siba(4) Sonics/OCP address
* spaces.
*/
u_int
siba_addrspace_region_count(struct siba_devinfo *dinfo, u_int port)
{
u_int num_addrspace = dinfo->core_id.num_addrspace;
/* The first address space, if any, is mapped to device0.0 */
if (port == 0)
return (min(num_addrspace, 1));
/* All remaining address spaces are mapped to device0.(n - 1) */
if (port == 1 && num_addrspace >= 2)
return (num_addrspace - 1);
/* No region mapping */
return (0);
}
/**
* Return true if @p port is defined on @p dinfo, false otherwise.
*
* Refer to the siba_find_addrspace() function for information on siba's
* mapping of bhnd(4) port and region identifiers.
*
* @param dinfo The device info to verify the port against.
* @param type The bhnd(4) port type.
* @param port The bhnd(4) port number.
*/
bool
siba_is_port_valid(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port)
{
/* Only device ports are supported */
if (type != BHND_PORT_DEVICE)
return (false);
/* Verify the index against the port count */
if (siba_addrspace_port_count(dinfo) <= port)
return (false);
return (true);
}
/**
* Map an bhnd(4) type/port/region triplet to its associated address space
* entry, if any.
*
* For compatibility with bcma(4), we map address spaces to port/region
* identifiers as follows:
*
* [port] [addrspace]
* device0.0 0
* device1.0 1
* device1.1 2
* device1.2 3
*
* The only supported port type is BHND_PORT_DEVICE.
*
* @param dinfo The device info to search for a matching address space.
* @param type The bhnd(4) port type.
* @param port The bhnd(4) port number.
* @param region The bhnd(4) port region.
*/
struct siba_addrspace *
siba_find_port_addrspace(struct siba_port *port, uint8_t sid)
siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
u_int region)
{
struct siba_addrspace *addrspace;
u_int addridx;
STAILQ_FOREACH(addrspace, &port->sp_addrs, sa_link) {
if (addrspace->sa_sid == sid)
return (addrspace);
}
if (!siba_is_port_valid(dinfo, type, port))
return (NULL);
/* not found */
return (NULL);
if (port == 0)
addridx = region;
else if (port == 1)
addridx = region + 1;
else
return (NULL);
/* Out of range? */
if (addridx >= dinfo->core_id.num_addrspace)
return (NULL);
/* Found */
return (&dinfo->addrspace[addridx]);
}
/**
* Append a new address space entry to @p port_num of type @p port_type
* in @p dinfo.
*
* The range will also be registered in @p dinfo resource list.
* Append an address space entry to @p dinfo.
*
* @param dinfo The device info entry to update.
* @param port_type The port type.
* @param port_num The port number.
* @param region_num The region index number.
* @param sid The siba-assigned core-unique address space identifier.
* @param addridx The address space index.
* @param base The mapping's base address.
* @param size The mapping size.
* @param bus_reserved Number of bytes to reserve in @p size for bus use
@ -237,12 +285,10 @@ siba_find_port_addrspace(struct siba_port *port, uint8_t sid)
* @retval non-zero An error occurred appending the entry.
*/
int
siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type,
u_int port_num, u_int region_num, uint8_t sid, uint32_t base, uint32_t size,
uint32_t bus_reserved)
siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,
uint32_t base, uint32_t size, uint32_t bus_reserved)
{
struct siba_addrspace *sa;
struct siba_port *port;
rman_res_t r_size;
/* Verify that base + size will not overflow */
@ -257,20 +303,14 @@ siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type,
if (size == 0)
return (EINVAL);
/* Determine target port */
port = siba_dinfo_get_port(dinfo, port_type, port_num);
if (port == NULL)
/* Must not exceed addrspace array size */
if (addridx >= nitems(dinfo->addrspace))
return (EINVAL);
/* Allocate new addrspace entry */
sa = malloc(sizeof(*sa), M_BHND, M_NOWAIT|M_ZERO);
if (sa == NULL)
return (ENOMEM);
/* Initialize new addrspace entry */
sa = &dinfo->addrspace[addridx];
sa->sa_base = base;
sa->sa_size = size;
sa->sa_sid = sid;
sa->sa_region_num = region_num;
sa->sa_bus_reserved = bus_reserved;
/* Populate the resource list */
@ -278,10 +318,6 @@ siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type,
sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
base, base + (r_size - 1), r_size);
/* Append to target port */
STAILQ_INSERT_TAIL(&port->sp_addrs, sa, sa_link);
port->sp_num_addrs++;
return (0);
}
@ -294,8 +330,6 @@ siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type,
void
siba_free_dinfo(device_t dev, struct siba_devinfo *dinfo)
{
siba_release_port(&dinfo->device_port);
resource_list_free(&dinfo->resources);
/* Free all mapped configuration blocks */

View File

@ -51,9 +51,6 @@
#define SIBA_MAX_CORES \
(SIBA_ENUM_SIZE/SIBA_CORE_SIZE) /**< Maximum number of cores */
#define SIBA_ADDRSPACE_CORE 0 /**< address space identifier of the
core enumeration block. */
/**< Evaluates to the bus address of the @p idx core register block */
#define SIBA_CORE_ADDR(idx) \
(SIBA_ENUM_ADDR + ((idx) * SIBA_CORE_SIZE))
@ -175,7 +172,7 @@
#define SIBA_IMCH_BEM_MASK 0xc0 /* bus error mode */
#define SIBA_IMCH_BEM_SHIFT 6
/* sbadmatch0 */
/* sbadmatch0-4 */
#define SIBA_AM_TYPE_MASK 0x3 /* address type */
#define SIBA_AM_TYPE_SHIFT 0x0
#define SIBA_AM_AD64 0x4 /* reserved */

View File

@ -47,7 +47,6 @@
struct siba_addrspace;
struct siba_devinfo;
struct siba_port;
struct siba_core_id;
int siba_probe(device_t dev);
@ -69,47 +68,53 @@ struct siba_devinfo *siba_alloc_dinfo(device_t dev,
void siba_free_dinfo(device_t dev,
struct siba_devinfo *dinfo);
struct siba_port *siba_dinfo_get_port(struct siba_devinfo *dinfo,
bhnd_port_type port_type, u_int port_num);
u_int siba_addrspace_port_count(struct siba_devinfo *dinfo);
u_int siba_addrspace_region_count(struct siba_devinfo *dinfo,
u_int port);
struct siba_addrspace *siba_find_port_addrspace(struct siba_port *port,
uint8_t sid);
u_int siba_addrspace_port(u_int addrspace);
u_int siba_addrspace_region(u_int addrspace);
bool siba_is_port_valid(struct siba_devinfo *dinfo,
bhnd_port_type type, u_int port);
struct siba_addrspace *siba_find_addrspace(struct siba_devinfo *dinfo,
bhnd_port_type type, u_int port, u_int region);
int siba_append_dinfo_region(struct siba_devinfo *dinfo,
bhnd_port_type port_type, u_int port_num,
u_int region_num, uint8_t sid, uint32_t base,
uint32_t size, uint32_t bus_reserved);
uint8_t sid, uint32_t base, uint32_t size,
uint32_t bus_reserved);
u_int siba_admatch_offset(uint8_t addrspace);
int siba_parse_admatch(uint32_t am, uint32_t *addr,
uint32_t *size);
/* Sonics configuration register blocks */
#define SIBA_CFG_NUM_2_2 1 /**< sonics <= 2.2 maps SIBA_CFG0. */
#define SIBA_CFG_NUM_2_3 2 /**< sonics <= 2.3 maps SIBA_CFG0 and SIBA_CFG1 */
#define SIBA_CFG_NUM_MAX SIBA_CFG_NUM_2_3 /**< maximum number of supported config
#define SIBA_MAX_CFG SIBA_CFG_NUM_2_3 /**< maximum number of supported config
register blocks */
/* Sonics/OCP address space mappings */
#define SIBA_CORE_ADDRSPACE 0 /**< Address space mapping the primary
device registers */
#define SIBA_MAX_ADDRSPACE 4 /**< Maximum number of Sonics/OCP
* address space mappings for a
* single core. */
/* bhnd(4) (port,region) representation of siba address space mappings */
#define SIBA_MAX_PORT 2 /**< maximum number of advertised
* bhnd(4) ports */
/** siba(4) address space descriptor */
struct siba_addrspace {
uint32_t sa_base; /**< base address */
uint32_t sa_size; /**< size */
u_int sa_region_num; /**< bhnd region id */
uint8_t sa_sid; /**< siba-assigned address space ID */
int sa_rid; /**< bus resource id */
uint32_t sa_bus_reserved;/**< number of bytes at high end of
* address space reserved for the bus */
STAILQ_ENTRY(siba_addrspace) sa_link;
};
/** siba(4) port descriptor */
struct siba_port {
bhnd_port_type sp_type; /**< port type */
u_int sp_num; /**< port number */
u_int sp_num_addrs; /**< number of address space mappings */
STAILQ_HEAD(, siba_addrspace) sp_addrs; /**< address spaces mapped to this port */
};
/**
@ -133,16 +138,10 @@ struct siba_core_id {
struct siba_devinfo {
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 siba_port device_port; /**< device port holding ownership
* of all siba address space
* entries for this core. */
/** SIBA_CFG* register blocks */
struct bhnd_resource *cfg[SIBA_CFG_NUM_MAX];
/** SIBA_CFG* resource IDs */
int cfg_rid[SIBA_CFG_NUM_MAX];
struct bhnd_resource *cfg[SIBA_MAX_CFG]; /**< SIBA_CFG_* registers */
int cfg_rid[SIBA_MAX_CFG]; /**< SIBA_CFG_* resource IDs */
};