Implement a generic bhnd(4) device enumeration table API.
This defines a new bhnd_erom_if API, providing a common interface to device enumeration on siba(4) and bcma(4) devices, for use both in the bhndb bridge and SoC early boot contexts, and migrates mips/broadcom over to the new API. This also replaces the previous adhoc device enumeration support implemented for mips/broadcom. Migration of bhndb to the new API will be implemented in a follow-up commit. - Defined new bhnd_erom_if interface for bhnd(4) device enumeration, along with bcma(4) and siba(4)-specific implementations. - Fixed a minor bug in bhndb that logged an error when we attempted to map the full siba(4) bus space (18000000-17FFFFFF) in the siba EROM parser. - Reverted use of the resource's start address as the ChipCommon enum_addr in bhnd_read_chipid(). When called from bhndb, this address is found within the host address space, resulting in an invalid bridged enum_addr. - Added support for falling back on standard bus_activate_resource() in bhnd_bus_generic_activate_resource(), enabling allocation of the bhnd_erom's bhnd_resource directly from a nexus-attached bhnd(4) device. - Removed BHND_BUS_GET_CORE_TABLE(); it has been replaced by the erom API. - Added support for statically initializing bhnd_erom instances, for use prior to malloc availability. The statically allocated buffer size is verified both at runtime, and via a compile-time assertion (see BHND_EROM_STATIC_BYTES). - bhnd_erom classes are registered within a module via a linker set, allowing mips/broadcom to probe available EROM parser instances without creating a strong reference to bcma/siba-specific symbols. - Migrated mips/broadcom to bhnd_erom_if, replacing the previous MIPS-specific device enumeration implementation. Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D7748
This commit is contained in:
parent
cd3912b6be
commit
664a749708
@ -1140,6 +1140,8 @@ dev/bce/if_bce.c optional bce
|
||||
dev/bfe/if_bfe.c optional bfe
|
||||
dev/bge/if_bge.c optional bge
|
||||
dev/bhnd/bhnd.c optional bhnd
|
||||
dev/bhnd/bhnd_erom.c optional bhnd
|
||||
dev/bhnd/bhnd_erom_if.m optional bhnd
|
||||
dev/bhnd/bhnd_nexus.c optional bhnd siba_nexus | \
|
||||
bhnd bcma_nexus
|
||||
dev/bhnd/bhnd_subr.c optional bhnd
|
||||
@ -1188,6 +1190,7 @@ dev/bhnd/nvram/bhnd_sprom.c optional bhnd
|
||||
dev/bhnd/nvram/bhnd_sprom_parser.c optional bhnd
|
||||
dev/bhnd/siba/siba.c optional siba bhnd
|
||||
dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb
|
||||
dev/bhnd/siba/siba_erom.c optional siba bhnd
|
||||
dev/bhnd/siba/siba_nexus.c optional siba_nexus siba bhnd
|
||||
dev/bhnd/siba/siba_subr.c optional siba bhnd
|
||||
#
|
||||
|
@ -45,6 +45,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include "bcma_eromvar.h"
|
||||
#include <dev/bhnd/bhnd_core.h>
|
||||
|
||||
/* RID used when allocating EROM table */
|
||||
#define BCMA_EROM_RID 0
|
||||
|
||||
int
|
||||
bcma_probe(device_t dev)
|
||||
{
|
||||
@ -492,76 +495,35 @@ bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
|
||||
bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bcma_get_core_table(device_t dev, device_t child, struct bhnd_core_info **cores,
|
||||
u_int *num_cores)
|
||||
{
|
||||
struct bcma_softc *sc;
|
||||
struct bcma_erom erom;
|
||||
const struct bhnd_chipid *cid;
|
||||
struct resource *r;
|
||||
int error;
|
||||
int rid;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Map the EROM table. */
|
||||
cid = BHND_BUS_GET_CHIPID(dev, dev);
|
||||
rid = 0;
|
||||
r = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, cid->enum_addr,
|
||||
cid->enum_addr + BCMA_EROM_TABLE_SIZE, BCMA_EROM_TABLE_SIZE,
|
||||
RF_ACTIVE);
|
||||
if (r == NULL) {
|
||||
device_printf(dev, "failed to allocate EROM resource\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Enumerate all declared cores */
|
||||
if ((error = bcma_erom_open(&erom, r, BCMA_EROM_TABLE_START)))
|
||||
goto cleanup;
|
||||
|
||||
error = bcma_erom_get_core_info(&erom, cores, num_cores);
|
||||
|
||||
cleanup:
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a device enumeration ROM table, adding all valid discovered cores to
|
||||
* Scan the device enumeration ROM table, adding all valid discovered cores to
|
||||
* the bus.
|
||||
*
|
||||
* @param bus The bcma bus.
|
||||
* @param erom_res An active resource mapping the EROM core.
|
||||
* @param erom_offset Base offset of the EROM core's register mapping.
|
||||
*/
|
||||
int
|
||||
bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
|
||||
bcma_add_children(device_t bus)
|
||||
{
|
||||
struct bcma_erom erom;
|
||||
bhnd_erom_t *erom;
|
||||
struct bcma_erom *bcma_erom;
|
||||
const struct bhnd_chipid *cid;
|
||||
struct bcma_corecfg *corecfg;
|
||||
struct bcma_devinfo *dinfo;
|
||||
device_t child;
|
||||
int error;
|
||||
|
||||
cid = BHND_BUS_GET_CHIPID(bus, bus);
|
||||
corecfg = NULL;
|
||||
|
||||
/* Initialize our reader */
|
||||
error = bcma_erom_open(&erom, erom_res, erom_offset);
|
||||
if (error)
|
||||
return (error);
|
||||
/* Allocate our EROM parser */
|
||||
erom = bhnd_erom_alloc(&bcma_erom_parser, bus, BCMA_EROM_RID,
|
||||
cid->enum_addr);
|
||||
if (erom == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
/* Add all cores. */
|
||||
while (!error) {
|
||||
/* Parse next core */
|
||||
error = bcma_erom_parse_corecfg(&erom, &corecfg);
|
||||
if (error && error == ENOENT) {
|
||||
return (0);
|
||||
} else if (error) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
bcma_erom = (struct bcma_erom *)erom;
|
||||
while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
|
||||
/* Add the child device */
|
||||
child = BUS_ADD_CHILD(bus, 0, NULL, -1);
|
||||
if (child == NULL) {
|
||||
@ -588,9 +550,11 @@ bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offse
|
||||
|
||||
/* Hit EOF parsing cores? */
|
||||
if (error == ENOENT)
|
||||
return (0);
|
||||
error = 0;
|
||||
|
||||
failed:
|
||||
bhnd_erom_free(erom);
|
||||
|
||||
if (corecfg != NULL)
|
||||
bcma_free_corecfg(corecfg);
|
||||
|
||||
@ -613,7 +577,6 @@ static device_method_t bcma_methods[] = {
|
||||
DEVMETHOD(bhnd_bus_find_hostb_device, bcma_find_hostb_device),
|
||||
DEVMETHOD(bhnd_bus_alloc_devinfo, bcma_alloc_bhnd_dinfo),
|
||||
DEVMETHOD(bhnd_bus_free_devinfo, bcma_free_bhnd_dinfo),
|
||||
DEVMETHOD(bhnd_bus_get_core_table, bcma_get_core_table),
|
||||
DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core),
|
||||
DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core),
|
||||
DEVMETHOD(bhnd_bus_read_config, bcma_read_config),
|
||||
|
@ -45,5 +45,6 @@
|
||||
*/
|
||||
|
||||
DECLARE_CLASS(bcma_driver);
|
||||
DECLARE_CLASS(bcma_erom_parser);
|
||||
|
||||
#endif /* _BCMA_BCMA_H_ */
|
@ -73,29 +73,12 @@ static int
|
||||
bcma_bhndb_attach(device_t dev)
|
||||
{
|
||||
struct bcma_softc *sc;
|
||||
const struct bhnd_chipid *cid;
|
||||
struct resource *erom_res;
|
||||
int error;
|
||||
int rid;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Map the EROM resource and enumerate our children. */
|
||||
cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev);
|
||||
rid = 0;
|
||||
erom_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, cid->enum_addr,
|
||||
cid->enum_addr + BCMA_EROM_TABLE_SIZE, BCMA_EROM_TABLE_SIZE,
|
||||
RF_ACTIVE);
|
||||
if (erom_res == NULL) {
|
||||
device_printf(dev, "failed to allocate EROM resource\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
error = bcma_add_children(dev, erom_res, BCMA_EROM_TABLE_START);
|
||||
|
||||
/* Clean up */
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, erom_res);
|
||||
if (error)
|
||||
/* Enumerate our children. */
|
||||
if ((error = bcma_add_children(dev)))
|
||||
return (error);
|
||||
|
||||
/* Initialize full bridge configuration */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -33,16 +33,30 @@
|
||||
#define _BCMA_BCMA_EROMVAR_H_
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhnd_erom.h>
|
||||
|
||||
#include "bcmavar.h"
|
||||
|
||||
struct bcma_erom;
|
||||
|
||||
int bcma_erom_next_corecfg(struct bcma_erom *sc,
|
||||
struct bcma_corecfg **result);
|
||||
|
||||
/**
|
||||
* EROM read context.
|
||||
* BCMA EROM per-instance state.
|
||||
*/
|
||||
struct bcma_erom {
|
||||
device_t dev; /**< EROM parent device */
|
||||
struct bhnd_erom obj;
|
||||
device_t dev; /**< EROM parent device, or NULL
|
||||
if none. */
|
||||
struct bhnd_resource *res; /**< EROM table resource, or
|
||||
NULL if initialized with
|
||||
bus space tag and handle */
|
||||
int rid; /**< EROM table rid, or -1 */
|
||||
|
||||
bus_space_tag_t bst; /**< EROM table bus space */
|
||||
bus_space_handle_t bsh; /**< EROM table bus handle */
|
||||
|
||||
bus_size_t start; /**< EROM table offset */
|
||||
bus_size_t offset; /**< current read offset */
|
||||
};
|
||||
@ -76,42 +90,4 @@ struct bcma_erom_sport_region {
|
||||
bhnd_addr_t size; /**< region size */
|
||||
};
|
||||
|
||||
int bcma_erom_open(struct bcma_erom *erom, struct resource *r,
|
||||
bus_size_t offset);
|
||||
|
||||
int bhnd_erom_bus_space_open(struct bcma_erom *erom, device_t owner,
|
||||
bus_space_tag_t bst, bus_space_handle_t bsh,
|
||||
bus_size_t offset);
|
||||
|
||||
int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry);
|
||||
bus_size_t bcma_erom_tell(struct bcma_erom *erom);
|
||||
void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset);
|
||||
void bcma_erom_reset(struct bcma_erom *erom);
|
||||
|
||||
int bcma_erom_seek_next_core(struct bcma_erom *erom);
|
||||
int bcma_erom_seek_core_index(struct bcma_erom *erom,
|
||||
u_int core_index);
|
||||
int bcma_erom_parse_core(struct bcma_erom *erom,
|
||||
struct bcma_erom_core *core);
|
||||
|
||||
int bcma_erom_seek_core_sport_region(struct bcma_erom *erom,
|
||||
u_int core_index, bhnd_port_type port_type, u_int port_num,
|
||||
u_int region_num);
|
||||
|
||||
int bcma_erom_parse_mport(struct bcma_erom *erom,
|
||||
struct bcma_erom_mport *mport);
|
||||
|
||||
int bcma_erom_parse_sport_region(struct bcma_erom *erom,
|
||||
struct bcma_erom_sport_region *region);
|
||||
|
||||
void bcma_erom_to_core_info(const struct bcma_erom_core *core,
|
||||
u_int core_idx, int core_unit, struct bhnd_core_info *info);
|
||||
|
||||
int bcma_erom_get_core_info(struct bcma_erom *erom,
|
||||
struct bhnd_core_info **cores,
|
||||
u_int *num_cores);
|
||||
|
||||
int bcma_erom_parse_corecfg(struct bcma_erom *erom,
|
||||
struct bcma_corecfg **result);
|
||||
|
||||
#endif /* _BCMA_BCMA_EROMVAR_H_ */
|
||||
|
@ -91,28 +91,9 @@ bcma_nexus_probe(device_t dev)
|
||||
static int
|
||||
bcma_nexus_attach(device_t dev)
|
||||
{
|
||||
struct bcma_nexus_softc *sc;
|
||||
struct resource *erom_res;
|
||||
int error;
|
||||
int rid;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Map the EROM resource and enumerate the bus. */
|
||||
rid = 0;
|
||||
erom_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
|
||||
sc->bcma_cid.enum_addr,
|
||||
sc->bcma_cid.enum_addr + BCMA_EROM_TABLE_SIZE,
|
||||
BCMA_EROM_TABLE_SIZE, RF_ACTIVE);
|
||||
if (erom_res == NULL) {
|
||||
device_printf(dev, "failed to allocate EROM resource\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
error = bcma_add_children(dev, erom_res, BCMA_EROM_TABLE_START);
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, erom_res);
|
||||
|
||||
if (error)
|
||||
if ((error = bcma_add_children(dev)))
|
||||
return (error);
|
||||
|
||||
return (bcma_attach(dev));
|
||||
|
@ -63,8 +63,7 @@ int bcma_probe(device_t dev);
|
||||
int bcma_attach(device_t dev);
|
||||
int bcma_detach(device_t dev);
|
||||
|
||||
int bcma_add_children(device_t bus,
|
||||
struct resource *erom_res, bus_size_t erom_offset);
|
||||
int bcma_add_children(device_t bus);
|
||||
|
||||
struct bcma_sport_list *bcma_corecfg_get_port_list(struct bcma_corecfg *cfg,
|
||||
bhnd_port_type type);
|
||||
|
@ -428,32 +428,6 @@ bhnd_get_chipid(device_t dev) {
|
||||
return (BHND_BUS_GET_CHIPID(device_get_parent(dev), dev));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of all cores discoverable on the bhnd bus.
|
||||
*
|
||||
* Enumerates all cores discoverable on @p dev, returning the list in
|
||||
* @p cores and the count in @p num_cores.
|
||||
*
|
||||
* The memory allocated for the list should be freed using
|
||||
* `free(*cores, M_BHND)`. @p cores and @p num_cores are not changed
|
||||
* when an error is returned.
|
||||
*
|
||||
* @param dev A bhnd bus child device.
|
||||
* @param[out] cores The table of core descriptors.
|
||||
* @param[out] num_cores The number of core descriptors in @p cores.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero if an error occurs enumerating @p dev, a regular UNIX
|
||||
* error code should be returned.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_get_core_table(device_t dev, struct bhnd_core_info **cores,
|
||||
u_int *num_cores)
|
||||
{
|
||||
return (BHND_BUS_GET_CORE_TABLE(device_get_parent(dev), dev, cores,
|
||||
num_cores));
|
||||
}
|
||||
|
||||
/**
|
||||
* If supported by the chipset, return the clock source for the given clock.
|
||||
*
|
||||
|
@ -56,13 +56,6 @@ CODE {
|
||||
panic("bhnd_bus_get_chipid unimplemented");
|
||||
}
|
||||
|
||||
static int
|
||||
bhnd_bus_null_get_core_table(device_t dev, device_t child,
|
||||
struct bhnd_core_info **cores, u_int *num_cores)
|
||||
{
|
||||
panic("bhnd_bus_get_core_table unimplemented");
|
||||
}
|
||||
|
||||
static bhnd_attach_type
|
||||
bhnd_bus_null_get_attach_type(device_t dev, device_t child)
|
||||
{
|
||||
@ -277,32 +270,6 @@ METHOD const struct bhnd_chipid * get_chipid {
|
||||
device_t child;
|
||||
} DEFAULT bhnd_bus_null_get_chipid;
|
||||
|
||||
/**
|
||||
* Get a list of all cores discoverable on @p dev.
|
||||
*
|
||||
* Enumerates all cores discoverable on @p dev, returning the list in
|
||||
* @p cores and the count in @p num_cores.
|
||||
*
|
||||
* The memory allocated for the list should be freed using
|
||||
* `free(*cores, M_BHND)`. @p cores and @p num_cores are not changed
|
||||
* when an error is returned.
|
||||
*
|
||||
* @param dev The bhnd bus device.
|
||||
* @param child The requesting bhnd bus child.
|
||||
* @param[out] cores The table of core descriptors.
|
||||
* @param[out] num_cores The number of core descriptors in @p cores.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero if an error occurs enumerating @p dev, a regular UNIX
|
||||
* error code should be returned.
|
||||
*/
|
||||
METHOD int get_core_table {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
struct bhnd_core_info **cores;
|
||||
u_int *num_cores;
|
||||
} DEFAULT bhnd_bus_null_get_core_table;
|
||||
|
||||
/**
|
||||
* Return the BHND attachment type of the parent bus.
|
||||
*
|
||||
|
140
sys/dev/bhnd/bhnd_erom.c
Normal file
140
sys/dev/bhnd/bhnd_erom.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 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,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kobj.h>
|
||||
|
||||
#include <dev/bhnd/bhndvar.h>
|
||||
#include <dev/bhnd/bhnd_erom.h>
|
||||
|
||||
/**
|
||||
* Allocate and return a new device enumeration table parser.
|
||||
*
|
||||
* @param cls The parser class for which an instance will be
|
||||
* allocated.
|
||||
* @param parent The parent device from which EROM resources should
|
||||
* be allocated.
|
||||
* @param rid The resource ID to be used when allocating EROM
|
||||
* resources.
|
||||
* @param enum_addr The base address of the device enumeration table.
|
||||
*
|
||||
* @retval non-NULL success
|
||||
* @retval NULL if an error occured allocating or initializing the
|
||||
* EROM parser.
|
||||
*/
|
||||
bhnd_erom_t *
|
||||
bhnd_erom_alloc(bhnd_erom_class_t *cls, device_t parent, int rid,
|
||||
bus_addr_t enum_addr)
|
||||
{
|
||||
bhnd_erom_t *erom;
|
||||
int error;
|
||||
|
||||
erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
|
||||
M_WAITOK|M_ZERO);
|
||||
|
||||
if ((error = BHND_EROM_INIT(erom, parent, rid, enum_addr))) {
|
||||
printf("error initializing %s parser at %#jx with "
|
||||
"rid %d: %d\n", cls->name, (uintmax_t)enum_addr, rid,
|
||||
error);
|
||||
|
||||
kobj_delete((kobj_t)erom, M_BHND);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (erom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform static initialization of aa device enumeration table parser using
|
||||
* the provided bus space tag and handle.
|
||||
*
|
||||
* This may be used to initialize a caller-allocated erom instance state
|
||||
* during early boot, prior to malloc availability.
|
||||
*
|
||||
* @param cls The parser class for which an instance will be
|
||||
* allocated.
|
||||
* @param erom The erom parser instance to initialize.
|
||||
* @param esize The total available number of bytes allocated for
|
||||
* @p erom. If this is less than is required by @p cls,
|
||||
* ENOMEM will be returned.
|
||||
* @param bst Bus space tag.
|
||||
* @param bsh Bus space handle mapping the device enumeration
|
||||
* space.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENOMEM if @p esize is smaller than required by @p cls.
|
||||
* @retval non-zero if an error occurs initializing the EROM parser,
|
||||
* a regular unix error code will be returned.
|
||||
*/
|
||||
int
|
||||
bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
|
||||
bus_space_tag_t bst, bus_space_handle_t bsh)
|
||||
{
|
||||
kobj_class_t kcls;
|
||||
|
||||
kcls = (kobj_class_t)cls;
|
||||
|
||||
/* Verify allocation size */
|
||||
if (kcls->size > esize)
|
||||
return (ENOMEM);
|
||||
|
||||
/* Perform instance initialization */
|
||||
kobj_init_static((kobj_t)erom, kcls);
|
||||
return (BHND_EROM_INIT_STATIC(erom, bst, bsh));
|
||||
}
|
||||
|
||||
/**
|
||||
* Release any resources held by a @p erom parser previously
|
||||
* initialized via bhnd_erom_init_static().
|
||||
*
|
||||
* @param erom An erom parser instance previously initialized via
|
||||
* bhnd_erom_init_static().
|
||||
*/
|
||||
void
|
||||
bhnd_erom_fini_static(bhnd_erom_t *erom)
|
||||
{
|
||||
return (BHND_EROM_FINI(erom));
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all resources held by a @p erom parser previously
|
||||
* allocated via bhnd_erom_alloc().
|
||||
*
|
||||
* @param erom An erom parser instance previously allocated via
|
||||
* bhnd_erom_alloc().
|
||||
*/
|
||||
void
|
||||
bhnd_erom_free(bhnd_erom_t *erom)
|
||||
{
|
||||
BHND_EROM_FINI(erom);
|
||||
kobj_delete((kobj_t)erom, M_BHND);
|
||||
}
|
208
sys/dev/bhnd/bhnd_erom.h
Normal file
208
sys/dev/bhnd/bhnd_erom.h
Normal file
@ -0,0 +1,208 @@
|
||||
/*-
|
||||
* Copyright (c) 2015-2016 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,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _BHND_EROM_BHND_EROM_H_
|
||||
#define _BHND_EROM_BHND_EROM_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kobj.h>
|
||||
#include <sys/linker_set.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhnd_erom_types.h>
|
||||
|
||||
#include "bhnd_erom_if.h"
|
||||
|
||||
bhnd_erom_t *bhnd_erom_alloc(bhnd_erom_class_t *cls,
|
||||
device_t parent, int rid,
|
||||
bus_addr_t enum_addr);
|
||||
|
||||
int bhnd_erom_init_static(bhnd_erom_class_t *cls,
|
||||
bhnd_erom_t *erom, size_t esize,
|
||||
bus_space_tag_t bst,
|
||||
bus_space_handle_t bsh);
|
||||
|
||||
void bhnd_erom_fini_static(bhnd_erom_t *erom);
|
||||
|
||||
void bhnd_erom_free(bhnd_erom_t *erom);
|
||||
|
||||
/**
|
||||
* Abstract bhnd_erom instance state. Must be first member of all subclass
|
||||
* instances.
|
||||
*/
|
||||
struct bhnd_erom {
|
||||
KOBJ_FIELDS;
|
||||
};
|
||||
|
||||
|
||||
/** Number of additional bytes to reserve for statically allocated
|
||||
* bhnd_erom instances. */
|
||||
#define BHND_EROM_STATIC_BYTES 64
|
||||
|
||||
/**
|
||||
* A bhnd_erom instance structure large enough to statically allocate
|
||||
* any known bhnd_erom subclass.
|
||||
*
|
||||
* The maximum size of subclasses is verified statically in
|
||||
* BHND_EROM_DEFINE_CLASS(), and at runtime in bhnd_erom_init_static().
|
||||
*/
|
||||
struct bhnd_erom_static {
|
||||
struct bhnd_erom obj;
|
||||
uint8_t idata[BHND_EROM_STATIC_BYTES];
|
||||
};
|
||||
|
||||
/** Registered EROM parser class instances. */
|
||||
SET_DECLARE(bhnd_erom_class_set, bhnd_erom_class_t);
|
||||
|
||||
#define BHND_EROM_DEFINE_CLASS(name, classvar, methods, size) \
|
||||
DEFINE_CLASS_0(name, classvar, methods, size); \
|
||||
BHND_EROM_CLASS_DEF(classvar); \
|
||||
_Static_assert(size <= sizeof(struct bhnd_erom_static), \
|
||||
"cannot statically allocate instance data; " \
|
||||
"increase BHND_EROM_STATIC_BYTES");
|
||||
|
||||
#define BHND_EROM_CLASS_DEF(classvar) DATA_SET(bhnd_erom_class_set, classvar)
|
||||
|
||||
|
||||
/**
|
||||
* Probe to see if this device enumeration class supports the bhnd bus
|
||||
* mapped at the given bus space tag and handle, returning a standard
|
||||
* newbus device probe result (see BUS_PROBE_*) and the probed
|
||||
* chip identification.
|
||||
*
|
||||
* @param cls The parser class to be probed.
|
||||
* @param bst Bus space tag.
|
||||
* @param bsh Bus space handle mapping the EXTIF or ChipCommon core.
|
||||
* @param paddr The physical address of the core mapped by @p bst and
|
||||
* @p bsh.
|
||||
* @param[out] cid On success, the probed chip identifier.
|
||||
*
|
||||
* @retval 0 if this is the only possible device enumeration
|
||||
* parser for the probed bus.
|
||||
* @retval negative if the probe succeeds, a negative value should be
|
||||
* returned; the parser returning the lowest value will
|
||||
* be selected to handle device enumeration.
|
||||
* @retval ENXIO If the bhnd bus type is not handled by this parser.
|
||||
* @retval positive if an error occurs during probing, a regular unix error
|
||||
* code should be returned.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
|
||||
bus_space_handle_t bsh, bus_addr_t paddr, struct bhnd_chipid *cid)
|
||||
{
|
||||
return (BHND_EROM_PROBE_STATIC(cls, bst, bsh, paddr, cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse all cores descriptors in @p erom, returning the array in @p cores and
|
||||
* the count in @p num_cores.
|
||||
*
|
||||
* The memory allocated for the table must be freed via
|
||||
* bhnd_erom_free_core_table().
|
||||
*
|
||||
* @param erom The erom parser to be queried.
|
||||
* @param[out] cores The table of parsed core descriptors.
|
||||
* @param[out] num_cores The number of core records in @p cores.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero if an error occurs, a regular unix error code will
|
||||
* be returned.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
|
||||
u_int *num_cores)
|
||||
{
|
||||
return (BHND_EROM_GET_CORE_TABLE(erom, cores, num_cores));
|
||||
}
|
||||
|
||||
/**
|
||||
* Free any memory allocated in a previous call to BHND_EROM_GET_CORE_TABLE().
|
||||
*
|
||||
* @param erom The erom parser instance.
|
||||
* @param cores A core table allocated by @p erom.
|
||||
*/
|
||||
static inline void
|
||||
bhnd_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
|
||||
{
|
||||
return (BHND_EROM_FREE_CORE_TABLE(erom, cores));
|
||||
};
|
||||
|
||||
/**
|
||||
* Locate the first core table entry in @p erom that matches @p desc.
|
||||
*
|
||||
* @param erom The erom parser to be queried.
|
||||
* @param desc A core match descriptor.
|
||||
* @param[out] core On success, the matching core info record.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENOENT No core matching @p desc was found.
|
||||
* @retval non-zero Reading or parsing failed.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
|
||||
struct bhnd_core_info *core)
|
||||
{
|
||||
return (BHND_EROM_LOOKUP_CORE(erom, desc, core));
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the first core table entry in @p erom that matches @p desc,
|
||||
* and return the specified port region's base address and size.
|
||||
*
|
||||
* If a core matching @p desc is not found, or the requested port region
|
||||
* is not mapped to the matching core, ENOENT is returned.
|
||||
*
|
||||
* @param erom The erom parser to be queried.
|
||||
* @param desc A core match descriptor.
|
||||
* @param type The port type to search for.
|
||||
* @param port The port to search for.
|
||||
* @param region The port region to search for.
|
||||
* @param[out] core If not NULL, will be populated with the matched core
|
||||
* info record on success.
|
||||
* @param[out] addr On success, the base address of the port region.
|
||||
* @param[out] size On success, the total size of the port region.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENOENT No core matching @p desc was found.
|
||||
* @retval ENOENT No port region matching @p type, @p port, and @p region
|
||||
* was found.
|
||||
* @retval non-zero Reading or parsing failed.
|
||||
*/
|
||||
static inline int
|
||||
bhnd_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
|
||||
bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *core,
|
||||
bhnd_addr_t *addr, bhnd_size_t *size)
|
||||
{
|
||||
return (BHND_EROM_LOOKUP_CORE_ADDR(erom, desc, type, port, region,
|
||||
core, addr, size));
|
||||
};
|
||||
|
||||
#endif /* _BHND_EROM_BHND_EROM_H_ */
|
207
sys/dev/bhnd/bhnd_erom_if.m
Normal file
207
sys/dev/bhnd/bhnd_erom_if.m
Normal file
@ -0,0 +1,207 @@
|
||||
#-
|
||||
# Copyright (c) 2016 Landon Fuller <landon@landonf.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 ``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$
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhnd_erom_types.h>
|
||||
|
||||
INTERFACE bhnd_erom;
|
||||
|
||||
#
|
||||
# bhnd(4) device enumeration.
|
||||
#
|
||||
# Provides a common parser interface to the incompatible device enumeration
|
||||
# tables used by bhnd(4) buses.
|
||||
#
|
||||
|
||||
/**
|
||||
* Probe to see if this device enumeration class supports the bhnd bus
|
||||
* mapped at the given bus space tag and handle, returning a standard
|
||||
* newbus device probe result (see BUS_PROBE_*) and the probed
|
||||
* chip identification.
|
||||
*
|
||||
* @param cls The erom parse class to probe.
|
||||
* @param bst Bus space tag.
|
||||
* @param bsh Bus space handle mapping the EXTIF or ChipCommon core.
|
||||
* @param paddr The physical address of the core mapped by @p bst and
|
||||
* @p bsh.
|
||||
* @param[out] cid On success, the probed chip identifier.
|
||||
*
|
||||
* @retval 0 if this is the only possible device enumeration
|
||||
* parser for the probed bus.
|
||||
* @retval negative if the probe succeeds, a negative value should be
|
||||
* returned; the parser returning the highest negative
|
||||
* value will be selected to handle device enumeration.
|
||||
* @retval ENXIO If the bhnd bus type is not handled by this parser.
|
||||
* @retval positive if an error occurs during probing, a regular unix error
|
||||
* code should be returned.
|
||||
*/
|
||||
STATICMETHOD int probe_static {
|
||||
bhnd_erom_class_t *cls;
|
||||
bus_space_tag_t bst;
|
||||
bus_space_handle_t bsh;
|
||||
bus_addr_t paddr;
|
||||
struct bhnd_chipid *cid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a device enumeration table parser.
|
||||
*
|
||||
* @param erom The erom parser to initialize.
|
||||
* @param parent The parent device from which EROM resources should
|
||||
* be allocated.
|
||||
* @param rid The resource id to be used when allocating the
|
||||
* enumeration table.
|
||||
* @param enum_addr The base address of the device enumeration table.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero if an error occurs initializing the EROM parser,
|
||||
* a regular unix error code will be returned.
|
||||
*/
|
||||
METHOD int init {
|
||||
bhnd_erom_t *erom;
|
||||
device_t parent;
|
||||
int rid;
|
||||
bus_addr_t enum_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize an device enumeration table parser using the provided bus space
|
||||
* tag and handle.
|
||||
*
|
||||
* @param erom The erom parser to initialize.
|
||||
* @param bst Bus space tag.
|
||||
* @param bsh bus space handle mapping the full bus enumeration
|
||||
* space.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero if an error occurs initializing the EROM parser,
|
||||
* a regular unix error code will be returned.
|
||||
*/
|
||||
METHOD int init_static {
|
||||
bhnd_erom_t *erom;
|
||||
bus_space_tag_t bst;
|
||||
bus_space_handle_t bsh;
|
||||
};
|
||||
|
||||
/**
|
||||
* Release all resources held by @p erom.
|
||||
*
|
||||
* @param erom An erom parser instance previously initialized via
|
||||
* BHND_EROM_INIT() or BHND_EROM_INIT_STATIC().
|
||||
*/
|
||||
METHOD void fini {
|
||||
bhnd_erom_t *erom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse all cores descriptors, returning the array in @p cores and the count
|
||||
* in @p num_cores.
|
||||
*
|
||||
* The memory allocated for the table must be freed via
|
||||
* BHND_EROM_FREE_CORE_TABLE().
|
||||
*
|
||||
* @param erom The erom parser to be queried.
|
||||
* @param[out] cores The table of parsed core descriptors.
|
||||
* @param[out] num_cores The number of core records in @p cores.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval non-zero if an error occurs, a regular unix error code will
|
||||
* be returned.
|
||||
*/
|
||||
METHOD int get_core_table {
|
||||
bhnd_erom_t *erom;
|
||||
struct bhnd_core_info **cores;
|
||||
u_int *num_cores;
|
||||
};
|
||||
|
||||
/**
|
||||
* Free any memory allocated in a previous call to BHND_EROM_GET_CORE_TABLE().
|
||||
*
|
||||
* @param erom The erom parser instance.
|
||||
* @param cores A core table allocated by @p erom.
|
||||
*/
|
||||
METHOD void free_core_table {
|
||||
bhnd_erom_t *erom;
|
||||
struct bhnd_core_info *cores;
|
||||
};
|
||||
|
||||
/**
|
||||
* Locate the first core table entry in @p erom that matches @p desc.
|
||||
*
|
||||
* @param erom The erom parser to be queried.
|
||||
* @param desc A core match descriptor.
|
||||
* @param[out] core On success, the matching core info record.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENOENT No core matching @p desc was found.
|
||||
* @retval non-zero Reading or parsing failed.
|
||||
*/
|
||||
METHOD int lookup_core {
|
||||
bhnd_erom_t *erom;
|
||||
const struct bhnd_core_match *desc;
|
||||
struct bhnd_core_info *core;
|
||||
};
|
||||
|
||||
/**
|
||||
* Locate the first core table entry in @p erom that matches @p desc,
|
||||
* and return the specified port region's base address and size.
|
||||
*
|
||||
* If a core matching @p desc is not found, or the requested port region
|
||||
* is not mapped to the matching core, ENOENT is returned.
|
||||
*
|
||||
* @param erom The erom parser to be queried.
|
||||
* @param desc A core match descriptor.
|
||||
* @param type The port type to search for.
|
||||
* @param port The port to search for.
|
||||
* @param region The port region to search for.
|
||||
* @param[out] core If not NULL, will be populated with the matched core
|
||||
* info record on success.
|
||||
* @param[out] addr On success, the base address of the port region.
|
||||
* @param[out] size On success, the total size of the port region.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENOENT No core matching @p desc was found.
|
||||
* @retval ENOENT No port region matching @p type, @p port, and @p region
|
||||
* was found.
|
||||
* @retval non-zero Reading or parsing failed.
|
||||
*/
|
||||
METHOD int lookup_core_addr {
|
||||
bhnd_erom_t *erom;
|
||||
const struct bhnd_core_match *desc;
|
||||
bhnd_port_type type;
|
||||
u_int port;
|
||||
u_int region;
|
||||
struct bhnd_core_info *core;
|
||||
bhnd_addr_t *addr;
|
||||
bhnd_size_t *size;
|
||||
};
|
42
sys/dev/bhnd/bhnd_erom_types.h
Normal file
42
sys/dev/bhnd/bhnd_erom_types.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 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,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _BHND_EROM_BHND_EROM_TYPES_H_
|
||||
#define _BHND_EROM_BHND_EROM_TYPES_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kobj.h>
|
||||
|
||||
typedef struct kobj_class bhnd_erom_class_t; /**< bhnd_erom parser class */
|
||||
typedef struct bhnd_erom bhnd_erom_t; /**< bhnd_erom parser instance */
|
||||
typedef struct bhnd_erom_static bhnd_erom_static_t; /**< bhnd_erom parser static instance data */
|
||||
|
||||
#endif /* _BHND_EROM_BHND_EROM_TYPES_H_ */
|
@ -944,7 +944,7 @@ bhnd_read_chipid(device_t dev, struct resource_spec *rs,
|
||||
} else if (chip_type == BHND_CHIPTYPE_SIBA) {
|
||||
/* siba(4) uses the ChipCommon base address as the enumeration
|
||||
* address */
|
||||
enum_addr = rman_get_start(res) + chipc_offset;
|
||||
enum_addr = BHND_DEFAULT_CHIPC_ADDR;
|
||||
} else {
|
||||
device_printf(dev, "unknown chip type %hhu\n", chip_type);
|
||||
error = ENODEV;
|
||||
@ -1578,19 +1578,41 @@ bhnd_bus_generic_release_resource(device_t dev, device_t child, int type,
|
||||
/**
|
||||
* Helper function for implementing BHND_BUS_ACTIVATE_RESOURCE().
|
||||
*
|
||||
* This implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the
|
||||
* This implementation of BHND_BUS_ACTIVATE_RESOURCE() first calls the
|
||||
* BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev.
|
||||
*
|
||||
* If this fails, and if @p dev is the direct parent of @p child, standard
|
||||
* resource activation is attempted via bus_activate_resource(). This enables
|
||||
* direct use of the bhnd(4) resource APIs on devices that may not be attached
|
||||
* to a parent bhnd bus or bridge.
|
||||
*/
|
||||
int
|
||||
bhnd_bus_generic_activate_resource(device_t dev, device_t child, int type,
|
||||
int rid, struct bhnd_resource *r)
|
||||
{
|
||||
/* Try to delegate to the parent */
|
||||
if (device_get_parent(dev) != NULL)
|
||||
return (BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev),
|
||||
child, type, rid, r));
|
||||
int error;
|
||||
bool passthrough;
|
||||
|
||||
return (EINVAL);
|
||||
passthrough = (device_get_parent(child) != dev);
|
||||
|
||||
/* Try to delegate to the parent */
|
||||
if (device_get_parent(dev) != NULL) {
|
||||
error = BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev),
|
||||
child, type, rid, r);
|
||||
} else {
|
||||
error = ENODEV;
|
||||
}
|
||||
|
||||
/* If bhnd(4) activation has failed and we're the child's direct
|
||||
* parent, try falling back on standard resource activation.
|
||||
*/
|
||||
if (error && !passthrough) {
|
||||
error = bus_activate_resource(child, type, rid, r->res);
|
||||
if (!error)
|
||||
r->direct = true;
|
||||
}
|
||||
|
||||
return (error);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1328,6 +1328,10 @@ bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Window must be large enough to map the entire resource */
|
||||
if (dwa->win->win_size < rman_get_size(r))
|
||||
return (NULL);
|
||||
|
||||
/* Set the window target */
|
||||
error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r),
|
||||
rman_get_size(r));
|
||||
|
@ -335,7 +335,7 @@ siba_get_port_count(device_t dev, device_t child, bhnd_port_type type)
|
||||
type));
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
return (siba_addrspace_port_count(dinfo));
|
||||
return (siba_addrspace_port_count(dinfo->core_id.num_addrspace));
|
||||
}
|
||||
|
||||
static u_int
|
||||
@ -350,10 +350,11 @@ siba_get_region_count(device_t dev, device_t child, bhnd_port_type type,
|
||||
type, port));
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
if (!siba_is_port_valid(dinfo, type, port))
|
||||
if (!siba_is_port_valid(dinfo->core_id.num_addrspace, type, port))
|
||||
return (0);
|
||||
|
||||
return (siba_addrspace_region_count(dinfo, port));
|
||||
return (siba_addrspace_region_count(dinfo->core_id.num_addrspace,
|
||||
port));
|
||||
}
|
||||
|
||||
static int
|
||||
@ -441,7 +442,7 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
|
||||
*/
|
||||
static int
|
||||
siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
|
||||
struct resource *r)
|
||||
struct bhnd_resource *r)
|
||||
{
|
||||
struct siba_core_id *cid;
|
||||
uint32_t addr;
|
||||
@ -465,7 +466,7 @@ siba_register_addrspaces(device_t dev, struct siba_devinfo *di,
|
||||
}
|
||||
|
||||
/* Fetch the address match register value */
|
||||
adm = bus_read_4(r, adm_offset);
|
||||
adm = bhnd_bus_read_4(r, adm_offset);
|
||||
|
||||
/* Parse the value */
|
||||
if ((error = siba_parse_admatch(adm, &addr, &size))) {
|
||||
@ -504,76 +505,6 @@ siba_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
|
||||
siba_free_dinfo(dev, (struct siba_devinfo *)dinfo);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
siba_get_core_table(device_t dev, device_t child, struct bhnd_core_info **cores,
|
||||
u_int *num_cores)
|
||||
{
|
||||
const struct bhnd_chipid *chipid;
|
||||
struct bhnd_core_info *table;
|
||||
struct bhnd_resource *r;
|
||||
int error;
|
||||
int rid;
|
||||
|
||||
/* Fetch the core count from our chip identification */
|
||||
chipid = BHND_BUS_GET_CHIPID(dev, dev);
|
||||
|
||||
/* Allocate our local core table */
|
||||
table = malloc(sizeof(*table) * chipid->ncores, M_BHND, M_NOWAIT);
|
||||
if (table == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
/* Enumerate all cores. */
|
||||
for (u_int i = 0; i < chipid->ncores; i++) {
|
||||
struct siba_core_id cid;
|
||||
uint32_t idhigh, idlow;
|
||||
|
||||
/* Map the core's register block */
|
||||
rid = 0;
|
||||
r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid,
|
||||
SIBA_CORE_ADDR(i), SIBA_CORE_ADDR(i) + SIBA_CORE_SIZE - 1,
|
||||
SIBA_CORE_SIZE, RF_ACTIVE);
|
||||
if (r == NULL) {
|
||||
error = ENXIO;
|
||||
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));
|
||||
|
||||
cid = siba_parse_core_id(idhigh, idlow, i, 0);
|
||||
table[i] = cid.core_info;
|
||||
|
||||
/* Determine unit number */
|
||||
for (u_int j = 0; j < i; j++) {
|
||||
if (table[j].vendor == table[i].vendor &&
|
||||
table[j].device == table[i].device)
|
||||
table[i].unit++;
|
||||
}
|
||||
|
||||
/* Release our resource */
|
||||
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
r = NULL;
|
||||
}
|
||||
|
||||
/* Provide the result values (performed last to avoid modifying
|
||||
* cores/num_cores if enumeration failed). */
|
||||
*cores = table;
|
||||
*num_cores = chipid->ncores;
|
||||
|
||||
return (0);
|
||||
|
||||
failed:
|
||||
if (table != NULL)
|
||||
free(table, M_BHND);
|
||||
|
||||
if (r != NULL)
|
||||
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the core table and add all valid discovered cores to
|
||||
* the bus.
|
||||
@ -588,7 +519,7 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
struct bhnd_chipid ccid;
|
||||
struct bhnd_core_info *cores;
|
||||
struct siba_devinfo *dinfo;
|
||||
struct resource *r;
|
||||
struct bhnd_resource *r;
|
||||
int rid;
|
||||
int error;
|
||||
|
||||
@ -612,13 +543,13 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
/* Map the first core's register block. If the ChipCommon core
|
||||
* exists, it will always be the first core. */
|
||||
rid = 0;
|
||||
r = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
|
||||
r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid,
|
||||
SIBA_CORE_ADDR(0), SIBA_CORE_SIZE,
|
||||
SIBA_CORE_ADDR(0) + SIBA_CORE_SIZE - 1,
|
||||
RF_ACTIVE);
|
||||
|
||||
/* Identify the core */
|
||||
idhigh = bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
vendor = SIBA_REG_GET(idhigh, IDH_VENDOR);
|
||||
device = SIBA_REG_GET(idhigh, IDH_DEVICE);
|
||||
ccrev = SIBA_IDH_CORE_REV(idhigh);
|
||||
@ -632,7 +563,7 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
}
|
||||
|
||||
/* Identify the chipset */
|
||||
ccreg = bus_read_4(r, CHIPC_ID);
|
||||
ccreg = bhnd_bus_read_4(r, CHIPC_ID);
|
||||
ccid = bhnd_parse_chipid(ccreg, SIBA_ENUM_ADDR);
|
||||
|
||||
/* Fix up the core count */
|
||||
@ -644,7 +575,7 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
}
|
||||
|
||||
chipid = &ccid;
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
}
|
||||
|
||||
/* Allocate our temporary core table and enumerate all cores */
|
||||
@ -664,7 +595,7 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
r_start = SIBA_CORE_ADDR(i);
|
||||
r_count = SIBA_CORE_SIZE;
|
||||
r_end = r_start + SIBA_CORE_SIZE - 1;
|
||||
r = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, r_start,
|
||||
r = bhnd_alloc_resource(dev, SYS_RES_MEMORY, &rid, r_start,
|
||||
r_end, r_count, RF_ACTIVE);
|
||||
if (r == NULL) {
|
||||
error = ENXIO;
|
||||
@ -679,8 +610,8 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
}
|
||||
|
||||
/* Read the core info */
|
||||
idhigh = bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
idlow = bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDLOW));
|
||||
idhigh = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
idlow = bhnd_bus_read_4(r, SB0_REG_ABS(SIBA_CFG0_IDLOW));
|
||||
|
||||
cid = siba_parse_core_id(idhigh, idlow, i, 0);
|
||||
cores[i] = cid.core_info;
|
||||
@ -711,7 +642,7 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
device_disable(child);
|
||||
|
||||
/* Release our resource */
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
r = NULL;
|
||||
|
||||
/* Issue bus callback for fully initialized child. */
|
||||
@ -723,7 +654,7 @@ siba_add_children(device_t dev, const struct bhnd_chipid *chipid)
|
||||
free(cores, M_BHND);
|
||||
|
||||
if (r != NULL)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -743,7 +674,6 @@ static device_method_t siba_methods[] = {
|
||||
|
||||
/* BHND interface */
|
||||
DEVMETHOD(bhnd_bus_find_hostb_device, siba_find_hostb_device),
|
||||
DEVMETHOD(bhnd_bus_get_core_table, siba_get_core_table),
|
||||
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),
|
||||
|
@ -45,5 +45,6 @@
|
||||
*/
|
||||
|
||||
DECLARE_CLASS(siba_driver);
|
||||
DECLARE_CLASS(siba_erom_parser);
|
||||
|
||||
#endif /* _SIBA_SIBA_H_ */
|
420
sys/dev/bhnd/siba/siba_erom.c
Normal file
420
sys/dev/bhnd/siba/siba_erom.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*-
|
||||
* Copyright (c) 2015-2016 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,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/bhnd/bhnd_erom.h>
|
||||
|
||||
#include <dev/bhnd/cores/chipc/chipcreg.h>
|
||||
|
||||
#include "sibareg.h"
|
||||
#include "sibavar.h"
|
||||
|
||||
struct siba_erom;
|
||||
|
||||
static int siba_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst,
|
||||
bus_space_handle_t bsh);
|
||||
static void siba_erom_fini(bhnd_erom_t *erom);
|
||||
|
||||
static uint32_t siba_erom_read_4(struct siba_erom *sc, u_int core_idx,
|
||||
bus_size_t offset);
|
||||
static int siba_erom_read_chipid(struct siba_erom *sc,
|
||||
bus_addr_t enum_addr, struct bhnd_chipid *cid);
|
||||
|
||||
struct siba_erom {
|
||||
struct bhnd_erom obj;
|
||||
u_int ncores; /**< core count */
|
||||
|
||||
/* resource state */
|
||||
device_t dev; /**< parent dev to use for resource allocations,
|
||||
or NULL if initialized with bst/bsh */
|
||||
struct bhnd_resource *res; /**< siba bus mapping, or NULL */
|
||||
int rid; /**< siba bus maping resource ID */
|
||||
|
||||
/* bus tag state */
|
||||
bus_space_tag_t bst; /**< chipc bus tag */
|
||||
bus_space_handle_t bsh; /**< chipc bus handle */
|
||||
};
|
||||
|
||||
#define EROM_LOG(sc, fmt, ...) do { \
|
||||
if (sc->dev != NULL) { \
|
||||
device_printf(sc->dev, "%s: " fmt, __FUNCTION__, \
|
||||
##__VA_ARGS__); \
|
||||
} else { \
|
||||
printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static uint32_t
|
||||
siba_erom_read_4(struct siba_erom *sc, u_int core_idx, bus_size_t offset)
|
||||
{
|
||||
bus_size_t core_offset;
|
||||
|
||||
/* Sanity check core index and offset */
|
||||
if (core_idx >= sc->ncores)
|
||||
panic("core index %u out of range (ncores=%u)", core_idx,
|
||||
sc->ncores);
|
||||
|
||||
if (offset > SIBA_CORE_SIZE - sizeof(uint32_t))
|
||||
panic("invalid core offset %#jx", (uintmax_t)offset);
|
||||
|
||||
/* Perform read */
|
||||
core_offset = SIBA_CORE_OFFSET(core_idx) + offset;
|
||||
if (sc->res != NULL)
|
||||
return (bhnd_bus_read_4(sc->res, core_offset));
|
||||
else
|
||||
return (bus_space_read_4(sc->bst, sc->bsh, core_offset));
|
||||
}
|
||||
|
||||
/** Fetch and parse a siba core's identification registers */
|
||||
static struct siba_core_id
|
||||
siba_erom_parse_core_id(struct siba_erom *sc, u_int core_idx, int unit)
|
||||
{
|
||||
uint32_t idhigh, idlow;
|
||||
|
||||
idhigh = siba_erom_read_4(sc, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
idlow = siba_erom_read_4(sc, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW));
|
||||
|
||||
return (siba_parse_core_id(idhigh, idlow, core_idx, unit));
|
||||
}
|
||||
|
||||
/** Fetch and parse the chip identification register */
|
||||
static int
|
||||
siba_erom_read_chipid(struct siba_erom *sc, bus_addr_t enum_addr,
|
||||
struct bhnd_chipid *cid)
|
||||
{
|
||||
struct siba_core_id ccid;
|
||||
uint32_t idreg;
|
||||
|
||||
/* Identify the chipcommon core */
|
||||
ccid = siba_erom_parse_core_id(sc, 0, 0);
|
||||
if (ccid.core_info.vendor != BHND_MFGID_BCM ||
|
||||
ccid.core_info.device != BHND_COREID_CC)
|
||||
{
|
||||
EROM_LOG(sc,
|
||||
"first core not chipcommon (vendor=%#hx, core=%#hx)\n",
|
||||
ccid.core_info.vendor, ccid.core_info.device);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Identify the chipset */
|
||||
idreg = siba_erom_read_4(sc, 0, CHIPC_ID);
|
||||
*cid = bhnd_parse_chipid(idreg, enum_addr);
|
||||
|
||||
/* Fix up the core count in-place */
|
||||
return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev,
|
||||
&cid->ncores));
|
||||
}
|
||||
|
||||
static int
|
||||
siba_erom_init_common(struct siba_erom *sc)
|
||||
{
|
||||
struct bhnd_chipid cid;
|
||||
int error;
|
||||
|
||||
/* There's always at least one core */
|
||||
sc->ncores = 1;
|
||||
|
||||
/* Identify the chipset */
|
||||
if ((error = siba_erom_read_chipid(sc, SIBA_ENUM_ADDR, &cid)))
|
||||
return (error);
|
||||
|
||||
/* Verify the chip type */
|
||||
if (cid.chip_type != BHND_CHIPTYPE_SIBA)
|
||||
return (ENXIO);
|
||||
|
||||
/*
|
||||
* gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES
|
||||
* without triggering build failure due to -Wtype-limits
|
||||
*
|
||||
* if (cid.ncores > SIBA_MAX_CORES)
|
||||
* return (EINVAL)
|
||||
*/
|
||||
_Static_assert((2^sizeof(cid.ncores)) <= SIBA_MAX_CORES,
|
||||
"ncores could result in over-read of backing resource");
|
||||
|
||||
/* Update our core count */
|
||||
sc->ncores = cid.ncores;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_erom_init(bhnd_erom_t *erom, device_t parent, int rid,
|
||||
bus_addr_t enum_addr)
|
||||
{
|
||||
struct siba_erom *sc = (struct siba_erom *)erom;
|
||||
|
||||
sc->dev = parent;
|
||||
sc->rid = rid;
|
||||
|
||||
sc->res = bhnd_alloc_resource(sc->dev, SYS_RES_MEMORY, &sc->rid,
|
||||
enum_addr, enum_addr + SIBA_ENUM_SIZE -1, SIBA_ENUM_SIZE,
|
||||
RF_ACTIVE|RF_SHAREABLE);
|
||||
if (sc->res == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
return (siba_erom_init_common(sc));
|
||||
}
|
||||
|
||||
static int
|
||||
siba_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
|
||||
bus_space_handle_t bsh, bus_addr_t paddr, struct bhnd_chipid *cid)
|
||||
{
|
||||
struct siba_erom sc;
|
||||
uint32_t idreg;
|
||||
uint8_t chip_type;
|
||||
int error;
|
||||
|
||||
idreg = bus_space_read_4(bst, bsh, CHIPC_ID);
|
||||
chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);
|
||||
|
||||
if (chip_type != BHND_CHIPTYPE_SIBA)
|
||||
return (ENXIO);
|
||||
|
||||
/* Initialize a static EROM instance that we can use to fetch
|
||||
* the chip identifier */
|
||||
if ((error = siba_erom_init_static((bhnd_erom_t *)&sc, bst, bsh)))
|
||||
return (error);
|
||||
|
||||
/* Try to read the chip ID, clean up the static instance */
|
||||
error = siba_erom_read_chipid(&sc, paddr, cid);
|
||||
siba_erom_fini((bhnd_erom_t *)&sc);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_erom_init_static(bhnd_erom_t *erom, bus_space_tag_t bst,
|
||||
bus_space_handle_t bsh)
|
||||
{
|
||||
struct siba_erom *sc = (struct siba_erom *)erom;
|
||||
|
||||
sc->dev = NULL;
|
||||
sc->rid = -1;
|
||||
sc->res = NULL;
|
||||
sc->bst = bst;
|
||||
sc->bsh = bsh;
|
||||
|
||||
return (siba_erom_init_common(sc));
|
||||
}
|
||||
|
||||
static void
|
||||
siba_erom_fini(bhnd_erom_t *erom)
|
||||
{
|
||||
struct siba_erom *sc = (struct siba_erom *)erom;
|
||||
|
||||
if (sc->res != NULL) {
|
||||
bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->rid,
|
||||
sc->res);
|
||||
|
||||
sc->res = NULL;
|
||||
sc->rid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
|
||||
struct bhnd_core_info *core)
|
||||
{
|
||||
struct siba_erom *sc;
|
||||
struct bhnd_core_match imatch;
|
||||
|
||||
sc = (struct siba_erom *)erom;
|
||||
|
||||
/* We can't determine a core's unit number during the initial scan. */
|
||||
imatch = *desc;
|
||||
imatch.m.match.core_unit = 0;
|
||||
|
||||
/* Locate the first matching core */
|
||||
for (u_int i = 0; i < sc->ncores; i++) {
|
||||
struct siba_core_id sid;
|
||||
struct bhnd_core_info ci;
|
||||
|
||||
/* Read the core info */
|
||||
sid = siba_erom_parse_core_id(sc, i, 0);
|
||||
ci = sid.core_info;
|
||||
|
||||
/* Check for initial match */
|
||||
if (!bhnd_core_matches(&ci, &imatch))
|
||||
continue;
|
||||
|
||||
/* Re-scan preceding cores to determine the unit number. */
|
||||
for (u_int j = 0; j < i; j++) {
|
||||
sid = siba_erom_parse_core_id(sc, i, 0);
|
||||
|
||||
/* Bump the unit number? */
|
||||
if (sid.core_info.vendor == ci.vendor &&
|
||||
sid.core_info.device == ci.device)
|
||||
ci.unit++;
|
||||
}
|
||||
|
||||
/* Check for full match against now-valid unit number */
|
||||
if (!bhnd_core_matches(&ci, desc))
|
||||
continue;
|
||||
|
||||
/* Matching core found */
|
||||
*core = ci;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
static int
|
||||
siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
|
||||
bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *info,
|
||||
bhnd_addr_t *addr, bhnd_size_t *size)
|
||||
{
|
||||
struct siba_erom *sc;
|
||||
struct bhnd_core_info core;
|
||||
struct siba_core_id sid;
|
||||
uint32_t am, am_addr, am_size;
|
||||
u_int am_offset;
|
||||
u_int addrspace;
|
||||
int error;
|
||||
|
||||
sc = (struct siba_erom *)erom;
|
||||
|
||||
/* Locate the requested core */
|
||||
if ((error = siba_erom_lookup_core(erom, desc, &core)))
|
||||
return (error);
|
||||
|
||||
/* Fetch full siba core ident */
|
||||
sid = siba_erom_parse_core_id(sc, core.core_idx, core.unit);
|
||||
|
||||
/* Is port valid? */
|
||||
if (!siba_is_port_valid(sid.num_addrspace, type, port))
|
||||
return (ENOENT);
|
||||
|
||||
/* Is region valid? */
|
||||
if (region >= siba_addrspace_region_count(sid.num_addrspace, port))
|
||||
return (ENOENT);
|
||||
|
||||
/* Map the bhnd port values to a siba addrspace index */
|
||||
error = siba_addrspace_index(sid.num_addrspace, type, port, region,
|
||||
&addrspace);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Determine the register offset */
|
||||
am_offset = siba_admatch_offset(addrspace);
|
||||
if (am_offset == 0) {
|
||||
printf("addrspace %u is unsupported", addrspace);
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
/* Read and parse the address match register */
|
||||
am = siba_erom_read_4(sc, core.core_idx, am_offset);
|
||||
|
||||
if ((error = siba_parse_admatch(am, &am_addr, &am_size))) {
|
||||
printf("failed to decode address match register value 0x%x\n",
|
||||
am);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (info != NULL)
|
||||
*info = core;
|
||||
|
||||
*addr = am_addr;
|
||||
*size = am_size;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* BHND_EROM_GET_CORE_TABLE() */
|
||||
static int
|
||||
siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
|
||||
u_int *num_cores)
|
||||
{
|
||||
struct siba_erom *sc;
|
||||
struct bhnd_core_info *out;
|
||||
|
||||
sc = (struct siba_erom *)erom;
|
||||
|
||||
/* Allocate our core array */
|
||||
out = malloc(sizeof(*out) * sc->ncores, M_BHND, M_NOWAIT);
|
||||
if (out == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
*cores = out;
|
||||
*num_cores = sc->ncores;
|
||||
|
||||
/* Enumerate all cores. */
|
||||
for (u_int i = 0; i < sc->ncores; i++) {
|
||||
struct siba_core_id sid;
|
||||
|
||||
/* Read the core info */
|
||||
sid = siba_erom_parse_core_id(sc, i, 0);
|
||||
out[i] = sid.core_info;
|
||||
|
||||
/* Determine unit number */
|
||||
for (u_int j = 0; j < i; j++) {
|
||||
if (out[j].vendor == out[i].vendor &&
|
||||
out[j].device == out[i].device)
|
||||
out[i].unit++;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* BHND_EROM_FREE_CORE_TABLE() */
|
||||
static void
|
||||
siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
|
||||
{
|
||||
free(cores, M_BHND);
|
||||
}
|
||||
|
||||
static kobj_method_t siba_erom_methods[] = {
|
||||
KOBJMETHOD(bhnd_erom_probe_static, siba_erom_probe_static),
|
||||
KOBJMETHOD(bhnd_erom_init, siba_erom_init),
|
||||
KOBJMETHOD(bhnd_erom_init_static, siba_erom_init_static),
|
||||
KOBJMETHOD(bhnd_erom_fini, siba_erom_fini),
|
||||
KOBJMETHOD(bhnd_erom_get_core_table, siba_erom_get_core_table),
|
||||
KOBJMETHOD(bhnd_erom_free_core_table, siba_erom_free_core_table),
|
||||
KOBJMETHOD(bhnd_erom_lookup_core, siba_erom_lookup_core),
|
||||
KOBJMETHOD(bhnd_erom_lookup_core_addr, siba_erom_lookup_core_addr),
|
||||
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
BHND_EROM_DEFINE_CLASS(siba_erom, siba_erom_parser, siba_erom_methods, sizeof(struct siba_erom));
|
@ -184,15 +184,15 @@ siba_addrspace_region(u_int addrspace)
|
||||
|
||||
/**
|
||||
* Return the number of bhnd(4) ports to advertise for the given
|
||||
* @p dinfo.
|
||||
* @p num_addrspace.
|
||||
*
|
||||
* @param dinfo The device info to query.
|
||||
* @param num_addrspace The number of siba address spaces.
|
||||
*/
|
||||
u_int
|
||||
siba_addrspace_port_count(struct siba_devinfo *dinfo)
|
||||
siba_addrspace_port_count(u_int num_addrspace)
|
||||
{
|
||||
/* 0, 1, or 2 ports */
|
||||
return min(dinfo->core_id.num_addrspace, 2);
|
||||
return min(num_addrspace, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -203,10 +203,8 @@ siba_addrspace_port_count(struct siba_devinfo *dinfo)
|
||||
* spaces.
|
||||
*/
|
||||
u_int
|
||||
siba_addrspace_region_count(struct siba_devinfo *dinfo, u_int port)
|
||||
siba_addrspace_region_count(u_int num_addrspace, 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));
|
||||
@ -220,32 +218,33 @@ siba_addrspace_region_count(struct siba_devinfo *dinfo, u_int port)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if @p port is defined on @p dinfo, false otherwise.
|
||||
* Return true if @p port is defined given an address space count
|
||||
* of @p num_addrspace, 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 num_addrspace The number of address spaces 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)
|
||||
siba_is_port_valid(u_int num_addrspace, 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)
|
||||
if (siba_addrspace_port_count(num_addrspace) <= port)
|
||||
return (false);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an bhnd(4) type/port/region triplet to its associated address space
|
||||
* entry, if any.
|
||||
* Map a bhnd(4) type/port/region triplet to its associated address space
|
||||
* index, if any.
|
||||
*
|
||||
* For compatibility with bcma(4), we map address spaces to port/region
|
||||
* identifiers as follows:
|
||||
@ -258,6 +257,46 @@ siba_is_port_valid(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port)
|
||||
*
|
||||
* The only supported port type is BHND_PORT_DEVICE.
|
||||
*
|
||||
* @param num_addrspace The number of available siba address spaces.
|
||||
* @param type The bhnd(4) port type.
|
||||
* @param port The bhnd(4) port number.
|
||||
* @param region The bhnd(4) port region.
|
||||
* @param addridx On success, the corresponding addrspace index.
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval ENOENT if the given type/port/region cannot be mapped to a
|
||||
* siba address space.
|
||||
*/
|
||||
int
|
||||
siba_addrspace_index(u_int num_addrspace, bhnd_port_type type, u_int port,
|
||||
u_int region, u_int *addridx)
|
||||
{
|
||||
u_int idx;
|
||||
|
||||
if (!siba_is_port_valid(num_addrspace, type, port))
|
||||
return (ENOENT);
|
||||
|
||||
if (port == 0)
|
||||
idx = region;
|
||||
else if (port == 1)
|
||||
idx = region + 1;
|
||||
else
|
||||
return (ENOENT);
|
||||
|
||||
if (idx >= num_addrspace)
|
||||
return (ENOENT);
|
||||
|
||||
/* Found */
|
||||
*addridx = idx;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an bhnd(4) type/port/region triplet to its associated address space
|
||||
* entry, if any.
|
||||
*
|
||||
* 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.
|
||||
@ -268,22 +307,18 @@ siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
|
||||
u_int region)
|
||||
{
|
||||
u_int addridx;
|
||||
int error;
|
||||
|
||||
if (!siba_is_port_valid(dinfo, type, port))
|
||||
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)
|
||||
/* Map to addrspace index */
|
||||
error = siba_addrspace_index(dinfo->core_id.num_addrspace, type, port,
|
||||
region, &addridx);
|
||||
if (error)
|
||||
return (NULL);
|
||||
|
||||
/* Found */
|
||||
if (addridx >= SIBA_MAX_ADDRSPACE)
|
||||
return (NULL);
|
||||
|
||||
return (&dinfo->addrspace[addridx]);
|
||||
}
|
||||
|
||||
|
@ -51,9 +51,11 @@
|
||||
#define SIBA_MAX_CORES \
|
||||
(SIBA_ENUM_SIZE/SIBA_CORE_SIZE) /**< Maximum number of cores */
|
||||
|
||||
/**< Evaluates to the bus address of the @p idx core register block */
|
||||
#define SIBA_CORE_ADDR(idx) \
|
||||
(SIBA_ENUM_ADDR + ((idx) * SIBA_CORE_SIZE))
|
||||
/** Evaluates to the bus address offset of the @p idx core register block */
|
||||
#define SIBA_CORE_OFFSET(idx) ((idx) * SIBA_CORE_SIZE)
|
||||
|
||||
/** Evaluates to the bus address of the @p idx core register block */
|
||||
#define SIBA_CORE_ADDR(idx) (SIBA_ENUM_ADDR + SIBA_CORE_OFFSET(idx))
|
||||
|
||||
/*
|
||||
* Sonics configuration registers are mapped to each core's enumeration
|
||||
|
@ -70,14 +70,16 @@ int siba_init_dinfo(device_t dev,
|
||||
void siba_free_dinfo(device_t dev,
|
||||
struct siba_devinfo *dinfo);
|
||||
|
||||
u_int siba_addrspace_port_count(struct siba_devinfo *dinfo);
|
||||
u_int siba_addrspace_region_count(struct siba_devinfo *dinfo,
|
||||
u_int siba_addrspace_port_count(u_int num_addrspace);
|
||||
u_int siba_addrspace_region_count(u_int num_addrspace,
|
||||
u_int port);
|
||||
|
||||
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,
|
||||
int siba_addrspace_index(u_int num_addrspace,
|
||||
bhnd_port_type type, u_int port, u_int region,
|
||||
u_int *addridx);
|
||||
bool siba_is_port_valid(u_int num_addrspace,
|
||||
bhnd_port_type type, u_int port);
|
||||
|
||||
struct siba_addrspace *siba_find_addrspace(struct siba_devinfo *dinfo,
|
||||
|
@ -1,100 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bcma/bcma_eromvar.h>
|
||||
|
||||
#include "bcm_machdep.h"
|
||||
|
||||
#define BCMFC_ERR(fmt, ...) printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
int
|
||||
bcm_find_core_bcma(struct bhnd_chipid *chipid, bhnd_devclass_t devclass,
|
||||
int unit, struct bhnd_core_info *info, uintptr_t *addr)
|
||||
{
|
||||
struct bcma_erom erom;
|
||||
struct bcma_erom_core core;
|
||||
struct bcma_erom_sport_region region;
|
||||
bhnd_devclass_t core_class;
|
||||
int error;
|
||||
|
||||
error = bhnd_erom_bus_space_open(&erom, NULL, mips_bus_space_generic,
|
||||
(bus_space_handle_t) BCM_SOC_ADDR(chipid->enum_addr, 0), 0);
|
||||
if (error) {
|
||||
BCMFC_ERR("erom open failed: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
for (u_long core_index = 0; core_index < ULONG_MAX; core_index++) {
|
||||
/* Fetch next core record */
|
||||
if ((error = bcma_erom_seek_next_core(&erom)))
|
||||
return (error);
|
||||
|
||||
if ((error = bcma_erom_parse_core(&erom, &core))) {
|
||||
BCMFC_ERR("core parse failed: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Check for match */
|
||||
core_class = bhnd_find_core_class(core.vendor,
|
||||
core.device);
|
||||
if (core_class != devclass)
|
||||
continue;
|
||||
|
||||
/* Provide the basic core info */
|
||||
if (info != NULL)
|
||||
bcma_erom_to_core_info(&core, core_index, 0, info);
|
||||
|
||||
/* Provide the core's device0.0 port address */
|
||||
error = bcma_erom_seek_core_sport_region(&erom, core_index,
|
||||
BHND_PORT_DEVICE, 0, 0);
|
||||
if (error) {
|
||||
BCMFC_ERR("sport not found: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if ((error = bcma_erom_parse_sport_region(&erom, ®ion))) {
|
||||
BCMFC_ERR("sport parse failed: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (addr != NULL)
|
||||
*addr = region.base_addr;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return (ENOENT);
|
||||
}
|
@ -96,15 +96,15 @@ __FBSDID("$FreeBSD$");
|
||||
#define BCM_TRACE(_fmt, ...)
|
||||
#endif
|
||||
|
||||
static int bcm_find_core(struct bhnd_chipid *chipid,
|
||||
bhnd_devclass_t devclass, int unit,
|
||||
struct bhnd_core_info *info, uintptr_t *addr);
|
||||
static int bcm_init_platform_data(struct bcm_platform *pdata);
|
||||
static int bcm_init_platform_data(struct bcm_platform *bp);
|
||||
|
||||
/* Allow bus-specific implementations to override bcm_find_core_(bcma|siba)
|
||||
* symbols, if included in the kernel build */
|
||||
__weak_reference(bcm_find_core_default, bcm_find_core_bcma);
|
||||
__weak_reference(bcm_find_core_default, bcm_find_core_siba);
|
||||
static int bcm_find_core(struct bcm_platform *bp, uint16_t vendor,
|
||||
uint16_t device, int unit, struct bhnd_core_info *info,
|
||||
uintptr_t *addr);
|
||||
|
||||
static int bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls,
|
||||
kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize,
|
||||
struct bhnd_chipid *cid);
|
||||
|
||||
extern int *edata;
|
||||
extern int *end;
|
||||
@ -121,128 +121,213 @@ bcm_get_platform(void)
|
||||
return (&bcm_platform_data);
|
||||
}
|
||||
|
||||
/* Default (no-op) bcm_find_core() implementation. */
|
||||
int
|
||||
bcm_find_core_default(struct bhnd_chipid *chipid, bhnd_devclass_t devclass,
|
||||
int unit, struct bhnd_core_info *info, uintptr_t *addr)
|
||||
static bus_addr_t
|
||||
bcm_get_bus_addr(void)
|
||||
{
|
||||
return (ENODEV);
|
||||
long maddr;
|
||||
|
||||
if (resource_long_value("bhnd", 0, "maddr", &maddr) == 0)
|
||||
return ((u_long)maddr);
|
||||
|
||||
return (BHND_DEFAULT_CHIPC_ADDR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search @p chipid's enumeration table for a core with @p devclass and
|
||||
* @p unit.
|
||||
* Search the device enumeration table for a core matching @p vendor,
|
||||
* @p device, and @p unit.
|
||||
*
|
||||
* @param chipid Chip identification data, including the address
|
||||
* of the enumeration table to be searched.
|
||||
* @param devclass Search for a core matching this device class.
|
||||
* @param bp Platform state containing a valid EROM parser.
|
||||
* @param vendor The core's required vendor.
|
||||
* @param device The core's required device id.
|
||||
* @param unit The core's required unit number.
|
||||
* @param[out] info On success, will be populated with the core
|
||||
* @param[out] info If non-NULL, will be populated with the core
|
||||
* info.
|
||||
* @param[out] addr If non-NULL, will be populated with the core's
|
||||
* physical register address.
|
||||
*/
|
||||
static int
|
||||
bcm_find_core(struct bhnd_chipid *chipid, bhnd_devclass_t devclass, int unit,
|
||||
struct bhnd_core_info *info, uintptr_t *addr)
|
||||
bcm_find_core(struct bcm_platform *bp, uint16_t vendor, uint16_t device,
|
||||
int unit, struct bhnd_core_info *info, uintptr_t *addr)
|
||||
{
|
||||
switch (chipid->chip_type) {
|
||||
case BHND_CHIPTYPE_SIBA:
|
||||
return (bcm_find_core_siba(chipid, devclass, unit, info, addr));
|
||||
struct bhnd_core_match md;
|
||||
bhnd_addr_t b_addr;
|
||||
bhnd_size_t b_size;
|
||||
int error;
|
||||
|
||||
md = (struct bhnd_core_match) {
|
||||
BHND_MATCH_CORE_VENDOR(vendor),
|
||||
BHND_MATCH_CORE_ID(BHND_COREID_CC),
|
||||
BHND_MATCH_CORE_UNIT(0)
|
||||
};
|
||||
|
||||
/* Fetch core info */
|
||||
error = bhnd_erom_lookup_core_addr(&bp->erom.obj, &md, BHND_PORT_DEVICE,
|
||||
0, 0, info, &b_addr, &b_size);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (addr != NULL && b_addr > UINTPTR_MAX) {
|
||||
BCM_ERR("core address %#jx overflows native address width\n",
|
||||
(uintmax_t)b_addr);
|
||||
return (ERANGE);
|
||||
}
|
||||
|
||||
if (addr != NULL)
|
||||
*addr = b_addr;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe and attach a bhnd_erom parser instance for the bhnd bus.
|
||||
*
|
||||
* @param[out] erom_cls The probed EROM class.
|
||||
* @param[out] erom_ops The storage to be used when compiling
|
||||
* @p erom_cls.
|
||||
* @param[out] erom The storage to be used when initializing the
|
||||
* static instance of @p erom_cls.
|
||||
* @param esize The total available number of bytes allocated
|
||||
* for @p erom. If this is less than is required
|
||||
* by @p erom_cls ENOMEM will be returned.
|
||||
* @param[out] cid On success, the probed chip identification.
|
||||
*/
|
||||
static int
|
||||
bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
|
||||
bhnd_erom_t *erom, size_t esize, struct bhnd_chipid *cid)
|
||||
{
|
||||
bhnd_erom_class_t **clsp;
|
||||
bus_space_tag_t bst;
|
||||
bus_space_handle_t bsh;
|
||||
bus_addr_t bus_addr;
|
||||
int error, prio, result;
|
||||
|
||||
bus_addr = bcm_get_bus_addr();
|
||||
*erom_cls = NULL;
|
||||
prio = 0;
|
||||
|
||||
bst = mips_bus_space_generic;
|
||||
bsh = BCM_SOC_BSH(bus_addr, 0);
|
||||
|
||||
SET_FOREACH(clsp, bhnd_erom_class_set) {
|
||||
struct bhnd_chipid pcid;
|
||||
bhnd_erom_class_t *cls;
|
||||
struct kobj_ops kops;
|
||||
|
||||
cls = *clsp;
|
||||
|
||||
/* Compile the class' ops table */
|
||||
kobj_class_compile_static(cls, &kops);
|
||||
|
||||
/* Probe the bus address */
|
||||
result = bhnd_erom_probe_static(cls, bst, bsh, bus_addr, &pcid);
|
||||
|
||||
/* Drop pointer to stack allocated ops table */
|
||||
cls->ops = NULL;
|
||||
|
||||
/* The parser did not match if an error was returned */
|
||||
if (result > 0)
|
||||
continue;
|
||||
|
||||
/* Check for a new highest priority match */
|
||||
if (*erom_cls == NULL || result > prio) {
|
||||
prio = result;
|
||||
|
||||
*cid = pcid;
|
||||
*erom_cls = cls;
|
||||
}
|
||||
|
||||
/* Terminate immediately on BUS_PROBE_SPECIFIC */
|
||||
if (result == BUS_PROBE_SPECIFIC)
|
||||
break;
|
||||
default:
|
||||
if (!BHND_CHIPTYPE_HAS_EROM(chipid->chip_type)) {
|
||||
printf("%s: unsupported chip type: %d\n", __FUNCTION__,
|
||||
chipid->chip_type);
|
||||
return (ENXIO);
|
||||
}
|
||||
return (bcm_find_core_bcma(chipid, devclass, unit, info, addr));
|
||||
|
||||
/* Valid EROM class probed? */
|
||||
if (*erom_cls == NULL) {
|
||||
BCM_ERR("no erom parser found for root bus at %#jx\n",
|
||||
(uintmax_t)bus_addr);
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
/* Using the provided storage, recompile the erom class ... */
|
||||
kobj_class_compile_static(*erom_cls, erom_ops);
|
||||
|
||||
/* ... and initialize the erom parser instance */
|
||||
bsh = BCM_SOC_BSH(cid->enum_addr, 0);
|
||||
error = bhnd_erom_init_static(*erom_cls, erom, esize,
|
||||
mips_bus_space_generic, bsh);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate platform configuration data.
|
||||
*/
|
||||
static int
|
||||
bcm_init_platform_data(struct bcm_platform *pdata)
|
||||
bcm_init_platform_data(struct bcm_platform *bp)
|
||||
{
|
||||
uint32_t reg;
|
||||
bhnd_addr_t enum_addr;
|
||||
long maddr;
|
||||
uint8_t chip_type;
|
||||
bool aob, pmu;
|
||||
int error;
|
||||
|
||||
/* Fetch CFE console handle (if any). Must be initialized before
|
||||
* any calls to printf/early_putc. */
|
||||
#ifdef CFE
|
||||
if ((pdata->cfe_console = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE)) < 0)
|
||||
pdata->cfe_console = -1;
|
||||
if ((bp->cfe_console = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE)) < 0)
|
||||
bp->cfe_console = -1;
|
||||
#endif
|
||||
|
||||
/* Fetch bhnd/chipc address */
|
||||
if (resource_long_value("bhnd", 0, "maddr", &maddr) == 0)
|
||||
pdata->cc_addr = (u_long)maddr;
|
||||
else
|
||||
pdata->cc_addr = BHND_DEFAULT_CHIPC_ADDR;
|
||||
|
||||
/* Read chip identifier from ChipCommon */
|
||||
reg = BCM_SOC_READ_4(pdata->cc_addr, CHIPC_ID);
|
||||
chip_type = CHIPC_GET_BITS(reg, CHIPC_ID_BUS);
|
||||
|
||||
if (BHND_CHIPTYPE_HAS_EROM(chip_type))
|
||||
enum_addr = BCM_SOC_READ_4(pdata->cc_addr, CHIPC_EROMPTR);
|
||||
else
|
||||
enum_addr = pdata->cc_addr;
|
||||
|
||||
pdata->id = bhnd_parse_chipid(reg, enum_addr);
|
||||
|
||||
/* Fetch chipc core info and capabilities */
|
||||
pdata->cc_caps = BCM_SOC_READ_4(pdata->cc_addr, CHIPC_CAPABILITIES);
|
||||
|
||||
error = bcm_find_core(&pdata->id, BHND_DEVCLASS_CC, 0, &pdata->cc_id,
|
||||
NULL);
|
||||
/* Probe and attach device table provider, populating our
|
||||
* chip identification */
|
||||
error = bcm_erom_probe_and_attach(&bp->erom_impl, &bp->erom_ops,
|
||||
&bp->erom.obj, sizeof(bp->erom), &bp->cid);
|
||||
if (error) {
|
||||
printf("%s: error locating chipc core: %d", __FUNCTION__,
|
||||
error);
|
||||
BCM_ERR("error attaching erom parser: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (CHIPC_HWREV_HAS_CAP_EXT(pdata->cc_id.hwrev)) {
|
||||
pdata->cc_caps_ext = BCM_SOC_READ_4(pdata->cc_addr,
|
||||
CHIPC_CAPABILITIES_EXT);
|
||||
} else {
|
||||
pdata->cc_caps_ext = 0x0;
|
||||
/* Fetch chipcommon core info */
|
||||
error = bcm_find_core(bp, BHND_MFGID_BCM, BHND_COREID_CC, 0, &bp->cc_id,
|
||||
&bp->cc_addr);
|
||||
if (error) {
|
||||
BCM_ERR("error locating chipc core: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Fetch chipc capability flags */
|
||||
bp->cc_caps = BCM_SOC_READ_4(bp->cc_addr, CHIPC_CAPABILITIES);
|
||||
bp->cc_caps_ext = 0x0;
|
||||
|
||||
if (CHIPC_HWREV_HAS_CAP_EXT(bp->cc_id.hwrev))
|
||||
bp->cc_caps_ext = BCM_CHIPC_READ_4(bp, CHIPC_CAPABILITIES_EXT);
|
||||
|
||||
/* Fetch PMU info */
|
||||
pmu = CHIPC_GET_FLAG(pdata->cc_caps, CHIPC_CAP_PMU);
|
||||
aob = CHIPC_GET_FLAG(pdata->cc_caps_ext, CHIPC_CAP2_AOB);
|
||||
pmu = CHIPC_GET_FLAG(bp->cc_caps, CHIPC_CAP_PMU);
|
||||
aob = CHIPC_GET_FLAG(bp->cc_caps_ext, CHIPC_CAP2_AOB);
|
||||
|
||||
if (pmu && aob) {
|
||||
/* PMU block mapped to a PMU core on the Always-on-Bus (aob) */
|
||||
error = bcm_find_core(&pdata->id, BHND_DEVCLASS_PMU, 0,
|
||||
&pdata->pmu_id, &pdata->pmu_addr);
|
||||
error = bcm_find_core(bp, BHND_MFGID_BCM, BHND_COREID_PMU, 0,
|
||||
&bp->pmu_id, &bp->pmu_addr);
|
||||
|
||||
if (error) {
|
||||
printf("%s: error locating pmu core: %d", __FUNCTION__,
|
||||
error);
|
||||
BCM_ERR("error locating pmu core: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
} else if (pmu) {
|
||||
/* PMU block mapped to chipc */
|
||||
pdata->pmu_addr = pdata->cc_addr;
|
||||
pdata->pmu_id = pdata->cc_id;
|
||||
bp->pmu_addr = bp->cc_addr;
|
||||
bp->pmu_id = bp->cc_id;
|
||||
} else {
|
||||
/* No PMU */
|
||||
pdata->pmu_addr = 0x0;
|
||||
memset(&pdata->pmu_id, 0, sizeof(pdata->pmu_id));
|
||||
bp->pmu_addr = 0x0;
|
||||
memset(&bp->pmu_id, 0, sizeof(bp->pmu_id));
|
||||
}
|
||||
|
||||
/* Initialize PMU query state */
|
||||
if (pmu) {
|
||||
error = bhnd_pmu_query_init(&pdata->pmu, NULL, pdata->id,
|
||||
&bcm_pmu_soc_io, pdata);
|
||||
error = bhnd_pmu_query_init(&bp->pmu, NULL, bp->cid,
|
||||
&bcm_pmu_soc_io, bp);
|
||||
if (error) {
|
||||
printf("%s: bhnd_pmu_query_init() failed: %d\n",
|
||||
__FUNCTION__, error);
|
||||
BCM_ERR("bhnd_pmu_query_init() failed: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
@ -346,7 +431,7 @@ platform_reset(void)
|
||||
bcm4785war = false;
|
||||
|
||||
/* Handle BCM4785-specific behavior */
|
||||
if (bp->id.chip_id == BHND_CHIPID_BCM4785) {
|
||||
if (bp->cid.chip_id == BHND_CHIPID_BCM4785) {
|
||||
bcm4785war = true;
|
||||
|
||||
/* Switch to async mode */
|
||||
|
@ -36,16 +36,14 @@
|
||||
#include <machine/cpuregs.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhnd_erom.h>
|
||||
|
||||
#include <dev/bhnd/cores/pmu/bhnd_pmuvar.h>
|
||||
|
||||
extern const struct bhnd_pmu_io bcm_pmu_soc_io;
|
||||
|
||||
typedef int (bcm_bus_find_core)(struct bhnd_chipid *chipid,
|
||||
bhnd_devclass_t devclass, int unit, struct bhnd_core_info *info,
|
||||
uintptr_t *addr);
|
||||
|
||||
struct bcm_platform {
|
||||
struct bhnd_chipid id; /**< chip id */
|
||||
struct bhnd_chipid cid; /**< chip id */
|
||||
struct bhnd_core_info cc_id; /**< chipc core info */
|
||||
uintptr_t cc_addr; /**< chipc core phys address */
|
||||
uint32_t cc_caps; /**< chipc capabilities */
|
||||
@ -60,6 +58,13 @@ struct bcm_platform {
|
||||
|
||||
struct bhnd_pmu_query pmu; /**< PMU query instance */
|
||||
|
||||
bhnd_erom_class_t *erom_impl; /**< erom parser class */
|
||||
struct kobj_ops erom_ops; /**< compiled kobj opcache */
|
||||
union {
|
||||
bhnd_erom_static_t data;
|
||||
bhnd_erom_t obj;
|
||||
} erom;
|
||||
|
||||
#ifdef CFE
|
||||
int cfe_console; /**< Console handle, or -1 */
|
||||
#endif
|
||||
@ -74,9 +79,11 @@ uint64_t bcm_get_ilpfreq(struct bcm_platform *bp);
|
||||
|
||||
u_int bcm_get_uart_rclk(struct bcm_platform *bp);
|
||||
|
||||
bcm_bus_find_core bcm_find_core_default;
|
||||
bcm_bus_find_core bcm_find_core_bcma;
|
||||
bcm_bus_find_core bcm_find_core_siba;
|
||||
#define BCM_ERR(fmt, ...) \
|
||||
printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
#define BCM_SOC_BSH(_addr, _offset) \
|
||||
((bus_space_handle_t)BCM_SOC_ADDR((_addr), (_offset)))
|
||||
|
||||
#define BCM_SOC_ADDR(_addr, _offset) \
|
||||
MIPS_PHYS_TO_KSEG1((_addr) + (_offset))
|
||||
|
@ -239,14 +239,14 @@ bcm_get_cpufreq(struct bcm_platform *bp)
|
||||
* PWRCTL support
|
||||
*/
|
||||
pll_type = CHIPC_GET_BITS(bp->cc_caps, CHIPC_CAP_PLL);
|
||||
mreg = bhnd_pwrctl_cpu_clkreg_m(&bp->id, pll_type, &fixed_hz);
|
||||
mreg = bhnd_pwrctl_cpu_clkreg_m(&bp->cid, pll_type, &fixed_hz);
|
||||
if (mreg == 0)
|
||||
return (fixed_hz);
|
||||
|
||||
n = BCM_CHIPC_READ_4(bp, CHIPC_CLKC_N);
|
||||
m = BCM_CHIPC_READ_4(bp, mreg);
|
||||
|
||||
return (bhnd_pwrctl_cpu_clock_rate(&bp->id, pll_type, n, m));
|
||||
return (bhnd_pwrctl_cpu_clock_rate(&bp->cid, pll_type, n, m));
|
||||
|
||||
}
|
||||
|
||||
@ -267,14 +267,14 @@ bcm_get_sifreq(struct bcm_platform *bp)
|
||||
* PWRCTL support
|
||||
*/
|
||||
pll_type = CHIPC_GET_BITS(bp->cc_caps, CHIPC_CAP_PLL);
|
||||
mreg = bhnd_pwrctl_si_clkreg_m(&bp->id, pll_type, &fixed_hz);
|
||||
mreg = bhnd_pwrctl_si_clkreg_m(&bp->cid, pll_type, &fixed_hz);
|
||||
if (mreg == 0)
|
||||
return (fixed_hz);
|
||||
|
||||
n = BCM_CHIPC_READ_4(bp, CHIPC_CLKC_N);
|
||||
m = BCM_CHIPC_READ_4(bp, mreg);
|
||||
|
||||
return (bhnd_pwrctl_si_clock_rate(&bp->id, pll_type, n, m));
|
||||
return (bhnd_pwrctl_si_clock_rate(&bp->cid, pll_type, n, m));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,64 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhndreg.h>
|
||||
|
||||
#include <dev/bhnd/siba/sibareg.h>
|
||||
#include <dev/bhnd/siba/sibavar.h>
|
||||
|
||||
#include "bcm_machdep.h"
|
||||
|
||||
int
|
||||
bcm_find_core_siba(struct bhnd_chipid *chipid, bhnd_devclass_t devclass,
|
||||
int unit, struct bhnd_core_info *info, uintptr_t *addr)
|
||||
{
|
||||
struct siba_core_id scid;
|
||||
uintptr_t cc_addr;
|
||||
uint32_t idhigh, idlow;
|
||||
|
||||
/* No other cores are required during early boot on siba(4) devices */
|
||||
if (devclass != BHND_DEVCLASS_CC || unit != 0)
|
||||
return (ENOENT);
|
||||
|
||||
cc_addr = chipid->enum_addr;
|
||||
idhigh = BCM_SOC_READ_4(cc_addr, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
idlow = BCM_SOC_READ_4(cc_addr, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
|
||||
|
||||
scid = siba_parse_core_id(idhigh, idlow, 0, 0);
|
||||
|
||||
if (info != NULL)
|
||||
*info = scid.core_info;
|
||||
|
||||
if (addr != NULL)
|
||||
*addr = cc_addr;
|
||||
|
||||
return (0);
|
||||
}
|
@ -4,10 +4,8 @@
|
||||
# for USB 1.1 OHCI, Ethernet and IPSEC cores
|
||||
# which are believed to be devices we have drivers for
|
||||
# which just need to be tweaked for attachment to an BHND system bus.
|
||||
mips/broadcom/bcm_bcma.c optional bcma_nexus bcma
|
||||
mips/broadcom/bcm_machdep.c standard
|
||||
mips/broadcom/bcm_pmu.c standard
|
||||
mips/broadcom/bcm_siba.c optional siba_nexus siba
|
||||
mips/mips/tick.c standard
|
||||
mips/mips/mips_pic.c standard
|
||||
kern/subr_intr.c standard
|
||||
|
@ -10,6 +10,9 @@ KMOD= bhnd
|
||||
SRCS= bhnd.c bhnd_subr.c
|
||||
SRCS+= bhnd_bus_if.c bhnd_bus_if.h
|
||||
|
||||
SRCS+= bhnd_erom.c
|
||||
SRCS+= bhnd_erom_if.c bhnd_erom_if.h
|
||||
|
||||
# ChipCommon
|
||||
SRCS+= chipc.c chipc_subr.c
|
||||
SRCS+= bhnd_sprom_chipc.c \
|
||||
|
@ -5,6 +5,7 @@
|
||||
KMOD= bcma
|
||||
SRCS= bcma.c bcma_subr.c bcma_erom.c
|
||||
|
||||
SRCS+= device_if.h bus_if.h bhnd_bus_if.h
|
||||
SRCS+= device_if.h bus_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
@ -5,7 +5,8 @@
|
||||
KMOD= bcma_bhndb
|
||||
SRCS= bcma_bhndb.c
|
||||
|
||||
SRCS+= bhnd_bus_if.h bhndb_bus_if.h bhndb_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h
|
||||
SRCS+= bhndb_bus_if.h bhndb_if.h
|
||||
SRCS+= device_if.h bus_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
@ -3,8 +3,10 @@
|
||||
.PATH: ${.CURDIR}/../../../dev/bhnd/siba
|
||||
|
||||
KMOD= siba
|
||||
SRCS= siba.c siba_subr.c
|
||||
SRCS= siba.c siba_subr.c \
|
||||
siba_erom.c
|
||||
|
||||
SRCS+= device_if.h bus_if.h bhnd_bus_if.h
|
||||
SRCS+= device_if.h bus_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
@ -5,7 +5,8 @@
|
||||
KMOD= siba_bhndb
|
||||
SRCS= siba_bhndb.c
|
||||
|
||||
SRCS+= bhnd_bus_if.h bhndb_bus_if.h bhndb_if.h
|
||||
SRCS+= bhnd_bus_if.h bhnd_erom_if.h
|
||||
SRCS+= bhndb_bus_if.h bhndb_if.h
|
||||
SRCS+= device_if.h bus_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
Loading…
Reference in New Issue
Block a user