bhnd: Implement bhnd(4) platform device registration.

Add bhnd(4) API for explicitly registering BHND platform devices (ChipCommon,
PMU, NVRAM, etc) with the bus, rather than walking the newbus hierarchy to
discover platform devices. These devices are now also refcounted; attempting
to deregister an actively used platform device will return EBUSY.

This resolves a lock ordering incompatibility with bwn(4)'s firmware loading
threads; previously it was necessary to acquire Giant to protect newbus access
when locating and querying the NVRAM device.

Approved by:	adrian (mentor)
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D12392
This commit is contained in:
Landon J. Fuller 2017-09-27 19:44:23 +00:00
parent 922f5528bd
commit 8e35bf8319
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324070
20 changed files with 1308 additions and 434 deletions

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -69,11 +73,9 @@ __FBSDID("$FreeBSD$");
#include "bhnd.h"
#include "bhndvar.h"
MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures");
#include "bhnd_private.h"
/* Bus pass at which all bus-required children must be available, and
* attachment may be finalized. */
#define BHND_FINISH_ATTACH_PASS BUS_PASS_DEFAULT
MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures");
/**
* bhnd_generic_probe_nomatch() reporting configuration.
@ -92,23 +94,8 @@ static const struct bhnd_nomatch {
{ BHND_MFGID_INVALID, BHND_COREID_INVALID, false }
};
static int bhnd_delete_children(struct bhnd_softc *sc);
static int bhnd_finish_attach(struct bhnd_softc *sc);
static device_t bhnd_find_chipc(struct bhnd_softc *sc);
static struct chipc_caps *bhnd_find_chipc_caps(struct bhnd_softc *sc);
static device_t bhnd_find_platform_dev(struct bhnd_softc *sc,
const char *classname);
static device_t bhnd_find_pmu(struct bhnd_softc *sc);
static device_t bhnd_find_nvram(struct bhnd_softc *sc);
static int compare_ascending_probe_order(const void *lhs,
const void *rhs);
static int compare_descending_probe_order(const void *lhs,
const void *rhs);
/**
* Default bhnd(4) bus driver implementation of DEVICE_ATTACH().
*
@ -119,8 +106,6 @@ int
bhnd_generic_attach(device_t dev)
{
struct bhnd_softc *sc;
device_t *devs;
int ndevs;
int error;
if (device_is_attached(dev))
@ -129,29 +114,13 @@ bhnd_generic_attach(device_t dev)
sc = device_get_softc(dev);
sc->dev = dev;
if ((error = device_get_children(dev, &devs, &ndevs)))
return (error);
/* Probe and attach all children */
qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order);
for (int i = 0; i < ndevs; i++) {
device_t child = devs[i];
device_probe_and_attach(child);
}
/* Try to finalize attachment */
if (bus_current_pass >= BHND_FINISH_ATTACH_PASS) {
if ((error = bhnd_finish_attach(sc)))
goto cleanup;
}
cleanup:
free(devs, M_TEMP);
if (error)
if ((error = bhnd_bus_probe_children(dev))) {
bhnd_delete_children(sc);
return (error);
}
return (error);
return (0);
}
/**
@ -164,11 +133,13 @@ bhnd_delete_children(struct bhnd_softc *sc)
int ndevs;
int error;
if ((error = device_get_children(sc->dev, &devs, &ndevs)))
/* Fetch children in detach order */
error = bhnd_bus_get_children(sc->dev, &devs, &ndevs,
BHND_DEVICE_ORDER_DETACH);
if (error)
return (error);
/* Detach in the reverse of attach order */
qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order);
/* Perform detach */
for (int i = 0; i < ndevs; i++) {
device_t child = devs[i];
@ -178,7 +149,7 @@ bhnd_delete_children(struct bhnd_softc *sc)
}
cleanup:
free(devs, M_TEMP);
bhnd_bus_free_children(devs);
return (error);
}
@ -193,12 +164,17 @@ int
bhnd_generic_detach(device_t dev)
{
struct bhnd_softc *sc;
int error;
if (!device_is_attached(dev))
return (EBUSY);
sc = device_get_softc(dev);
return (bhnd_delete_children(sc));
if ((error = bhnd_delete_children(sc)))
return (error);
return (0);
}
/**
@ -218,11 +194,13 @@ bhnd_generic_shutdown(device_t dev)
if (!device_is_attached(dev))
return (EBUSY);
if ((error = device_get_children(dev, &devs, &ndevs)))
/* Fetch children in detach order */
error = bhnd_bus_get_children(dev, &devs, &ndevs,
BHND_DEVICE_ORDER_DETACH);
if (error)
return (error);
/* Shutdown in the reverse of attach order */
qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order);
/* Perform shutdown */
for (int i = 0; i < ndevs; i++) {
device_t child = devs[i];
@ -232,7 +210,7 @@ bhnd_generic_shutdown(device_t dev)
}
cleanup:
free(devs, M_TEMP);
bhnd_bus_free_children(devs);
return (error);
}
@ -253,10 +231,13 @@ bhnd_generic_resume(device_t dev)
if (!device_is_attached(dev))
return (EBUSY);
if ((error = device_get_children(dev, &devs, &ndevs)))
/* Fetch children in attach order */
error = bhnd_bus_get_children(dev, &devs, &ndevs,
BHND_DEVICE_ORDER_ATTACH);
if (error)
return (error);
qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order);
/* Perform resume */
for (int i = 0; i < ndevs; i++) {
device_t child = devs[i];
@ -266,7 +247,7 @@ bhnd_generic_resume(device_t dev)
}
cleanup:
free(devs, M_TEMP);
bhnd_bus_free_children(devs);
return (error);
}
@ -289,11 +270,13 @@ bhnd_generic_suspend(device_t dev)
if (!device_is_attached(dev))
return (EBUSY);
if ((error = device_get_children(dev, &devs, &ndevs)))
/* Fetch children in detach order */
error = bhnd_bus_get_children(dev, &devs, &ndevs,
BHND_DEVICE_ORDER_DETACH);
if (error)
return (error);
/* Suspend in the reverse of attach order */
qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order);
/* Perform suspend */
for (int i = 0; i < ndevs; i++) {
device_t child = devs[i];
error = BUS_SUSPEND_CHILD(device_get_parent(child), child);
@ -310,259 +293,10 @@ bhnd_generic_suspend(device_t dev)
}
cleanup:
free(devs, M_TEMP);
bhnd_bus_free_children(devs);
return (error);
}
static void
bhnd_new_pass(device_t dev)
{
struct bhnd_softc *sc;
int error;
sc = device_get_softc(dev);
/* Attach any permissible children */
bus_generic_new_pass(dev);
/* Finalize attachment */
if (!sc->attach_done && bus_current_pass >= BHND_FINISH_ATTACH_PASS) {
if ((error = bhnd_finish_attach(sc))) {
panic("bhnd_finish_attach() failed: %d", error);
}
}
}
/*
* Finish any pending bus attachment operations.
*
* When attached as a SoC bus (as opposed to a bridged WiFi device), our
* platform devices may not be attached until later bus passes, necessitating
* delayed initialization on our part.
*/
static int
bhnd_finish_attach(struct bhnd_softc *sc)
{
struct chipc_caps *ccaps;
GIANT_REQUIRED; /* for newbus */
KASSERT(bus_current_pass >= BHND_FINISH_ATTACH_PASS,
("bhnd_finish_attach() called in pass %d", bus_current_pass));
KASSERT(!sc->attach_done, ("duplicate call to bhnd_finish_attach()"));
/* Locate chipc device */
if ((sc->chipc_dev = bhnd_find_chipc(sc)) == NULL) {
device_printf(sc->dev, "error: ChipCommon device not found\n");
return (ENXIO);
}
ccaps = BHND_CHIPC_GET_CAPS(sc->chipc_dev);
/* Look for NVRAM device */
if (ccaps->nvram_src != BHND_NVRAM_SRC_UNKNOWN) {
if ((sc->nvram_dev = bhnd_find_nvram(sc)) == NULL) {
device_printf(sc->dev,
"warning: NVRAM %s device not found\n",
bhnd_nvram_src_name(ccaps->nvram_src));
}
}
/* Look for a PMU */
if (ccaps->pmu || ccaps->pwr_ctrl) {
if ((sc->pmu_dev = bhnd_find_pmu(sc)) == NULL) {
device_printf(sc->dev,
"attach failed: supported PMU not found\n");
return (ENXIO);
}
}
/* Mark attach as completed */
sc->attach_done = true;
return (0);
}
/* Locate the ChipCommon core. */
static device_t
bhnd_find_chipc(struct bhnd_softc *sc)
{
device_t chipc;
/* Make sure we're holding Giant for newbus */
GIANT_REQUIRED;
/* chipc_dev is initialized during attachment */
if (sc->attach_done) {
if ((chipc = sc->chipc_dev) == NULL)
return (NULL);
goto found;
}
/* Locate chipc core with a core unit of 0 */
chipc = bhnd_find_child(sc->dev, BHND_DEVCLASS_CC, 0);
if (chipc == NULL)
return (NULL);
found:
if (device_get_state(chipc) < DS_ATTACHING) {
device_printf(sc->dev, "chipc found, but did not attach\n");
return (NULL);
}
return (chipc);
}
/* Locate the ChipCommon core and return the device capabilities */
static struct chipc_caps *
bhnd_find_chipc_caps(struct bhnd_softc *sc)
{
device_t chipc;
if ((chipc = bhnd_find_chipc(sc)) == NULL) {
device_printf(sc->dev,
"chipc unavailable; cannot fetch capabilities\n");
return (NULL);
}
return (BHND_CHIPC_GET_CAPS(chipc));
}
/**
* Find an attached platform device on @p dev, searching first for cores
* matching @p classname, and if not found, searching the children of the first
* bhnd_chipc device on the bus.
*
* @param sc Driver state.
* @param chipc Attached ChipCommon device.
* @param classname Device class to search for.
*
* @retval device_t A matching device.
* @retval NULL If no matching device is found.
*/
static device_t
bhnd_find_platform_dev(struct bhnd_softc *sc, const char *classname)
{
device_t chipc, child;
/* Make sure we're holding Giant for newbus */
GIANT_REQUIRED;
/* Look for a directly-attached child */
child = device_find_child(sc->dev, classname, -1);
if (child != NULL)
goto found;
/* Look for the first matching ChipCommon child */
if ((chipc = bhnd_find_chipc(sc)) == NULL) {
device_printf(sc->dev,
"chipc unavailable; cannot locate %s\n", classname);
return (NULL);
}
child = device_find_child(chipc, classname, -1);
if (child != NULL)
goto found;
/* Look for a parent-attached device (e.g. nexus0 -> bhnd_nvram) */
child = device_find_child(device_get_parent(sc->dev), classname, -1);
if (child == NULL)
return (NULL);
found:
if (device_get_state(child) < DS_ATTACHING)
return (NULL);
return (child);
}
/* Locate the PMU device, if any */
static device_t
bhnd_find_pmu(struct bhnd_softc *sc)
{
/* Make sure we're holding Giant for newbus */
GIANT_REQUIRED;
/* pmu_dev is initialized during attachment */
if (sc->attach_done) {
if (sc->pmu_dev == NULL)
return (NULL);
if (device_get_state(sc->pmu_dev) < DS_ATTACHING)
return (NULL);
return (sc->pmu_dev);
}
return (bhnd_find_platform_dev(sc, "bhnd_pmu"));
}
/* Locate the NVRAM device, if any */
static device_t
bhnd_find_nvram(struct bhnd_softc *sc)
{
struct chipc_caps *ccaps;
/* Make sure we're holding Giant for newbus */
GIANT_REQUIRED;
/* nvram_dev is initialized during attachment */
if (sc->attach_done) {
if (sc->nvram_dev == NULL)
return (NULL);
if (device_get_state(sc->nvram_dev) < DS_ATTACHING)
return (NULL);
return (sc->nvram_dev);
}
if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL)
return (NULL);
if (ccaps->nvram_src == BHND_NVRAM_SRC_UNKNOWN)
return (NULL);
return (bhnd_find_platform_dev(sc, "bhnd_nvram"));
}
/*
* Ascending comparison of bhnd device's probe order.
*/
static int
compare_ascending_probe_order(const void *lhs, const void *rhs)
{
device_t ldev, rdev;
int lorder, rorder;
ldev = (*(const device_t *) lhs);
rdev = (*(const device_t *) rhs);
lorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(ldev), ldev);
rorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(rdev), rdev);
if (lorder < rorder) {
return (-1);
} else if (lorder > rorder) {
return (1);
} else {
return (0);
}
}
/*
* Descending comparison of bhnd device's probe order.
*/
static int
compare_descending_probe_order(const void *lhs, const void *rhs)
{
return (compare_ascending_probe_order(rhs, lhs));
}
/**
* Default bhnd(4) bus driver implementation of BHND_BUS_GET_PROBE_ORDER().
*
@ -613,7 +347,7 @@ bhnd_generic_get_probe_order(device_t dev, device_t child)
case BHND_DEVCLASS_EROM:
case BHND_DEVCLASS_OTHER:
case BHND_DEVCLASS_INVALID:
if (bhnd_find_hostb_device(dev) == child)
if (bhnd_bus_find_hostb_device(dev) == child)
return (BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY);
return (BHND_PROBE_DEFAULT);
@ -630,7 +364,6 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
{
struct bhnd_softc *sc;
struct bhnd_resource *br;
struct chipc_caps *ccaps;
struct bhnd_core_pmu_info *pm;
struct resource_list *rl;
struct resource_list_entry *rle;
@ -646,18 +379,6 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
pm = bhnd_get_pmu_info(child);
pmu_regs = BHND_CLK_CTL_ST;
if ((ccaps = bhnd_find_chipc_caps(sc)) == NULL) {
device_printf(sc->dev, "alloc_pmu failed: chipc "
"capabilities unavailable\n");
return (ENXIO);
}
if ((pmu_dev = bhnd_find_pmu(sc)) == NULL) {
device_printf(sc->dev,
"pmu unavailable; cannot allocate request state\n");
return (ENXIO);
}
/* already allocated? */
if (pm != NULL) {
panic("duplicate PMU allocation for %s",
@ -719,23 +440,34 @@ bhnd_generic_alloc_pmu(device_t dev, device_t child)
else
pmu_regs -= r_addr - rman_get_start(rle->res);
/* Retain PMU reference on behalf of our caller */
pmu_dev = bhnd_retain_provider(child, BHND_SERVICE_PMU);
if (pmu_dev == NULL) {
device_printf(sc->dev,
"pmu unavailable; cannot allocate request state\n");
return (ENXIO);
}
/* Allocate and initialize PMU info */
br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT);
if (br == NULL)
if (br == NULL) {
bhnd_release_provider(child, pmu_dev, BHND_SERVICE_PMU);
return (ENOMEM);
}
br->res = rle->res;
br->direct = ((rman_get_flags(rle->res) & RF_ACTIVE) != 0);
pm = malloc(sizeof(*pm), M_BHND, M_NOWAIT);
if (pm == NULL) {
bhnd_release_provider(child, pmu_dev, BHND_SERVICE_PMU);
free(br, M_BHND);
return (ENOMEM);
}
pm->pm_dev = child;
pm->pm_pmu = pmu_dev;
pm->pm_res = br;
pm->pm_regs = pmu_regs;
pm->pm_pmu = pmu_dev;
bhnd_set_pmu_info(child, pm);
return (0);
@ -749,29 +481,24 @@ bhnd_generic_release_pmu(device_t dev, device_t child)
{
struct bhnd_softc *sc;
struct bhnd_core_pmu_info *pm;
device_t pmu;
int error;
GIANT_REQUIRED; /* for newbus */
sc = device_get_softc(dev);
if ((pmu = bhnd_find_pmu(sc)) == NULL) {
device_printf(sc->dev,
"pmu unavailable; cannot release request state\n");
return (ENXIO);
}
/* dispatch release request */
pm = bhnd_get_pmu_info(child);
if (pm == NULL)
panic("pmu over-release for %s", device_get_nameunit(child));
if ((error = BHND_PMU_CORE_RELEASE(pmu, pm)))
if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
return (error);
/* free PMU info */
bhnd_set_pmu_info(child, NULL);
bhnd_release_provider(pm->pm_dev, pm->pm_pmu, BHND_SERVICE_PMU);
free(pm->pm_res, M_BHND);
free(pm, M_BHND);
@ -875,9 +602,9 @@ bhnd_generic_is_region_valid(device_t dev, device_t child,
/**
* Default bhnd(4) bus driver implementation of BHND_BUS_GET_NVRAM_VAR().
*
* This implementation searches @p dev for a usable NVRAM child device.
* This implementation searches @p dev for a registered NVRAM child device.
*
* If no usable child device is found on @p dev, the request is delegated to
* If no NVRAM device is registered with @p dev, the request is delegated to
* the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev.
*/
int
@ -886,12 +613,17 @@ bhnd_generic_get_nvram_var(device_t dev, device_t child, const char *name,
{
struct bhnd_softc *sc;
device_t nvram, parent;
int error;
sc = device_get_softc(dev);
/* If a NVRAM device is available, consult it first */
if ((nvram = bhnd_find_nvram(sc)) != NULL)
return BHND_NVRAM_GETVAR(nvram, name, buf, size, type);
nvram = bhnd_retain_provider(child, BHND_SERVICE_NVRAM);
if (nvram != NULL) {
error = BHND_NVRAM_GETVAR(nvram, name, buf, size, type);
bhnd_release_provider(child, nvram, BHND_SERVICE_NVRAM);
return (error);
}
/* Otherwise, try to delegate to parent */
if ((parent = device_get_parent(dev)) == NULL)
@ -1046,15 +778,6 @@ bhnd_generic_child_deleted(device_t dev, device_t child)
panic("%s leaked device pmu state\n",
device_get_nameunit(child));
}
/* Clean up platform device references */
if (sc->chipc_dev == child) {
sc->chipc_dev = NULL;
} else if (sc->nvram_dev == child) {
sc->nvram_dev = NULL;
} else if (sc->pmu_dev == child) {
sc->pmu_dev = NULL;
}
}
/**
@ -1176,7 +899,6 @@ static device_method_t bhnd_methods[] = {
DEVMETHOD(device_resume, bhnd_generic_resume),
/* Bus interface */
DEVMETHOD(bus_new_pass, bhnd_new_pass),
DEVMETHOD(bus_child_deleted, bhnd_generic_child_deleted),
DEVMETHOD(bus_probe_nomatch, bhnd_generic_probe_nomatch),
DEVMETHOD(bus_print_child, bhnd_generic_print_child),

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -32,8 +36,10 @@
#ifndef _BHND_BHND_H_
#define _BHND_BHND_H_
#include <sys/types.h>
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
@ -289,6 +295,35 @@ struct bhnd_device {
#define BHND_DEVICE_IS_END(_d) \
(BHND_MATCH_IS_ANY(&(_d)->core) && (_d)->desc == NULL)
/**
* bhnd device sort order.
*/
typedef enum {
BHND_DEVICE_ORDER_ATTACH, /**< sort by bhnd(4) device attach order;
child devices should be probed/attached
in this order */
BHND_DEVICE_ORDER_DETACH, /**< sort by bhnd(4) device detach order;
child devices should be detached, suspended,
and shutdown in this order */
} bhnd_device_order;
/**
* A registry of bhnd service providers.
*/
struct bhnd_service_registry {
STAILQ_HEAD(,bhnd_service_entry) entries; /**< registered services */
struct mtx lock; /**< state lock */
};
/**
* bhnd service provider flags.
*/
enum {
BHND_SPF_INHERITED = (1<<0), /**< service provider reference was inherited from
a parent bus, and should be deregistered when the
last active reference is released */
};
const char *bhnd_vendor_name(uint16_t vendor);
const char *bhnd_port_type_name(bhnd_port_type port_type);
const char *bhnd_nvram_src_name(bhnd_nvram_src nvram_src);
@ -304,12 +339,23 @@ bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci);
int bhnd_format_chip_id(char *buffer, size_t size,
uint16_t chip_id);
device_t bhnd_match_child(device_t dev,
device_t bhnd_bus_match_child(device_t bus,
const struct bhnd_core_match *desc);
device_t bhnd_find_child(device_t dev,
device_t bhnd_bus_find_child(device_t bus,
bhnd_devclass_t class, int unit);
int bhnd_bus_get_children(device_t bus,
device_t **devlistp, int *devcountp,
bhnd_device_order order);
void bhnd_bus_free_children(device_t *devlist);
int bhnd_bus_probe_children(device_t bus);
int bhnd_sort_devices(device_t *devlist,
size_t devcount, bhnd_device_order order);
device_t bhnd_find_bridge_root(device_t dev,
devclass_t bus_class);
@ -410,6 +456,51 @@ int bhnd_nvram_getvar_array(device_t dev,
const char *name, void *buf, size_t count,
bhnd_nvram_type type);
int bhnd_service_registry_init(
struct bhnd_service_registry *bsr);
int bhnd_service_registry_fini(
struct bhnd_service_registry *bsr);
int bhnd_service_registry_add(
struct bhnd_service_registry *bsr,
device_t provider,
bhnd_service_t service,
uint32_t flags);
int bhnd_service_registry_remove(
struct bhnd_service_registry *bsr,
device_t provider,
bhnd_service_t service);
device_t bhnd_service_registry_retain(
struct bhnd_service_registry *bsr,
bhnd_service_t service);
bool bhnd_service_registry_release(
struct bhnd_service_registry *bsr,
device_t provider,
bhnd_service_t service);
int bhnd_bus_generic_register_provider(
device_t dev, device_t child,
device_t provider, bhnd_service_t service);
int bhnd_bus_generic_deregister_provider(
device_t dev, device_t child,
device_t provider, bhnd_service_t service);
device_t bhnd_bus_generic_retain_provider(device_t dev,
device_t child, bhnd_service_t service);
void bhnd_bus_generic_release_provider(device_t dev,
device_t child, device_t provider,
bhnd_service_t service);
int bhnd_bus_generic_sr_register_provider(
device_t dev, device_t child,
device_t provider, bhnd_service_t service);
int bhnd_bus_generic_sr_deregister_provider(
device_t dev, device_t child,
device_t provider, bhnd_service_t service);
device_t bhnd_bus_generic_sr_retain_provider(device_t dev,
device_t child, bhnd_service_t service);
void bhnd_bus_generic_sr_release_provider(device_t dev,
device_t child, device_t provider,
bhnd_service_t service);
bool bhnd_bus_generic_is_hw_disabled(device_t dev,
device_t child);
bool bhnd_bus_generic_is_region_valid(device_t dev,
@ -458,10 +549,84 @@ bhnd_driver_get_erom_class(driver_t *driver)
* @param dev A bhnd bus device.
*/
static inline device_t
bhnd_find_hostb_device(device_t dev) {
bhnd_bus_find_hostb_device(device_t dev) {
return (BHND_BUS_FIND_HOSTB_DEVICE(dev));
}
/**
* Register a provider for a given @p service.
*
* @param dev The device to register as a service provider
* with its parent bus.
* @param service The service for which @p dev will be registered.
*
* @retval 0 success
* @retval EEXIST if an entry for @p service already exists.
* @retval non-zero if registering @p dev otherwise fails, a regular
* unix error code will be returned.
*/
static inline int
bhnd_register_provider(device_t dev, bhnd_service_t service)
{
return (BHND_BUS_REGISTER_PROVIDER(device_get_parent(dev), dev, dev,
service));
}
/**
* Attempt to remove a service provider registration for @p dev.
*
* @param dev The device to be deregistered as a service provider.
* @param service The service for which @p dev will be deregistered, or
* BHND_SERVICE_INVALID to remove all service registrations
* for @p dev.
*
* @retval 0 success
* @retval EBUSY if active references to @p dev exist; @see
* bhnd_retain_provider() and bhnd_release_provider().
*/
static inline int
bhnd_deregister_provider(device_t dev, bhnd_service_t service)
{
return (BHND_BUS_DEREGISTER_PROVIDER(device_get_parent(dev), dev, dev,
service));
}
/**
* Retain and return a reference to the registered @p service provider, if any.
*
* @param dev The requesting device.
* @param service The service for which a provider should be returned.
*
* On success, the caller assumes ownership the returned provider, and
* is responsible for releasing this reference via
* BHND_BUS_RELEASE_PROVIDER().
*
* @retval device_t success
* @retval NULL if no provider is registered for @p service.
*/
static inline device_t
bhnd_retain_provider(device_t dev, bhnd_service_t service)
{
return (BHND_BUS_RETAIN_PROVIDER(device_get_parent(dev), dev,
service));
}
/**
* Release a reference to a provider device previously returned by
* bhnd_retain_provider().
*
* @param dev The requesting device.
* @param provider The provider to be released.
* @param service The service for which @p provider was previously retained.
*/
static inline void
bhnd_release_provider(device_t dev, device_t provider,
bhnd_service_t service)
{
return (BHND_BUS_RELEASE_PROVIDER(device_get_parent(dev), dev,
provider, service));
}
/**
* Return true if the hardware components required by @p dev are known to be
* unpopulated or otherwise unusable.

View File

@ -1,7 +1,11 @@
#-
# Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
# Copyright (c) 2017 The FreeBSD Foundation
# All rights reserved.
#
# Portions of this software were developed by Landon Fuller
# under sponsorship from the FreeBSD Foundation.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
@ -221,6 +225,12 @@ CODE {
return (NULL);
}
static struct bhnd_service_registry *
bhnd_bus_null_get_service_registry(device_t dev)
{
panic("bhnd_bus_get_service_registry unimplemented");
}
static bool
bhnd_bus_null_is_hw_disabled(device_t dev, device_t child)
{
@ -273,6 +283,100 @@ STATICMETHOD bhnd_erom_class_t * get_erom_class {
driver_t *driver;
} DEFAULT bhnd_bus_null_get_erom_class;
/**
* Register a shared bus @p provider for a given @p service.
*
* @param dev The parent of @p child.
* @param child The requesting child device.
* @param provider The service provider to register.
* @param service The service for which @p provider will be registered.
*
* @retval 0 success
* @retval EEXIST if an entry for @p service already exists.
* @retval non-zero if registering @p provider otherwise fails, a regular
* unix error code will be returned.
*/
METHOD int register_provider {
device_t dev;
device_t child;
device_t provider;
bhnd_service_t service;
} DEFAULT bhnd_bus_generic_register_provider;
/**
* Attempt to remove the @p service provider registration for @p provider.
*
* @param dev The parent of @p child.
* @param child The requesting child device.
* @param provider The service provider to be deregistered.
* @param service The service for which @p provider will be deregistered,
* or BHND_SERVICE_INVALID to remove all service
* registrations for @p provider.
*
* @retval 0 success
* @retval EBUSY if active references to @p provider exist; @see
* BHND_BUS_RETAIN_PROVIDER() and
* BHND_BUS_RELEASE_PROVIDER().
*/
METHOD int deregister_provider {
device_t dev;
device_t child;
device_t provider;
bhnd_service_t service;
} DEFAULT bhnd_bus_generic_deregister_provider;
/**
* Retain and return a reference to the registered @p service provider, if any.
*
* @param dev The parent of @p child.
* @param child The requesting child device.
* @param service The service for which a provider should be returned.
*
* On success, the caller assumes ownership the returned provider, and
* is responsible for releasing this reference via
* BHND_BUS_RELEASE_PROVIDER().
*
* @retval device_t success
* @retval NULL if no provider is registered for @p service.
*/
METHOD device_t retain_provider {
device_t dev;
device_t child;
bhnd_service_t service;
} DEFAULT bhnd_bus_generic_retain_provider;
/**
* Release a reference to a service provider previously returned by
* BHND_BUS_RETAIN_PROVIDER().
*
* @param dev The parent of @p child.
* @param child The requesting child device.
* @param provider The provider to be released.
* @param service The service for which @p provider was previously
* retained.
*/
METHOD void release_provider {
device_t dev;
device_t child;
device_t provider;
bhnd_service_t service;
} DEFAULT bhnd_bus_generic_release_provider;
/**
* Return a struct bhnd_service_registry.
*
* Used by drivers which use bhnd_bus_generic_sr_register_provider() etc.
* to implement service provider registration. It should return a service
* registry that may be used to resolve provider requests from @p child.
*
* @param dev The parent of @p child.
* @param child The requesting child device.
*/
METHOD struct bhnd_service_registry * get_service_registry {
device_t dev;
device_t child;
} DEFAULT bhnd_bus_null_get_service_registry;
/**
* Return the active host bridge core for the bhnd bus, if any.
*

View File

@ -0,0 +1,57 @@
/*-
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _BHND_BHND_PRIVATE_H_
#define _BHND_BHND_PRIVATE_H_
#include <sys/param.h>
#include <sys/queue.h>
#include "bhnd_types.h"
/*
* Private bhnd(4) driver definitions.
*/
/**
* A bhnd(4) service registry entry.
*/
struct bhnd_service_entry {
device_t provider; /**< service provider */
bhnd_service_t service; /**< service implemented */
uint32_t flags; /**< entry flags (see BHND_SPF_*) */
volatile u_int refs; /**< reference count; updated atomically
with only a shared lock held */
STAILQ_ENTRY(bhnd_service_entry) link;
};
#endif /* _BHND_BHND_PRIVATE_H_ */

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -32,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/refcount.h>
#include <sys/systm.h>
#include <machine/bus.h>
@ -51,6 +56,14 @@ __FBSDID("$FreeBSD$");
#include "bhndreg.h"
#include "bhndvar.h"
#include "bhnd_private.h"
static void bhnd_service_registry_free_entry(
struct bhnd_service_entry *entry);
static int compare_ascending_probe_order(const void *lhs, const void *rhs);
static int compare_descending_probe_order(const void *lhs,
const void *rhs);
/* BHND core device description table. */
static const struct bhnd_core_desc {
@ -331,9 +344,9 @@ bhnd_get_core_info(device_t dev) {
}
/**
* Find a @p class child device with @p unit on @p dev.
* Find a @p class child device with @p unit on @p bus.
*
* @param parent The bhnd-compatible bus to be searched.
* @param bus The bhnd-compatible bus to be searched.
* @param class The device class to match on.
* @param unit The core unit number; specify -1 to return the first match
* regardless of unit number.
@ -342,7 +355,7 @@ bhnd_get_core_info(device_t dev) {
* @retval NULL if no matching child device is found.
*/
device_t
bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit)
bhnd_bus_find_child(device_t bus, bhnd_devclass_t class, int unit)
{
struct bhnd_core_match md = {
BHND_MATCH_CORE_CLASS(class),
@ -352,27 +365,27 @@ bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit)
if (unit == -1)
md.m.match.core_unit = 0;
return bhnd_match_child(dev, &md);
return bhnd_bus_match_child(bus, &md);
}
/**
* Find the first child device on @p dev that matches @p desc.
* Find the first child device on @p bus that matches @p desc.
*
* @param parent The bhnd-compatible bus to be searched.
* @param bus The bhnd-compatible bus to be searched.
* @param desc A match descriptor.
*
* @retval device_t if a matching child device is found.
* @retval NULL if no matching child device is found.
*/
device_t
bhnd_match_child(device_t dev, const struct bhnd_core_match *desc)
bhnd_bus_match_child(device_t bus, const struct bhnd_core_match *desc)
{
device_t *devlistp;
device_t match;
int devcnt;
int error;
error = device_get_children(dev, &devlistp, &devcnt);
error = device_get_children(bus, &devlistp, &devcnt);
if (error != 0)
return (NULL);
@ -391,6 +404,146 @@ bhnd_match_child(device_t dev, const struct bhnd_core_match *desc)
return match;
}
/**
* Retrieve an ordered list of all device instances currently connected to
* @p bus, returning a pointer to the array in @p devlistp and the count
* in @p ndevs.
*
* The memory allocated for the table must be freed via
* bhnd_bus_free_children().
*
* @param bus The bhnd-compatible bus to be queried.
* @param[out] devlist The array of devices.
* @param[out] devcount The number of devices in @p devlistp
* @param order The order in which devices will be returned
* in @p devlist.
*
* @retval 0 success
* @retval non-zero if an error occurs, a regular unix error code will
* be returned.
*/
int
bhnd_bus_get_children(device_t bus, device_t **devlist, int *devcount,
bhnd_device_order order)
{
int error;
/* Fetch device array */
if ((error = device_get_children(bus, devlist, devcount)))
return (error);
/* Perform requested sorting */
if ((error = bhnd_sort_devices(*devlist, *devcount, order))) {
bhnd_bus_free_children(*devlist);
return (error);
}
return (0);
}
/**
* Free any memory allocated in a previous call to bhnd_bus_get_children().
*
* @param devlist The device array returned by bhnd_bus_get_children().
*/
void
bhnd_bus_free_children(device_t *devlist)
{
free(devlist, M_TEMP);
}
/**
* Perform in-place sorting of an array of bhnd device instances.
*
* @param devlist An array of bhnd devices.
* @param devcount The number of devices in @p devs.
* @param order The sort order to be used.
*/
int
bhnd_sort_devices(device_t *devlist, size_t devcount, bhnd_device_order order)
{
int (*compare)(const void *, const void *);
switch (order) {
case BHND_DEVICE_ORDER_ATTACH:
compare = compare_ascending_probe_order;
break;
case BHND_DEVICE_ORDER_DETACH:
compare = compare_descending_probe_order;
break;
default:
printf("unknown sort order: %d\n", order);
return (EINVAL);
}
qsort(devlist, devcount, sizeof(*devlist), compare);
return (0);
}
/*
* Ascending comparison of bhnd device's probe order.
*/
static int
compare_ascending_probe_order(const void *lhs, const void *rhs)
{
device_t ldev, rdev;
int lorder, rorder;
ldev = (*(const device_t *) lhs);
rdev = (*(const device_t *) rhs);
lorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(ldev), ldev);
rorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(rdev), rdev);
if (lorder < rorder) {
return (-1);
} else if (lorder > rorder) {
return (1);
} else {
return (0);
}
}
/*
* Descending comparison of bhnd device's probe order.
*/
static int
compare_descending_probe_order(const void *lhs, const void *rhs)
{
return (compare_ascending_probe_order(rhs, lhs));
}
/**
* Call device_probe_and_attach() for each of the bhnd bus device's
* children, in bhnd attach order.
*
* @param bus The bhnd-compatible bus for which all children should be probed
* and attached.
*/
int
bhnd_bus_probe_children(device_t bus)
{
device_t *devs;
int ndevs;
int error;
/* Fetch children in attach order */
error = bhnd_bus_get_children(bus, &devs, &ndevs,
BHND_DEVICE_ORDER_ATTACH);
if (error)
return (error);
/* Probe and attach all children */
for (int i = 0; i < ndevs; i++) {
device_t child = devs[i];
device_probe_and_attach(child);
}
bhnd_bus_free_children(devs);
return (0);
}
/**
* Walk up the bhnd device hierarchy to locate the root device
* to which the bhndb bridge is attached.
@ -727,7 +880,7 @@ bhnd_device_lookup(device_t dev, const struct bhnd_device *table,
uint32_t dflags;
parent = device_get_parent(dev);
hostb = bhnd_find_hostb_device(parent);
hostb = bhnd_bus_find_hostb_device(parent);
attach_type = bhnd_get_attach_type(dev);
for (entry = table; !BHND_DEVICE_IS_END(entry); entry =
@ -1344,6 +1497,277 @@ bhnd_nvram_getvar_array(device_t dev, const char *name, void *buf, size_t size,
return (0);
}
/**
* Initialize a service provider registry.
*
* @param bsr The service registry to initialize.
*
* @retval 0 success
* @retval non-zero if an error occurs initializing the service registry,
* a regular unix error code will be returned.
*/
int
bhnd_service_registry_init(struct bhnd_service_registry *bsr)
{
STAILQ_INIT(&bsr->entries);
mtx_init(&bsr->lock, "bhnd_service_registry lock", NULL, MTX_DEF);
return (0);
}
/**
* Release all resources held by @p bsr.
*
* @param bsr A service registry instance previously successfully
* initialized via bhnd_service_registry_init().
*
* @retval 0 success
* @retval EBUSY if active references to service providers registered
* with @p bsr exist.
*/
int
bhnd_service_registry_fini(struct bhnd_service_registry *bsr)
{
struct bhnd_service_entry *entry, *enext;
/* Remove everthing we can */
mtx_lock(&bsr->lock);
STAILQ_FOREACH_SAFE(entry, &bsr->entries, link, enext) {
if (entry->refs > 0)
continue;
STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, link);
free(entry, M_BHND);
}
if (!STAILQ_EMPTY(&bsr->entries)) {
mtx_unlock(&bsr->lock);
return (EBUSY);
}
mtx_unlock(&bsr->lock);
mtx_destroy(&bsr->lock);
return (0);
}
/**
* Register a @p provider for the given @p service.
*
* @param bsr Service registry to be modified.
* @param provider Service provider to register.
* @param service Service for which @p provider will be registered.
* @param flags Service provider flags (see BHND_SPF_*).
*
* @retval 0 success
* @retval EEXIST if an entry for @p service already exists.
* @retval EINVAL if @p service is BHND_SERVICE_ANY.
* @retval non-zero if registering @p provider otherwise fails, a regular
* unix error code will be returned.
*/
int
bhnd_service_registry_add(struct bhnd_service_registry *bsr, device_t provider,
bhnd_service_t service, uint32_t flags)
{
struct bhnd_service_entry *entry;
if (service == BHND_SERVICE_ANY)
return (EINVAL);
mtx_lock(&bsr->lock);
/* Is a service provider already registered? */
STAILQ_FOREACH(entry, &bsr->entries, link) {
if (entry->service == service) {
mtx_unlock(&bsr->lock);
return (EEXIST);
}
}
/* Initialize and insert our new entry */
entry = malloc(sizeof(*entry), M_BHND, M_NOWAIT);
if (entry == NULL) {
mtx_unlock(&bsr->lock);
return (ENOMEM);
}
entry->provider = provider;
entry->service = service;
entry->flags = flags;
refcount_init(&entry->refs, 0);
STAILQ_INSERT_HEAD(&bsr->entries, entry, link);
mtx_unlock(&bsr->lock);
return (0);
}
/**
* Free an unreferenced registry entry.
*
* @param entry The entry to be deallocated.
*/
static void
bhnd_service_registry_free_entry(struct bhnd_service_entry *entry)
{
KASSERT(entry->refs == 0, ("provider has active references"));
free(entry, M_BHND);
}
/**
* Attempt to remove the @p service provider registration for @p provider.
*
* @param bsr The service registry to be modified.
* @param provider The service provider to be deregistered.
* @param service The service for which @p provider will be deregistered,
* or BHND_SERVICE_ANY to remove all service
* registrations for @p provider.
*
* @retval 0 success
* @retval EBUSY if active references to @p provider exist; @see
* bhnd_service_registry_retain() and
* bhnd_service_registry_release().
*/
int
bhnd_service_registry_remove(struct bhnd_service_registry *bsr,
device_t provider, bhnd_service_t service)
{
struct bhnd_service_entry *entry, *enext;
mtx_lock(&bsr->lock);
#define BHND_PROV_MATCH(_e) \
((_e)->provider == provider && \
(service == BHND_SERVICE_ANY || (_e)->service == service))
/* Validate matching provider entries before making any
* modifications */
STAILQ_FOREACH(entry, &bsr->entries, link) {
/* Skip non-matching entries */
if (!BHND_PROV_MATCH(entry))
continue;
/* Entry is in use? */
if (entry->refs > 0) {
mtx_unlock(&bsr->lock);
return (EBUSY);
}
}
/* We can now safely remove matching entries */
STAILQ_FOREACH_SAFE(entry, &bsr->entries, link, enext) {
/* Skip non-matching entries */
if (!BHND_PROV_MATCH(entry))
continue;
/* Remove from list */
STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, link);
/* Free provider entry */
bhnd_service_registry_free_entry(entry);
}
#undef BHND_PROV_MATCH
mtx_unlock(&bsr->lock);
return (0);
}
/**
* Retain and return a reference to a registered @p service provider, if any.
*
* @param bsr The service registry to be queried.
* @param service The service for which a provider should be returned.
*
* On success, the caller assumes ownership the returned provider, and
* is responsible for releasing this reference via
* bhnd_service_registry_release().
*
* @retval device_t success
* @retval NULL if no provider is registered for @p service.
*/
device_t
bhnd_service_registry_retain(struct bhnd_service_registry *bsr,
bhnd_service_t service)
{
struct bhnd_service_entry *entry;
mtx_lock(&bsr->lock);
STAILQ_FOREACH(entry, &bsr->entries, link) {
if (entry->service != service)
continue;
/* With a live refcount, entry is gauranteed to remain alive
* after we release our lock */
refcount_acquire(&entry->refs);
mtx_unlock(&bsr->lock);
return (entry->provider);
}
mtx_unlock(&bsr->lock);
/* Not found */
return (NULL);
}
/**
* Release a reference to a service provider previously returned by
* bhnd_service_registry_retain().
*
* If this is the last reference to an inherited service provider registration
* (@see BHND_SPF_INHERITED), the registration will also be removed, and
* true will be returned.
*
* @param bsr The service registry from which @p provider
* was returned.
* @param provider The provider to be released.
* @param service The service for which @p provider was previously
* retained.
* @retval true The inherited service provider registration was removed;
* the caller should release its own reference to the
* provider.
* @retval false The service provider was not inherited, or active
* references to the provider remain.
*/
bool
bhnd_service_registry_release(struct bhnd_service_registry *bsr,
device_t provider, bhnd_service_t service)
{
struct bhnd_service_entry *entry;
/* Exclusive lock, as we need to prevent any new references to the
* entry from being taken if it's to be removed */
mtx_lock(&bsr->lock);
STAILQ_FOREACH(entry, &bsr->entries, link) {
bool removed;
if (entry->provider != provider)
continue;
if (entry->service != service)
continue;
if (refcount_release(&entry->refs) &&
(entry->flags & BHND_SPF_INHERITED))
{
/* If an inherited entry is no longer actively
* referenced, remove the local registration and inform
* the caller. */
STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry,
link);
bhnd_service_registry_free_entry(entry);
removed = true;
} else {
removed = false;
}
mtx_unlock(&bsr->lock);
return (removed);
}
/* Caller owns a reference, but no such provider is registered? */
panic("invalid service provider reference");
}
/**
* Using the bhnd(4) bus-level core information and a custom core name,
* populate @p dev's device description.
@ -1427,6 +1851,222 @@ bhnd_set_default_bus_desc(device_t dev, const struct bhnd_chipid *chip_id)
}
/**
* Helper function for implementing BHND_BUS_REGISTER_PROVIDER().
*
* This implementation delegates the request to the BHND_BUS_REGISTER_PROVIDER()
* method on the parent of @p dev. If no parent exists, the implementation
* will return an error.
*/
int
bhnd_bus_generic_register_provider(device_t dev, device_t child,
device_t provider, bhnd_service_t service)
{
device_t parent = device_get_parent(dev);
if (parent != NULL) {
return (BHND_BUS_REGISTER_PROVIDER(parent, child,
provider, service));
}
return (ENXIO);
}
/**
* Helper function for implementing BHND_BUS_DEREGISTER_PROVIDER().
*
* This implementation delegates the request to the
* BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent
* exists, the implementation will panic.
*/
int
bhnd_bus_generic_deregister_provider(device_t dev, device_t child,
device_t provider, bhnd_service_t service)
{
device_t parent = device_get_parent(dev);
if (parent != NULL) {
return (BHND_BUS_DEREGISTER_PROVIDER(parent, child,
provider, service));
}
panic("missing BHND_BUS_DEREGISTER_PROVIDER()");
}
/**
* Helper function for implementing BHND_BUS_RETAIN_PROVIDER().
*
* This implementation delegates the request to the
* BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent
* exists, the implementation will return NULL.
*/
device_t
bhnd_bus_generic_retain_provider(device_t dev, device_t child,
bhnd_service_t service)
{
device_t parent = device_get_parent(dev);
if (parent != NULL) {
return (BHND_BUS_RETAIN_PROVIDER(parent, child,
service));
}
return (NULL);
}
/**
* Helper function for implementing BHND_BUS_RELEASE_PROVIDER().
*
* This implementation delegates the request to the
* BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent
* exists, the implementation will panic.
*/
void
bhnd_bus_generic_release_provider(device_t dev, device_t child,
device_t provider, bhnd_service_t service)
{
device_t parent = device_get_parent(dev);
if (parent != NULL) {
return (BHND_BUS_RELEASE_PROVIDER(parent, child,
provider, service));
}
panic("missing BHND_BUS_RELEASE_PROVIDER()");
}
/**
* Helper function for implementing BHND_BUS_REGISTER_PROVIDER().
*
* This implementation uses the bhnd_service_registry_add() function to
* do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
* a suitable service registry to edit.
*/
int
bhnd_bus_generic_sr_register_provider(device_t dev, device_t child,
device_t provider, bhnd_service_t service)
{
struct bhnd_service_registry *bsr;
bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
KASSERT(bsr != NULL, ("NULL service registry"));
return (bhnd_service_registry_add(bsr, provider, service, 0));
}
/**
* Helper function for implementing BHND_BUS_DEREGISTER_PROVIDER().
*
* This implementation uses the bhnd_service_registry_remove() function to
* do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
* a suitable service registry to edit.
*/
int
bhnd_bus_generic_sr_deregister_provider(device_t dev, device_t child,
device_t provider, bhnd_service_t service)
{
struct bhnd_service_registry *bsr;
bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
KASSERT(bsr != NULL, ("NULL service registry"));
return (bhnd_service_registry_remove(bsr, provider, service));
}
/**
* Helper function for implementing BHND_BUS_RETAIN_PROVIDER().
*
* This implementation uses the bhnd_service_registry_retain() function to
* do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
* a suitable service registry.
*
* If a local provider for the service is not available, and a parent device is
* available, this implementation will attempt to fetch and locally register
* a service provider reference from the parent of @p dev.
*/
device_t
bhnd_bus_generic_sr_retain_provider(device_t dev, device_t child,
bhnd_service_t service)
{
struct bhnd_service_registry *bsr;
device_t parent, provider;
int error;
bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
KASSERT(bsr != NULL, ("NULL service registry"));
/*
* Attempt to fetch a service provider reference from either the local
* service registry, or if not found, from our parent.
*
* If we fetch a provider from our parent, we register the provider
* with the local service registry to prevent conflicting local
* registrations from being added.
*/
while (1) {
/* Check the local service registry first */
provider = bhnd_service_registry_retain(bsr, service);
if (provider != NULL)
return (provider);
/* Otherwise, try to delegate to our parent (if any) */
if ((parent = device_get_parent(dev)) == NULL)
return (NULL);
provider = BHND_BUS_RETAIN_PROVIDER(parent, dev, service);
if (provider == NULL)
return (NULL);
/* Register the inherited service registration with the local
* registry */
error = bhnd_service_registry_add(bsr, provider, service,
BHND_SPF_INHERITED);
if (error) {
BHND_BUS_RELEASE_PROVIDER(parent, dev, provider,
service);
if (error == EEXIST) {
/* A valid service provider was registered
* concurrently; retry fetching from the local
* registry */
continue;
}
device_printf(dev, "failed to register service "
"provider: %d\n", error);
return (NULL);
}
}
}
/**
* Helper function for implementing BHND_BUS_RELEASE_PROVIDER().
*
* This implementation uses the bhnd_service_registry_release() function to
* do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
* a suitable service registry.
*/
void
bhnd_bus_generic_sr_release_provider(device_t dev, device_t child,
device_t provider, bhnd_service_t service)
{
struct bhnd_service_registry *bsr;
bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
KASSERT(bsr != NULL, ("NULL service registry"));
/* Release the provider reference; if the refcount hits zero on an
* inherited reference, true will be returned, and we need to drop
* our own bus reference to the provider */
if (!bhnd_service_registry_release(bsr, provider, service))
return;
/* Drop our reference to the borrowed provider */
BHND_BUS_RELEASE_PROVIDER(device_get_parent(dev), dev, provider,
service);
}
/**
* Helper function for implementing BHND_BUS_IS_HW_DISABLED().
*

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -65,6 +69,14 @@ typedef enum {
BHND_DEVCLASS_INVALID /**< no/invalid class */
} bhnd_devclass_t;
/** bhnd(4) platform services. */
typedef enum {
BHND_SERVICE_CHIPC, /**< chipcommon service; implements the bhnd_chipc interface */
BHND_SERVICE_PMU, /**< pmu service; implements the bhnd_pmu interface */
BHND_SERVICE_NVRAM, /**< nvram service; implements the bhnd_nvram interface */
BHND_SERVICE_ANY = 1000, /**< match on any service type */
} bhnd_service_t;
/**
* bhnd(4) port types.

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -90,7 +94,7 @@ bhnd_bhndb_find_hostb_device(device_t dev)
/* Find the corresponding bus device */
md = bhnd_core_get_match_desc(&core);
return (bhnd_match_child(dev, &md));
return (bhnd_bus_match_child(dev, &md));
}
static int

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -522,14 +526,16 @@ bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
sc->parent_dev = device_get_parent(dev);
sc->bridge_class = bridge_devclass;
if ((error = bhnd_service_registry_init(&sc->services)))
return (error);
BHNDB_LOCK_INIT(sc);
/* Populate generic resource allocation state. */
cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev);
sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg);
if (sc->bus_res == NULL) {
return (ENXIO);
}
if (sc->bus_res == NULL)
goto failed;
/* Allocate our host resources */
if ((error = bhndb_alloc_host_resources(sc->bus_res)))
@ -573,6 +579,8 @@ bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
if (sc->bus_res != NULL)
bhndb_free_resources(sc->bus_res);
bhnd_service_registry_fini(&sc->services);
return (error);
}
@ -911,11 +919,15 @@ bhndb_generic_detach(device_t dev)
int error;
sc = device_get_softc(dev);
/* Detach children */
if ((error = bus_generic_detach(dev)))
return (error);
/* Clean up our service registry */
if ((error = bhnd_service_registry_fini(&sc->services)))
return (error);
/* Clean up our driver state. */
bhndb_free_resources(sc->bus_res);
@ -1211,6 +1223,17 @@ bhndb_get_hostb_core(device_t dev, device_t child, struct bhnd_core_info *core)
return (0);
}
/**
* Default bhndb(4) implementation of BHND_BUS_GET_SERVICE_REGISTRY().
*/
static struct bhnd_service_registry *
bhndb_get_service_registry(device_t dev, device_t child)
{
struct bhndb_softc *sc = device_get_softc(dev);
return (&sc->services);
}
/**
* Default bhndb(4) implementation of BUS_ALLOC_RESOURCE().
*/
@ -2146,6 +2169,13 @@ static device_method_t bhndb_methods[] = {
DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource),
DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource),
DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var),
DEVMETHOD(bhnd_bus_get_service_registry,bhndb_get_service_registry),
DEVMETHOD(bhnd_bus_register_provider, bhnd_bus_generic_sr_register_provider),
DEVMETHOD(bhnd_bus_deregister_provider, bhnd_bus_generic_sr_deregister_provider),
DEVMETHOD(bhnd_bus_retain_provider, bhnd_bus_generic_sr_retain_provider),
DEVMETHOD(bhnd_bus_release_provider, bhnd_bus_generic_sr_release_provider),
DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1),
DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2),
DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4),

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -96,6 +100,8 @@ struct bhndb_softc {
device_t parent_dev; /**< parent device */
device_t bus_dev; /**< child bhnd(4) bus */
struct bhnd_service_registry services; /**< local service registry */
struct mtx sc_mtx; /**< resource lock. */
struct bhndb_resources *bus_res; /**< bus resource state */
};

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -34,7 +38,10 @@
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/sx.h>
#include "bhnd.h"
@ -45,57 +52,51 @@
MALLOC_DECLARE(M_BHND);
DECLARE_CLASS(bhnd_driver);
int bhnd_generic_attach(device_t dev);
int bhnd_generic_detach(device_t dev);
int bhnd_generic_shutdown(device_t dev);
int bhnd_generic_resume(device_t dev);
int bhnd_generic_suspend(device_t dev);
int bhnd_generic_attach(device_t dev);
int bhnd_generic_detach(device_t dev);
int bhnd_generic_shutdown(device_t dev);
int bhnd_generic_resume(device_t dev);
int bhnd_generic_suspend(device_t dev);
int bhnd_generic_get_probe_order(device_t dev,
device_t child);
int bhnd_generic_get_probe_order(device_t dev,
device_t child);
int bhnd_generic_alloc_pmu(device_t dev,
device_t child);
int bhnd_generic_release_pmu(device_t dev,
device_t child);
int bhnd_generic_request_clock(device_t dev,
device_t child, bhnd_clock clock);
int bhnd_generic_enable_clocks(device_t dev,
device_t child, uint32_t clocks);
int bhnd_generic_request_ext_rsrc(device_t dev,
device_t child, u_int rsrc);
int bhnd_generic_release_ext_rsrc(device_t dev,
device_t child, u_int rsrc);
int bhnd_generic_alloc_pmu(device_t dev,
device_t child);
int bhnd_generic_release_pmu(device_t dev,
device_t child);
int bhnd_generic_request_clock(device_t dev,
device_t child, bhnd_clock clock);
int bhnd_generic_enable_clocks(device_t dev,
device_t child, uint32_t clocks);
int bhnd_generic_request_ext_rsrc(device_t dev,
device_t child, u_int rsrc);
int bhnd_generic_release_ext_rsrc(device_t dev,
device_t child, u_int rsrc);
int bhnd_generic_print_child(device_t dev,
device_t child);
void bhnd_generic_probe_nomatch(device_t dev,
device_t child);
int bhnd_generic_print_child(device_t dev,
device_t child);
void bhnd_generic_probe_nomatch(device_t dev,
device_t child);
void bhnd_generic_child_deleted(device_t dev,
device_t child);
int bhnd_generic_suspend_child(device_t dev,
device_t child);
int bhnd_generic_resume_child(device_t dev,
device_t child);
void bhnd_generic_child_deleted(device_t dev,
device_t child);
int bhnd_generic_suspend_child(device_t dev,
device_t child);
int bhnd_generic_resume_child(device_t dev,
device_t child);
int bhnd_generic_get_nvram_var(device_t dev,
device_t child, const char *name, void *buf,
size_t *size, bhnd_nvram_type type);
int bhnd_generic_get_nvram_var(device_t dev,
device_t child, const char *name,
void *buf, size_t *size,
bhnd_nvram_type type);
/**
* bhnd driver instance state. Must be first member of all subclass
* softc structures.
*/
struct bhnd_softc {
device_t dev; /**< bus device */
bool attach_done; /**< true if initialization of
* all platform devices has
* been completed */
device_t chipc_dev; /**< bhnd_chipc device */
device_t nvram_dev; /**< bhnd_nvram device, if any */
device_t pmu_dev; /**< bhnd_pmu device, if any */
device_t dev; /**< bus device */
};
#endif /* _BHND_BHNDVAR_H_ */

View File

@ -1,8 +1,12 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -211,6 +215,10 @@ chipc_attach(device_t dev)
if ((error = bus_generic_attach(dev)))
goto failed;
/* Register ourselves with the bus */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_CHIPC)))
goto failed;
return (0);
failed:
@ -234,6 +242,9 @@ chipc_detach(device_t dev)
sc = device_get_softc(dev);
if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
return (error);
if ((error = bus_generic_detach(dev)))
return (error);
@ -1087,7 +1098,7 @@ chipc_should_enable_muxed_sprom(struct chipc_softc *sc)
mtx_lock(&Giant); /* for newbus */
parent = device_get_parent(sc->dev);
hostb = bhnd_find_hostb_device(parent);
hostb = bhnd_bus_find_hostb_device(parent);
if ((error = device_get_children(parent, &devs, &devcount))) {
mtx_unlock(&Giant);

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2010, Broadcom Corporation.
* Copyright (c) 2010 Broadcom Corporation.
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Landon Fuller under sponsorship from
* the FreeBSD Foundation.
*
* This file is derived from the siutils.c source distributed with the
* Asus RT-N16 firmware source code release.
@ -116,6 +120,7 @@ bhnd_pwrctl_attach(device_t dev)
struct chipc_softc *chipc_sc;
bhnd_devclass_t hostb_class;
device_t hostb_dev;
device_t bus;
int error;
sc = device_get_softc(dev);
@ -128,10 +133,12 @@ bhnd_pwrctl_attach(device_t dev)
sc->quirks = bhnd_device_quirks(sc->chipc_dev, pwrctl_devices,
sizeof(pwrctl_devices[0]));
bus = device_get_parent(sc->chipc_dev);
/* On devices that lack a slow clock source, HT must always be
* enabled. */
hostb_class = BHND_DEVCLASS_INVALID;
hostb_dev = bhnd_find_hostb_device(device_get_parent(sc->chipc_dev));
hostb_dev = bhnd_bus_find_hostb_device(device_get_parent(sc->chipc_dev));
if (hostb_dev != NULL)
hostb_class = bhnd_get_class(hostb_dev);
@ -177,6 +184,13 @@ bhnd_pwrctl_attach(device_t dev)
PWRCTL_UNLOCK(sc);
/* Register as the bus PMU provider */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_PMU))) {
device_printf(sc->dev, "failed to register PMU with bus : %d\n",
error);
goto cleanup;
}
return (0);
cleanup:
@ -193,9 +207,16 @@ bhnd_pwrctl_detach(device_t dev)
sc = device_get_softc(dev);
if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
return (error);
PWRCTL_LOCK(sc);
if ((error = bhnd_pwrctl_setclk(sc, BHND_CLOCK_DYN)))
return (error);
PWRCTL_UNLOCK(sc);
STAILQ_FOREACH_SAFE(clkres, &sc->clkres_list, cr_link, crnext)
free(clkres, M_DEVBUF);

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -561,7 +565,7 @@ bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state)
bus_size_t reg;
bhnd = device_get_parent(sc->dev);
chipc = bhnd_find_child(bhnd, BHND_DEVCLASS_CC, 0);
chipc = bhnd_bus_find_child(bhnd, BHND_DEVCLASS_CC, 0);
KASSERT(chipc != NULL, ("missing chipcommon device"));
/* Write SerDes PLL disable flag to the ChipCommon core */

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -128,7 +132,7 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
/* Fetch capability flags */
sc->caps = bhnd_bus_read_4(sc->res, BHND_PMU_CAP);
/* Find the bus-attached core */
/* Find the bus and bus-attached core */
bhnd_class = devclass_find("bhnd");
core = sc->dev;
while ((bus = device_get_parent(core)) != NULL) {
@ -153,7 +157,7 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
}
/* Locate ChipCommon device */
sc->chipc_dev = bhnd_find_child(bus, BHND_DEVCLASS_CC, 0);
sc->chipc_dev = bhnd_bus_find_child(bus, BHND_DEVCLASS_CC, 0);
if (sc->chipc_dev == NULL) {
device_printf(sc->dev, "chipcommon device not found\n");
return (ENXIO);
@ -186,6 +190,13 @@ bhnd_pmu_attach(device_t dev, struct bhnd_resource *res)
goto failed;
}
/* Register ourselves with the bus */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_PMU))) {
device_printf(sc->dev, "failed to register PMU with bus : %d\n",
error);
goto failed;
}
/* Set up sysctl nodes */
ctx = device_get_sysctl_ctx(dev);
tree = device_get_sysctl_tree(dev);
@ -217,9 +228,13 @@ int
bhnd_pmu_detach(device_t dev)
{
struct bhnd_pmu_softc *sc;
int error;
sc = device_get_softc(dev);
if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
return (error);
BPMU_LOCK_DESTROY(sc);
bhnd_pmu_query_fini(&sc->query);

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -99,8 +103,10 @@ bhnd_sprom_attach(device_t dev, bus_size_t offset)
sc = device_get_softc(dev);
sc->dev = dev;
sc->store = NULL;
io = NULL;
r = NULL;
/* Allocate SPROM resource */
rid = 0;
@ -138,6 +144,16 @@ bhnd_sprom_attach(device_t dev, bus_size_t offset)
bhnd_nvram_io_free(io);
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
io = NULL;
r = NULL;
/* Register ourselves with the bus */
if ((error = bhnd_register_provider(dev, BHND_SERVICE_NVRAM))) {
device_printf(dev, "failed to register NVRAM provider: %d\n",
error);
goto failed;
}
return (0);
failed:
@ -145,7 +161,11 @@ bhnd_sprom_attach(device_t dev, bus_size_t offset)
if (io != NULL)
bhnd_nvram_io_free(io);
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
if (r != NULL)
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
if (sc->store != NULL)
bhnd_nvram_store_free(sc->store);
return (error);
}
@ -175,9 +195,13 @@ int
bhnd_sprom_detach(device_t dev)
{
struct bhnd_sprom_softc *sc;
int error;
sc = device_get_softc(dev);
if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
return (error);
bhnd_nvram_store_free(sc->store);
return (0);

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -222,14 +226,14 @@ siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_softc *sc)
uint32_t imcfg;
/* Only applies when bridged by PCIe */
if ((hostb_dev = bhnd_find_hostb_device(sc->dev)) == NULL)
if ((hostb_dev = bhnd_bus_find_hostb_device(sc->dev)) == NULL)
return (ENXIO);
if (bhnd_get_class(hostb_dev) != BHND_DEVCLASS_PCIE)
return (0);
/* Only applies if there's a D11 core */
d11 = bhnd_match_child(sc->dev, &(struct bhnd_core_match) {
d11 = bhnd_bus_match_child(sc->dev, &(struct bhnd_core_match) {
BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11),
BHND_MATCH_CORE_UNIT(0)
});
@ -259,7 +263,7 @@ siba_bhndb_wars_hwup(struct siba_softc *sc)
uint32_t quirks;
int error;
if ((hostb_dev = bhnd_find_hostb_device(sc->dev)) == NULL)
if ((hostb_dev = bhnd_bus_find_hostb_device(sc->dev)) == NULL)
return (ENXIO);
quirks = bhnd_device_quirks(hostb_dev, bridge_devs,

View File

@ -2,9 +2,12 @@
* Copyright (c) 2007 Bruce M. Simpson.
* Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
*
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -393,6 +396,12 @@ bcm_init_platform_data(struct bcm_platform *bp)
}
}
/* Initialize our platform service registry */
if ((error = bhnd_service_registry_init(&bp->services))) {
BCM_ERR("error initializing service registry: %d\n", error);
return (error);
}
bcm_platform_data_avail = true;
return (0);
}

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -45,33 +49,35 @@
extern const struct bhnd_pmu_io bcm_pmu_soc_io;
struct bcm_platform {
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 */
uint32_t cc_caps_ext; /**< chipc extended capabilies */
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 */
uint32_t cc_caps_ext; /**< chipc extended capabilies */
/* On non-AOB devices, the PMU register block is mapped to chipc;
* the pmu_id and pmu_addr values will be copied from cc_id
* and cc_addr. */
struct bhnd_core_info pmu_id; /**< PMU core info */
uintptr_t pmu_addr; /**< PMU core phys address, or
struct bhnd_core_info pmu_id; /**< PMU core info */
uintptr_t pmu_addr; /**< PMU core phys address, or
0x0 if no PMU */
struct bhnd_pmu_query pmu; /**< PMU query instance */
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 */
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;
struct bhnd_nvram_io *nvram_io; /**< NVRAM I/O context, or NULL if unavailable */
bhnd_nvram_data_class *nvram_cls; /**< NVRAM data class, or NULL if unavailable */
struct bhnd_nvram_io *nvram_io; /**< NVRAM I/O context, or NULL if unavailable */
bhnd_nvram_data_class *nvram_cls; /**< NVRAM data class, or NULL if unavailable */
struct bhnd_service_registry services; /**< platform service providers */
#ifdef CFE
int cfe_console; /**< Console handle, or -1 */
int cfe_console; /**< Console handle, or -1 */
#endif
};

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -120,6 +124,13 @@ bhnd_nvram_cfe_attach(device_t dev)
if (error)
return (error);
error = bhnd_service_registry_add(&bp->services, dev,
BHND_SERVICE_NVRAM, 0);
if (error) {
bhnd_nvram_store_free(sc->store);
return (error);
}
return (error);
}
@ -138,10 +149,18 @@ bhnd_nvram_cfe_suspend(device_t dev)
static int
bhnd_nvram_cfe_detach(device_t dev)
{
struct bhnd_nvram_cfe_softc *sc;
struct bcm_platform *bp;
struct bhnd_nvram_cfe_softc *sc;
int error;
bp = bcm_get_platform();
sc = device_get_softc(dev);
error = bhnd_service_registry_remove(&bp->services, dev,
BHND_SERVICE_ANY);
if (error)
return (error);
bhnd_nvram_store_free(sc->store);
return (0);

View File

@ -1,7 +1,11 @@
/*-
* Copyright (c) 2015-2016 Landon Fuller <landon@freebsd.org>
* Copyright (c) 2017 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Landon Fuller
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -54,6 +58,17 @@ __FBSDID("$FreeBSD$");
#include "bhnd_nexusvar.h"
/**
* Default bhnd_nexus implementation of BHND_BUS_GET_SERVICE_REGISTRY().
*/
static struct bhnd_service_registry *
bhnd_nexus_get_service_registry(device_t dev, device_t child)
{
struct bcm_platform *bp = bcm_get_platform();
return (&bp->services);
}
/**
* Default bhnd_nexus implementation of BHND_BUS_ACTIVATE_RESOURCE().
*/
@ -160,6 +175,11 @@ bhnd_nexus_assign_intr(device_t dev, device_t child, int rid)
static device_method_t bhnd_nexus_methods[] = {
/* bhnd interface */
DEVMETHOD(bhnd_bus_get_service_registry,bhnd_nexus_get_service_registry),
DEVMETHOD(bhnd_bus_register_provider, bhnd_bus_generic_sr_register_provider),
DEVMETHOD(bhnd_bus_deregister_provider, bhnd_bus_generic_sr_deregister_provider),
DEVMETHOD(bhnd_bus_retain_provider, bhnd_bus_generic_sr_retain_provider),
DEVMETHOD(bhnd_bus_release_provider, bhnd_bus_generic_sr_release_provider),
DEVMETHOD(bhnd_bus_activate_resource, bhnd_nexus_activate_resource),
DEVMETHOD(bhnd_bus_deactivate_resource, bhnd_nexus_deactivate_resource),
DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_nexus_is_hw_disabled),