siba(4): Ignore disabled per-core address match entries.

Previously, the address regions described by disabled admatch entries would
be treated as being mapped to the given core; while incorrect, this was
essentially harmless given that the entries describe unused address space
on the few affected devices.

We now perform parsing of per-core admatch registers and interrupt flags in
siba_erom, correctly skip any disabled admatch entries, and use the
siba_erom API in siba_add_children() to perform enumeration of attached
cores.
This commit is contained in:
Landon J. Fuller 2018-02-12 19:36:26 +00:00
parent 21b8a7a6ca
commit 7c7c726bca
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=329180
5 changed files with 387 additions and 279 deletions

View File

@ -47,9 +47,14 @@ __FBSDID("$FreeBSD$");
#include <dev/bhnd/cores/chipc/chipc.h>
#include <dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctl.h>
#include "siba_eromvar.h"
#include "sibareg.h"
#include "sibavar.h"
/* RID used when allocating EROM resources */
#define SIBA_EROM_RID 0
static bhnd_erom_class_t *
siba_get_erom_class(driver_t *driver)
{
@ -1057,7 +1062,7 @@ siba_decode_port_rid(device_t dev, device_t child, int type, int rid,
return (EINVAL);
/* Look for a matching addrspace entry */
for (u_int i = 0; i < dinfo->core_id.num_addrspace; i++) {
for (u_int i = 0; i < dinfo->core_id.num_admatch; i++) {
if (dinfo->addrspace[i].sa_rid != rid)
continue;
@ -1131,7 +1136,7 @@ siba_get_intr_count(device_t dev, device_t child)
return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
dinfo = device_get_ivars(child);
if (!dinfo->intr_en) {
if (!dinfo->core_id.intr_en) {
/* No interrupts */
return (0);
} else {
@ -1161,117 +1166,10 @@ siba_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
dinfo = device_get_ivars(child);
KASSERT(dinfo->intr_en, ("core does not have an interrupt assigned"));
*ivec = dinfo->intr.flag;
return (0);
}
/**
* Register all address space mappings for @p di.
*
* @param dev The siba bus device.
* @param di The device info instance on which to register all address
* space entries.
* @param r A resource mapping the enumeration table block for @p di.
*/
static int
siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
struct bhnd_resource *r)
{
struct siba_core_id *cid;
uint32_t addr;
uint32_t size;
int error;
cid = &di->core_id;
/* Register the device address space entries */
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(i);
if (adm_offset == 0) {
device_printf(dev, "addrspace %hhu is unsupported", i);
return (ENODEV);
}
/* Fetch the address match register value */
adm = bhnd_bus_read_4(r, adm_offset);
/* Parse the value */
if ((error = siba_parse_admatch(adm, &addr, &size))) {
device_printf(dev, "failed to decode address "
" match register value 0x%x\n", adm);
return (error);
}
/* If this is the device's core/enumeration addrespace,
* reserve the Sonics configuration register blocks for the
* use of our bus. */
bus_reserved = 0;
if (i == SIBA_CORE_ADDRSPACE)
bus_reserved = cid->num_cfg_blocks * SIBA_CFG_SIZE;
/* Append the region info */
error = siba_append_dinfo_region(di, i, addr, size,
bus_reserved);
if (error)
return (error);
}
return (0);
}
/**
* Register all interrupt descriptors for @p dinfo. Must be called after
* configuration blocks have been mapped.
*
* @param dev The siba bus device.
* @param child The siba child device.
* @param dinfo The device info instance on which to register all interrupt
* descriptor entries.
* @param r A resource mapping the enumeration table block for @p di.
*/
static int
siba_register_interrupts(device_t dev, device_t child,
struct siba_devinfo *dinfo, struct bhnd_resource *r)
{
uint32_t tpsflag;
int error;
/* Is backplane interrupt distribution enabled for this core? */
tpsflag = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_TPSFLAG));
if ((tpsflag & SIBA_TPS_F0EN0) == 0) {
dinfo->intr_en = false;
return (0);
}
/* Have one interrupt */
dinfo->intr_en = true;
dinfo->intr.flag = SIBA_REG_GET(tpsflag, TPS_NUM0);
dinfo->intr.mapped = false;
dinfo->intr.irq = 0;
dinfo->intr.rid = -1;
/* Map the interrupt */
error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,
&dinfo->intr.irq);
if (error) {
device_printf(dev, "failed mapping interrupt line for core %u: "
"%d\n", dinfo->core_id.core_info.core_idx, error);
return (error);
}
dinfo->intr.mapped = true;
/* Update the resource list */
dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,
dinfo->intr.irq, dinfo->intr.irq, 1);
KASSERT(dinfo->core_id.intr_en,
("core does not have an interrupt assigned"));
*ivec = dinfo->core_id.intr_flag;
return (0);
}
@ -1386,21 +1284,27 @@ siba_child_deleted(device_t dev, device_t child)
int
siba_add_children(device_t dev)
{
const struct bhnd_chipid *chipid;
bhnd_erom_t *erom;
struct siba_erom *siba_erom;
struct bhnd_erom_io *eio;
const struct bhnd_chipid *cid;
struct siba_core_id *cores;
struct bhnd_resource *r;
device_t *children;
int rid;
int error;
cores = NULL;
r = NULL;
cid = BHND_BUS_GET_CHIPID(dev, dev);
chipid = BHND_BUS_GET_CHIPID(dev, dev);
/* Allocate our EROM parser */
eio = bhnd_erom_iores_new(dev, SIBA_EROM_RID);
erom = bhnd_erom_alloc(&siba_erom_parser, cid, eio);
if (erom == NULL) {
bhnd_erom_io_fini(eio);
return (ENODEV);
}
/* Allocate our temporary core and device table */
cores = malloc(sizeof(*cores) * chipid->ncores, M_BHND, M_WAITOK);
children = malloc(sizeof(*children) * chipid->ncores, M_BHND,
cores = malloc(sizeof(*cores) * cid->ncores, M_BHND, M_WAITOK);
children = malloc(sizeof(*children) * cid->ncores, M_BHND,
M_WAITOK | M_ZERO);
/*
@ -1411,39 +1315,13 @@ siba_add_children(device_t dev)
* defer mapping of the per-core siba(4) config blocks until all cores
* have been enumerated and otherwise configured.
*/
for (u_int i = 0; i < chipid->ncores; i++) {
siba_erom = (struct siba_erom *)erom;
for (u_int i = 0; i < cid->ncores; i++) {
struct siba_devinfo *dinfo;
device_t child;
uint32_t idhigh, idlow;
rman_res_t r_count, r_end, r_start;
/* Map the core's register block */
rid = 0;
r_start = SIBA_CORE_ADDR(i);
r_count = SIBA_CORE_SIZE;
r_end = r_start + SIBA_CORE_SIZE - 1;
r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid, r_start,
r_end, r_count, RF_ACTIVE);
if (r == NULL) {
error = ENXIO;
if ((error = siba_erom_get_core_id(siba_erom, i, &cores[i])))
goto failed;
}
/* Read the core info */
idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
idlow = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDLOW));
cores[i] = siba_parse_core_id(idhigh, idlow, i, 0);
/* Determine and set unit number */
for (u_int j = 0; j < i; j++) {
struct bhnd_core_info *cur = &cores[i].core_info;
struct bhnd_core_info *prev = &cores[j].core_info;
if (prev->vendor == cur->vendor &&
prev->device == cur->device)
cur->unit++;
}
/* Add the child device */
child = BUS_ADD_CHILD(dev, 0, NULL, -1);
@ -1460,30 +1338,22 @@ siba_add_children(device_t dev)
goto failed;
}
if ((error = siba_init_dinfo(dev, dinfo, &cores[i])))
if ((error = siba_init_dinfo(dev, child, dinfo, &cores[i])))
goto failed;
/* Register the core's address space(s). */
if ((error = siba_register_addrspaces(dev, dinfo, r)))
goto failed;
/* Register the core's interrupts */
if ((error = siba_register_interrupts(dev, child, dinfo, r)))
goto failed;
/* Unmap the core's register block */
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
r = NULL;
/* If pins are floating or the hardware is otherwise
* unpopulated, the device shouldn't be used. */
if (bhnd_is_hw_disabled(child))
device_disable(child);
}
/* Free EROM (and any bridge register windows it might hold) */
bhnd_erom_free(erom);
erom = NULL;
/* Map all valid core's config register blocks and perform interrupt
* assignment */
for (u_int i = 0; i < chipid->ncores; i++) {
for (u_int i = 0; i < cid->ncores; i++) {
struct siba_devinfo *dinfo;
device_t child;
@ -1509,7 +1379,7 @@ siba_add_children(device_t dev)
return (0);
failed:
for (u_int i = 0; i < chipid->ncores; i++) {
for (u_int i = 0; i < cid->ncores; i++) {
if (children[i] == NULL)
continue;
@ -1518,9 +1388,8 @@ siba_add_children(device_t dev)
free(cores, M_BHND);
free(children, M_BHND);
if (r != NULL)
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
if (erom != NULL)
bhnd_erom_free(erom);
return (error);
}

View File

@ -49,6 +49,8 @@ __FBSDID("$FreeBSD$");
#include "sibareg.h"
#include "sibavar.h"
#include "siba_eromvar.h"
struct siba_erom;
struct siba_erom_io;
@ -59,8 +61,9 @@ static int siba_eio_init(struct siba_erom_io *io,
static uint32_t siba_eio_read_4(struct siba_erom_io *io,
u_int core_idx, bus_size_t offset);
static struct siba_core_id siba_eio_read_core_id(struct siba_erom_io *io,
u_int core_idx, int unit);
static int siba_eio_read_core_id(struct siba_erom_io *io,
u_int core_idx, int unit,
struct siba_core_id *sid);
static int siba_eio_read_chipid(struct siba_erom_io *io,
bus_addr_t enum_addr,
@ -118,7 +121,8 @@ siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio,
* BCM4710, it's a SDRAM core (0x803).
*/
sid = siba_eio_read_core_id(&io, 0, 0);
if ((error = siba_eio_read_core_id(&io, 0, 0, &sid)))
return (error);
if (sid.core_info.vendor != BHND_MFGID_BCM)
return (ENXIO);
@ -227,16 +231,151 @@ siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset)
* @param core_idx The core index.
* @param unit The caller-specified unit number to be included in the return
* value.
* @param[out] sid On success, the parsed siba core id.
*
* @retval 0 success
* @retval non-zero if reading or parsing the identification registers
* otherwise fails, a regular unix error code will be
* returned.
*/
static struct siba_core_id
siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit)
static int
siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit,
struct siba_core_id *sid)
{
uint32_t idhigh, idlow;
struct siba_admatch admatch[SIBA_MAX_ADDRSPACE];
uint32_t idhigh, idlow;
uint32_t tpsflag;
uint16_t ocp_vendor;
uint8_t sonics_rev;
uint8_t num_admatch;
uint8_t num_admatch_en;
uint8_t num_cfg;
bool intr_en;
u_int intr_flag;
int error;
idhigh = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
idlow = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW));
tpsflag = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_TPSFLAG));
return (siba_parse_core_id(idhigh, idlow, core_idx, unit));
ocp_vendor = SIBA_REG_GET(idhigh, IDH_VENDOR);
sonics_rev = SIBA_REG_GET(idlow, IDL_SBREV);
num_admatch = SIBA_REG_GET(idlow, IDL_NRADDR) + 1 /* + enum block */;
if (num_admatch > nitems(admatch)) {
printf("core%u: invalid admatch count %hhu\n", core_idx,
num_admatch);
return (EINVAL);
}
/* Determine backplane interrupt distribution configuration */
intr_en = ((tpsflag & SIBA_TPS_F0EN0) != 0);
intr_flag = SIBA_REG_GET(tpsflag, TPS_NUM0);
/* Determine the number of sonics config register blocks */
num_cfg = SIBA_CFG_NUM_2_2;
if (sonics_rev >= SIBA_IDL_SBREV_2_3)
num_cfg = SIBA_CFG_NUM_2_3;
/* Parse all admatch descriptors */
num_admatch_en = 0;
for (uint8_t i = 0; i < num_admatch; i++) {
uint32_t am_value;
u_int am_offset;
KASSERT(i < nitems(admatch), ("invalid admatch index"));
/* Determine the register offset */
am_offset = siba_admatch_offset(i);
if (am_offset == 0) {
printf("core%u: addrspace %hhu is unsupported",
core_idx, i);
return (ENODEV);
}
/* Read and parse the address match register */
am_value = siba_eio_read_4(io, core_idx, am_offset);
error = siba_parse_admatch(am_value, &admatch[num_admatch_en]);
if (error) {
printf("core%u: failed to decode admatch[%hhu] "
"register value 0x%x\n", core_idx, i, am_value);
return (error);
}
/* Skip disabled entries */
if (!admatch[num_admatch_en].am_enabled)
continue;
/* Reject unsupported negative matches. These are not used on
* any known devices */
if (admatch[num_admatch_en].am_negative) {
printf("core%u: unsupported negative admatch[%hhu] "
"value 0x%x\n", core_idx, i, am_value);
return (ENXIO);
}
num_admatch_en++;
}
/* Populate the result */
*sid = (struct siba_core_id) {
.core_info = {
.vendor = siba_get_bhnd_mfgid(ocp_vendor),
.device = SIBA_REG_GET(idhigh, IDH_DEVICE),
.hwrev = SIBA_IDH_CORE_REV(idhigh),
.core_idx = core_idx,
.unit = unit
},
.sonics_vendor = ocp_vendor,
.sonics_rev = sonics_rev,
.intr_en = intr_en,
.intr_flag = intr_flag,
.num_admatch = num_admatch_en,
.num_cfg_blocks = num_cfg
};
memcpy(sid->admatch, admatch, num_admatch_en * sizeof(admatch[0]));
return (0);
}
/**
* Read and parse the SSB identification registers for the given @p core_index,
* returning the siba(4) core identification in @p sid.
*
* @param sc A siba EROM instance.
* @param core_idx The index of the core to be identified.
* @param[out] result On success, the parsed siba core id.
*
* @retval 0 success
* @retval non-zero if reading or parsing the identification registers
* otherwise fails, a regular unix error code will be
* returned.
*/
int
siba_erom_get_core_id(struct siba_erom *sc, u_int core_idx,
struct siba_core_id *result)
{
struct siba_core_id sid;
int error;
/* Fetch the core info, assuming a unit number of 0 */
if ((error = siba_eio_read_core_id(&sc->io, core_idx, 0, &sid)))
return (error);
/* Scan preceding cores to determine the real unit number. */
for (u_int i = 0; i < core_idx; i++) {
struct siba_core_id prev;
if ((error = siba_eio_read_core_id(&sc->io, i, 0, &prev)))
return (error);
/* Bump the unit number? */
if (sid.core_info.vendor == prev.core_info.vendor &&
sid.core_info.device == prev.core_info.device)
sid.core_info.unit++;
}
*result = sid;
return (0);
}
/**
@ -252,9 +391,12 @@ siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr,
{
struct siba_core_id ccid;
uint32_t idreg;
int error;
/* Identify the chipcommon core */
ccid = siba_eio_read_core_id(io, 0, 0);
if ((error = siba_eio_read_core_id(io, 0, 0, &ccid)))
return (error);
if (ccid.core_info.vendor != BHND_MFGID_BCM ||
ccid.core_info.device != BHND_COREID_CC)
{
@ -281,6 +423,7 @@ siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
{
struct siba_erom *sc;
struct bhnd_core_match imatch;
int error;
sc = (struct siba_erom *)erom;
@ -294,7 +437,9 @@ siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
struct bhnd_core_info ci;
/* Read the core info */
sid = siba_eio_read_core_id(&sc->io, i, 0);
if ((error = siba_eio_read_core_id(&sc->io, i, 0, &sid)))
return (error);
ci = sid.core_info;
/* Check for initial match */
@ -303,7 +448,9 @@ siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
/* Re-scan preceding cores to determine the unit number. */
for (u_int j = 0; j < i; j++) {
sid = siba_eio_read_core_id(&sc->io, j, 0);
error = siba_eio_read_core_id(&sc->io, j, 0, &sid);
if (error)
return (error);
/* Bump the unit number? */
if (sid.core_info.vendor == ci.vendor &&
@ -332,7 +479,8 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
struct siba_erom *sc;
struct bhnd_core_info core;
struct siba_core_id sid;
uint32_t am, am_addr, am_size;
struct siba_admatch admatch;
uint32_t am;
u_int am_offset;
u_int addrspace, cfg;
@ -345,7 +493,9 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
return (error);
/* Fetch full siba core ident */
sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit);
error = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit, &sid);
if (error)
return (error);
/* Is port valid? */
if (!siba_is_port_valid(&sid, type, port))
@ -419,7 +569,7 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
/* Read and parse the address match register */
am = siba_eio_read_4(&sc->io, core.core_idx, am_offset);
if ((error = siba_parse_admatch(am, &am_addr, &am_size))) {
if ((error = siba_parse_admatch(am, &admatch))) {
printf("failed to decode address match register value 0x%x\n",
am);
return (error);
@ -428,8 +578,8 @@ siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc
if (info != NULL)
*info = core;
*addr = am_addr;
*size = am_size;
*addr = admatch.am_base;
*size = admatch.am_size;
return (0);
}
@ -441,6 +591,7 @@ siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
{
struct siba_erom *sc;
struct bhnd_core_info *out;
int error;
sc = (struct siba_erom *)erom;
@ -457,7 +608,9 @@ siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
struct siba_core_id sid;
/* Read the core info */
sid = siba_eio_read_core_id(&sc->io, i, 0);
if ((error = siba_eio_read_core_id(&sc->io, i, 0, &sid)))
return (error);
out[i] = sid.core_info;
/* Determine unit number */
@ -508,8 +661,9 @@ siba_erom_dump(bhnd_erom_t *erom)
printf("\tnraddr\t0x%04x\n", nraddr);
for (size_t addrspace = 0; addrspace < nraddr; addrspace++) {
uint32_t am, am_addr, am_size;
u_int am_offset;
struct siba_admatch admatch;
uint32_t am;
u_int am_offset;
/* Determine the register offset */
am_offset = siba_admatch_offset(addrspace);
@ -521,16 +675,15 @@ siba_erom_dump(bhnd_erom_t *erom)
/* Read and parse the address match register */
am = siba_eio_read_4(&sc->io, i, am_offset);
error = siba_parse_admatch(am, &am_addr, &am_size);
if (error) {
if ((error = siba_parse_admatch(am, &admatch))) {
printf("failed to decode address match "
"register value 0x%x\n", am);
continue;
}
printf("\taddrspace %zu\n", addrspace);
printf("\t\taddr: 0x%08x\n", am_addr);
printf("\t\tsize: 0x%08x\n", am_size);
printf("\t\taddr: 0x%08x\n", admatch.am_base);
printf("\t\tsize: 0x%08x\n", admatch.am_size);
}
}

View File

@ -0,0 +1,46 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2018 Landon Fuller <landonf@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _SIBA_SIBA_EROMVAR_H_
#define _SIBA_SIBA_EROMVAR_H_
#include <dev/bhnd/bhnd.h>
#include <dev/bhnd/bhnd_erom.h>
#include "sibavar.h"
struct siba_erom;
#define SIBA_EROM_
int siba_erom_get_core_id(struct siba_erom *sc, u_int core_idx,
struct siba_core_id *result);
#endif /* _SIBA_SIBA_EROMVAR_H_ */

View File

@ -48,6 +48,12 @@ __FBSDID("$FreeBSD$");
#include "sibareg.h"
#include "sibavar.h"
static int siba_register_interrupts(device_t dev, device_t child,
struct siba_devinfo *dinfo);
static int siba_append_dinfo_region(struct siba_devinfo *dinfo,
uint8_t addridx, uint32_t base, uint32_t size,
uint32_t bus_reserved);
/**
* Map a siba(4) OCP vendor code to its corresponding JEDEC JEP-106 vendor
* code.
@ -67,48 +73,6 @@ siba_get_bhnd_mfgid(uint16_t ocp_vendor)
}
}
/**
* Parse the SIBA_IDH_* fields from the per-core identification
* registers, returning a siba_core_id representation.
*
* @param idhigh The SIBA_R0_IDHIGH register.
* @param idlow The SIBA_R0_IDLOW register.
* @param core_id The core id (index) to include in the result.
* @param unit The unit number to include in the result.
*/
struct siba_core_id
siba_parse_core_id(uint32_t idhigh, uint32_t idlow, u_int core_idx, int unit)
{
uint16_t ocp_vendor;
uint8_t sonics_rev;
uint8_t num_addrspace;
uint8_t num_cfg;
ocp_vendor = SIBA_REG_GET(idhigh, IDH_VENDOR);
sonics_rev = SIBA_REG_GET(idlow, IDL_SBREV);
num_addrspace = SIBA_REG_GET(idlow, IDL_NRADDR) + 1 /* + enum block */;
/* Determine the number of sonics config register blocks */
num_cfg = SIBA_CFG_NUM_2_2;
if (sonics_rev >= SIBA_IDL_SBREV_2_3)
num_cfg = SIBA_CFG_NUM_2_3;
return (struct siba_core_id) {
.core_info = {
.vendor = siba_get_bhnd_mfgid(ocp_vendor),
.device = SIBA_REG_GET(idhigh, IDH_DEVICE),
.hwrev = SIBA_IDH_CORE_REV(idhigh),
.core_idx = core_idx,
.unit = unit
},
.sonics_vendor = ocp_vendor,
.sonics_rev = sonics_rev,
.num_addrspace = num_addrspace,
.num_cfg_blocks = num_cfg
};
}
/**
* Allocate and return a new empty device info structure.
*
@ -138,7 +102,11 @@ siba_alloc_dinfo(device_t bus)
resource_list_init(&dinfo->resources);
dinfo->pmu_state = SIBA_PMU_NONE;
dinfo->intr_en = false;
dinfo->intr = (struct siba_intr) {
.mapped = false,
.rid = -1
};
return dinfo;
}
@ -148,6 +116,7 @@ siba_alloc_dinfo(device_t bus)
* siba_alloc_dinfo, copying the provided core id.
*
* @param dev The requesting bus device.
* @param child The siba child device.
* @param dinfo The device info instance.
* @param core Device core info.
*
@ -155,10 +124,77 @@ siba_alloc_dinfo(device_t bus)
* @retval non-zero initialization failed.
*/
int
siba_init_dinfo(device_t dev, struct siba_devinfo *dinfo,
siba_init_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo,
const struct siba_core_id *core_id)
{
int error;
dinfo->core_id = *core_id;
/* Register all address space mappings */
for (uint8_t i = 0; i < core_id->num_admatch; i++) {
uint32_t bus_reserved;
/* If this is the device's core/enumeration addrespace,
* reserve the Sonics configuration register blocks for the
* use of our bus. */
bus_reserved = 0;
if (i == SIBA_CORE_ADDRSPACE)
bus_reserved = core_id->num_cfg_blocks * SIBA_CFG_SIZE;
/* Append the region info */
error = siba_append_dinfo_region(dinfo, i,
core_id->admatch[i].am_base, core_id->admatch[i].am_size,
bus_reserved);
if (error)
return (error);
}
/* Register all interrupt(s) */
if ((error = siba_register_interrupts(dev, child, dinfo)))
return (error);
return (0);
}
/**
* Register and map all interrupts for @p dinfo.
*
* @param dev The siba bus device.
* @param child The siba child device.
* @param dinfo The device info instance on which to register all interrupt
* entries.
*/
static int
siba_register_interrupts(device_t dev, device_t child,
struct siba_devinfo *dinfo)
{
int error;
/* Is backplane interrupt distribution enabled for this core? */
if (!dinfo->core_id.intr_en)
return (0);
/* Have one interrupt */
dinfo->intr.mapped = false;
dinfo->intr.irq = 0;
dinfo->intr.rid = -1;
/* Map the interrupt */
error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,
&dinfo->intr.irq);
if (error) {
device_printf(dev, "failed mapping interrupt line for core %u: "
"%d\n", dinfo->core_id.core_info.core_idx, error);
return (error);
}
dinfo->intr.mapped = true;
/* Update the resource list */
dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,
dinfo->intr.irq, dinfo->intr.irq, 1);
return (0);
}
@ -238,7 +274,7 @@ siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)
switch (port_type) {
case BHND_PORT_DEVICE:
/* 0, 1, or 2 ports */
return (min(core_id->num_addrspace, 2));
return (min(core_id->num_admatch, 2));
case BHND_PORT_AGENT:
/* One agent port maps all configuration blocks */
@ -292,11 +328,11 @@ siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,
case BHND_PORT_DEVICE:
/* The first address space, if any, is mapped to device0.0 */
if (port == 0)
return (min(core_id->num_addrspace, 1));
return (min(core_id->num_admatch, 1));
/* All remaining address spaces are mapped to device0.(n - 1) */
if (port == 1 && core_id->num_addrspace >= 2)
return (core_id->num_addrspace - 1);
if (port == 1 && core_id->num_admatch >= 2)
return (core_id->num_admatch - 1);
break;
@ -327,7 +363,6 @@ siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,
* agent0.0 0
* agent0.1 1
*
* @param num_addrspace The number of available siba address spaces.
* @param port_type The bhnd(4) port type.
* @param port The bhnd(4) port number.
* @param region The bhnd(4) port region.
@ -394,7 +429,7 @@ siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
* For compatibility with bcma(4), we map address spaces to port/region
* identifiers as follows:
*
* [port] [addrspace]
* [port.region] [admatch index]
* device0.0 0
* device1.0 1
* device1.1 2
@ -431,7 +466,7 @@ siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,
else
return (ENOENT);
if (idx >= core_id->num_addrspace)
if (idx >= core_id->num_admatch)
return (ENOENT);
/* Found */
@ -484,7 +519,7 @@ siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
* @retval 0 success
* @retval non-zero An error occurred appending the entry.
*/
int
static int
siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,
uint32_t base, uint32_t size, uint32_t bus_reserved)
{
@ -546,7 +581,7 @@ siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)
}
/* Unmap the core's interrupt */
if (dinfo->intr_en && dinfo->intr.mapped) {
if (dinfo->core_id.intr_en && dinfo->intr.mapped) {
BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);
dinfo->intr.mapped = false;
}
@ -585,36 +620,38 @@ siba_admatch_offset(uint8_t addrspace)
*
* @param addrspace The address space index.
* @param am The address match register value to be parsed.
* @param[out] addr The parsed address.
* @param[out] size The parsed size.
* @param[out] admatch The parsed address match descriptor
*
* @retval 0 success
* @retval non-zero a parse error occurred.
*/
int
siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size)
siba_parse_admatch(uint32_t am, struct siba_admatch *admatch)
{
u_int am_type;
/* Negative encoding is not supported. This is not used on any
* currently known devices*/
if (am & SIBA_AM_ADNEG)
return (EINVAL);
u_int am_type;
/* Extract the base address and size */
am_type = SIBA_REG_GET(am, AM_TYPE);
switch (am_type) {
case 0:
*addr = am & SIBA_AM_BASE0_MASK;
*size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
/* Type 0 entries are always enabled, and do not support
* negative matching */
admatch->am_base = am & SIBA_AM_BASE0_MASK;
admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
admatch->am_enabled = true;
admatch->am_negative = false;
break;
case 1:
*addr = am & SIBA_AM_BASE1_MASK;
*size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
admatch->am_base = am & SIBA_AM_BASE1_MASK;
admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
break;
case 2:
*addr = am & SIBA_AM_BASE2_MASK;
*size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
admatch->am_base = am & SIBA_AM_BASE2_MASK;
admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
break;
default:
return (EINVAL);

View File

@ -37,6 +37,7 @@
#define _SIBA_SIBAVAR_H_
#include <sys/param.h>
#include <sys/bitstring.h>
#include <sys/bus.h>
#include <sys/limits.h>
#include <sys/lock.h>
@ -52,6 +53,7 @@
*/
struct siba_addrspace;
struct siba_admatch;
struct siba_cfg_block;
struct siba_devinfo;
struct siba_core_id;
@ -68,13 +70,10 @@ int siba_get_intr_ivec(device_t dev, device_t child,
uint16_t siba_get_bhnd_mfgid(uint16_t ocp_vendor);
struct siba_core_id siba_parse_core_id(uint32_t idhigh, uint32_t idlow,
u_int core_idx, int unit);
int siba_add_children(device_t bus);
struct siba_devinfo *siba_alloc_dinfo(device_t dev);
int siba_init_dinfo(device_t dev,
int siba_init_dinfo(device_t dev, device_t child,
struct siba_devinfo *dinfo,
const struct siba_core_id *core_id);
void siba_free_dinfo(device_t dev, device_t child,
@ -109,13 +108,9 @@ struct siba_addrspace *siba_find_addrspace(struct siba_devinfo *dinfo,
struct siba_cfg_block *siba_find_cfg_block(struct siba_devinfo *dinfo,
bhnd_port_type type, u_int port, u_int region);
int siba_append_dinfo_region(struct siba_devinfo *dinfo,
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);
int siba_parse_admatch(uint32_t am,
struct siba_admatch *admatch);
void siba_write_target_state(device_t dev,
struct siba_devinfo *dinfo, bus_size_t reg,
@ -148,6 +143,14 @@ int siba_wait_target_state(device_t dev,
#define SIBA_MAX_PORT 2 /**< maximum number of advertised
* bhnd(4) ports */
/** siba(4) address match descriptor */
struct siba_admatch {
uint32_t am_base; /**< base address. */
uint32_t am_size; /**< size. */
bool am_negative; /**< if true, negative matching is performed. */
bool am_enabled; /**< if true, matching on this entry is enabled. */
};
/** siba(4) address space descriptor */
struct siba_addrspace {
uint32_t sa_base; /**< base address */
@ -166,7 +169,6 @@ struct siba_cfg_block {
/** siba(4) backplane interrupt flag descriptor */
struct siba_intr {
u_int flag; /**< backplane flag # */
bool mapped; /**< if an irq has been mapped */
int rid; /**< bus resource id, or -1 if unassigned */
rman_res_t irq; /**< the mapped bus irq, if any */
@ -176,15 +178,17 @@ struct siba_intr {
* siba(4) per-core identification info.
*/
struct siba_core_id {
struct bhnd_core_info core_info; /**< standard bhnd(4) core info */
uint16_t sonics_vendor; /**< OCP vendor identifier used to generate
* the JEDEC-106 bhnd(4) vendor identifier. */
uint8_t sonics_rev; /**< sonics backplane revision code */
uint8_t num_addrspace; /**< number of address ranges mapped to
this core. */
uint8_t num_cfg_blocks; /**< number of Sonics configuration register
blocks mapped to the core's enumeration
space */
struct bhnd_core_info core_info; /**< standard bhnd(4) core info */
uint16_t sonics_vendor; /**< OCP vendor identifier used to generate
* the JEDEC-106 bhnd(4) vendor identifier. */
uint8_t sonics_rev; /**< sonics backplane revision code */
bool intr_en; /**< if backplane interrupt distribution is enabled for this core */
u_int intr_flag; /**< backplane interrupt flag # */
struct siba_admatch admatch[SIBA_MAX_ADDRSPACE]; /**< active address match descriptors defined by this core. */
uint8_t num_admatch; /**< number of address match descriptors. */
uint8_t num_cfg_blocks; /**< number of Sonics configuration register
blocks mapped to the core's enumeration
space */
};
/**
@ -205,8 +209,7 @@ struct siba_devinfo {
struct siba_core_id core_id; /**< core identification info */
struct siba_addrspace addrspace[SIBA_MAX_ADDRSPACE]; /**< memory map descriptors */
struct siba_cfg_block cfg[SIBA_MAX_CFG]; /**< config block descriptors */
struct siba_intr intr; /**< interrupt flag descriptor, if any */
bool intr_en; /**< if true, core has an assigned interrupt flag */
struct siba_intr intr; /**< interrupt flag mapping, if any */
struct bhnd_resource *cfg_res[SIBA_MAX_CFG]; /**< bus-mapped config block registers */
int cfg_rid[SIBA_MAX_CFG]; /**< bus-mapped config block resource IDs */