Index: head/sys/dev/bhnd/bhnd.c =================================================================== --- head/sys/dev/bhnd/bhnd.c (revision 300249) +++ head/sys/dev/bhnd/bhnd.c (revision 300250) @@ -1,667 +1,707 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __FBSDID("$FreeBSD$"); /* * Broadcom Home Networking Division (HND) Bus Driver. * * The Broadcom HND family of devices consists of both SoCs and host-connected * networking chipsets containing a common family of Broadcom IP cores, * including an integrated MIPS and/or ARM cores. * * HND devices expose a nearly identical interface whether accessible over a * native SoC interconnect, or when connected via a host interface such as * PCIe. As a result, the majority of hardware support code should be re-usable * across host drivers for HND networking chipsets, as well as FreeBSD support * for Broadcom MIPS/ARM HND SoCs. * * Earlier HND models used the siba(4) on-chip interconnect, while later models * use bcma(4); the programming model is almost entirely independent * of the actual underlying interconect. */ #include #include #include #include #include #include #include #include #include "bhnd.h" #include "bhndvar.h" MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures"); /** * bhnd_generic_probe_nomatch() reporting configuration. */ static const struct bhnd_nomatch { uint16_t vendor; /**< core designer */ uint16_t device; /**< core id */ bool if_verbose; /**< print when bootverbose is set. */ } bhnd_nomatch_table[] = { { BHND_MFGID_ARM, BHND_COREID_OOB_ROUTER, true }, { BHND_MFGID_ARM, BHND_COREID_EROM, true }, { BHND_MFGID_ARM, BHND_COREID_PL301, true }, { BHND_MFGID_ARM, BHND_COREID_APB_BRIDGE, true }, { BHND_MFGID_ARM, BHND_COREID_AXI_UNMAPPED, false }, { BHND_MFGID_INVALID, BHND_COREID_INVALID, false } }; 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(). * * This implementation calls device_probe_and_attach() for each of the device's * children, in bhnd probe order. */ int bhnd_generic_attach(device_t dev) { device_t *devs; int ndevs; int error; if (device_is_attached(dev)) return (EBUSY); if ((error = device_get_children(dev, &devs, &ndevs))) return (error); 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); } free(devs, M_TEMP); return (0); } /** * Default bhnd(4) bus driver implementation of DEVICE_DETACH(). * * This implementation calls device_detach() for each of the device's * children, in reverse bhnd probe order, terminating if any call to * device_detach() fails. */ int bhnd_generic_detach(device_t dev) { device_t *devs; int ndevs; int error; if (!device_is_attached(dev)) return (EBUSY); if ((error = device_get_children(dev, &devs, &ndevs))) return (error); /* Detach in the reverse of attach order */ qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order); for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; /* Terminate on first error */ if ((error = device_detach(child))) goto cleanup; } cleanup: free(devs, M_TEMP); return (error); } /** * Default bhnd(4) bus driver implementation of DEVICE_SHUTDOWN(). * * This implementation calls device_shutdown() for each of the device's * children, in reverse bhnd probe order, terminating if any call to * device_shutdown() fails. */ int bhnd_generic_shutdown(device_t dev) { device_t *devs; int ndevs; int error; if (!device_is_attached(dev)) return (EBUSY); if ((error = device_get_children(dev, &devs, &ndevs))) return (error); /* Shutdown in the reverse of attach order */ qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order); for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; /* Terminate on first error */ if ((error = device_shutdown(child))) goto cleanup; } cleanup: free(devs, M_TEMP); return (error); } /** * Default bhnd(4) bus driver implementation of DEVICE_RESUME(). * * This implementation calls BUS_RESUME_CHILD() for each of the device's * children in bhnd probe order, terminating if any call to BUS_RESUME_CHILD() * fails. */ int bhnd_generic_resume(device_t dev) { device_t *devs; int ndevs; int error; if (!device_is_attached(dev)) return (EBUSY); if ((error = device_get_children(dev, &devs, &ndevs))) return (error); qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order); for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; /* Terminate on first error */ if ((error = BUS_RESUME_CHILD(device_get_parent(child), child))) goto cleanup; } cleanup: free(devs, M_TEMP); return (error); } /** * Default bhnd(4) bus driver implementation of DEVICE_SUSPEND(). * * This implementation calls BUS_SUSPEND_CHILD() for each of the device's * children in reverse bhnd probe order. If any call to BUS_SUSPEND_CHILD() * fails, the suspend operation is terminated and any devices that were * suspended are resumed immediately by calling their BUS_RESUME_CHILD() * methods. */ int bhnd_generic_suspend(device_t dev) { device_t *devs; int ndevs; int error; if (!device_is_attached(dev)) return (EBUSY); if ((error = device_get_children(dev, &devs, &ndevs))) return (error); /* Suspend in the reverse of attach order */ qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order); for (int i = 0; i < ndevs; i++) { device_t child = devs[i]; error = BUS_SUSPEND_CHILD(device_get_parent(child), child); /* On error, resume suspended devices and then terminate */ if (error) { for (int j = 0; j < i; j++) { BUS_RESUME_CHILD(device_get_parent(devs[j]), devs[j]); } goto cleanup; } } cleanup: free(devs, M_TEMP); return (error); } /* * 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(). * * This implementation determines probe ordering based on the device's class * and other properties, including whether the device is serving as a host * bridge. */ int bhnd_generic_get_probe_order(device_t dev, device_t child) { switch (bhnd_get_class(child)) { case BHND_DEVCLASS_CC: /* Must be early enough to provide NVRAM access to the * host bridge */ return (BHND_PROBE_ROOT + BHND_PROBE_ORDER_FIRST); case BHND_DEVCLASS_CC_B: /* fall through */ case BHND_DEVCLASS_PMU: return (BHND_PROBE_BUS + BHND_PROBE_ORDER_EARLY); case BHND_DEVCLASS_SOC_ROUTER: return (BHND_PROBE_BUS + BHND_PROBE_ORDER_LATE); case BHND_DEVCLASS_SOC_BRIDGE: return (BHND_PROBE_BUS + BHND_PROBE_ORDER_LAST); case BHND_DEVCLASS_CPU: return (BHND_PROBE_CPU + BHND_PROBE_ORDER_FIRST); case BHND_DEVCLASS_RAM: /* fall through */ case BHND_DEVCLASS_MEMC: return (BHND_PROBE_CPU + BHND_PROBE_ORDER_EARLY); case BHND_DEVCLASS_NVRAM: return (BHND_PROBE_RESOURCE + BHND_PROBE_ORDER_EARLY); case BHND_DEVCLASS_PCI: case BHND_DEVCLASS_PCIE: case BHND_DEVCLASS_PCCARD: case BHND_DEVCLASS_ENET: case BHND_DEVCLASS_ENET_MAC: case BHND_DEVCLASS_ENET_PHY: case BHND_DEVCLASS_WLAN: case BHND_DEVCLASS_WLAN_MAC: case BHND_DEVCLASS_WLAN_PHY: case BHND_DEVCLASS_EROM: case BHND_DEVCLASS_OTHER: case BHND_DEVCLASS_INVALID: if (bhnd_find_hostb_device(dev) == child) return (BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY); return (BHND_PROBE_DEFAULT); default: return (BHND_PROBE_DEFAULT); } } /** * Default bhnd(4) bus driver implementation of BHND_BUS_IS_REGION_VALID(). * * This implementation assumes that port and region numbers are 0-indexed and * are allocated non-sparsely, using BHND_BUS_GET_PORT_COUNT() and * BHND_BUS_GET_REGION_COUNT() to determine if @p port and @p region fall * within the defined range. */ static bool bhnd_generic_is_region_valid(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region) { if (port >= bhnd_get_port_count(child, type)) return (false); if (region >= bhnd_get_region_count(child, type, port)) return (false); return (true); } /** * Default bhnd(4) bus driver implementation of BUS_PRINT_CHILD(). * * This implementation requests the device's struct resource_list via * BUS_GET_RESOURCE_LIST. */ int bhnd_generic_print_child(device_t dev, device_t child) { struct resource_list *rl; int retval = 0; retval += bus_print_child_header(dev, child); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); } retval += printf(" at core %u", bhnd_get_core_index(child)); retval += bus_print_child_domain(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } /** * Default bhnd(4) bus driver implementation of BUS_PROBE_NOMATCH(). * * This implementation requests the device's struct resource_list via * BUS_GET_RESOURCE_LIST. */ void bhnd_generic_probe_nomatch(device_t dev, device_t child) { struct resource_list *rl; const struct bhnd_nomatch *nm; bool report; /* Fetch reporting configuration for this device */ report = true; for (nm = bhnd_nomatch_table; nm->device != BHND_COREID_INVALID; nm++) { if (nm->vendor != bhnd_get_vendor(child)) continue; if (nm->device != bhnd_get_device(child)) continue; report = false; if (bootverbose && nm->if_verbose) report = true; break; } if (!report) return; /* Print the non-matched device info */ device_printf(dev, "<%s %s>", bhnd_get_vendor_name(child), bhnd_get_device_name(child)); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); printf(" at core %u (no driver attached)\n", bhnd_get_core_index(child)); } /** * Default implementation of BUS_CHILD_PNPINFO_STR(). */ static int bhnd_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) { if (device_get_parent(child) != dev) { return (BUS_CHILD_PNPINFO_STR(device_get_parent(dev), child, buf, buflen)); } snprintf(buf, buflen, "vendor=0x%hx device=0x%hx rev=0x%hhx", bhnd_get_vendor(child), bhnd_get_device(child), bhnd_get_hwrev(child)); return (0); } /** * Default implementation of BUS_CHILD_LOCATION_STR(). */ static int bhnd_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { bhnd_addr_t addr; bhnd_size_t size; if (device_get_parent(child) != dev) { return (BUS_CHILD_LOCATION_STR(device_get_parent(dev), child, buf, buflen)); } if (bhnd_get_region_addr(child, BHND_PORT_DEVICE, 0, 0, &addr, &size)) { /* No device default port/region */ if (buflen > 0) *buf = '\0'; return (0); } snprintf(buf, buflen, "port0.0=0x%llx", (unsigned long long) addr); return (0); } /** * Helper function for implementing BUS_SUSPEND_CHILD(). * * TODO: Power management * * If @p child is not a direct child of @p dev, suspension is delegated to * the @p dev parent. */ int bhnd_generic_suspend_child(device_t dev, device_t child) { if (device_get_parent(child) != dev) BUS_SUSPEND_CHILD(device_get_parent(dev), child); return bus_generic_suspend_child(dev, child); } /** * Helper function for implementing BUS_RESUME_CHILD(). * * TODO: Power management * * If @p child is not a direct child of @p dev, suspension is delegated to * the @p dev parent. */ int bhnd_generic_resume_child(device_t dev, device_t child) { if (device_get_parent(child) != dev) BUS_RESUME_CHILD(device_get_parent(dev), child); return bus_generic_resume_child(dev, child); } /* * Delegate all indirect I/O to the parent device. When inherited by * non-bridged bus implementations, resources will never be marked as - * indirect, and these methods should never be called. + * indirect, and these methods will never be called. */ -#define BHND_IO_READ(_type, _name, _method) \ -static _type \ -bhnd_read_ ## _name (device_t dev, device_t child, \ - struct bhnd_resource *r, bus_size_t offset) \ -{ \ - return (BHND_BUS_READ_ ## _method( \ - device_get_parent(dev), child, r, offset)); \ +#define BHND_IO_READ(_type, _name, _method) \ +static _type \ +bhnd_read_ ## _name (device_t dev, device_t child, \ + struct bhnd_resource *r, bus_size_t offset) \ +{ \ + return (BHND_BUS_READ_ ## _method( \ + device_get_parent(dev), child, r, offset)); \ } -#define BHND_IO_WRITE(_type, _name, _method) \ -static void \ -bhnd_write_ ## _name (device_t dev, device_t child, \ - struct bhnd_resource *r, bus_size_t offset, _type value) \ -{ \ - return (BHND_BUS_WRITE_ ## _method( \ - device_get_parent(dev), child, r, offset, \ +#define BHND_IO_WRITE(_type, _name, _method) \ +static void \ +bhnd_write_ ## _name (device_t dev, device_t child, \ + struct bhnd_resource *r, bus_size_t offset, _type value) \ +{ \ + return (BHND_BUS_WRITE_ ## _method( \ + device_get_parent(dev), child, r, offset, \ value)); \ } -#define BHND_IO_MULTI(_type, _rw, _name, _method) \ +#define BHND_IO_MISC(_type, _op, _method) \ static void \ -bhnd_ ## _rw ## _multi_ ## _name (device_t dev, device_t child, \ - struct bhnd_resource *r, bus_size_t offset, _type *datap, \ +bhnd_ ## _op (device_t dev, device_t child, \ + struct bhnd_resource *r, bus_size_t offset, _type datap, \ bus_size_t count) \ { \ BHND_BUS_ ## _method(device_get_parent(dev), child, r, \ offset, datap, count); \ -} +} #define BHND_IO_METHODS(_type, _size) \ BHND_IO_READ(_type, _size, _size) \ BHND_IO_WRITE(_type, _size, _size) \ \ BHND_IO_READ(_type, stream_ ## _size, STREAM_ ## _size) \ BHND_IO_WRITE(_type, stream_ ## _size, STREAM_ ## _size) \ \ - BHND_IO_MULTI(_type, read, _size, READ_MULTI_ ## _size) \ - BHND_IO_MULTI(_type, write, _size, WRITE_MULTI_ ## _size) \ + BHND_IO_MISC(_type*, read_multi_ ## _size, \ + READ_MULTI_ ## _size) \ + BHND_IO_MISC(_type*, write_multi_ ## _size, \ + WRITE_MULTI_ ## _size) \ \ - BHND_IO_MULTI(_type, read, stream_ ## _size, \ + BHND_IO_MISC(_type*, read_multi_stream_ ## _size, \ READ_MULTI_STREAM_ ## _size) \ - BHND_IO_MULTI(_type, write, stream_ ## _size, \ + BHND_IO_MISC(_type*, write_multi_stream_ ## _size, \ WRITE_MULTI_STREAM_ ## _size) \ + \ + BHND_IO_MISC(_type, set_multi_ ## _size, SET_MULTI_ ## _size) \ + BHND_IO_MISC(_type, set_region_ ## _size, SET_REGION_ ## _size) \ + \ + BHND_IO_MISC(_type*, read_region_ ## _size, \ + READ_REGION_ ## _size) \ + BHND_IO_MISC(_type*, write_region_ ## _size, \ + WRITE_REGION_ ## _size) \ + \ + BHND_IO_MISC(_type*, read_region_stream_ ## _size, \ + READ_REGION_STREAM_ ## _size) \ + BHND_IO_MISC(_type*, write_region_stream_ ## _size, \ + WRITE_REGION_STREAM_ ## _size) \ BHND_IO_METHODS(uint8_t, 1); BHND_IO_METHODS(uint16_t, 2); BHND_IO_METHODS(uint32_t, 4); static void bhnd_barrier(device_t dev, device_t child, struct bhnd_resource *r, bus_size_t offset, bus_size_t length, int flags) { BHND_BUS_BARRIER(device_get_parent(dev), child, r, offset, length, flags); } static device_method_t bhnd_methods[] = { /* Device interface */ \ DEVMETHOD(device_attach, bhnd_generic_attach), DEVMETHOD(device_detach, bhnd_generic_detach), DEVMETHOD(device_shutdown, bhnd_generic_shutdown), DEVMETHOD(device_suspend, bhnd_generic_suspend), DEVMETHOD(device_resume, bhnd_generic_resume), /* Bus interface */ DEVMETHOD(bus_probe_nomatch, bhnd_generic_probe_nomatch), DEVMETHOD(bus_print_child, bhnd_generic_print_child), DEVMETHOD(bus_child_pnpinfo_str, bhnd_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, bhnd_child_location_str), DEVMETHOD(bus_suspend_child, bhnd_generic_suspend_child), DEVMETHOD(bus_resume_child, bhnd_generic_resume_child), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_config_intr, bus_generic_config_intr), DEVMETHOD(bus_bind_intr, bus_generic_bind_intr), DEVMETHOD(bus_describe_intr, bus_generic_describe_intr), DEVMETHOD(bus_get_dma_tag, bus_generic_get_dma_tag), /* BHND interface */ DEVMETHOD(bhnd_bus_get_chipid, bhnd_bus_generic_get_chipid), DEVMETHOD(bhnd_bus_get_probe_order, bhnd_generic_get_probe_order), DEVMETHOD(bhnd_bus_is_region_valid, bhnd_generic_is_region_valid), DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bus_generic_is_hw_disabled), DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), + + /* BHND interface (bus I/O) */ DEVMETHOD(bhnd_bus_read_1, bhnd_read_1), DEVMETHOD(bhnd_bus_read_2, bhnd_read_2), DEVMETHOD(bhnd_bus_read_4, bhnd_read_4), DEVMETHOD(bhnd_bus_write_1, bhnd_write_1), DEVMETHOD(bhnd_bus_write_2, bhnd_write_2), DEVMETHOD(bhnd_bus_write_4, bhnd_write_4), + DEVMETHOD(bhnd_bus_read_stream_1, bhnd_read_stream_1), DEVMETHOD(bhnd_bus_read_stream_2, bhnd_read_stream_2), DEVMETHOD(bhnd_bus_read_stream_4, bhnd_read_stream_4), DEVMETHOD(bhnd_bus_write_stream_1, bhnd_write_stream_1), DEVMETHOD(bhnd_bus_write_stream_2, bhnd_write_stream_2), DEVMETHOD(bhnd_bus_write_stream_4, bhnd_write_stream_4), DEVMETHOD(bhnd_bus_read_multi_1, bhnd_read_multi_1), DEVMETHOD(bhnd_bus_read_multi_2, bhnd_read_multi_2), DEVMETHOD(bhnd_bus_read_multi_4, bhnd_read_multi_4), DEVMETHOD(bhnd_bus_write_multi_1, bhnd_write_multi_1), DEVMETHOD(bhnd_bus_write_multi_2, bhnd_write_multi_2), DEVMETHOD(bhnd_bus_write_multi_4, bhnd_write_multi_4), DEVMETHOD(bhnd_bus_read_multi_stream_1, bhnd_read_multi_stream_1), DEVMETHOD(bhnd_bus_read_multi_stream_2, bhnd_read_multi_stream_2), DEVMETHOD(bhnd_bus_read_multi_stream_4, bhnd_read_multi_stream_4), DEVMETHOD(bhnd_bus_write_multi_stream_1,bhnd_write_multi_stream_1), DEVMETHOD(bhnd_bus_write_multi_stream_2,bhnd_write_multi_stream_2), DEVMETHOD(bhnd_bus_write_multi_stream_4,bhnd_write_multi_stream_4), - DEVMETHOD(bhnd_bus_barrier, bhnd_barrier), + DEVMETHOD(bhnd_bus_set_multi_1, bhnd_set_multi_1), + DEVMETHOD(bhnd_bus_set_multi_2, bhnd_set_multi_2), + DEVMETHOD(bhnd_bus_set_multi_4, bhnd_set_multi_4), + + DEVMETHOD(bhnd_bus_set_region_1, bhnd_set_region_1), + DEVMETHOD(bhnd_bus_set_region_2, bhnd_set_region_2), + DEVMETHOD(bhnd_bus_set_region_4, bhnd_set_region_4), + + DEVMETHOD(bhnd_bus_read_region_1, bhnd_read_region_1), + DEVMETHOD(bhnd_bus_read_region_2, bhnd_read_region_2), + DEVMETHOD(bhnd_bus_read_region_4, bhnd_read_region_4), + DEVMETHOD(bhnd_bus_write_region_1, bhnd_write_region_1), + DEVMETHOD(bhnd_bus_write_region_2, bhnd_write_region_2), + DEVMETHOD(bhnd_bus_write_region_4, bhnd_write_region_4), + + DEVMETHOD(bhnd_bus_read_region_stream_1,bhnd_read_region_stream_1), + DEVMETHOD(bhnd_bus_read_region_stream_2,bhnd_read_region_stream_2), + DEVMETHOD(bhnd_bus_read_region_stream_4,bhnd_read_region_stream_4), + DEVMETHOD(bhnd_bus_write_region_stream_1, bhnd_write_region_stream_1), + DEVMETHOD(bhnd_bus_write_region_stream_2, bhnd_write_region_stream_2), + DEVMETHOD(bhnd_bus_write_region_stream_4, bhnd_write_region_stream_4), + + DEVMETHOD(bhnd_bus_barrier, bhnd_barrier), DEVMETHOD_END }; devclass_t bhnd_devclass; /**< bhnd bus. */ devclass_t bhnd_hostb_devclass; /**< bhnd bus host bridge. */ devclass_t bhnd_nvram_devclass; /**< bhnd NVRAM device */ DEFINE_CLASS_0(bhnd, bhnd_driver, bhnd_methods, sizeof(struct bhnd_softc)); MODULE_VERSION(bhnd, 1); Index: head/sys/dev/bhnd/bhnd.h =================================================================== --- head/sys/dev/bhnd/bhnd.h (revision 300249) +++ head/sys/dev/bhnd/bhnd.h (revision 300250) @@ -1,1005 +1,1113 @@ /*- * Copyright (c) 2015 Landon Fuller * 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_BHND_H_ #define _BHND_BHND_H_ #include #include #include #include "bhnd_ids.h" #include "bhnd_types.h" #include "bhnd_debug.h" #include "bhnd_bus_if.h" extern devclass_t bhnd_devclass; extern devclass_t bhnd_hostb_devclass; extern devclass_t bhnd_nvram_devclass; /** * bhnd child instance variables */ enum bhnd_device_vars { BHND_IVAR_VENDOR, /**< Designer's JEP-106 manufacturer ID. */ BHND_IVAR_DEVICE, /**< Part number */ BHND_IVAR_HWREV, /**< Core revision */ BHND_IVAR_DEVICE_CLASS, /**< Core class (@sa bhnd_devclass_t) */ BHND_IVAR_VENDOR_NAME, /**< Core vendor name */ BHND_IVAR_DEVICE_NAME, /**< Core name */ BHND_IVAR_CORE_INDEX, /**< Bus-assigned core number */ BHND_IVAR_CORE_UNIT, /**< Bus-assigned core unit number, assigned sequentially (starting at 0) for each vendor/device pair. */ }; /** * bhnd device probe priority bands. */ enum { BHND_PROBE_ROOT = 0, /**< Nexus or host bridge */ BHND_PROBE_BUS = 1000, /**< Busses and bridges */ BHND_PROBE_CPU = 2000, /**< CPU devices */ BHND_PROBE_INTERRUPT = 3000, /**< Interrupt controllers. */ BHND_PROBE_TIMER = 4000, /**< Timers and clocks. */ BHND_PROBE_RESOURCE = 5000, /**< Resource discovery (including NVRAM/SPROM) */ BHND_PROBE_DEFAULT = 6000, /**< Default device priority */ }; /** * Constants defining fine grained ordering within a BHND_PROBE_* priority band. * * Example: * @code * BHND_PROBE_BUS + BHND_PROBE_ORDER_FIRST * @endcode */ enum { BHND_PROBE_ORDER_FIRST = 0, BHND_PROBE_ORDER_EARLY = 25, BHND_PROBE_ORDER_MIDDLE = 50, BHND_PROBE_ORDER_LATE = 75, BHND_PROBE_ORDER_LAST = 100 }; /* * Simplified accessors for bhnd device ivars */ #define BHND_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(bhnd, var, BHND, ivar, type) BHND_ACCESSOR(vendor, VENDOR, uint16_t); BHND_ACCESSOR(device, DEVICE, uint16_t); BHND_ACCESSOR(hwrev, HWREV, uint8_t); BHND_ACCESSOR(class, DEVICE_CLASS, bhnd_devclass_t); BHND_ACCESSOR(vendor_name, VENDOR_NAME, const char *); BHND_ACCESSOR(device_name, DEVICE_NAME, const char *); BHND_ACCESSOR(core_index, CORE_INDEX, u_int); BHND_ACCESSOR(core_unit, CORE_UNIT, int); #undef BHND_ACCESSOR /** * A bhnd(4) board descriptor. */ struct bhnd_board_info { uint16_t board_vendor; /**< PCI-SIG vendor ID (even on non-PCI * devices). * * On PCI devices, this will generally * be the subsystem vendor ID, but the * value may be overridden in device * NVRAM. */ uint16_t board_type; /**< Board type (See BHND_BOARD_*) * * On PCI devices, this will generally * be the subsystem device ID, but the * value may be overridden in device * NVRAM. */ uint16_t board_rev; /**< Board revision. */ uint8_t board_srom_rev; /**< Board SROM format revision */ uint32_t board_flags; /**< Board flags (see BHND_BFL_*) */ uint32_t board_flags2; /**< Board flags 2 (see BHND_BFL2_*) */ uint32_t board_flags3; /**< Board flags 3 (see BHND_BFL3_*) */ }; /** * Chip Identification * * This is read from the ChipCommon ID register; on earlier bhnd(4) devices * where ChipCommon is unavailable, known values must be supplied. */ struct bhnd_chipid { uint16_t chip_id; /**< chip id (BHND_CHIPID_*) */ uint8_t chip_rev; /**< chip revision */ uint8_t chip_pkg; /**< chip package (BHND_PKGID_*) */ uint8_t chip_type; /**< chip type (BHND_CHIPTYPE_*) */ bhnd_addr_t enum_addr; /**< chip_type-specific enumeration * address; either the siba(4) base * core register block, or the bcma(4) * EROM core address. */ uint8_t ncores; /**< number of cores, if known. 0 if * not available. */ }; /** * A bhnd(4) core descriptor. */ struct bhnd_core_info { uint16_t vendor; /**< JEP-106 vendor (BHND_MFGID_*) */ uint16_t device; /**< device */ uint16_t hwrev; /**< hardware revision */ u_int core_idx; /**< bus-assigned core index */ int unit; /**< bus-assigned core unit */ }; /** * A hardware revision match descriptor. */ struct bhnd_hwrev_match { uint16_t start; /**< first revision, or BHND_HWREV_INVALID to match on any revision. */ uint16_t end; /**< last revision, or BHND_HWREV_INVALID to match on any revision. */ }; /** * A bhnd(4) bus resource. * * This provides an abstract interface to per-core resources that may require * bus-level remapping of address windows prior to access. */ struct bhnd_resource { struct resource *res; /**< the system resource. */ bool direct; /**< false if the resource requires * bus window remapping before it * is MMIO accessible. */ }; /** * Wildcard hardware revision match descriptor. */ #define BHND_HWREV_ANY { BHND_HWREV_INVALID, BHND_HWREV_INVALID } #define BHND_HWREV_IS_ANY(_m) \ ((_m)->start == BHND_HWREV_INVALID && (_m)->end == BHND_HWREV_INVALID) /** * Hardware revision match descriptor for an inclusive range. * * @param _start The first applicable hardware revision. * @param _end The last applicable hardware revision, or BHND_HWREV_INVALID * to match on any revision. */ #define BHND_HWREV_RANGE(_start, _end) { _start, _end } /** * Hardware revision match descriptor for a single revision. * * @param _hwrev The hardware revision to match on. */ #define BHND_HWREV_EQ(_hwrev) BHND_HWREV_RANGE(_hwrev, _hwrev) /** * Hardware revision match descriptor for any revision equal to or greater * than @p _start. * * @param _start The first hardware revision to match on. */ #define BHND_HWREV_GTE(_start) BHND_HWREV_RANGE(_start, BHND_HWREV_INVALID) /** * Hardware revision match descriptor for any revision equal to or less * than @p _end. * * @param _end The last hardware revision to match on. */ #define BHND_HWREV_LTE(_end) BHND_HWREV_RANGE(0, _end) /** A core match descriptor. */ struct bhnd_core_match { uint16_t vendor; /**< required JEP106 device vendor or BHND_MFGID_INVALID. */ uint16_t device; /**< required core ID or BHND_COREID_INVALID */ struct bhnd_hwrev_match hwrev; /**< matching revisions. */ bhnd_devclass_t class; /**< required class or BHND_DEVCLASS_INVALID */ int unit; /**< required core unit, or -1 */ }; /** * Core match descriptor matching against the given @p _vendor, @p _device, * and @p _hwrev match descriptors. */ #define BHND_CORE_MATCH(_vendor, _device, _hwrev) \ { _vendor, _device, _hwrev, BHND_DEVCLASS_INVALID, -1 } /** * Wildcard core match descriptor. */ #define BHND_CORE_MATCH_ANY \ { \ .vendor = BHND_MFGID_INVALID, \ .device = BHND_COREID_INVALID, \ .hwrev = BHND_HWREV_ANY, \ .class = BHND_DEVCLASS_INVALID, \ .unit = -1 \ } /** * A chipset match descriptor. * * @warning Matching on board/nvram attributes relies on NVRAM access, and will * fail if a valid NVRAM device cannot be found, or is not yet attached. */ struct bhnd_chip_match { /** Select fields to be matched */ uint16_t match_id:1, match_rev:1, match_pkg:1, match_bvendor:1, match_btype:1, match_brev:1, match_srom_rev:1, match_any:1, match_flags_unused:8; uint16_t chip_id; /**< required chip id */ struct bhnd_hwrev_match chip_rev; /**< matching chip revisions */ uint8_t chip_pkg; /**< required package */ uint16_t board_vendor; /**< required board vendor */ uint16_t board_type; /**< required board type */ struct bhnd_hwrev_match board_rev; /**< matching board revisions */ struct bhnd_hwrev_match board_srom_rev; /**< matching board srom revisions */ }; #define BHND_CHIP_MATCH_ANY \ { .match_any = 1 } #define BHND_CHIP_MATCH_IS_ANY(_m) \ ((_m)->match_any == 1) #define BHND_CHIP_MATCH_REQ_BOARD_INFO(_m) \ ((_m)->match_srom_rev || (_m)->match_bvendor || \ (_m)->match_btype || (_m)->match_brev) /** Set the required chip ID within a bhnd_chip_match instance */ #define BHND_CHIP_ID(_cid) \ .match_id = 1, .chip_id = BHND_CHIPID_BCM ## _cid /** Set the required chip revision range within a bhnd_chip_match instance */ #define BHND_CHIP_REV(_rev) \ .match_rev = 1, .chip_rev = BHND_ ## _rev /** Set the required package ID within a bhnd_chip_match instance */ #define BHND_CHIP_PKG(_pkg) \ .match_pkg = 1, .chip_pkg = BHND_PKGID_BCM ## _pkg /** Set the required board vendor within a bhnd_chip_match instance */ #define BHND_CHIP_BVENDOR(_vend) \ .match_bvendor = 1, .board_vendor = _vend /** Set the required board type within a bhnd_chip_match instance */ #define BHND_CHIP_BTYPE(_btype) \ .match_btype = 1, .board_type = BHND_BOARD_ ## _btype /** Set the required SROM revision range within a bhnd_chip_match instance */ #define BHND_CHIP_SROMREV(_rev) \ .match_srom_rev = 1, .board_srom_rev = BHND_ ## _rev /** Set the required board revision range within a bhnd_chip_match instance */ #define BHND_CHIP_BREV(_rev) \ .match_brev = 1, .board_rev = BHND_ ## _rev /** Set the required board vendor and type within a bhnd_chip_match instance */ #define BHND_CHIP_BVT(_vend, _type) \ BHND_CHIP_BVENDOR(_vend), BHND_CHIP_BTYPE(_type) /** Set the required board vendor, type, and revision within a bhnd_chip_match * instance */ #define BHND_CHIP_BVTR(_vend, _type, _rev) \ BHND_CHIP_BVT(_vend, _type), BHND_CHIP_BREV(_rev) /** Set the required chip and package ID within a bhnd_chip_match instance */ #define BHND_CHIP_IP(_cid, _pkg) \ BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg) /** Set the required chip ID, package ID, and revision within a bhnd_chip_match * instance */ #define BHND_CHIP_IPR(_cid, _pkg, _rev) \ BHND_CHIP_ID(_cid), BHND_CHIP_PKG(_pkg), BHND_CHIP_REV(_rev) /** Set the required chip ID and revision within a bhnd_chip_match * instance */ #define BHND_CHIP_IR(_cid, _rev) \ BHND_CHIP_ID(_cid), BHND_CHIP_REV(_rev) /** * Chipset quirk table descriptor. */ struct bhnd_chip_quirk { const struct bhnd_chip_match chip; /**< chip match descriptor */ uint32_t quirks; /**< quirk flags */ }; #define BHND_CHIP_QUIRK_END { BHND_CHIP_MATCH_ANY, 0 } #define BHND_CHIP_QUIRK_IS_END(_q) \ (BHND_CHIP_MATCH_IS_ANY(&(_q)->chip) && (_q)->quirks == 0) /** * Device quirk table descriptor. */ struct bhnd_device_quirk { struct bhnd_hwrev_match hwrev; /**< applicable hardware revisions */ uint32_t quirks; /**< quirk flags */ }; #define BHND_DEVICE_QUIRK_END { BHND_HWREV_ANY, 0 } #define BHND_DEVICE_QUIRK_IS_END(_q) \ (BHND_HWREV_IS_ANY(&(_q)->hwrev) && (_q)->quirks == 0) enum { BHND_DF_ANY = 0, BHND_DF_HOSTB = (1<<0) /**< core is serving as the bus' * host bridge */ }; /** Device probe table descriptor */ struct bhnd_device { const struct bhnd_core_match core; /**< core match descriptor */ const char *desc; /**< device description, or NULL. */ const struct bhnd_device_quirk *quirks_table; /**< quirks table for this device, or NULL */ const struct bhnd_chip_quirk *chip_quirks_table; /**< chipset-specific quirks for this device, or NULL */ uint32_t device_flags; /**< required BHND_DF_* flags */ }; #define _BHND_DEVICE(_vendor, _device, _desc, _quirks, _chip_quirks, \ _flags, ...) \ { BHND_CORE_MATCH(BHND_MFGID_ ## _vendor, \ BHND_COREID_ ## _device, BHND_HWREV_ANY), _desc, _quirks, \ _chip_quirks, _flags } #define BHND_MIPS_DEVICE(_device, _desc, _quirks, _chip_quirks, ...) \ _BHND_DEVICE(MIPS, _device, _desc, _quirks, _chip_quirks, \ ## __VA_ARGS__, 0) #define BHND_ARM_DEVICE(_device, _desc, _quirks, _chip_quirks, ...) \ _BHND_DEVICE(ARM, _device, _desc, _quirks, _chip_quirks, \ ## __VA_ARGS__, 0) #define BHND_DEVICE(_device, _desc, _quirks, _chip_quirks, ...) \ _BHND_DEVICE(BCM, _device, _desc, _quirks, _chip_quirks, \ ## __VA_ARGS__, 0) #define BHND_DEVICE_END { BHND_CORE_MATCH_ANY, NULL, NULL, NULL, 0 } const char *bhnd_vendor_name(uint16_t vendor); const char *bhnd_port_type_name(bhnd_port_type port_type); const char *bhnd_find_core_name(uint16_t vendor, uint16_t device); bhnd_devclass_t bhnd_find_core_class(uint16_t vendor, uint16_t device); const char *bhnd_core_name(const struct bhnd_core_info *ci); bhnd_devclass_t bhnd_core_class(const struct bhnd_core_info *ci); device_t bhnd_match_child(device_t dev, const struct bhnd_core_match *desc); device_t bhnd_find_child(device_t dev, bhnd_devclass_t class, int unit); device_t bhnd_find_bridge_root(device_t dev, devclass_t bus_class); const struct bhnd_core_info *bhnd_match_core( const struct bhnd_core_info *cores, u_int num_cores, const struct bhnd_core_match *desc); const struct bhnd_core_info *bhnd_find_core( const struct bhnd_core_info *cores, u_int num_cores, bhnd_devclass_t class); bool bhnd_core_matches( const struct bhnd_core_info *core, const struct bhnd_core_match *desc); bool bhnd_chip_matches( const struct bhnd_chipid *chipid, const struct bhnd_board_info *binfo, const struct bhnd_chip_match *desc); bool bhnd_hwrev_matches(uint16_t hwrev, const struct bhnd_hwrev_match *desc); uint32_t bhnd_chip_quirks(device_t dev, const struct bhnd_chip_quirk *table); bool bhnd_device_matches(device_t dev, const struct bhnd_core_match *desc); const struct bhnd_device *bhnd_device_lookup(device_t dev, const struct bhnd_device *table, size_t entry_size); uint32_t bhnd_device_quirks(device_t dev, const struct bhnd_device *table, size_t entry_size); struct bhnd_core_info bhnd_get_core_info(device_t dev); int bhnd_alloc_resources(device_t dev, struct resource_spec *rs, struct bhnd_resource **res); void bhnd_release_resources(device_t dev, const struct resource_spec *rs, struct bhnd_resource **res); struct bhnd_chipid bhnd_parse_chipid(uint32_t idreg, bhnd_addr_t enum_addr); int bhnd_read_chipid(device_t dev, struct resource_spec *rs, bus_size_t chipc_offset, struct bhnd_chipid *result); void bhnd_set_custom_core_desc(device_t dev, const char *name); void bhnd_set_default_core_desc(device_t dev); bool bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child); bool bhnd_bus_generic_is_region_valid(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region); int bhnd_bus_generic_read_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size); const struct bhnd_chipid *bhnd_bus_generic_get_chipid(device_t dev, device_t child); int bhnd_bus_generic_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info); int bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size); struct bhnd_resource *bhnd_bus_generic_alloc_resource (device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); int bhnd_bus_generic_release_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); int bhnd_bus_generic_activate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); int bhnd_bus_generic_deactivate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); /** * Return the active host bridge core for the bhnd bus, if any, or NULL if * not found. * * @param dev A bhnd bus device. */ static inline device_t bhnd_find_hostb_device(device_t dev) { return (BHND_BUS_FIND_HOSTB_DEVICE(dev)); } /** * Return true if the hardware components required by @p dev are known to be * unpopulated or otherwise unusable. * * In some cases, enumerated devices may have pins that are left floating, or * the hardware may otherwise be non-functional; this method allows a parent * device to explicitly specify if a successfully enumerated @p dev should * be disabled. * * @param dev A bhnd bus child device. */ static inline bool bhnd_is_hw_disabled(device_t dev) { return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), dev)); } /** * Return the BHND chip identification info for the bhnd bus. * * @param dev A bhnd bus child device. */ static inline const struct bhnd_chipid * bhnd_get_chipid(device_t dev) { return (BHND_BUS_GET_CHIPID(device_get_parent(dev), dev)); }; /** * Attempt to read the BHND board identification from the bhnd bus. * * This relies on NVRAM access, and will fail if a valid NVRAM device cannot * be found, or is not yet attached. * * @param dev The parent of @p child. * @param child The bhnd device requesting board info. * @param[out] info On success, will be populated with the bhnd(4) device's * board information. * * @retval 0 success * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_read_board_info(device_t dev, struct bhnd_board_info *info) { return (BHND_BUS_READ_BOARD_INFO(device_get_parent(dev), dev, info)); } /** * Determine an NVRAM variable's expected size. * * @param dev A bhnd bus child device. * @param name The variable name. * @param[out] len On success, the variable's size, in bytes. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_nvram_getvarlen(device_t dev, const char *name, size_t *len) { return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, NULL, len)); } /** * Read an NVRAM variable. * * @param dev A bhnd bus child device. * @param name The NVRAM variable name. * @param buf A buffer large enough to hold @p len bytes. On success, * the requested value will be written to this buffer. * @param len The required variable length. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval EINVAL If @p len does not match the actual variable size. * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ static inline int bhnd_nvram_getvar(device_t dev, const char *name, void *buf, size_t len) { size_t var_len; int error; if ((error = bhnd_nvram_getvarlen(dev, name, &var_len))) return (error); if (len != var_len) return (EINVAL); return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), dev, name, buf, &len)); } /** * Allocate a resource from a device's parent bhnd(4) bus. * * @param dev The device requesting resource ownership. * @param type The type of resource to allocate. This may be any type supported * by the standard bus APIs. * @param rid The bus-specific handle identifying the resource being allocated. * @param start The start address of the resource. * @param end The end address of the resource. * @param count The size of the resource. * @param flags The flags for the resource to be allocated. These may be any * values supported by the standard bus APIs. * * To request the resource's default addresses, pass @p start and * @p end values of @c 0 and @c ~0, respectively, and * a @p count of @c 1. * * @retval NULL The resource could not be allocated. * @retval resource The allocated resource. */ static inline struct bhnd_resource * bhnd_alloc_resource(device_t dev, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { return BHND_BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, rid, start, end, count, flags); } /** * Allocate a resource from a device's parent bhnd(4) bus, using the * resource's default start, end, and count values. * * @param dev The device requesting resource ownership. * @param type The type of resource to allocate. This may be any type supported * by the standard bus APIs. * @param rid The bus-specific handle identifying the resource being allocated. * @param flags The flags for the resource to be allocated. These may be any * values supported by the standard bus APIs. * * @retval NULL The resource could not be allocated. * @retval resource The allocated resource. */ static inline struct bhnd_resource * bhnd_alloc_resource_any(device_t dev, int type, int *rid, u_int flags) { return bhnd_alloc_resource(dev, type, rid, 0, ~0, 1, flags); } /** * Activate a previously allocated bhnd resource. * * @param dev The device holding ownership of the allocated resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_BUS_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_activate_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Deactivate a previously activated bhnd resource. * * @param dev The device holding ownership of the activated resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_BUS_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_deactivate_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Free a resource allocated by bhnd_alloc_resource(). * * @param dev The device holding ownership of the resource. * @param type The type of the resource. * @param rid The bus-specific handle identifying the resource. * @param r A pointer to the resource returned by bhnd_alloc_resource or * BHND_ALLOC_RESOURCE. * * @retval 0 success * @retval non-zero an error occurred while activating the resource. */ static inline int bhnd_release_resource(device_t dev, int type, int rid, struct bhnd_resource *r) { return BHND_BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, rid, r); } /** * Return true if @p region_num is a valid region on @p port_num of * @p type attached to @p dev. * * @param dev A bhnd bus child device. * @param type The port type being queried. * @param port_num The port number being queried. * @param region_num The region number being queried. */ static inline bool bhnd_is_region_valid(device_t dev, bhnd_port_type type, u_int port_num, u_int region_num) { return (BHND_BUS_IS_REGION_VALID(device_get_parent(dev), dev, type, port_num, region_num)); } /** * Return the number of ports of type @p type attached to @p def. * * @param dev A bhnd bus child device. * @param type The port type being queried. */ static inline u_int bhnd_get_port_count(device_t dev, bhnd_port_type type) { return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), dev, type)); } /** * Return the number of memory regions mapped to @p child @p port of * type @p type. * * @param dev A bhnd bus child device. * @param port The port number being queried. * @param type The port type being queried. */ static inline u_int bhnd_get_region_count(device_t dev, bhnd_port_type type, u_int port) { return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), dev, type, port)); } /** * Return the resource-ID for a memory region on the given device port. * * @param dev A bhnd bus child device. * @param type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * * @retval int The RID for the given @p port and @p region on @p device. * @retval -1 No such port/region found. */ static inline int bhnd_get_port_rid(device_t dev, bhnd_port_type type, u_int port, u_int region) { return BHND_BUS_GET_PORT_RID(device_get_parent(dev), dev, type, port, region); } /** * Decode a port / region pair on @p dev defined by @p rid. * * @param dev A bhnd bus child device. * @param type The resource type. * @param rid The resource identifier. * @param[out] port_type The decoded port type. * @param[out] port The decoded port identifier. * @param[out] region The decoded region identifier. * * @retval 0 success * @retval non-zero No matching port/region found. */ static inline int bhnd_decode_port_rid(device_t dev, int type, int rid, bhnd_port_type *port_type, u_int *port, u_int *region) { return BHND_BUS_DECODE_PORT_RID(device_get_parent(dev), dev, type, rid, port_type, port, region); } /** * Get the address and size of @p region on @p port. * * @param dev A bhnd bus child device. * @param port_type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * @param[out] region_addr The region's base address. * @param[out] region_size The region's size. * * @retval 0 success * @retval non-zero No matching port/region found. */ static inline int bhnd_get_region_addr(device_t dev, bhnd_port_type port_type, u_int port, u_int region, bhnd_addr_t *region_addr, bhnd_size_t *region_size) { return BHND_BUS_GET_REGION_ADDR(device_get_parent(dev), dev, port_type, port, region, region_addr, region_size); } /* * bhnd bus-level equivalents of the bus_(read|write|set|barrier|...) * macros (compatible with bhnd_resource). * * Generated with bhnd/tools/bus_macro.sh */ #define bhnd_bus_barrier(r, o, l, f) \ ((r)->direct) ? \ bus_barrier((r)->res, (o), (l), (f)) : \ BHND_BUS_BARRIER( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (l), (f)) #define bhnd_bus_read_1(r, o) \ ((r)->direct) ? \ bus_read_1((r)->res, (o)) : \ BHND_BUS_READ_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_read_region_1(r, o, d, c) \ + ((r)->direct) ? \ + bus_read_region_1((r)->res, (o), (d), (c)) : \ + BHND_BUS_READ_REGION_1( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_1(r, o, v) \ ((r)->direct) ? \ bus_write_1((r)->res, (o), (v)) : \ BHND_BUS_WRITE_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_write_region_1(r, o, d, c) \ + ((r)->direct) ? \ + bus_write_region_1((r)->res, (o), (d), (c)) : \ + BHND_BUS_WRITE_REGION_1( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_1(r, o) \ ((r)->direct) ? \ bus_read_stream_1((r)->res, (o)) : \ BHND_BUS_READ_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_read_region_stream_1(r, o, d, c) \ + ((r)->direct) ? \ + bus_read_region_stream_1((r)->res, (o), (d), (c)) : \ + BHND_BUS_READ_REGION_STREAM_1( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_1(r, o, v) \ ((r)->direct) ? \ bus_write_stream_1((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_1(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_1((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_1( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_write_region_stream_1(r, o, d, c) \ + ((r)->direct) ? \ + bus_write_region_stream_1((r)->res, (o), (d), (c)) : \ + BHND_BUS_WRITE_REGION_STREAM_1( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_set_multi_1(r, o, v, c) \ + ((r)->direct) ? \ + bus_set_multi_1((r)->res, (o), (v), (c)) : \ + BHND_BUS_SET_MULTI_1( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (v), (c)) +#define bhnd_bus_set_region_1(r, o, v, c) \ + ((r)->direct) ? \ + bus_set_region_1((r)->res, (o), (v), (c)) : \ + BHND_BUS_SET_REGION_1( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_read_2(r, o) \ ((r)->direct) ? \ bus_read_2((r)->res, (o)) : \ BHND_BUS_READ_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_read_region_2(r, o, d, c) \ + ((r)->direct) ? \ + bus_read_region_2((r)->res, (o), (d), (c)) : \ + BHND_BUS_READ_REGION_2( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_2(r, o, v) \ ((r)->direct) ? \ bus_write_2((r)->res, (o), (v)) : \ BHND_BUS_WRITE_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_write_region_2(r, o, d, c) \ + ((r)->direct) ? \ + bus_write_region_2((r)->res, (o), (d), (c)) : \ + BHND_BUS_WRITE_REGION_2( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_2(r, o) \ ((r)->direct) ? \ bus_read_stream_2((r)->res, (o)) : \ BHND_BUS_READ_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_read_region_stream_2(r, o, d, c) \ + ((r)->direct) ? \ + bus_read_region_stream_2((r)->res, (o), (d), (c)) : \ + BHND_BUS_READ_REGION_STREAM_2( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_2(r, o, v) \ ((r)->direct) ? \ bus_write_stream_2((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_2(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_2((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_2( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_write_region_stream_2(r, o, d, c) \ + ((r)->direct) ? \ + bus_write_region_stream_2((r)->res, (o), (d), (c)) : \ + BHND_BUS_WRITE_REGION_STREAM_2( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_set_multi_2(r, o, v, c) \ + ((r)->direct) ? \ + bus_set_multi_2((r)->res, (o), (v), (c)) : \ + BHND_BUS_SET_MULTI_2( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (v), (c)) +#define bhnd_bus_set_region_2(r, o, v, c) \ + ((r)->direct) ? \ + bus_set_region_2((r)->res, (o), (v), (c)) : \ + BHND_BUS_SET_REGION_2( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (v), (c)) #define bhnd_bus_read_4(r, o) \ ((r)->direct) ? \ bus_read_4((r)->res, (o)) : \ BHND_BUS_READ_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_read_region_4(r, o, d, c) \ + ((r)->direct) ? \ + bus_read_region_4((r)->res, (o), (d), (c)) : \ + BHND_BUS_READ_REGION_4( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_4(r, o, v) \ ((r)->direct) ? \ bus_write_4((r)->res, (o), (v)) : \ BHND_BUS_WRITE_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_write_region_4(r, o, d, c) \ + ((r)->direct) ? \ + bus_write_region_4((r)->res, (o), (d), (c)) : \ + BHND_BUS_WRITE_REGION_4( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_read_stream_4(r, o) \ ((r)->direct) ? \ bus_read_stream_4((r)->res, (o)) : \ BHND_BUS_READ_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o)) #define bhnd_bus_read_multi_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_read_multi_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_READ_MULTI_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_read_region_stream_4(r, o, d, c) \ + ((r)->direct) ? \ + bus_read_region_stream_4((r)->res, (o), (d), (c)) : \ + BHND_BUS_READ_REGION_STREAM_4( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) #define bhnd_bus_write_stream_4(r, o, v) \ ((r)->direct) ? \ bus_write_stream_4((r)->res, (o), (v)) : \ BHND_BUS_WRITE_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (v)) #define bhnd_bus_write_multi_stream_4(r, o, d, c) \ ((r)->direct) ? \ bus_write_multi_stream_4((r)->res, (o), (d), (c)) : \ BHND_BUS_WRITE_MULTI_STREAM_4( \ device_get_parent(rman_get_device((r)->res)), \ rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_write_region_stream_4(r, o, d, c) \ + ((r)->direct) ? \ + bus_write_region_stream_4((r)->res, (o), (d), (c)) : \ + BHND_BUS_WRITE_REGION_STREAM_4( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (d), (c)) +#define bhnd_bus_set_multi_4(r, o, v, c) \ + ((r)->direct) ? \ + bus_set_multi_4((r)->res, (o), (v), (c)) : \ + BHND_BUS_SET_MULTI_4( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (v), (c)) +#define bhnd_bus_set_region_4(r, o, v, c) \ + ((r)->direct) ? \ + bus_set_region_4((r)->res, (o), (v), (c)) : \ + BHND_BUS_SET_REGION_4( \ + device_get_parent(rman_get_device((r)->res)), \ + rman_get_device((r)->res), (r), (o), (v), (c)) #endif /* _BHND_BHND_H_ */ Index: head/sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_bus_if.m (revision 300249) +++ head/sys/dev/bhnd/bhnd_bus_if.m (revision 300250) @@ -1,681 +1,867 @@ #- # Copyright (c) 2015 Landon Fuller # 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 #include #include #include INTERFACE bhnd_bus; # # bhnd(4) bus interface # HEADER { /* forward declarations */ struct bhnd_board_info; struct bhnd_core_info; struct bhnd_chipid; struct bhnd_resource; } CODE { #include #include static struct bhnd_chipid * bhnd_bus_null_get_chipid(device_t dev, device_t child) { panic("bhnd_bus_get_chipid unimplemented"); } static int bhnd_bus_null_read_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { panic("bhnd_bus_read_boardinfo unimplemented"); } static device_t bhnd_bus_null_find_hostb_device(device_t dev) { panic("bhnd_bus_find_hostb_device unimplemented"); } static bool bhnd_bus_null_is_hw_disabled(device_t dev, device_t child) { panic("bhnd_bus_is_hw_disabled unimplemented"); } static int bhnd_bus_null_get_probe_order(device_t dev, device_t child) { panic("bhnd_bus_get_probe_order unimplemented"); } static int bhnd_bus_null_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, u_int port, u_int region) { return (-1); } static int bhnd_bus_null_decode_port_rid(device_t dev, device_t child, int type, int rid, bhnd_port_type *port_type, u_int *port, u_int *region) { return (ENOENT); } static int bhnd_bus_null_get_region_addr(device_t dev, device_t child, bhnd_port_type type, u_int port, u_int region, bhnd_addr_t *addr, bhnd_size_t *size) { return (ENOENT); } static int bhnd_bus_null_get_nvram_var(device_t dev, device_t child, const char *name, void *buf, size_t *size) { return (ENODEV); } } /** * Return the active host bridge core for the bhnd bus, if any. * * @param dev The bhnd bus device. * * @retval device_t if a hostb device exists * @retval NULL if no hostb device is found. */ METHOD device_t find_hostb_device { device_t dev; } DEFAULT bhnd_bus_null_find_hostb_device; /** * Return true if the hardware components required by @p child are unpopulated * or otherwise unusable. * * In some cases, enumerated devices may have pins that are left floating, or * the hardware may otherwise be non-functional; this method allows a parent * device to explicitly specify if a successfully enumerated @p child should * be disabled. * * @param dev The device whose child is being examined. * @param child The child device. */ METHOD bool is_hw_disabled { device_t dev; device_t child; } DEFAULT bhnd_bus_null_is_hw_disabled; /** * Return the probe (and attach) order for @p child. * * All devices on the bhnd(4) bus will be probed, attached, or resumed in * ascending order; they will be suspended, shutdown, and detached in * descending order. * * The following device methods will be dispatched in ascending probe order * by the bus: * * - DEVICE_PROBE() * - DEVICE_ATTACH() * - DEVICE_RESUME() * * The following device methods will be dispatched in descending probe order * by the bus: * * - DEVICE_SHUTDOWN() * - DEVICE_DETACH() * - DEVICE_SUSPEND() * * @param dev The device whose child is being examined. * @param child The child device. * * Refer to BHND_PROBE_* and BHND_PROBE_ORDER_* for the standard set of * priorities. */ METHOD int get_probe_order { device_t dev; device_t child; } DEFAULT bhnd_bus_null_get_probe_order; /** * Return the BHND chip identification for the parent bus. * * @param dev The device whose child is being examined. * @param child The child device. */ METHOD const struct bhnd_chipid * get_chipid { device_t dev; device_t child; } DEFAULT bhnd_bus_null_get_chipid; /** * Attempt to read the BHND board identification from the parent bus. * * This relies on NVRAM access, and will fail if a valid NVRAM device cannot * be found, or is not yet attached. * * @param dev The parent of @p child. * @param child The bhnd device requesting board info. * @param[out] info On success, will be populated with the bhnd(4) device's * board information. * * @retval 0 success * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ METHOD int read_board_info { device_t dev; device_t child; struct bhnd_board_info *info; } DEFAULT bhnd_bus_null_read_board_info; /** * Reset the device's hardware core. * * @param dev The parent of @p child. * @param child The device to be reset. * @param flags Device-specific core flags to be supplied on reset. * * @retval 0 success * @retval non-zero error */ METHOD int reset_core { device_t dev; device_t child; uint16_t flags; } /** * Suspend a device hardware core. * * @param dev The parent of @p child. * @param child The device to be reset. * * @retval 0 success * @retval non-zero error */ METHOD int suspend_core { device_t dev; device_t child; } /** * Allocate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_ALLOC_RESOURCE for complete documentation. */ METHOD struct bhnd_resource * alloc_resource { device_t dev; device_t child; int type; int *rid; rman_res_t start; rman_res_t end; rman_res_t count; u_int flags; } DEFAULT bhnd_bus_generic_alloc_resource; /** * Release a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_RELEASE_RESOURCE for complete documentation. */ METHOD int release_resource { device_t dev; device_t child; int type; int rid; struct bhnd_resource *res; } DEFAULT bhnd_bus_generic_release_resource; /** * Activate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_ACTIVATE_RESOURCE for complete documentation. */ METHOD int activate_resource { device_t dev; device_t child; int type; int rid; struct bhnd_resource *r; } DEFAULT bhnd_bus_generic_activate_resource; /** * Deactivate a bhnd resource. * * This method's semantics are functionally identical to the bus API of the same * name; refer to BUS_DEACTIVATE_RESOURCE for complete documentation. */ METHOD int deactivate_resource { device_t dev; device_t child; int type; int rid; struct bhnd_resource *r; } DEFAULT bhnd_bus_generic_deactivate_resource; /** * Return true if @p region_num is a valid region on @p port_num of * @p type attached to @p child. * * @param dev The device whose child is being examined. * @param child The child device. * @param type The port type being queried. * @param port_num The port number being queried. * @param region_num The region number being queried. */ METHOD bool is_region_valid { device_t dev; device_t child; bhnd_port_type type; u_int port_num; u_int region_num; }; /** * Return the number of ports of type @p type attached to @p child. * * @param dev The device whose child is being examined. * @param child The child device. * @param type The port type being queried. */ METHOD u_int get_port_count { device_t dev; device_t child; bhnd_port_type type; }; /** * Return the number of memory regions mapped to @p child @p port of * type @p type. * * @param dev The device whose child is being examined. * @param child The child device. * @param port The port number being queried. * @param type The port type being queried. */ METHOD u_int get_region_count { device_t dev; device_t child; bhnd_port_type type; u_int port; }; /** * Return the SYS_RES_MEMORY resource-ID for a port/region pair attached to * @p child. * * @param dev The bus device. * @param child The bhnd child. * @param port_type The port type. * @param port_num The index of the child interconnect port. * @param region_num The index of the port-mapped address region. * * @retval -1 No such port/region found. */ METHOD int get_port_rid { device_t dev; device_t child; bhnd_port_type port_type; u_int port_num; u_int region_num; } DEFAULT bhnd_bus_null_get_port_rid; /** * Decode a port / region pair on @p child defined by @p type and @p rid. * * @param dev The bus device. * @param child The bhnd child. * @param type The resource type. * @param rid The resource ID. * @param[out] port_type The port's type. * @param[out] port The port identifier. * @param[out] region The identifier of the memory region on @p port. * * @retval 0 success * @retval non-zero No matching type/rid found. */ METHOD int decode_port_rid { device_t dev; device_t child; int type; int rid; bhnd_port_type *port_type; u_int *port; u_int *region; } DEFAULT bhnd_bus_null_decode_port_rid; /** * Get the address and size of @p region on @p port. * * @param dev The bus device. * @param child The bhnd child. * @param port_type The port type. * @param port The port identifier. * @param region The identifier of the memory region on @p port. * @param[out] region_addr The region's base address. * @param[out] region_size The region's size. * * @retval 0 success * @retval non-zero No matching port/region found. */ METHOD int get_region_addr { device_t dev; device_t child; bhnd_port_type port_type; u_int port; u_int region; bhnd_addr_t *region_addr; bhnd_size_t *region_size; } DEFAULT bhnd_bus_null_get_region_addr; /** * Read an NVRAM variable. * * It is the responsibility of the bus to delegate this request to * the appropriate NVRAM child device, or to a parent bus implementation. * * @param dev The bus device. * @param child The requesting device. * @param name The NVRAM variable name. * @param[out] buf On success, the requested value will be written * to this buffer. This argment may be NULL if * the value is not desired. * @param[in,out] size The capacity of @p buf. On success, will be set * to the actual size of the requested value. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENOMEM If @p buf is non-NULL and a buffer of @p size is too * small to hold the requested value. * @retval ENODEV No valid NVRAM source could be found. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ METHOD int get_nvram_var { device_t dev; device_t child; const char *name; void *buf; size_t *size; } DEFAULT bhnd_bus_null_get_nvram_var; /** An implementation of bus_read_1() compatible with bhnd_resource */ METHOD uint8_t read_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_2() compatible with bhnd_resource */ METHOD uint16_t read_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_4() compatible with bhnd_resource */ METHOD uint32_t read_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_write_1() compatible with bhnd_resource */ METHOD void write_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t value; } /** An implementation of bus_write_2() compatible with bhnd_resource */ METHOD void write_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t value; } /** An implementation of bus_write_4() compatible with bhnd_resource */ METHOD void write_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t value; } /** An implementation of bus_read_stream_1() compatible with bhnd_resource */ METHOD uint8_t read_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_stream_2() compatible with bhnd_resource */ METHOD uint16_t read_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_read_stream_4() compatible with bhnd_resource */ METHOD uint32_t read_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; } /** An implementation of bus_write_stream_1() compatible with bhnd_resource */ METHOD void write_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t value; } /** An implementation of bus_write_stream_2() compatible with bhnd_resource */ METHOD void write_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t value; } /** An implementation of bus_write_stream_4() compatible with bhnd_resource */ METHOD void write_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t value; } /** An implementation of bus_read_multi_1() compatible with bhnd_resource */ METHOD void read_multi_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_2() compatible with bhnd_resource */ METHOD void read_multi_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_4() compatible with bhnd_resource */ METHOD void read_multi_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_1() compatible with bhnd_resource */ METHOD void write_multi_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_2() compatible with bhnd_resource */ METHOD void write_multi_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_4() compatible with bhnd_resource */ METHOD void write_multi_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_stream_1() compatible * bhnd_resource */ METHOD void read_multi_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_stream_2() compatible * bhnd_resource */ METHOD void read_multi_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_read_multi_stream_4() compatible * bhnd_resource */ METHOD void read_multi_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_stream_1() compatible * bhnd_resource */ METHOD void write_multi_stream_1 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint8_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_stream_2() compatible with * bhnd_resource */ METHOD void write_multi_stream_2 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint16_t *datap; bus_size_t count; } /** An implementation of bus_write_multi_stream_4() compatible with * bhnd_resource */ METHOD void write_multi_stream_4 { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; uint32_t *datap; bus_size_t count; } +/** An implementation of bus_set_multi_1() compatible with bhnd_resource */ +METHOD void set_multi_1 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint8_t value; + bus_size_t count; +} + +/** An implementation of bus_set_multi_2() compatible with bhnd_resource */ +METHOD void set_multi_2 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint16_t value; + bus_size_t count; +} + +/** An implementation of bus_set_multi_4() compatible with bhnd_resource */ +METHOD void set_multi_4 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint32_t value; + bus_size_t count; +} + +/** An implementation of bus_set_region_1() compatible with bhnd_resource */ +METHOD void set_region_1 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint8_t value; + bus_size_t count; +} + +/** An implementation of bus_set_region_2() compatible with bhnd_resource */ +METHOD void set_region_2 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint16_t value; + bus_size_t count; +} + +/** An implementation of bus_set_region_4() compatible with bhnd_resource */ +METHOD void set_region_4 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint32_t value; + bus_size_t count; +} + +/** An implementation of bus_read_region_1() compatible with bhnd_resource */ +METHOD void read_region_1 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint8_t *datap; + bus_size_t count; +} + +/** An implementation of bus_read_region_2() compatible with bhnd_resource */ +METHOD void read_region_2 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint16_t *datap; + bus_size_t count; +} + +/** An implementation of bus_read_region_4() compatible with bhnd_resource */ +METHOD void read_region_4 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint32_t *datap; + bus_size_t count; +} + +/** An implementation of bus_read_region_stream_1() compatible with + * bhnd_resource */ +METHOD void read_region_stream_1 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint8_t *datap; + bus_size_t count; +} + +/** An implementation of bus_read_region_stream_2() compatible with + * bhnd_resource */ +METHOD void read_region_stream_2 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint16_t *datap; + bus_size_t count; +} + +/** An implementation of bus_read_region_stream_4() compatible with + * bhnd_resource */ +METHOD void read_region_stream_4 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint32_t *datap; + bus_size_t count; +} + +/** An implementation of bus_write_region_1() compatible with bhnd_resource */ +METHOD void write_region_1 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint8_t *datap; + bus_size_t count; +} + +/** An implementation of bus_write_region_2() compatible with bhnd_resource */ +METHOD void write_region_2 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint16_t *datap; + bus_size_t count; +} + +/** An implementation of bus_write_region_4() compatible with bhnd_resource */ +METHOD void write_region_4 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint32_t *datap; + bus_size_t count; +} + +/** An implementation of bus_write_region_stream_1() compatible with + * bhnd_resource */ +METHOD void write_region_stream_1 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint8_t *datap; + bus_size_t count; +} + +/** An implementation of bus_write_region_stream_2() compatible with + * bhnd_resource */ +METHOD void write_region_stream_2 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint16_t *datap; + bus_size_t count; +} + +/** An implementation of bus_write_region_stream_4() compatible with + * bhnd_resource */ +METHOD void write_region_stream_4 { + device_t dev; + device_t child; + struct bhnd_resource *r; + bus_size_t offset; + uint32_t *datap; + bus_size_t count; +} + /** An implementation of bus_barrier() compatible with bhnd_resource */ METHOD void barrier { device_t dev; device_t child; struct bhnd_resource *r; bus_size_t offset; bus_size_t length; int flags; } Index: head/sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c (revision 300249) +++ head/sys/dev/bhnd/bhndb/bhndb.c (revision 300250) @@ -1,1984 +1,1998 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __FBSDID("$FreeBSD$"); /* * Abstract BHND Bridge Device Driver * * Provides generic support for bridging from a parent bus (such as PCI) to * a BHND-compatible bus (e.g. bcma or siba). */ #include #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_chipc_if.h" #include "bhnd_nvram_if.h" #include "bhndbvar.h" #include "bhndb_bus_if.h" #include "bhndb_hwdata.h" #include "bhndb_private.h" /* Debugging flags */ static u_long bhndb_debug = 0; TUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug); enum { BHNDB_DEBUG_PRIO = 1 << 0, }; #define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug) static bool bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw); static int bhndb_initialize_region_cfg( struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw_priority *table, struct bhndb_resources *r); static int bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw **hw); static int bhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, struct bhnd_chipid *result); bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, device_t child); static struct rman *bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type); static int bhndb_init_child_resource(struct resource *r, struct resource *parent, bhnd_size_t offset, bhnd_size_t size); static int bhndb_activate_static_region( struct bhndb_softc *sc, struct bhndb_region *region, device_t child, int type, int rid, struct resource *r); static int bhndb_try_activate_resource( struct bhndb_softc *sc, device_t child, int type, int rid, struct resource *r, bool *indirect); /** * Default bhndb(4) implementation of DEVICE_PROBE(). * * This function provides the default bhndb implementation of DEVICE_PROBE(), * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge(). */ int bhndb_generic_probe(device_t dev) { return (BUS_PROBE_NOWILDCARD); } static void bhndb_probe_nomatch(device_t dev, device_t child) { const char *name; name = device_get_name(child); if (name == NULL) name = "unknown device"; device_printf(dev, "<%s> (no driver attached)\n", name); } static int bhndb_print_child(device_t dev, device_t child) { struct bhndb_softc *sc; struct resource_list *rl; int retval = 0; sc = device_get_softc(dev); retval += bus_print_child_header(dev, child); rl = BUS_GET_RESOURCE_LIST(dev, child); if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); } retval += bus_print_child_domain(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } static int bhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static int bhndb_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { struct bhndb_softc *sc; sc = device_get_softc(dev); snprintf(buf, buflen, "base=0x%llx", (unsigned long long) sc->chipid.enum_addr); return (0); } /** * Return true if @p devlist matches the @p hw specification. * * @param devlist A device table to match against. * @param num_devs The number of devices in @p devlist. * @param hw The hardware description to be matched against. */ static bool bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw) { for (u_int i = 0; i < hw->num_hw_reqs; i++) { const struct bhnd_core_match *match; bool found; match = &hw->hw_reqs[i]; found = false; for (int d = 0; d < num_devs; d++) { if (!bhnd_device_matches(devlist[d], match)) continue; found = true; break; } if (!found) return (false); } return (true); } /** * Initialize the region maps and priority configuration in @p r using * the provided priority @p table and the set of devices attached to * the bridged @p bus_dev . * * @param sc The bhndb device state. * @param devs All devices enumerated on the bridged bhnd bus. * @param ndevs The length of @p devs. * @param table Hardware priority table to be used to determine the relative * priorities of per-core port resources. * @param r The resource state to be configured. */ static int bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw_priority *table, struct bhndb_resources *r) { const struct bhndb_hw_priority *hp; bhnd_addr_t addr; bhnd_size_t size; size_t prio_low, prio_default, prio_high; int error; /* The number of port regions per priority band that must be accessible * via dynamic register windows */ prio_low = 0; prio_default = 0; prio_high = 0; /* * Register bridge regions covering all statically mapped ports. */ for (int i = 0; i < ndevs; i++) { const struct bhndb_regwin *regw; device_t child; child = devs[i]; for (regw = r->cfg->register_windows; regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) { /* Only core windows are supported */ if (regw->win_type != BHNDB_REGWIN_T_CORE) continue; /* Skip non-applicable register windows. */ if (!bhndb_regwin_matches_device(regw, child)) continue; /* Fetch the base address of the mapped port. */ error = bhnd_get_region_addr(child, regw->d.core.port_type, regw->d.core.port, regw->d.core.region, &addr, &size); if (error) return (error); /* * Always defer to the register window's size. * * If the port size is smaller than the window size, * this ensures that we fully utilize register windows * larger than the referenced port. * * If the port size is larger than the window size, this * ensures that we do not directly map the allocations * within the region to a too-small window. */ size = regw->win_size; /* * Add to the bus region list. * * The window priority for a statically mapped * region is always HIGH. */ error = bhndb_add_resource_region(r, addr, size, BHNDB_PRIORITY_HIGH, regw); if (error) return (error); } } /* * Perform priority accounting and register bridge regions for all * ports defined in the priority table */ for (int i = 0; i < ndevs; i++) { struct bhndb_region *region; device_t child; child = devs[i]; /* * Skip priority accounting for cores that ... */ /* ... do not require bridge resources */ if (bhnd_is_hw_disabled(child) || !device_is_enabled(child)) continue; /* ... do not have a priority table entry */ hp = bhndb_hw_priority_find_device(table, child); if (hp == NULL) continue; /* ... are explicitly disabled in the priority table. */ if (hp->priority == BHNDB_PRIORITY_NONE) continue; /* Determine the number of dynamic windows required and * register their bus_region entries. */ for (u_int i = 0; i < hp->num_ports; i++) { const struct bhndb_port_priority *pp; pp = &hp->ports[i]; /* Skip ports not defined on this device */ if (!bhnd_is_region_valid(child, pp->type, pp->port, pp->region)) { continue; } /* Fetch the address+size of the mapped port. */ error = bhnd_get_region_addr(child, pp->type, pp->port, pp->region, &addr, &size); if (error) return (error); /* Skip ports with an existing static mapping */ region = bhndb_find_resource_region(r, addr, size); if (region != NULL && region->static_regwin != NULL) continue; /* Define a dynamic region for this port */ error = bhndb_add_resource_region(r, addr, size, pp->priority, NULL); if (error) return (error); /* Update port mapping counts */ switch (pp->priority) { case BHNDB_PRIORITY_NONE: break; case BHNDB_PRIORITY_LOW: prio_low++; break; case BHNDB_PRIORITY_DEFAULT: prio_default++; break; case BHNDB_PRIORITY_HIGH: prio_high++; break; } } } /* Determine the minimum priority at which we'll allocate direct * register windows from our dynamic pool */ size_t prio_total = prio_low + prio_default + prio_high; if (prio_total <= r->dwa_count) { /* low+default+high priority regions get windows */ r->min_prio = BHNDB_PRIORITY_LOW; } else if (prio_default + prio_high <= r->dwa_count) { /* default+high priority regions get windows */ r->min_prio = BHNDB_PRIORITY_DEFAULT; } else { /* high priority regions get windows */ r->min_prio = BHNDB_PRIORITY_HIGH; } if (BHNDB_DEBUG(PRIO)) { struct bhndb_region *region; const char *direct_msg, *type_msg; bhndb_priority_t prio, prio_min; prio_min = r->min_prio; device_printf(sc->dev, "min_prio: %d\n", prio_min); STAILQ_FOREACH(region, &r->bus_regions, link) { prio = region->priority; direct_msg = prio >= prio_min ? "direct" : "indirect"; type_msg = region->static_regwin ? "static" : "dynamic"; device_printf(sc->dev, "region 0x%llx+0x%llx priority " "%u %s/%s\n", (unsigned long long) region->addr, (unsigned long long) region->size, region->priority, direct_msg, type_msg); } } return (0); } /** * Find a hardware specification for @p dev. * * @param sc The bhndb device state. * @param devs All devices enumerated on the bridged bhnd bus. * @param ndevs The length of @p devs. * @param[out] hw On success, the matched hardware specification. * with @p dev. * * @retval 0 success * @retval non-zero if an error occurs fetching device info for comparison. */ static int bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, const struct bhndb_hw **hw) { const struct bhndb_hw *next, *hw_table; /* Search for the first matching hardware config. */ hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev); for (next = hw_table; next->hw_reqs != NULL; next++) { if (!bhndb_hw_matches(devs, ndevs, next)) continue; /* Found */ *hw = next; return (0); } return (ENOENT); } /** * Read the ChipCommon identification data for this device. * * @param sc bhndb device state. * @param cfg The hardware configuration to use when mapping the ChipCommon * registers. * @param[out] result the chip identification data. * * @retval 0 success * @retval non-zero if the ChipCommon identification data could not be read. */ static int bhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, struct bhnd_chipid *result) { const struct bhnd_chipid *parent_cid; const struct bhndb_regwin *cc_win; struct resource_spec rs; int error; /* Let our parent device override the discovery process */ parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); if (parent_cid != NULL) { *result = *parent_cid; return (0); } /* Find a register window we can use to map the first CHIPC_CHIPID_SIZE * of ChipCommon registers. */ cc_win = bhndb_regwin_find_best(cfg->register_windows, BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE); if (cc_win == NULL) { device_printf(sc->dev, "no chipcommon register window\n"); return (0); } /* We can assume a device without a static ChipCommon window uses the * default ChipCommon address. */ if (cc_win->win_type == BHNDB_REGWIN_T_DYN) { error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win, BHND_DEFAULT_CHIPC_ADDR); if (error) { device_printf(sc->dev, "failed to set chipcommon " "register window\n"); return (error); } } /* Let the default bhnd implemenation alloc/release the resource and * perform the read */ rs.type = cc_win->res.type; rs.rid = cc_win->res.rid; rs.flags = RF_ACTIVE; return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset, result)); } /** * Helper function that must be called by subclass bhndb(4) drivers * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4) * APIs on the bridge device. * * @param dev The bridge device to attach. * @param bridge_devclass The device class of the bridging core. This is used * to automatically detect the bridge core, and to disable additional bridge * cores (e.g. PCMCIA on a PCIe device). */ int bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass) { struct bhndb_devinfo *dinfo; struct bhndb_softc *sc; const struct bhndb_hwcfg *cfg; int error; sc = device_get_softc(dev); sc->dev = dev; sc->parent_dev = device_get_parent(dev); sc->bridge_class = bridge_devclass; BHNDB_LOCK_INIT(sc); /* Read our chip identification data */ cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid))) return (error); /* Populate generic resource allocation state. */ sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); if (sc->bus_res == NULL) { return (ENXIO); } /* Attach our bridged bus device */ sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1); if (sc->bus_dev == NULL) { error = ENXIO; goto failed; } /* Configure address space */ dinfo = device_get_ivars(sc->bus_dev); dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED; /* Finish attach */ return (bus_generic_attach(dev)); failed: BHNDB_LOCK_DESTROY(sc); if (sc->bus_res != NULL) bhndb_free_resources(sc->bus_res); return (error); } /** * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG(). * * This function provides the default bhndb implementation of * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver * overriding BHNDB_INIT_FULL_CONFIG(). * * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final * bridge configuration based on the hardware information enumerated by the * child bus, and will reset all resource allocation state on the bridge. * * When calling this method: * - Any bus resources previously allocated by @p child must be deallocated. * - The @p child bus must have performed initial enumeration -- but not * probe or attachment -- of its children. */ int bhndb_generic_init_full_config(device_t dev, device_t child, const struct bhndb_hw_priority *hw_prio_table) { struct bhndb_softc *sc; const struct bhndb_hw *hw; struct bhndb_resources *r; device_t *devs; device_t hostb; int ndevs; int error; sc = device_get_softc(dev); hostb = NULL; /* Fetch the full set of bhnd-attached cores */ if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) return (error); /* Find our host bridge device */ hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child); if (hostb == NULL) { device_printf(sc->dev, "no host bridge core found\n"); error = ENODEV; goto cleanup; } /* Find our full register window configuration */ if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) { device_printf(sc->dev, "unable to identify device, " " using generic bridge resource definitions\n"); error = 0; goto cleanup; } if (bootverbose || BHNDB_DEBUG(PRIO)) device_printf(sc->dev, "%s resource configuration\n", hw->name); /* Release existing resource state */ BHNDB_LOCK(sc); bhndb_free_resources(sc->bus_res); sc->bus_res = NULL; BHNDB_UNLOCK(sc); /* Allocate new resource state */ r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg); if (r == NULL) { error = ENXIO; goto cleanup; } /* Initialize our resource priority configuration */ error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r); if (error) { bhndb_free_resources(r); goto cleanup; } /* Update our bridge state */ BHNDB_LOCK(sc); sc->bus_res = r; sc->hostb_dev = hostb; BHNDB_UNLOCK(sc); cleanup: free(devs, M_TEMP); return (error); } /** * Default bhndb(4) implementation of DEVICE_DETACH(). * * This function detaches any child devices, and if successful, releases all * resources held by the bridge device. */ int bhndb_generic_detach(device_t dev) { struct bhndb_softc *sc; int error; sc = device_get_softc(dev); /* Detach children */ if ((error = bus_generic_detach(dev))) return (error); /* Clean up our driver state. */ bhndb_free_resources(sc->bus_res); BHNDB_LOCK_DESTROY(sc); return (0); } /** * Default bhndb(4) implementation of DEVICE_SUSPEND(). * * This function calls bus_generic_suspend() (or implements equivalent * behavior). */ int bhndb_generic_suspend(device_t dev) { return (bus_generic_suspend(dev)); } /** * Default bhndb(4) implementation of DEVICE_RESUME(). * * This function calls bus_generic_resume() (or implements equivalent * behavior). */ int bhndb_generic_resume(device_t dev) { struct bhndb_softc *sc; struct bhndb_resources *bus_res; struct bhndb_dw_alloc *dwa; int error; sc = device_get_softc(dev); bus_res = sc->bus_res; /* Guarantee that all in-use dynamic register windows are mapped to * their previously configured target address. */ BHNDB_LOCK(sc); for (size_t i = 0; i < bus_res->dwa_count; i++) { dwa = &bus_res->dw_alloc[i]; /* Skip regions that were not previously used */ if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0) continue; /* Otherwise, ensure the register window is correct before * any children attempt MMIO */ error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); if (error) break; } BHNDB_UNLOCK(sc); /* Error restoring hardware state; children cannot be safely resumed */ if (error) { device_printf(dev, "Unable to restore hardware configuration; " "cannot resume: %d\n", error); return (error); } return (bus_generic_resume(dev)); } /** * Default implementation of BHNDB_SUSPEND_RESOURCE. */ static void bhndb_suspend_resource(device_t dev, device_t child, int type, struct resource *r) { struct bhndb_softc *sc; struct bhndb_dw_alloc *dwa; sc = device_get_softc(dev); // TODO: IRQs? if (type != SYS_RES_MEMORY) return; BHNDB_LOCK(sc); dwa = bhndb_dw_find_resource(sc->bus_res, r); if (dwa == NULL) { BHNDB_UNLOCK(sc); return; } if (BHNDB_DEBUG(PRIO)) device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n", type, rman_get_start(r), rman_get_size(r)); /* Release the resource's window reference */ bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); } /** * Default implementation of BHNDB_RESUME_RESOURCE. */ static int bhndb_resume_resource(device_t dev, device_t child, int type, struct resource *r) { struct bhndb_softc *sc; sc = device_get_softc(dev); // TODO: IRQs? if (type != SYS_RES_MEMORY) return (0); /* Inactive resources don't require reallocation of bridge resources */ if (!(rman_get_flags(r) & RF_ACTIVE)) return (0); if (BHNDB_DEBUG(PRIO)) device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n", type, rman_get_start(r), rman_get_size(r)); return (bhndb_try_activate_resource(sc, rman_get_device(r), type, rman_get_rid(r), r, NULL)); } /** * Default bhndb(4) implementation of BUS_READ_IVAR(). */ static int bhndb_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { return (ENOENT); } /** * Default bhndb(4) implementation of BUS_WRITE_IVAR(). */ static int bhndb_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { return (ENOENT); } /** * Return the address space for the given @p child device. */ bhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, device_t child) { struct bhndb_devinfo *dinfo; device_t imd_dev; /* Find the directly attached parent of the requesting device */ imd_dev = child; while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev) imd_dev = device_get_parent(imd_dev); if (imd_dev == NULL) panic("bhndb address space request for non-child device %s\n", device_get_nameunit(child)); dinfo = device_get_ivars(imd_dev); return (dinfo->addrspace); } /** * Return the rman instance for a given resource @p type, if any. * * @param sc The bhndb device state. * @param child The requesting child. * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) */ static struct rman * bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type) { switch (bhndb_get_addrspace(sc, child)) { case BHNDB_ADDRSPACE_NATIVE: switch (type) { case SYS_RES_MEMORY: return (&sc->bus_res->ht_mem_rman); case SYS_RES_IRQ: return (NULL); default: return (NULL); }; case BHNDB_ADDRSPACE_BRIDGED: switch (type) { case SYS_RES_MEMORY: return (&sc->bus_res->br_mem_rman); case SYS_RES_IRQ: // TODO // return &sc->irq_rman; return (NULL); default: return (NULL); }; } /* Quieten gcc */ return (NULL); } /** * Default implementation of BUS_ADD_CHILD() */ static device_t bhndb_add_child(device_t dev, u_int order, const char *name, int unit) { struct bhndb_devinfo *dinfo; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (NULL); dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT); if (dinfo == NULL) { device_delete_child(dev, child); return (NULL); } dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; resource_list_init(&dinfo->resources); device_set_ivars(child, dinfo); return (child); } /** * Default implementation of BUS_CHILD_DELETED(). */ static void bhndb_child_deleted(device_t dev, device_t child) { struct bhndb_devinfo *dinfo = device_get_ivars(child); if (dinfo != NULL) { resource_list_free(&dinfo->resources); free(dinfo, M_BHND); } device_set_ivars(child, NULL); } /** * Default implementation of BHNDB_GET_CHIPID(). */ static const struct bhnd_chipid * bhndb_get_chipid(device_t dev, device_t child) { struct bhndb_softc *sc = device_get_softc(dev); return (&sc->chipid); } /** * Default implementation of BHNDB_IS_HW_DISABLED(). */ static bool bhndb_is_hw_disabled(device_t dev, device_t child) { struct bhndb_softc *sc; struct bhnd_core_info core; sc = device_get_softc(dev); /* Requestor must be attached to the bhnd bus */ if (device_get_parent(child) != sc->bus_dev) { return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); } /* Fetch core info */ core = bhnd_get_core_info(child); /* Try to defer to the bhndb bus parent */ if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core)) return (true); /* Otherwise, we treat bridge-capable cores as unpopulated if they're * not the configured host bridge */ if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core))) return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child); /* Otherwise, assume the core is populated */ return (false); } /* ascending core index comparison used by bhndb_find_hostb_device() */ static int compare_core_index(const void *lhs, const void *rhs) { u_int left = bhnd_get_core_index(*(const device_t *) lhs); u_int right = bhnd_get_core_index(*(const device_t *) rhs); if (left < right) return (-1); else if (left > right) return (1); else return (0); } /** * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE(). * * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged * bhnd(4) devices to determine the hostb core: * * - The core must have a Broadcom vendor ID. * - The core devclass must match the bridge type. * - The core must be the first device on the bus with the bridged device * class. * * @param dev The bhndb device * @param child The requesting bhnd bus. */ static device_t bhndb_find_hostb_device(device_t dev, device_t child) { struct bhndb_softc *sc; struct bhnd_core_match md; device_t hostb_dev, *devlist; int devcnt, error; sc = device_get_softc(dev); /* Determine required device class and set up a match descriptor. */ md = (struct bhnd_core_match) { .vendor = BHND_MFGID_BCM, .device = BHND_COREID_INVALID, .hwrev = { BHND_HWREV_INVALID, BHND_HWREV_INVALID }, .class = sc->bridge_class, .unit = 0 }; /* Must be the absolute first matching device on the bus. */ if ((error = device_get_children(child, &devlist, &devcnt))) return (false); /* Sort by core index value, ascending */ qsort(devlist, devcnt, sizeof(*devlist), compare_core_index); /* Find the hostb device */ hostb_dev = NULL; for (int i = 0; i < devcnt; i++) { if (bhnd_device_matches(devlist[i], &md)) { hostb_dev = devlist[i]; break; } } /* Clean up */ free(devlist, M_TEMP); return (hostb_dev); } /** * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). */ static struct resource * bhndb_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct bhndb_softc *sc; struct resource_list_entry *rle; struct resource *rv; struct rman *rm; int error; bool passthrough, isdefault; sc = device_get_softc(dev); passthrough = (device_get_parent(child) != dev); isdefault = RMAN_IS_DEFAULT_RANGE(start, end); rle = NULL; /* Populate defaults */ if (!passthrough && isdefault) { /* Fetch the resource list entry. */ rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), type, *rid); if (rle == NULL) { device_printf(dev, "default resource %#x type %d for child %s " "not found\n", *rid, type, device_get_nameunit(child)); return (NULL); } if (rle->res != NULL) { device_printf(dev, "resource entry %#x type %d for child %s is busy\n", *rid, type, device_get_nameunit(child)); return (NULL); } start = rle->start; end = rle->end; count = ulmax(count, rle->count); } /* Validate resource addresses */ if (start > end || count > ((end - start) + 1)) return (NULL); /* Fetch the resource manager */ rm = bhndb_get_rman(sc, child, type); if (rm == NULL) return (NULL); /* Make our reservation */ rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); /* Activate */ if (flags & RF_ACTIVE) { error = bus_activate_resource(child, type, *rid, rv); if (error) { device_printf(dev, "failed to activate entry %#x type %d for " "child %s: %d\n", *rid, type, device_get_nameunit(child), error); rman_release_resource(rv); return (NULL); } } /* Update child's resource list entry */ if (rle != NULL) { rle->res = rv; rle->start = rman_get_start(rv); rle->end = rman_get_end(rv); rle->count = rman_get_size(rv); } return (rv); } /** * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE(). */ static int bhndb_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { int error; /* Deactivate resources */ if (rman_get_flags(r) & RF_ACTIVE) { error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); if (error) return (error); } if ((error = rman_release_resource(r))) return (error); return (0); } /** * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE(). */ static int bhndb_adjust_resource(device_t dev, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end) { struct bhndb_softc *sc; struct rman *rm; int error; sc = device_get_softc(dev); error = 0; /* Fetch resource manager */ rm = bhndb_get_rman(sc, child, type); if (rm == NULL) return (ENXIO); if (!rman_is_region_manager(r, rm)) return (ENXIO); /* If active, adjustment is limited by the assigned window. */ BHNDB_LOCK(sc); // TODO: Currently unsupported error = ENODEV; BHNDB_UNLOCK(sc); if (!error) error = rman_adjust_resource(r, start, end); return (error); } /** * Initialize child resource @p r with a virtual address, tag, and handle * copied from @p parent, adjusted to contain only the range defined by * @p offsize and @p size. * * @param r The register to be initialized. * @param parent The parent bus resource that fully contains the subregion. * @param offset The subregion offset within @p parent. * @param size The subregion size. * @p r. */ static int bhndb_init_child_resource(struct resource *r, struct resource *parent, bhnd_size_t offset, bhnd_size_t size) { bus_space_handle_t bh, child_bh; bus_space_tag_t bt; uintptr_t vaddr; int error; /* Fetch the parent resource's real bus values */ vaddr = (uintptr_t) rman_get_virtual(parent); bt = rman_get_bustag(parent); bh = rman_get_bushandle(parent); /* Configure child resource with window-adjusted real bus values */ vaddr += offset; error = bus_space_subregion(bt, bh, offset, size, &child_bh); if (error) return (error); rman_set_virtual(r, (void *) vaddr); rman_set_bustag(r, bt); rman_set_bushandle(r, child_bh); return (0); } /** * Attempt activation of a fixed register window mapping for @p child. * * @param sc BHNDB device state. * @param region The static region definition capable of mapping @p r. * @param child A child requesting resource activation. * @param type Resource type. * @param rid Resource identifier. * @param r Resource to be activated. * * @retval 0 if @p r was activated successfully * @retval ENOENT if no fixed register window was found. * @retval non-zero if @p r could not be activated. */ static int bhndb_activate_static_region(struct bhndb_softc *sc, struct bhndb_region *region, device_t child, int type, int rid, struct resource *r) { struct resource *bridge_res; const struct bhndb_regwin *win; bhnd_size_t parent_offset; rman_res_t r_start, r_size; int error; win = region->static_regwin; KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type), ("can't activate non-static region")); r_start = rman_get_start(r); r_size = rman_get_size(r); /* Find the corresponding bridge resource */ bridge_res = bhndb_find_regwin_resource(sc->bus_res, win); if (bridge_res == NULL) return (ENXIO); /* Calculate subregion offset within the parent resource */ parent_offset = r_start - region->addr; parent_offset += win->win_offset; /* Configure resource with its real bus values. */ error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size); if (error) return (error); /* Mark active */ if ((error = rman_activate_resource(r))) return (error); return (0); } /** * Attempt to allocate/retain a dynamic register window for @p r, returning * the retained window. * * @param sc The bhndb driver state. * @param r The resource for which a window will be retained. */ static struct bhndb_dw_alloc * bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r) { struct bhndb_dw_alloc *dwa; rman_res_t r_start, r_size; int error; BHNDB_LOCK_ASSERT(sc, MA_OWNED); r_start = rman_get_start(r); r_size = rman_get_size(r); /* Look for an existing dynamic window we can reference */ dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size); if (dwa != NULL) { if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0) return (dwa); return (NULL); } /* Otherwise, try to reserve a free window */ dwa = bhndb_dw_next_free(sc->bus_res); if (dwa == NULL) { /* No free windows */ 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)); if (error) { device_printf(sc->dev, "dynamic window initialization " "for 0x%llx-0x%llx failed: %d\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1, error); return (NULL); } /* Add our reservation */ if (bhndb_dw_retain(sc->bus_res, dwa, r)) return (NULL); return (dwa); } /** * Activate a resource using any viable static or dynamic register window. * * @param sc The bhndb driver state. * @param child The child holding ownership of @p r. * @param type The type of the resource to be activated. * @param rid The resource ID of @p r. * @param r The resource to be activated * @param[out] indirect On error and if not NULL, will be set to 'true' if * the caller should instead use an indirect resource mapping. * * @retval 0 success * @retval non-zero activation failed. */ static int bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type, int rid, struct resource *r, bool *indirect) { struct bhndb_region *region; struct bhndb_dw_alloc *dwa; bhndb_priority_t dw_priority; rman_res_t r_start, r_size; rman_res_t parent_offset; int error; BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); // TODO - IRQs if (type != SYS_RES_MEMORY) return (ENXIO); if (indirect) *indirect = false; r_start = rman_get_start(r); r_size = rman_get_size(r); /* Activate native addrspace resources using the host address space */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) { struct resource *parent; /* Find the bridge resource referenced by the child */ parent = bhndb_find_resource_range(sc->bus_res, r_start, r_size); if (parent == NULL) { device_printf(sc->dev, "host resource not found " "for 0x%llx-0x%llx\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1); return (ENOENT); } /* Initialize child resource with the real bus values */ error = bhndb_init_child_resource(r, parent, r_start - rman_get_start(parent), r_size); if (error) return (error); /* Try to activate child resource */ return (rman_activate_resource(r)); } /* Default to low priority */ dw_priority = BHNDB_PRIORITY_LOW; /* Look for a bus region matching the resource's address range */ region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); if (region != NULL) dw_priority = region->priority; /* Prefer static mappings over consuming a dynamic windows. */ if (region && region->static_regwin) { error = bhndb_activate_static_region(sc, region, child, type, rid, r); if (error) device_printf(sc->dev, "static window allocation " "for 0x%llx-0x%llx failed\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1); return (error); } /* A dynamic window will be required; is this resource high enough * priority to be reserved a dynamic window? */ if (dw_priority < sc->bus_res->min_prio) { if (indirect) *indirect = true; return (ENOMEM); } /* Find and retain a usable window */ BHNDB_LOCK(sc); { dwa = bhndb_retain_dynamic_window(sc, r); } BHNDB_UNLOCK(sc); if (dwa == NULL) { if (indirect) *indirect = true; return (ENOMEM); } /* Configure resource with its real bus values. */ parent_offset = dwa->win->win_offset; parent_offset += r_start - dwa->target; error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset, dwa->win->win_size); if (error) goto failed; /* Mark active */ if ((error = rman_activate_resource(r))) goto failed; return (0); failed: /* Release our region allocation. */ BHNDB_LOCK(sc); bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); return (error); } /** * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE(). * * Maps resource activation requests to a viable static or dynamic * register window, if any. */ static int bhndb_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct bhndb_softc *sc = device_get_softc(dev); return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); } /** * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). */ static int bhndb_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct bhndb_dw_alloc *dwa; struct bhndb_softc *sc; struct rman *rm; int error; sc = device_get_softc(dev); if ((rm = bhndb_get_rman(sc, child, type)) == NULL) return (EINVAL); /* Mark inactive */ if ((error = rman_deactivate_resource(r))) return (error); /* Free any dynamic window allocation. */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { BHNDB_LOCK(sc); dwa = bhndb_dw_find_resource(sc->bus_res, r); if (dwa != NULL) bhndb_dw_release(sc->bus_res, dwa, r); BHNDB_UNLOCK(sc); } return (0); } /** * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST(). */ static struct resource_list * bhndb_get_resource_list(device_t dev, device_t child) { struct bhndb_devinfo *dinfo = device_get_ivars(child); return (&dinfo->resources); } /** * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE(). * * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to * be activated by the bridge. * * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register * window, a dynamic register window, or configures @p r as an indirect * resource -- in that order. */ static int bhndb_activate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { struct bhndb_softc *sc; struct bhndb_region *region; rman_res_t r_start, r_size; int error; bool indirect; KASSERT(!r->direct, ("direct flag set on inactive resource")); KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE), ("RF_ACTIVE set on inactive resource")); sc = device_get_softc(dev); r_start = rman_get_start(r->res); r_size = rman_get_size(r->res); /* Verify bridged address range's resource priority, and skip direct * allocation if the priority is too low. */ if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { bhndb_priority_t r_prio; region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); if (region != NULL) r_prio = region->priority; else r_prio = BHNDB_PRIORITY_NONE; /* If less than the minimum dynamic window priority, this * resource should always be indirect. */ if (r_prio < sc->bus_res->min_prio) return (0); } /* Attempt direct activation */ error = bhndb_try_activate_resource(sc, child, type, rid, r->res, &indirect); if (!error) { r->direct = true; } else if (indirect) { /* The request was valid, but no viable register window is * available; indirection must be employed. */ error = 0; r->direct = false; } if (BHNDB_DEBUG(PRIO) && bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { device_printf(child, "activated 0x%llx-0x%llx as %s " "resource\n", (unsigned long long) r_start, (unsigned long long) r_start + r_size - 1, r->direct ? "direct" : "indirect"); } return (error); }; /** * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE(). */ static int bhndb_deactivate_bhnd_resource(device_t dev, device_t child, int type, int rid, struct bhnd_resource *r) { int error; /* Indirect resources don't require activation */ if (!r->direct) return (0); KASSERT(rman_get_flags(r->res) & RF_ACTIVE, ("RF_ACTIVE not set on direct resource")); /* Perform deactivation */ error = bus_deactivate_resource(child, type, rid, r->res); if (!error) r->direct = false; return (error); }; /** * Slow path for bhndb_io_resource(). * * Iterates over the existing allocated dynamic windows looking for a viable * in-use region; the first matching region is returned. */ static struct bhndb_dw_alloc * bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, bus_size_t *offset) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; BHNDB_LOCK_ASSERT(sc, MA_OWNED); br = sc->bus_res; /* Search for an existing dynamic mapping of this address range. * Static regions are not searched, as a statically mapped * region would never be allocated as an indirect resource. */ for (size_t i = 0; i < br->dwa_count; i++) { const struct bhndb_regwin *win; dwa = &br->dw_alloc[i]; win = dwa->win; KASSERT(win->win_type == BHNDB_REGWIN_T_DYN, ("invalid register window type")); /* Verify the range */ if (addr < dwa->target) continue; if (addr + size > dwa->target + win->win_size) continue; /* Found */ *offset = dwa->win->win_offset; *offset += addr - dwa->target; return (dwa); } /* not found */ return (NULL); } /** * Find the bridge resource to be used for I/O requests. * * @param sc Bridge driver state. * @param addr The I/O target address. * @param size The size of the I/O operation to be performed at @p addr. * @param[out] offset The offset within the returned resource at which * to perform the I/O request. */ static inline struct bhndb_dw_alloc * bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, bus_size_t *offset) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; int error; BHNDB_LOCK_ASSERT(sc, MA_OWNED); br = sc->bus_res; /* Try to fetch a free window */ dwa = bhndb_dw_next_free(br); /* * If no dynamic windows are available, look for an existing * region that maps the target range. * * If none are found, this is a child driver bug -- our window * over-commit should only fail in the case where a child driver leaks * resources, or perform operations out-of-order. * * Broadcom HND chipsets are designed to not require register window * swapping during execution; as long as the child devices are * attached/detached correctly, using the hardware's required order * of operations, there should always be a window available for the * current operation. */ if (dwa == NULL) { dwa = bhndb_io_resource_slow(sc, addr, size, offset); if (dwa == NULL) { panic("register windows exhausted attempting to map " "0x%llx-0x%llx\n", (unsigned long long) addr, (unsigned long long) addr+size-1); } return (dwa); } /* Adjust the window if the I/O request won't fit in the current * target range. */ if (addr < dwa->target || (dwa->target + dwa->win->win_size) - addr < size) { error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr, size); if (error) { panic("failed to set register window target mapping " "0x%llx-0x%llx\n", (unsigned long long) addr, (unsigned long long) addr+size-1); } } /* Calculate the offset and return */ *offset = (addr - dwa->target) + dwa->win->win_offset; return (dwa); } /* * BHND_BUS_(READ|WRITE_* implementations */ /* bhndb_bus_(read|write) common implementation */ #define BHNDB_IO_COMMON_SETUP(_io_size) \ struct bhndb_softc *sc; \ struct bhndb_dw_alloc *dwa; \ struct resource *io_res; \ bus_size_t io_offset; \ \ sc = device_get_softc(dev); \ \ BHNDB_LOCK(sc); \ dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ offset, _io_size, &io_offset); \ io_res = dwa->parent_res; \ \ KASSERT(!r->direct, \ ("bhnd_bus slow path used for direct resource")); \ \ KASSERT(rman_get_flags(io_res) & RF_ACTIVE, \ ("i/o resource is not active")); #define BHNDB_IO_COMMON_TEARDOWN() \ BHNDB_UNLOCK(sc); /* Defines a bhndb_bus_read_* method implementation */ #define BHNDB_IO_READ(_type, _name) \ static _type \ bhndb_bus_read_ ## _name (device_t dev, device_t child, \ struct bhnd_resource *r, bus_size_t offset) \ { \ _type v; \ BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ v = bus_read_ ## _name (io_res, io_offset); \ BHNDB_IO_COMMON_TEARDOWN(); \ \ return (v); \ } /* Defines a bhndb_bus_write_* method implementation */ #define BHNDB_IO_WRITE(_type, _name) \ static void \ bhndb_bus_write_ ## _name (device_t dev, device_t child, \ struct bhnd_resource *r, bus_size_t offset, _type value) \ { \ BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ bus_write_ ## _name (io_res, io_offset, value); \ BHNDB_IO_COMMON_TEARDOWN(); \ } -/* Defines a bhndb_bus_(read|write)_multi_* method implementation */ -#define BHNDB_IO_MULTI(_type, _rw, _name) \ +/* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */ +#define BHNDB_IO_MISC(_type, _ptr, _op, _size) \ static void \ -bhndb_bus_ ## _rw ## _multi_ ## _name (device_t dev, \ +bhndb_bus_ ## _op ## _ ## _size (device_t dev, \ device_t child, struct bhnd_resource *r, bus_size_t offset, \ - _type *datap, bus_size_t count) \ + _type _ptr datap, bus_size_t count) \ { \ BHNDB_IO_COMMON_SETUP(sizeof(_type) * count); \ - bus_ ## _rw ## _multi_ ## _name (io_res, io_offset, \ + bus_ ## _op ## _ ## _size (io_res, io_offset, \ datap, count); \ BHNDB_IO_COMMON_TEARDOWN(); \ } /* Defines a complete set of read/write methods */ #define BHNDB_IO_METHODS(_type, _size) \ BHNDB_IO_READ(_type, _size) \ BHNDB_IO_WRITE(_type, _size) \ \ BHNDB_IO_READ(_type, stream_ ## _size) \ BHNDB_IO_WRITE(_type, stream_ ## _size) \ \ - BHNDB_IO_MULTI(_type, read, _size) \ - BHNDB_IO_MULTI(_type, write, _size) \ + BHNDB_IO_MISC(_type, *, read_multi, _size) \ + BHNDB_IO_MISC(_type, *, write_multi, _size) \ \ - BHNDB_IO_MULTI(_type, read, stream_ ## _size) \ - BHNDB_IO_MULTI(_type, write, stream_ ## _size) + BHNDB_IO_MISC(_type, *, read_multi_stream, _size) \ + BHNDB_IO_MISC(_type, *, write_multi_stream, _size) \ + \ + BHNDB_IO_MISC(_type, , set_multi, _size) \ + BHNDB_IO_MISC(_type, , set_region, _size) \ + BHNDB_IO_MISC(_type, *, read_region, _size) \ + BHNDB_IO_MISC(_type, *, write_region, _size) \ + \ + BHNDB_IO_MISC(_type, *, read_region_stream, _size) \ + BHNDB_IO_MISC(_type, *, write_region_stream, _size) BHNDB_IO_METHODS(uint8_t, 1); BHNDB_IO_METHODS(uint16_t, 2); BHNDB_IO_METHODS(uint32_t, 4); /** * Default bhndb(4) implementation of BHND_BUS_BARRIER(). */ static void bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r, bus_size_t offset, bus_size_t length, int flags) { - bus_size_t remain; - BHNDB_IO_COMMON_SETUP(length); - /* TODO: It's unclear whether we need a barrier implementation, - * and if we do, what it needs to actually do. This may need - * revisiting once we have a better idea of requirements after - * porting the core drivers. */ - panic("implementation incorrect"); + bus_barrier(io_res, io_offset + offset, length, flags); - /* Use 4-byte reads where possible */ - remain = length % sizeof(uint32_t); - for (bus_size_t i = 0; i < (length - remain); i += 4) - bus_read_4(io_res, io_offset + offset + i); - - /* Use 1 byte reads for the remainder */ - for (bus_size_t i = 0; i < remain; i++) - bus_read_1(io_res, io_offset + offset + length + i); - BHNDB_IO_COMMON_TEARDOWN(); } /** * Default bhndb(4) implementation of BUS_SETUP_INTR(). */ static int bhndb_setup_intr(device_t dev, device_t child, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). */ static int bhndb_teardown_intr(device_t dev, device_t child, struct resource *r, void *cookie) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_CONFIG_INTR(). */ static int bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_BIND_INTR(). */ static int bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). */ static int bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, const char *descr) { // TODO return (EOPNOTSUPP); } /** * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). */ static bus_dma_tag_t bhndb_get_dma_tag(device_t dev, device_t child) { // TODO return (NULL); } static device_method_t bhndb_methods[] = { /* Device interface */ \ DEVMETHOD(device_probe, bhndb_generic_probe), DEVMETHOD(device_detach, bhndb_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bhndb_generic_suspend), DEVMETHOD(device_resume, bhndb_generic_resume), /* Bus interface */ DEVMETHOD(bus_probe_nomatch, bhndb_probe_nomatch), DEVMETHOD(bus_print_child, bhndb_print_child), DEVMETHOD(bus_child_pnpinfo_str, bhndb_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, bhndb_child_location_str), DEVMETHOD(bus_add_child, bhndb_add_child), DEVMETHOD(bus_child_deleted, bhndb_child_deleted), DEVMETHOD(bus_alloc_resource, bhndb_alloc_resource), DEVMETHOD(bus_release_resource, bhndb_release_resource), DEVMETHOD(bus_activate_resource, bhndb_activate_resource), DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), DEVMETHOD(bus_setup_intr, bhndb_setup_intr), DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), DEVMETHOD(bus_config_intr, bhndb_config_intr), DEVMETHOD(bus_bind_intr, bhndb_bind_intr), DEVMETHOD(bus_describe_intr, bhndb_describe_intr), DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), DEVMETHOD(bus_adjust_resource, bhndb_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_get_resource_list, bhndb_get_resource_list), DEVMETHOD(bus_read_ivar, bhndb_read_ivar), DEVMETHOD(bus_write_ivar, bhndb_write_ivar), /* BHNDB interface */ DEVMETHOD(bhndb_get_chipid, bhndb_get_chipid), DEVMETHOD(bhndb_init_full_config, bhndb_generic_init_full_config), DEVMETHOD(bhndb_find_hostb_device, bhndb_find_hostb_device), DEVMETHOD(bhndb_suspend_resource, bhndb_suspend_resource), DEVMETHOD(bhndb_resume_resource, bhndb_resume_resource), /* BHND interface */ DEVMETHOD(bhnd_bus_is_hw_disabled, bhndb_is_hw_disabled), DEVMETHOD(bhnd_bus_get_chipid, bhndb_get_chipid), 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_read_1, bhndb_bus_read_1), DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), DEVMETHOD(bhnd_bus_write_1, bhndb_bus_write_1), DEVMETHOD(bhnd_bus_write_2, bhndb_bus_write_2), DEVMETHOD(bhnd_bus_write_4, bhndb_bus_write_4), DEVMETHOD(bhnd_bus_read_stream_1, bhndb_bus_read_stream_1), DEVMETHOD(bhnd_bus_read_stream_2, bhndb_bus_read_stream_2), DEVMETHOD(bhnd_bus_read_stream_4, bhndb_bus_read_stream_4), DEVMETHOD(bhnd_bus_write_stream_1, bhndb_bus_write_stream_1), DEVMETHOD(bhnd_bus_write_stream_2, bhndb_bus_write_stream_2), DEVMETHOD(bhnd_bus_write_stream_4, bhndb_bus_write_stream_4), DEVMETHOD(bhnd_bus_read_multi_1, bhndb_bus_read_multi_1), DEVMETHOD(bhnd_bus_read_multi_2, bhndb_bus_read_multi_2), DEVMETHOD(bhnd_bus_read_multi_4, bhndb_bus_read_multi_4), DEVMETHOD(bhnd_bus_write_multi_1, bhndb_bus_write_multi_1), DEVMETHOD(bhnd_bus_write_multi_2, bhndb_bus_write_multi_2), DEVMETHOD(bhnd_bus_write_multi_4, bhndb_bus_write_multi_4), DEVMETHOD(bhnd_bus_read_multi_stream_1, bhndb_bus_read_multi_stream_1), DEVMETHOD(bhnd_bus_read_multi_stream_2, bhndb_bus_read_multi_stream_2), DEVMETHOD(bhnd_bus_read_multi_stream_4, bhndb_bus_read_multi_stream_4), DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1), DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2), DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4), + + DEVMETHOD(bhnd_bus_set_multi_1, bhndb_bus_set_multi_1), + DEVMETHOD(bhnd_bus_set_multi_2, bhndb_bus_set_multi_2), + DEVMETHOD(bhnd_bus_set_multi_4, bhndb_bus_set_multi_4), + DEVMETHOD(bhnd_bus_set_region_1, bhndb_bus_set_region_1), + DEVMETHOD(bhnd_bus_set_region_2, bhndb_bus_set_region_2), + DEVMETHOD(bhnd_bus_set_region_4, bhndb_bus_set_region_4), + + DEVMETHOD(bhnd_bus_read_region_1, bhndb_bus_read_region_1), + DEVMETHOD(bhnd_bus_read_region_2, bhndb_bus_read_region_2), + DEVMETHOD(bhnd_bus_read_region_4, bhndb_bus_read_region_4), + DEVMETHOD(bhnd_bus_write_region_1, bhndb_bus_write_region_1), + DEVMETHOD(bhnd_bus_write_region_2, bhndb_bus_write_region_2), + DEVMETHOD(bhnd_bus_write_region_4, bhndb_bus_write_region_4), + + DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1), + DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2), + DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4), + DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1), + DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2), + DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4), DEVMETHOD(bhnd_bus_barrier, bhndb_bus_barrier), DEVMETHOD_END }; devclass_t bhndb_devclass; DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc)); MODULE_VERSION(bhndb, 1); MODULE_DEPEND(bhndb, bhnd, 1, 1, 1); MODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1); Index: head/sys/dev/bhnd/nvram/bhnd_sprom.c =================================================================== --- head/sys/dev/bhnd/nvram/bhnd_sprom.c (revision 300249) +++ head/sys/dev/bhnd/nvram/bhnd_sprom.c (revision 300250) @@ -1,572 +1,568 @@ /*- * Copyright (c) 2015 Landon Fuller * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvramvar.h" #include "bhnd_spromreg.h" #include "bhnd_spromvar.h" /* * BHND SPROM Parsing * * Provides identification and parsing of BHND SPROM data. */ static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf, size_t nbytes, uint8_t *crc); static int sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, uint8_t *crc); static int sprom_populate_shadow(struct bhnd_sprom *sc); static int sprom_var_defn(struct bhnd_sprom *sc, const char *name, const struct bhnd_nvram_var **var, const struct bhnd_sprom_var **sprom, size_t *size); /* SPROM revision is always located at the second-to-last byte */ #define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2) /* SPROM CRC is always located at the last byte */ #define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc) /* SPROM CRC covers all but the final CRC byte */ #define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1) /* SPROM shadow I/O (with byte-order translation) */ #define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off) #define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off)) #define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off)) #define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v)) #define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \ htole16(_v)) #define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \ htole32(_v)) /* SPROM shadow I/O (without byte-order translation) */ #define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off)) #define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off)) #define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off)) #define SPROM_WRITE_ENC_1(_sc, _off, _v) \ *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v) #define SPROM_WRITE_ENC_2(_sc, _off, _v) \ *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v) #define SPROM_WRITE_ENC_4(_sc, _off, _v) \ *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v) /* Call @p _next macro with the C type, widened (signed or unsigned) C * type, and width associated with @p _dtype */ #define SPROM_SWITCH_TYPE(_dtype, _next, ...) \ do { \ switch (_dtype) { \ case BHND_NVRAM_DT_UINT8: \ _next (uint8_t, uint32_t, 1, \ ## __VA_ARGS__); \ break; \ case BHND_NVRAM_DT_UINT16: \ _next (uint16_t, uint32_t, 2, \ ## __VA_ARGS__); \ break; \ case BHND_NVRAM_DT_UINT32: \ _next (uint32_t, uint32_t, 4, \ ## __VA_ARGS__); \ break; \ case BHND_NVRAM_DT_INT8: \ _next (int8_t, int32_t, 1, \ ## __VA_ARGS__); \ break; \ case BHND_NVRAM_DT_INT16: \ _next (int16_t, int32_t, 2, \ ## __VA_ARGS__); \ break; \ case BHND_NVRAM_DT_INT32: \ _next (int32_t, int32_t, 4, \ ## __VA_ARGS__); \ break; \ case BHND_NVRAM_DT_CHAR: \ _next (uint8_t, uint32_t, 1, \ ## __VA_ARGS__); \ break; \ } \ } while (0) /* * Table of supported SPROM image formats, sorted by image size, ascending. */ #define SPROM_FMT(_sz, _revmin, _revmax, _sig) \ { SPROM_SZ_ ## _sz, _revmin, _revmax, \ SPROM_SIG_ ## _sig ## _OFF, \ SPROM_SIG_ ## _sig } static const struct sprom_fmt { size_t size; uint8_t rev_min; uint8_t rev_max; size_t sig_offset; uint16_t sig_req; } sprom_fmts[] = { SPROM_FMT(R1_3, 1, 3, NONE), SPROM_FMT(R4_8_9, 4, 4, R4), SPROM_FMT(R4_8_9, 8, 9, R8_9), SPROM_FMT(R10, 10, 10, R10), SPROM_FMT(R11, 11, 11, R11) }; /** * Identify the SPROM format at @p offset within @p r, verify the CRC, * and allocate a local shadow copy of the SPROM data. * * After successful initialization, @p r will not be accessed; any pin * configuration required for SPROM access may be reset. * * @param[out] sprom On success, will be initialized with shadow of the SPROM * data. * @param r An active resource mapping the SPROM data. * @param offset Offset of the SPROM data within @p resource. */ int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, bus_size_t offset) { bus_size_t res_size; int error; sprom->dev = rman_get_device(r->res); sprom->sp_res = r; sprom->sp_res_off = offset; /* Determine maximum possible SPROM image size */ res_size = rman_get_size(r->res); if (offset >= res_size) return (EINVAL); sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX); /* Allocate and populate SPROM shadow */ sprom->sp_size = 0; sprom->sp_capacity = sprom->sp_size_max; sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND, M_NOWAIT); if (sprom->sp_shadow == NULL) return (ENOMEM); /* Read and identify SPROM image */ if ((error = sprom_populate_shadow(sprom))) return (error); return (0); } /** * Release all resources held by @p sprom. * * @param sprom A SPROM instance previously initialized via bhnd_sprom_init(). */ void bhnd_sprom_fini(struct bhnd_sprom *sprom) { free(sprom->sp_shadow, M_BHND); } /* Perform a read using a SPROM offset descriptor, safely widening the * result to its 32-bit representation before assigning it to @p _dest. */ #define SPROM_GETVAR_READ(_type, _widen, _width, _sc, _off, _dest) \ do { \ _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \ if (_off->shift > 0) { \ _v >>= _off->shift; \ } else if (off->shift < 0) { \ _v <<= -_off->shift; \ } \ _dest = ((uint32_t) (_widen) _v) & _off->mask; \ } while(0) /* Emit a value read using a SPROM offset descriptor, narrowing the * result output representation and, if necessary, OR'ing it with the * previously read value from @p _buf. */ #define SPROM_GETVAR_WRITE(_type, _widen, _width, _off, _src, _buf) \ do { \ _type _v = (_type) (_widen) _src; \ if (_off->cont) \ _v |= *((_type *)_buf); \ *((_type *)_buf) = _v; \ } while(0) /** * Read a SPROM variable, performing conversion to host byte order. * * @param sc The SPROM parser state. * @param name The SPROM variable name. * @param[out] buf On success, the requested value will be written * to this buffer. This argment may be NULL if * the value is not desired. * @param[in,out] len The capacity of @p buf. On success, will be set * to the actual size of the requested value. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too * small to hold the requested value. * @retval non-zero If reading @p name otherwise fails, a regular unix * error code will be returned. */ int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, size_t *len) { const struct bhnd_nvram_var *nv; const struct bhnd_sprom_var *sv; size_t all1_offs; size_t req_size; int error; if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) return (error); /* Provide required size */ if (buf == NULL) { *len = req_size; return (0); } /* Check (and update) target buffer len */ if (*len < req_size) return (ENOMEM); else *len = req_size; /* Read data */ all1_offs = 0; for (size_t i = 0; i < sv->num_offsets; i++) { const struct bhnd_sprom_offset *off; uint32_t val; off = &sv->offsets[i]; KASSERT(!off->cont || i > 0, ("cont marked on first offset")); /* If not a continuation, advance the output buffer */ if (i > 0 && !off->cont) { buf = ((uint8_t *)buf) + bhnd_nvram_type_width(sv->offsets[i-1].type); } /* Read the value, widening to a common uint32 * representation */ SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val); /* If IGNALL1, record whether value has all bits set. */ if (nv->flags & BHND_NVRAM_VF_IGNALL1) { uint32_t all1; all1 = off->mask; if (off->shift > 0) all1 >>= off->shift; else if (off->shift < 0) all1 <<= -off->shift; if ((val & all1) == all1) all1_offs++; } /* Write the value, narrowing to the appropriate output * width. */ SPROM_SWITCH_TYPE(nv->type, SPROM_GETVAR_WRITE, off, val, buf); } /* Should value should be treated as uninitialized? */ if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets) return (ENOENT); return (0); } /* Perform a read of a variable offset from _src, safely widening the result * to its 32-bit representation before assigning it to @p * _dest. */ #define SPROM_SETVAR_READ(_type, _widen, _width, _off, _src, _dest) \ do { \ _type _v = *(const _type *)_src; \ if (_off->shift > 0) { \ _v <<= _off->shift; \ } else if (off->shift < 0) { \ _v >>= -_off->shift; \ } \ _dest = ((uint32_t) (_widen) _v) & _off->mask; \ } while(0) /* Emit a value read using a SPROM offset descriptor, narrowing the * result output representation and, if necessary, OR'ing it with the * previously read value from @p _buf. */ #define SPROM_SETVAR_WRITE(_type, _widen, _width, _sc, _off, _src) \ do { \ _type _v = (_type) (_widen) _src; \ if (_off->cont) \ _v |= SPROM_READ_ ## _width(_sc, _off->offset); \ SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \ } while(0) /** * Set a local value for a SPROM variable, performing conversion to SPROM byte * order. * * The new value will be written to the backing SPROM shadow. * * @param sc The SPROM parser state. * @param name The SPROM variable name. * @param[out] buf The new value. * @param[in,out] len The size of @p buf. * * @retval 0 success * @retval ENOENT The requested variable was not found. * @retval EINVAL If @p len does not match the expected variable size. */ int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf, size_t len) { const struct bhnd_nvram_var *nv; const struct bhnd_sprom_var *sv; size_t req_size; int error; uint8_t crc; if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) return (error); /* Provide required size */ if (len != req_size) return (EINVAL); /* Write data */ for (size_t i = 0; i < sv->num_offsets; i++) { const struct bhnd_sprom_offset *off; uint32_t val; off = &sv->offsets[i]; KASSERT(!off->cont || i > 0, ("cont marked on first offset")); /* If not a continuation, advance the input pointer */ if (i > 0 && !off->cont) { buf = ((const uint8_t *)buf) + bhnd_nvram_type_width(sv->offsets[i-1].type); } /* Read the value, widening to a common uint32 * representation */ SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val); /* Write the value, narrowing to the appropriate output * width. */ SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val); } /* Update CRC */ crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc), BHND_NVRAM_CRC8_INITIAL); SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc); return (0); } /* Read and identify the SPROM image by incrementally performing * read + CRC of all supported image formats */ static int sprom_populate_shadow(struct bhnd_sprom *sc) { const struct sprom_fmt *fmt; int error; uint16_t sig; uint8_t srom_rev; uint8_t crc; crc = BHND_NVRAM_CRC8_INITIAL; /* Identify the SPROM revision (and populate the SPROM shadow) */ for (size_t i = 0; i < nitems(sprom_fmts); i++) { fmt = &sprom_fmts[i]; /* Read image data and check CRC */ if ((error = sprom_extend_shadow(sc, fmt->size, &crc))) return (error); /* Skip on invalid CRC */ if (crc != BHND_NVRAM_CRC8_VALID) continue; /* Fetch SROM revision */ srom_rev = SPROM_REV(sc); /* Early sromrev 1 devices (specifically some BCM440x enet * cards) are reported to have been incorrectly programmed * with a revision of 0x10. */ if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10) srom_rev = 0x1; /* Verify revision range */ if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max) continue; /* Verify signature (if any) */ sig = SPROM_SIG_NONE; if (fmt->sig_offset != SPROM_SIG_NONE_OFF) sig = SPROM_READ_2(sc, fmt->sig_offset); if (sig != fmt->sig_req) { device_printf(sc->dev, "invalid sprom %hhu signature: 0x%hx " "(expected 0x%hx)\n", srom_rev, sig, fmt->sig_req); return (EINVAL); } /* Identified */ sc->sp_rev = srom_rev; return (0); } /* identification failed */ device_printf(sc->dev, "unrecognized sprom format\n"); return (EINVAL); } /* * Extend the shadowed SPROM buffer to image_size, reading any required * data from the backing SPROM resource and updating the CRC. */ static int sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, uint8_t *crc) { int error; KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported"))); /* Verify the request fits within our shadow buffer */ if (image_size > sc->sp_capacity) return (ENOSPC); /* Skip no-op requests */ if (sc->sp_size == image_size) return (0); /* Populate the extended range */ error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size, image_size - sc->sp_size, crc); if (error) return (error); sc->sp_size = image_size; return (0); } /** * Read nbytes at the given offset from the backing SPROM resource, and * update the CRC. */ static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf, size_t nbytes, uint8_t *crc) { bus_size_t res_offset; - size_t nread; uint16_t *p; KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size")); KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset")); /* Check for read overrun */ if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) { device_printf(sc->dev, "requested SPROM read would overrun\n"); return (EINVAL); } + /* Perform read and update CRC */ p = (uint16_t *)buf; res_offset = sc->sp_res_off + offset; - /* Perform read */ - for (nread = 0; nread < nbytes; nread += 2) { - *p = bhnd_bus_read_stream_2(sc->sp_res, res_offset+nread); - *crc = bhnd_nvram_crc8(p, sizeof(*p), *crc); - p++; - }; + bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p, nbytes); + *crc = bhnd_nvram_crc8(p, nbytes, *crc); return (0); } /** * Locate the variable and SPROM revision-specific definitions * for variable with @p name. */ static int sprom_var_defn(struct bhnd_sprom *sc, const char *name, const struct bhnd_nvram_var **var, const struct bhnd_sprom_var **sprom, size_t *size) { /* Find variable definition */ *var = bhnd_nvram_var_defn(name); if (*var == NULL) return (ENOENT); /* Find revision-specific SPROM definition */ for (size_t i = 0; i < (*var)->num_sp_descs; i++) { const struct bhnd_sprom_var *sp = &(*var)->sprom_descs[i]; if (sc->sp_rev < sp->compat.first) continue; if (sc->sp_rev > sp->compat.last) continue; /* Found */ *sprom = sp; /* Calculate size in bytes */ *size = bhnd_nvram_type_width((*var)->type) * sp->num_offsets; return (0); } /* Not supported by this SPROM revision */ return (ENOENT); -} \ No newline at end of file +} Index: head/sys/dev/bhnd/tools/bus_macro.sh =================================================================== --- head/sys/dev/bhnd/tools/bus_macro.sh (revision 300249) +++ head/sys/dev/bhnd/tools/bus_macro.sh (revision 300250) @@ -1,84 +1,91 @@ #!/bin/sh # -# Copyright (c) 2015 Landon Fuller +# Copyright (c) 2015-2016 Landon Fuller # Copyright (c) 2004-2005 Poul-Henning Kamp. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # # Generate the bhnd resource macros at the bottom of dev/bhnd/bhnd.h # # Derived from PHK's tools/bus_macros.sh # macro () { n=${1} bus_n=$(echo $n | tr "[:lower:]" "[:upper:]") shift echo -n "#define bhnd_bus_${n}(r" for i do echo -n ", ${i}" done echo ") \\" echo " ((r)->direct) ? \\" echo -n " bus_${n}((r)->res" for i do echo -n ", (${i})" done echo ") : \\" echo " BHND_BUS_${bus_n}( \\" echo " device_get_parent(rman_get_device((r)->res)), \\" echo -n " rman_get_device((r)->res), (r)" for i do echo -n ", (${i})" done echo ")" } macro barrier o l f -# We only support a subset of the bus I/O methods; this may -# be expanded when/if additional functions are required. for w in 1 2 4 #8 do # macro copy_region_$w so dh do c # macro copy_region_stream_$w ? # macro peek_$w for s in "" stream_ do macro read_$s$w o macro read_multi_$s$w o d c -# macro read_region_$s$w o d c -# macro set_multi_$s$w o v c -# macro set_region_$s$w o v c + macro read_region_$s$w o d c macro write_$s$w o v macro write_multi_$s$w o d c -# macro write_region_$s$w o d c + macro write_region_$s$w o d c + done + + # set_(multi_)?_stream is not supported on ARM/ARM64, and so for + # simplicity, we don't support their use with bhnd resources. + # + # if that changes, these can be merged back into the stream-eanbled + # loop above. + for s in "" + do + macro set_multi_$s$w o v c + macro set_region_$s$w o v c done done