From f4a3eb02973afb7e2c2d1a116dbcb331f91ae022 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 24 May 2016 01:12:19 +0000 Subject: [PATCH] [bhnd] Implement pass-through resource management for ChipCommon. This patchset adds support to bhnd_chipc for sharing SYS_RES_MEMORY resources with its children, allowing us to hang devices off of bhnd_chipc that rely on access to a subset of the device register space that bhnd_chipc itself must also allocate. We could avoid most of this heavy lifting if RF_SHAREABLE+SYS_RES_MEMORY wasn't limited to use with allocations at the same size/offset. As a work-around, I implemented something similar to vga_pci.c, which implements similar reference counting of of PCI BAR resources for its children. With these changes, chipc will use reference counting of SYS_RES_MEMORY allocation/activation requests, to decide when to allocate/activate/ deactivate/release resources from the parent bhnd(4) bus. The requesting child device is allocated a new resource from chipc's rman, pointing to (possibly a subregion of) the refcounted bhnd resources allocated by chipc. Other resource types are just passed directly to the parent bhnd bus; RF_SHAREABLE works just fine with IRQs. I also lifted the SPROM device code out into a common driver, since this now allows me to hang simple subclasses off of a common driver off of both bhndb_pci and bhnd_chipc. Tested: * (landonf) Tested against BCM4331 and BCM4312, confirmed that SPROM still attaches and can be queried. Submitted by: Landon Fuller Reviewed by: mizkha@gmail.com Differential Revision: https://reviews.freebsd.org/D6471 --- sys/conf/files | 3 + sys/dev/bhnd/bhnd_subr.c | 22 +- sys/dev/bhnd/bhndb/bhndb_pci_sprom.c | 130 +-- sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m | 37 + sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c | 101 ++ sys/dev/bhnd/cores/chipc/chipc.c | 1083 ++++++++++++++++--- sys/dev/bhnd/cores/chipc/chipc.h | 2 +- sys/dev/bhnd/cores/chipc/chipc_private.h | 98 ++ sys/dev/bhnd/cores/chipc/chipc_subr.c | 365 +++++++ sys/dev/bhnd/cores/chipc/chipcreg.h | 230 +++- sys/dev/bhnd/cores/chipc/chipcvar.h | 100 +- sys/dev/bhnd/nvram/bhnd_nvram.h | 16 +- sys/dev/bhnd/nvram/bhnd_sprom.c | 621 +++-------- sys/dev/bhnd/nvram/bhnd_sprom_subr.c | 568 ++++++++++ sys/dev/bhnd/nvram/bhnd_spromvar.h | 46 +- sys/dev/bhnd/siba/siba.c | 2 +- sys/dev/bhnd/siba/siba_subr.c | 12 +- sys/dev/bhnd/siba/sibavar.h | 2 + sys/modules/bhnd/Makefile | 3 +- sys/modules/bhnd/cores/bhnd_chipc/Makefile | 3 +- 20 files changed, 2549 insertions(+), 895 deletions(-) create mode 100644 sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c create mode 100644 sys/dev/bhnd/cores/chipc/chipc_private.h create mode 100644 sys/dev/bhnd/cores/chipc/chipc_subr.c create mode 100644 sys/dev/bhnd/nvram/bhnd_sprom_subr.c diff --git a/sys/conf/files b/sys/conf/files index 428b89847c69..316dd177acc4 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1139,7 +1139,9 @@ dev/bhnd/bcma/bcma_bhndb.c optional bhndbus | bcma bhndb dev/bhnd/bcma/bcma_erom.c optional bhndbus | bcma dev/bhnd/bcma/bcma_subr.c optional bhndbus | bcma dev/bhnd/cores/chipc/chipc.c optional bhndbus | bhnd +dev/bhnd/cores/chipc/chipc_subr.c optional bhndbus | bhnd dev/bhnd/cores/chipc/bhnd_chipc_if.m optional bhndbus | bhnd +dev/bhnd/cores/chipc/bhnd_sprom_chipc.c optional bhndbus | bhnd dev/bhnd/cores/pci/bhnd_pci.c optional bhndbus pci | bhnd pci dev/bhnd/cores/pci/bhnd_pci_hostb.c optional bhndbus pci | bhndb pci dev/bhnd/cores/pci/bhnd_pcib.c optional bhnd_pcib bhnd pci @@ -1148,6 +1150,7 @@ dev/bhnd/cores/pcie2/bhnd_pcie2_hostb.c optional bhndbus pci | bhndb pci dev/bhnd/cores/pcie2/bhnd_pcie2b.c optional bhnd_pcie2b bhnd pci dev/bhnd/nvram/bhnd_nvram_if.m optional bhndbus | bhnd dev/bhnd/nvram/bhnd_sprom.c optional bhndbus | bhnd +dev/bhnd/nvram/bhnd_sprom_subr.c optional bhndbus | bhnd dev/bhnd/nvram/nvram_subr.c optional bhndbus | bhnd dev/bhnd/siba/siba.c optional bhndbus | siba dev/bhnd/siba/siba_bhndb.c optional bhndbus | siba bhndb diff --git a/sys/dev/bhnd/bhnd_subr.c b/sys/dev/bhnd/bhnd_subr.c index ee807b40adde..eee949932687 100644 --- a/sys/dev/bhnd/bhnd_subr.c +++ b/sys/dev/bhnd/bhnd_subr.c @@ -797,11 +797,11 @@ bhnd_parse_chipid(uint32_t idreg, bhnd_addr_t enum_addr) struct bhnd_chipid result; /* Fetch the basic chip info */ - result.chip_id = CHIPC_GET_ATTR(idreg, ID_CHIP); - result.chip_pkg = CHIPC_GET_ATTR(idreg, ID_PKG); - result.chip_rev = CHIPC_GET_ATTR(idreg, ID_REV); - result.chip_type = CHIPC_GET_ATTR(idreg, ID_BUS); - result.ncores = CHIPC_GET_ATTR(idreg, ID_NUMCORE); + result.chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP); + result.chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG); + result.chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV); + result.chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS); + result.ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE); result.enum_addr = enum_addr; @@ -1020,15 +1020,11 @@ find_nvram_child(device_t dev) if (device_get_devclass(dev) != bhnd_devclass) return (NULL); - /* Look for a ChipCommon device */ + /* Look for a ChipCommon-attached NVRAM device */ if ((chipc = bhnd_find_child(dev, BHND_DEVCLASS_CC, -1)) != NULL) { - bhnd_nvram_src_t src; - - /* Query the NVRAM source and determine whether it's - * accessible via the ChipCommon device */ - src = BHND_CHIPC_NVRAM_SRC(chipc); - if (BHND_NVRAM_SRC_CC(src)) - return (chipc); + nvram = device_find_child(chipc, "bhnd_nvram", 0); + if (nvram != NULL) + return (nvram); } /* Not found */ diff --git a/sys/dev/bhnd/bhndb/bhndb_pci_sprom.c b/sys/dev/bhnd/bhndb/bhndb_pci_sprom.c index 8f0ea5603f32..a6c99d0bf57f 100644 --- a/sys/dev/bhnd/bhndb/bhndb_pci_sprom.c +++ b/sys/dev/bhnd/bhndb/bhndb_pci_sprom.c @@ -53,29 +53,15 @@ __FBSDID("$FreeBSD$"); #include #include "bhnd_nvram_if.h" + #include "bhndb_pcireg.h" #include "bhndb_pcivar.h" -struct bhndb_pci_sprom_softc { - device_t dev; - struct bhnd_resource *sprom_res; /**< SPROM resource */ - int sprom_rid; /**< SPROM RID */ - struct bhnd_sprom shadow; /**< SPROM shadow */ - struct mtx mtx; /**< SPROM shadow mutex */ -}; - -#define SPROM_LOCK_INIT(sc) \ - mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ - "BHND PCI SPROM lock", MTX_DEF) -#define SPROM_LOCK(sc) mtx_lock(&(sc)->mtx) -#define SPROM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -#define SPROM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) -#define SPROM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) - static int bhndb_pci_sprom_probe(device_t dev) { device_t bridge, bus; + int error; /* Our parent must be a PCI-BHND bridge with an attached bhnd bus */ bridge = device_get_parent(dev); @@ -86,125 +72,23 @@ bhndb_pci_sprom_probe(device_t dev) if (bus == NULL) return (ENXIO); - /* Found */ - device_set_desc(dev, "PCI-BHNDB SPROM/OTP"); - if (!bootverbose) - device_quiet(dev); + /* Defer to default driver implementation */ + if ((error = bhnd_sprom_probe(dev)) > 0) + return (error); - /* Refuse wildcard attachments */ return (BUS_PROBE_NOWILDCARD); } -static int -bhndb_pci_sprom_attach(device_t dev) -{ - struct bhndb_pci_sprom_softc *sc; - int error; - - sc = device_get_softc(dev); - sc->dev = dev; - - /* Allocate SPROM resource */ - sc->sprom_rid = 0; - sc->sprom_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->sprom_rid, RF_ACTIVE); - if (sc->sprom_res == NULL) { - device_printf(dev, "failed to allocate resources\n"); - return (ENXIO); - } - - /* Initialize SPROM shadow */ - if ((error = bhnd_sprom_init(&sc->shadow, sc->sprom_res, 0))) { - device_printf(dev, "unrecognized SPROM format\n"); - goto failed; - } - - /* Initialize mutex */ - SPROM_LOCK_INIT(sc); - - return (0); - -failed: - bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, - sc->sprom_res); - return (error); -} - -static int -bhndb_pci_sprom_resume(device_t dev) -{ - return (0); -} - -static int -bhndb_pci_sprom_suspend(device_t dev) -{ - return (0); -} - -static int -bhndb_pci_sprom_detach(device_t dev) -{ - struct bhndb_pci_sprom_softc *sc; - - sc = device_get_softc(dev); - - bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, - sc->sprom_res); - bhnd_sprom_fini(&sc->shadow); - SPROM_LOCK_DESTROY(sc); - - return (0); -} - -static int -bhndb_pci_sprom_getvar(device_t dev, const char *name, void *buf, size_t *len) -{ - struct bhndb_pci_sprom_softc *sc; - int error; - - sc = device_get_softc(dev); - - SPROM_LOCK(sc); - error = bhnd_sprom_getvar(&sc->shadow, name, buf, len); - SPROM_UNLOCK(sc); - - return (error); -} - -static int -bhndb_pci_sprom_setvar(device_t dev, const char *name, const void *buf, - size_t len) -{ - struct bhndb_pci_sprom_softc *sc; - int error; - - sc = device_get_softc(dev); - - SPROM_LOCK(sc); - error = bhnd_sprom_setvar(&sc->shadow, name, buf, len); - SPROM_UNLOCK(sc); - - return (error); -} static device_method_t bhndb_pci_sprom_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhndb_pci_sprom_probe), - DEVMETHOD(device_attach, bhndb_pci_sprom_attach), - DEVMETHOD(device_resume, bhndb_pci_sprom_resume), - DEVMETHOD(device_suspend, bhndb_pci_sprom_suspend), - DEVMETHOD(device_detach, bhndb_pci_sprom_detach), - - /* NVRAM interface */ - DEVMETHOD(bhnd_nvram_getvar, bhndb_pci_sprom_getvar), - DEVMETHOD(bhnd_nvram_setvar, bhndb_pci_sprom_setvar), - DEVMETHOD_END }; -DEFINE_CLASS_0(bhnd_nvram, bhndb_pci_sprom_driver, bhndb_pci_sprom_methods, sizeof(struct bhndb_pci_sprom_softc)); +DEFINE_CLASS_1(bhnd_nvram, bhndb_pci_sprom_driver, bhndb_pci_sprom_methods, sizeof(struct bhnd_sprom_softc), bhnd_sprom_driver); DRIVER_MODULE(bhndb_pci_sprom, bhndb, bhndb_pci_sprom_driver, bhnd_nvram_devclass, NULL, NULL); MODULE_DEPEND(bhndb_pci_sprom, bhnd, 1, 1, 1); +MODULE_DEPEND(bhndb_pci_sprom, bhnd_sprom, 1, 1, 1); MODULE_VERSION(bhndb_pci_sprom, 1); diff --git a/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m b/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m index dfaeeef0bf14..8a214d682d5a 100644 --- a/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m +++ b/sys/dev/bhnd/cores/chipc/bhnd_chipc_if.m @@ -36,6 +36,11 @@ INTERFACE bhnd_chipc; # bhnd(4) ChipCommon interface. # +HEADER { + /* forward declarations */ + struct chipc_caps; +} + /** * Return the preferred NVRAM data source. * @@ -63,3 +68,35 @@ METHOD void write_chipctrl { uint32_t value; uint32_t mask; } + +/** + * Return a borrowed reference to ChipCommon's capability + * table. + * + * @param dev A bhnd(4) ChipCommon device + */ +METHOD struct chipc_caps * get_caps { + device_t dev; +} + +/** + * Enable hardware access to the SPROM. + * + * @param sc chipc driver state. + * + * @retval 0 success + * @retval EBUSY If enabling the hardware may conflict with + * other active devices. + */ +METHOD int enable_sprom { + device_t dev; +} + +/** + * Release hardware access to the SPROM. + * + * @param sc chipc driver state. + */ +METHOD void disable_sprom { + device_t dev; +} diff --git a/sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c b/sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c new file mode 100644 index 000000000000..3d91099b46ec --- /dev/null +++ b/sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c @@ -0,0 +1,101 @@ +/*- + * Copyright (c) 2015-2016 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$"); + +/* + * ChipCommon SPROM driver. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bhnd_chipc_if.h" +#include "bhnd_nvram_if.h" + +static int +chipc_sprom_probe(device_t dev) +{ + device_t chipc; + int error; + + chipc = device_get_parent(dev); + + /* Only match on SPROM devices */ + if (BHND_CHIPC_NVRAM_SRC(chipc) != BHND_NVRAM_SRC_SPROM) + return (ENXIO); + + /* Defer to default driver implementation */ + if ((error = bhnd_sprom_probe(dev)) > 0) + return (error); + + return (BUS_PROBE_NOWILDCARD); +} + +static int +chipc_sprom_attach(device_t dev) +{ + device_t chipc; + int error; + + /* Request that ChipCommon enable access to SPROM hardware before + * delegating attachment (and SPROM parsing) to the common driver */ + chipc = device_get_parent(dev); + if ((error = BHND_CHIPC_ENABLE_SPROM(chipc))) + return (error); + + error = bhnd_sprom_attach(dev); + BHND_CHIPC_DISABLE_SPROM(chipc); + return (error); +} + +static device_method_t chipc_sprom_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, chipc_sprom_probe), + DEVMETHOD(device_attach, chipc_sprom_attach), + DEVMETHOD_END +}; + +DEFINE_CLASS_1(bhnd_nvram, chipc_sprom_driver, chipc_sprom_methods, sizeof(struct bhnd_sprom_softc), bhnd_sprom_driver); +DRIVER_MODULE(bhnd_chipc_sprom, bhnd_chipc, chipc_sprom_driver, bhnd_nvram_devclass, NULL, NULL); + +MODULE_DEPEND(bhnd_chipc_sprom, bhnd, 1, 1, 1); +MODULE_DEPEND(bhnd_chipc_sprom, bhnd_chipc, 1, 1, 1); +MODULE_DEPEND(bhnd_chipc_sprom, bhnd_sprom, 1, 1, 1); +MODULE_VERSION(bhnd_chipc_sprom, 1); diff --git a/sys/dev/bhnd/cores/chipc/chipc.c b/sys/dev/bhnd/cores/chipc/chipc.c index ea1d7c2cf1c4..f95b8712090e 100644 --- a/sys/dev/bhnd/cores/chipc/chipc.c +++ b/sys/dev/bhnd/cores/chipc/chipc.c @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2016 Michael Zhilin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,8 +43,11 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include +#include #include +#include #include #include @@ -51,19 +55,14 @@ __FBSDID("$FreeBSD$"); #include #include - -#include "bhnd_nvram_if.h" +#include #include "chipcreg.h" #include "chipcvar.h" +#include "chipc_private.h" devclass_t bhnd_chipc_devclass; /**< bhnd(4) chipcommon device class */ -static const struct resource_spec chipc_rspec[CHIPC_MAX_RSPEC] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { -1, -1, 0 } -}; - static struct bhnd_device_quirk chipc_quirks[]; static struct bhnd_chip_quirk chipc_chip_quirks[]; @@ -77,7 +76,10 @@ static const struct bhnd_device chipc_devices[] = { /* Device quirks table */ static struct bhnd_device_quirk chipc_quirks[] = { { BHND_HWREV_GTE (32), CHIPC_QUIRK_SUPPORTS_SPROM }, - { BHND_HWREV_GTE (35), CHIPC_QUIRK_SUPPORTS_NFLASH }, + { BHND_HWREV_GTE (35), CHIPC_QUIRK_SUPPORTS_CAP_EXT }, + { BHND_HWREV_EQ (38), CHIPC_QUIRK_4706_NFLASH }, /*BCM5357 ?*/ + { BHND_HWREV_GTE (49), CHIPC_QUIRK_IPX_OTPLAYOUT_SIZE }, + BHND_DEVICE_QUIRK_END }; @@ -111,15 +113,37 @@ static struct bhnd_chip_quirk chipc_chip_quirks[] = { {{ BHND_CHIP_IR(43602, HWREV_LTE(2)) }, CHIPC_QUIRK_4360_FEM_MUX_SPROM }, + /* BCM4706 */ + {{ BHND_CHIP_ID(4306) }, + CHIPC_QUIRK_4706_NFLASH }, + BHND_CHIP_QUIRK_END }; +static int chipc_try_activate_resource( + struct chipc_softc *sc, device_t child, + int type, int rid, struct resource *r, + bool req_direct); + +static int chipc_read_caps(struct chipc_softc *sc, + struct chipc_caps *caps); + +static int chipc_nvram_attach(struct chipc_softc *sc); +static bhnd_nvram_src_t chipc_nvram_identify(struct chipc_softc *sc); +static bool chipc_should_enable_sprom( + struct chipc_softc *sc); + +static int chipc_init_rman(struct chipc_softc *sc); +static void chipc_free_rman(struct chipc_softc *sc); +static struct rman *chipc_get_rman(struct chipc_softc *sc, + int type); + /* quirk and capability flag convenience macros */ #define CHIPC_QUIRK(_sc, _name) \ ((_sc)->quirks & CHIPC_QUIRK_ ## _name) #define CHIPC_CAP(_sc, _name) \ - ((_sc)->caps & CHIPC_ ## _name) + ((_sc)->caps._name) #define CHIPC_ASSERT_QUIRK(_sc, name) \ KASSERT(CHIPC_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set")) @@ -127,12 +151,6 @@ static struct bhnd_chip_quirk chipc_chip_quirks[] = { #define CHIPC_ASSERT_CAP(_sc, name) \ KASSERT(CHIPC_CAP((_sc), name), ("capability " __STRING(_name) " not set")) -static bhnd_nvram_src_t chipc_nvram_identify(struct chipc_softc *sc); -static int chipc_sprom_init(struct chipc_softc *); -static int chipc_enable_sprom_pins(struct chipc_softc *); -static int chipc_disable_sprom_pins(struct chipc_softc *); - - static int chipc_probe(device_t dev) { @@ -159,19 +177,36 @@ chipc_attach(device_t dev) sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, chipc_devices, sizeof(chipc_devices[0])); - + sc->sprom_refcnt = 0; + CHIPC_LOCK_INIT(sc); + STAILQ_INIT(&sc->mem_regions); - /* Allocate bus resources */ - memcpy(sc->rspec, chipc_rspec, sizeof(sc->rspec)); - if ((error = bhnd_alloc_resources(dev, sc->rspec, sc->res))) - return (error); + /* Set up resource management */ + if ((error = chipc_init_rman(sc))) { + device_printf(sc->dev, + "failed to initialize chipc resource state: %d\n", error); + goto failed; + } + + /* Allocate the region containing our core registers */ + if ((sc->core_region = chipc_find_region_by_rid(sc, 0)) == NULL) { + error = ENXIO; + goto failed; + } + + error = chipc_retain_region(sc, sc->core_region, + RF_ALLOCATED|RF_ACTIVE); + if (error) { + sc->core_region = NULL; + goto failed; + } else { + sc->core = sc->core_region->cr_res; + } - sc->core = sc->res[0]; - /* Fetch our chipset identification data */ ccid_reg = bhnd_bus_read_4(sc->core, CHIPC_ID); - chip_type = CHIPC_GET_ATTR(ccid_reg, ID_BUS); + chip_type = CHIPC_GET_BITS(ccid_reg, CHIPC_ID_BUS); switch (chip_type) { case BHND_CHIPTYPE_SIBA: @@ -185,44 +220,36 @@ chipc_attach(device_t dev) default: device_printf(dev, "unsupported chip type %hhu\n", chip_type); error = ENODEV; - goto cleanup; + goto failed; } sc->ccid = bhnd_parse_chipid(ccid_reg, enum_addr); - /* Fetch capability and status register values */ - sc->caps = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); - sc->cst = bhnd_bus_read_4(sc->core, CHIPC_CHIPST); + /* Fetch and parse capability register(s) */ + if ((error = chipc_read_caps(sc, &sc->caps))) + goto failed; - /* Identify NVRAM source */ + if (bootverbose) + chipc_print_caps(sc->dev, &sc->caps); + + /* Identify NVRAM source and add child device. */ sc->nvram_src = chipc_nvram_identify(sc); + if ((error = chipc_nvram_attach(sc))) + goto failed; - /* Read NVRAM data */ - switch (sc->nvram_src) { - case BHND_NVRAM_SRC_OTP: - // TODO (requires access to OTP hardware) - device_printf(sc->dev, "NVRAM-OTP unsupported\n"); - break; - - case BHND_NVRAM_SRC_NFLASH: - // TODO (requires access to NFLASH hardware) - device_printf(sc->dev, "NVRAM-NFLASH unsupported\n"); - break; - - case BHND_NVRAM_SRC_SPROM: - if ((error = chipc_sprom_init(sc))) - goto cleanup; - break; - - case BHND_NVRAM_SRC_UNKNOWN: - /* Handled externally */ - break; - } + /* Standard bus probe */ + if ((error = bus_generic_attach(dev))) + goto failed; return (0); -cleanup: - bhnd_release_resources(dev, sc->rspec, sc->res); +failed: + if (sc->core_region != NULL) { + chipc_release_region(sc, sc->core_region, + RF_ALLOCATED|RF_ACTIVE); + } + + chipc_free_rman(sc); CHIPC_LOCK_DESTROY(sc); return (error); } @@ -231,9 +258,15 @@ static int chipc_detach(device_t dev) { struct chipc_softc *sc; + int error; sc = device_get_softc(dev); - bhnd_release_resources(dev, sc->rspec, sc->res); + + if ((error = bus_generic_detach(dev))) + return (error); + + chipc_release_region(sc, sc->core_region, RF_ALLOCATED|RF_ACTIVE); + chipc_free_rman(sc); bhnd_sprom_fini(&sc->sprom); CHIPC_LOCK_DESTROY(sc); @@ -241,58 +274,131 @@ chipc_detach(device_t dev) return (0); } +/* Read and parse chipc capabilities */ static int -chipc_suspend(device_t dev) +chipc_read_caps(struct chipc_softc *sc, struct chipc_caps *caps) { - return (0); -} + uint32_t cap_reg; + uint32_t cap_ext_reg; + uint32_t regval; + + /* Fetch cap registers */ + cap_reg = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); + cap_ext_reg = 0; + if (CHIPC_QUIRK(sc, SUPPORTS_CAP_EXT)) + cap_ext_reg = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES_EXT); + + /* Extract values */ + caps->num_uarts = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_NUM_UART); + caps->mipseb = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_MIPSEB); + caps->uart_gpio = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_UARTGPIO); + caps->uart_clock = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_UCLKSEL); + + caps->extbus_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_EXTBUS); + caps->power_control = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PWR_CTL); + caps->jtag_master = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_JTAGP); + + caps->pll_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_PLL); + caps->backplane_64 = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_BKPLN64); + caps->boot_rom = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_ROM); + caps->pmu = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PMU); + caps->eci = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_ECI); + caps->sprom = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_SPROM); + caps->otp_size = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_OTP_SIZE); + + caps->seci = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_SECI); + caps->gsio = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_GSIO); + caps->aob = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_AOB); + + /* Fetch OTP size for later IPX controller revisions */ + if (CHIPC_QUIRK(sc, IPX_OTPLAYOUT_SIZE)) { + regval = bhnd_bus_read_4(sc->core, CHIPC_OTPLAYOUT); + caps->otp_size = CHIPC_GET_BITS(regval, CHIPC_OTPL_SIZE); + } + + /* Determine flash type and paramters */ + caps->cfi_width = 0; + + switch (CHIPC_GET_BITS(cap_reg, CHIPC_CAP_FLASH)) { + case CHIPC_CAP_SFLASH_ST: + caps->flash_type = CHIPC_SFLASH_ST; + break; + case CHIPC_CAP_SFLASH_AT: + caps->flash_type = CHIPC_SFLASH_AT; + break; + case CHIPC_CAP_NFLASH: + caps->flash_type = CHIPC_NFLASH; + break; + case CHIPC_CAP_PFLASH: + caps->flash_type = CHIPC_PFLASH_CFI; + + /* determine cfi width */ + regval = bhnd_bus_read_4(sc->core, CHIPC_FLASH_CFG); + if (CHIPC_GET_FLAG(regval, CHIPC_FLASH_CFG_DS)) + caps->cfi_width = 2; + else + caps->cfi_width = 1; + + break; + case CHIPC_CAP_FLASH_NONE: + caps->flash_type = CHIPC_FLASH_NONE; + break; + + } + + /* Handle 4706_NFLASH fallback */ + if (CHIPC_QUIRK(sc, 4706_NFLASH) && + CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_4706_NFLASH)) + { + caps->flash_type = CHIPC_NFLASH_4706; + } -static int -chipc_resume(device_t dev) -{ return (0); } /** - * Initialize local SPROM shadow, if required. - * - * @param sc chipc driver state. + * If supported, add an appropriate NVRAM child device. */ static int -chipc_sprom_init(struct chipc_softc *sc) +chipc_nvram_attach(struct chipc_softc *sc) { - int error; + device_t nvram_dev; + rman_res_t start; + int error; - KASSERT(sc->nvram_src == BHND_NVRAM_SRC_SPROM, - ("non-SPROM source (%u)\n", sc->nvram_src)); + switch (sc->nvram_src) { + case BHND_NVRAM_SRC_OTP: + // TODO OTP support + device_printf(sc->dev, "OTP nvram source unsupported\n"); + return (0); - /* Enable access to the SPROM */ - CHIPC_LOCK(sc); - if ((error = chipc_enable_sprom_pins(sc))) - goto failed; + case BHND_NVRAM_SRC_SPROM: + /* Add OTP/SPROM device */ + nvram_dev = BUS_ADD_CHILD(sc->dev, 0, "bhnd_nvram", -1); + if (nvram_dev == NULL) { + device_printf(sc->dev, "failed to add NVRAM device\n"); + return (ENXIO); + } - /* Initialize SPROM parser */ - error = bhnd_sprom_init(&sc->sprom, sc->core, CHIPC_SPROM_OTP); - if (error) { - device_printf(sc->dev, "SPROM identification failed: %d\n", - error); + start = rman_get_start(sc->core->res) + CHIPC_SPROM_OTP; + error = bus_set_resource(nvram_dev, SYS_RES_MEMORY, 0, start, + CHIPC_SPROM_OTP_SIZE); + return (error); - chipc_disable_sprom_pins(sc); - goto failed; + case BHND_NVRAM_SRC_FLASH: + // TODO flash support + device_printf(sc->dev, "flash nvram source unsupported\n"); + return (0); + + case BHND_NVRAM_SRC_UNKNOWN: + /* Handled externally */ + return (0); + + default: + device_printf(sc->dev, "invalid nvram source: %u\n", + sc->nvram_src); + return (ENXIO); } - - /* Drop access to the SPROM lines */ - if ((error = chipc_disable_sprom_pins(sc))) { - bhnd_sprom_fini(&sc->sprom); - goto failed; - } - CHIPC_UNLOCK(sc); - - return (0); - -failed: - CHIPC_UNLOCK(sc); - return (error); } /** @@ -317,27 +423,645 @@ chipc_nvram_identify(struct chipc_softc *sc) * We check for hardware presence in order of precedence. For example, * SPROM is is always used in preference to internal OTP if found. */ - if (CHIPC_CAP(sc, CAP_SPROM)) { + if (CHIPC_CAP(sc, sprom)) { srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL); if (srom_ctrl & CHIPC_SRC_PRESENT) return (BHND_NVRAM_SRC_SPROM); } /* Check for OTP */ - if (CHIPC_CAP(sc, CAP_OTP_SIZE)) + if (CHIPC_CAP(sc, otp_size) != 0) return (BHND_NVRAM_SRC_OTP); - /* - * Finally, Northstar chipsets (and possibly other chipsets?) support - * external NAND flash. - */ - if (CHIPC_QUIRK(sc, SUPPORTS_NFLASH) && CHIPC_CAP(sc, CAP_NFLASH)) - return (BHND_NVRAM_SRC_NFLASH); + /* Check for flash */ + if (CHIPC_CAP(sc, flash_type) != CHIPC_FLASH_NONE) + return (BHND_NVRAM_SRC_FLASH); /* No NVRAM hardware capability declared */ return (BHND_NVRAM_SRC_UNKNOWN); } +static int +chipc_suspend(device_t dev) +{ + return (bus_generic_suspend(dev)); +} + +static int +chipc_resume(device_t dev) +{ + return (bus_generic_resume(dev)); +} + +static void +chipc_probe_nomatch(device_t dev, device_t child) +{ + struct resource_list *rl; + const char *name; + + name = device_get_name(child); + if (name == NULL) + name = "unknown device"; + + device_printf(dev, "<%s> at", name); + + rl = BUS_GET_RESOURCE_LIST(dev, child); + if (rl != NULL) { + resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); + } + + printf(" (no driver attached)\n"); +} + +static int +chipc_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 += 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 +chipc_child_pnpinfo_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + if (buflen == 0) + return (EOVERFLOW); + + *buf = '\0'; + return (0); +} + +static int +chipc_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + if (buflen == 0) + return (EOVERFLOW); + + *buf = '\0'; + return (ENXIO); +} + +static device_t +chipc_add_child(device_t dev, u_int order, const char *name, int unit) +{ + struct chipc_devinfo *dinfo; + device_t child; + + child = device_add_child_ordered(dev, order, name, unit); + if (child == NULL) + return (NULL); + + dinfo = malloc(sizeof(struct chipc_devinfo), M_BHND, M_NOWAIT); + if (dinfo == NULL) { + device_delete_child(dev, child); + return (NULL); + } + + resource_list_init(&dinfo->resources); + + device_set_ivars(child, dinfo); + + return (child); +} + +static void +chipc_child_deleted(device_t dev, device_t child) +{ + struct chipc_devinfo *dinfo = device_get_ivars(child); + + if (dinfo != NULL) { + resource_list_free(&dinfo->resources); + free(dinfo, M_BHND); + } + + device_set_ivars(child, NULL); +} + +static struct resource_list * +chipc_get_resource_list(device_t dev, device_t child) +{ + struct chipc_devinfo *dinfo = device_get_ivars(child); + return (&dinfo->resources); +} + + +/* Allocate region records for the given port, and add the port's memory + * range to the mem_rman */ +static int +chipc_rman_init_regions (struct chipc_softc *sc, bhnd_port_type type, + u_int port) +{ + struct chipc_region *cr; + rman_res_t start, end; + u_int num_regions; + int error; + + num_regions = bhnd_get_region_count(sc->dev, port, port); + for (u_int region = 0; region < num_regions; region++) { + /* Allocate new region record */ + cr = chipc_alloc_region(sc, type, port, region); + if (cr == NULL) + return (ENODEV); + + /* Can't manage regions that cannot be allocated */ + if (cr->cr_rid < 0) { + BHND_DEBUG_DEV(sc->dev, "no rid for chipc region " + "%s%u.%u", bhnd_port_type_name(type), port, region); + chipc_free_region(sc, cr); + continue; + } + + /* Add to rman's managed range */ + start = cr->cr_addr; + end = cr->cr_end; + if ((error = rman_manage_region(&sc->mem_rman, start, end))) { + chipc_free_region(sc, cr); + return (error); + } + + /* Add to region list */ + STAILQ_INSERT_TAIL(&sc->mem_regions, cr, cr_link); + } + + return (0); +} + +/* Initialize memory state for all chipc port regions */ +static int +chipc_init_rman(struct chipc_softc *sc) +{ + u_int num_ports; + int error; + + /* Port types for which we'll register chipc_region mappings */ + bhnd_port_type types[] = { + BHND_PORT_DEVICE + }; + + /* Initialize resource manager */ + sc->mem_rman.rm_start = 0; + sc->mem_rman.rm_end = BUS_SPACE_MAXADDR; + sc->mem_rman.rm_type = RMAN_ARRAY; + sc->mem_rman.rm_descr = "ChipCommon Device Memory"; + if ((error = rman_init(&sc->mem_rman))) { + device_printf(sc->dev, "could not initialize mem_rman: %d\n", + error); + return (error); + } + + /* Populate per-port-region state */ + for (u_int i = 0; i < nitems(types); i++) { + num_ports = bhnd_get_port_count(sc->dev, types[i]); + for (u_int port = 0; port < num_ports; port++) { + error = chipc_rman_init_regions(sc, types[i], port); + if (error) { + device_printf(sc->dev, + "region init failed for %s%u: %d\n", + bhnd_port_type_name(types[i]), port, + error); + + goto failed; + } + } + } + + return (0); + +failed: + chipc_free_rman(sc); + return (error); +} + +/* Free memory management state */ +static void +chipc_free_rman(struct chipc_softc *sc) +{ + struct chipc_region *cr, *cr_next; + + STAILQ_FOREACH_SAFE(cr, &sc->mem_regions, cr_link, cr_next) + chipc_free_region(sc, cr); + + rman_fini(&sc->mem_rman); +} + +/** + * Return the rman instance for a given resource @p type, if any. + * + * @param sc The chipc device state. + * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) + */ +static struct rman * +chipc_get_rman(struct chipc_softc *sc, int type) +{ + switch (type) { + case SYS_RES_MEMORY: + return (&sc->mem_rman); + + case SYS_RES_IRQ: + /* IRQs can be used with RF_SHAREABLE, so we don't perform + * any local proxying of resource requests. */ + return (NULL); + + default: + return (NULL); + }; +} + +static struct resource * +chipc_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 chipc_softc *sc; + struct chipc_region *cr; + 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; + + /* Fetch the resource manager, delegate request if necessary */ + rm = chipc_get_rman(sc, type); + if (rm == NULL) { + /* Requested resource type is delegated to our parent */ + rv = bus_generic_rl_alloc_resource(dev, child, type, rid, + start, end, count, flags); + return (rv); + } + + /* 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); + } + + /* Locate a mapping region */ + if ((cr = chipc_find_region(sc, start, end)) == NULL) { + /* Resource requests outside our shared port regions can be + * delegated to our parent. */ + rv = bus_generic_rl_alloc_resource(dev, child, type, rid, + start, end, count, flags); + return (rv); + } + + /* Try to retain a region reference */ + if ((error = chipc_retain_region(sc, cr, RF_ALLOCATED))) { + CHIPC_UNLOCK(sc); + return (NULL); + } + + /* Make our rman reservation */ + rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, + child); + if (rv == NULL) { + chipc_release_region(sc, cr, RF_ALLOCATED); + 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); + + chipc_release_region(sc, cr, RF_ALLOCATED); + 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); +} + +static int +chipc_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + struct chipc_softc *sc; + struct chipc_region *cr; + struct rman *rm; + int error; + + sc = device_get_softc(dev); + + /* Handled by parent bus? */ + rm = chipc_get_rman(sc, type); + if (rm == NULL || !rman_is_region_manager(r, rm)) { + return (bus_generic_rl_release_resource(dev, child, type, rid, + r)); + } + + /* Locate the mapping region */ + cr = chipc_find_region(sc, rman_get_start(r), rman_get_end(r)); + if (cr == NULL) + return (EINVAL); + + /* 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); + + /* Drop allocation reference */ + chipc_release_region(sc, cr, RF_ALLOCATED); + + return (0); +} + +static int +chipc_adjust_resource(device_t dev, device_t child, int type, + struct resource *r, rman_res_t start, rman_res_t end) +{ + struct chipc_softc *sc; + struct chipc_region *cr; + struct rman *rm; + + sc = device_get_softc(dev); + + /* Handled by parent bus? */ + rm = chipc_get_rman(sc, type); + if (rm == NULL || !rman_is_region_manager(r, rm)) { + return (bus_generic_adjust_resource(dev, child, type, r, start, + end)); + } + + /* The range is limited to the existing region mapping */ + cr = chipc_find_region(sc, rman_get_start(r), rman_get_end(r)); + if (cr == NULL) + return (EINVAL); + + if (end <= start) + return (EINVAL); + + if (start < cr->cr_addr || end > cr->cr_end) + return (EINVAL); + + /* Range falls within the existing region */ + return (rman_adjust_resource(r, start, end)); +} + +/** + * Retain an RF_ACTIVE reference to the region mapping @p r, and + * configure @p r with its subregion values. + * + * @param sc Driver instance state. + * @param child Requesting child device. + * @param type resource type of @p r. + * @param rid resource id of @p r + * @param r resource to be activated. + * @param req_direct If true, failure to allocate a direct bhnd resource + * will be treated as an error. If false, the resource will not be marked + * as RF_ACTIVE if bhnd direct resource allocation fails. + */ +static int +chipc_try_activate_resource(struct chipc_softc *sc, device_t child, int type, + int rid, struct resource *r, bool req_direct) +{ + struct rman *rm; + struct chipc_region *cr; + bhnd_size_t cr_offset; + rman_res_t r_start, r_end, r_size; + int error; + + rm = chipc_get_rman(sc, type); + if (rm == NULL || !rman_is_region_manager(r, rm)) + return (EINVAL); + + r_start = rman_get_start(r); + r_end = rman_get_end(r); + r_size = rman_get_size(r); + + /* Find the corresponding chipc region */ + cr = chipc_find_region(sc, r_start, r_end); + if (cr == NULL) + return (EINVAL); + + /* Calculate subregion offset within the chipc region */ + cr_offset = r_start - cr->cr_addr; + + /* Retain (and activate, if necessary) the chipc region */ + if ((error = chipc_retain_region(sc, cr, RF_ACTIVE))) + return (error); + + /* Configure child resource with its subregion values. */ + if (cr->cr_res->direct) { + error = chipc_init_child_resource(r, cr->cr_res->res, + cr_offset, r_size); + if (error) + goto cleanup; + + /* Mark active */ + if ((error = rman_activate_resource(r))) + goto cleanup; + } else if (req_direct) { + error = ENOMEM; + goto cleanup; + } + + return (0); + +cleanup: + chipc_release_region(sc, cr, RF_ACTIVE); + return (error); +} + +static int +chipc_activate_bhnd_resource(device_t dev, device_t child, int type, + int rid, struct bhnd_resource *r) +{ + struct chipc_softc *sc; + struct rman *rm; + int error; + + sc = device_get_softc(dev); + + /* Delegate non-locally managed resources to parent */ + rm = chipc_get_rman(sc, type); + if (rm == NULL || !rman_is_region_manager(r->res, rm)) { + return (bhnd_bus_generic_activate_resource(dev, child, type, + rid, r)); + } + + /* Try activating the chipc region resource */ + error = chipc_try_activate_resource(sc, child, type, rid, r->res, + false); + if (error) + return (error); + + /* Mark the child resource as direct according to the returned resource + * state */ + if (rman_get_flags(r->res) & RF_ACTIVE) + r->direct = true; + + return (0); +} + +static int +chipc_activate_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + struct chipc_softc *sc; + struct rman *rm; + + sc = device_get_softc(dev); + + /* Delegate non-locally managed resources to parent */ + rm = chipc_get_rman(sc, type); + if (rm == NULL || !rman_is_region_manager(r, rm)) { + return (bus_generic_activate_resource(dev, child, type, rid, + r)); + } + + /* Try activating the chipc region-based resource */ + return (chipc_try_activate_resource(sc, child, type, rid, r, true)); +} + +/** + * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). + */ +static int +chipc_deactivate_resource(device_t dev, device_t child, int type, + int rid, struct resource *r) +{ + struct chipc_softc *sc; + struct chipc_region *cr; + struct rman *rm; + int error; + + sc = device_get_softc(dev); + + /* Handled by parent bus? */ + rm = chipc_get_rman(sc, type); + if (rm == NULL || !rman_is_region_manager(r, rm)) { + return (bus_generic_deactivate_resource(dev, child, type, rid, + r)); + } + + /* Find the corresponding chipc region */ + cr = chipc_find_region(sc, rman_get_start(r), rman_get_end(r)); + if (cr == NULL) + return (EINVAL); + + /* Mark inactive */ + if ((error = rman_deactivate_resource(r))) + return (error); + + /* Drop associated RF_ACTIVE reference */ + chipc_release_region(sc, cr, RF_ACTIVE); + + return (0); +} + +/** + * Examine bus state and make a best effort determination of whether it's + * likely safe to enable the muxed SPROM pins. + * + * On devices that do not use SPROM pin muxing, always returns true. + * + * @param sc chipc driver state. + */ +static bool +chipc_should_enable_sprom(struct chipc_softc *sc) +{ + device_t *devs; + device_t hostb; + device_t parent; + int devcount; + int error; + bool result; + + mtx_assert(&Giant, MA_OWNED); /* for newbus */ + + /* Nothing to do? */ + if (!CHIPC_QUIRK(sc, MUX_SPROM)) + return (true); + + parent = device_get_parent(sc->dev); + hostb = bhnd_find_hostb_device(parent); + + if ((error = device_get_children(parent, &devs, &devcount))) + return (false); + + /* Reject any active devices other than ChipCommon, or the + * host bridge (if any). */ + result = true; + for (int i = 0; i < devcount; i++) { + if (devs[i] == hostb || devs[i] == sc->dev) + continue; + + if (!device_is_attached(devs[i])) + continue; + + if (device_is_suspended(devs[i])) + continue; + + /* Active device; assume SPROM is busy */ + result = false; + break; + } + + free(devs, M_TEMP); + return (result); +} /** * If required by this device, enable access to the SPROM. @@ -345,16 +1069,34 @@ chipc_nvram_identify(struct chipc_softc *sc) * @param sc chipc driver state. */ static int -chipc_enable_sprom_pins(struct chipc_softc *sc) +chipc_enable_sprom_pins(device_t dev) { - uint32_t cctrl; - - CHIPC_LOCK_ASSERT(sc, MA_OWNED); + struct chipc_softc *sc; + uint32_t cctrl; + int error; + + sc = device_get_softc(dev); /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return (0); + /* Make sure we're holding Giant for newbus */ + mtx_lock(&Giant); + CHIPC_LOCK(sc); + + /* Already enabled? */ + if (sc->sprom_refcnt >= 1) { + error = 0; + goto finished; + } + + /* Check whether bus is busy */ + if (!chipc_should_enable_sprom(sc)) { + error = EBUSY; + goto finished; + } + cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); /* 4331 devices */ @@ -368,7 +1110,8 @@ chipc_enable_sprom_pins(struct chipc_softc *sc) cctrl &= ~CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); - return (0); + error = 0; + goto finished; } /* 4360 devices */ @@ -378,7 +1121,17 @@ chipc_enable_sprom_pins(struct chipc_softc *sc) /* Refuse to proceed on unsupported devices with muxed SPROM pins */ device_printf(sc->dev, "muxed sprom lines on unrecognized device\n"); - return (ENXIO); + error = ENXIO; + +finished: + /* Bump the reference count */ + if (error == 0) + sc->sprom_refcnt++; + + CHIPC_UNLOCK(sc); + mtx_unlock(&Giant); + + return (error); } /** @@ -387,16 +1140,25 @@ chipc_enable_sprom_pins(struct chipc_softc *sc) * * @param sc chipc driver state. */ -static int -chipc_disable_sprom_pins(struct chipc_softc *sc) +static void +chipc_disable_sprom_pins(device_t dev) { - uint32_t cctrl; + struct chipc_softc *sc; + uint32_t cctrl; - CHIPC_LOCK_ASSERT(sc, MA_OWNED); + sc = device_get_softc(dev); /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) - return (0); + return; + + CHIPC_LOCK(sc); + + /* Check reference count, skip disable if in-use. */ + KASSERT(sc->sprom_refcnt > 0, ("sprom refcnt overrelease")); + sc->sprom_refcnt--; + if (sc->sprom_refcnt > 0) + goto finished; cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); @@ -411,17 +1173,16 @@ chipc_disable_sprom_pins(struct chipc_softc *sc) cctrl |= CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); - return (0); + goto finished; } /* 4360 devices */ if (CHIPC_QUIRK(sc, 4360_FEM_MUX_SPROM)) { /* Unimplemented */ } - - /* Refuse to proceed on unsupported devices with muxed SPROM pins */ - device_printf(sc->dev, "muxed sprom lines on unrecognized device\n"); - return (ENXIO); + +finished: + CHIPC_UNLOCK(sc); } static bhnd_nvram_src_t @@ -431,64 +1192,6 @@ chipc_nvram_src(device_t dev) return (sc->nvram_src); } -static int -chipc_nvram_getvar(device_t dev, const char *name, void *buf, size_t *len) -{ - struct chipc_softc *sc; - int error; - - sc = device_get_softc(dev); - - switch (sc->nvram_src) { - case BHND_NVRAM_SRC_SPROM: - CHIPC_LOCK(sc); - error = bhnd_sprom_getvar(&sc->sprom, name, buf, len); - CHIPC_UNLOCK(sc); - return (error); - - case BHND_NVRAM_SRC_OTP: - case BHND_NVRAM_SRC_NFLASH: - /* Currently unsupported */ - return (ENXIO); - - case BHND_NVRAM_SRC_UNKNOWN: - return (ENODEV); - } - - /* Unknown NVRAM source */ - return (ENODEV); -} - -static int -chipc_nvram_setvar(device_t dev, const char *name, const void *buf, - size_t len) -{ - struct chipc_softc *sc; - int error; - - sc = device_get_softc(dev); - - switch (sc->nvram_src) { - case BHND_NVRAM_SRC_SPROM: - CHIPC_LOCK(sc); - error = bhnd_sprom_setvar(&sc->sprom, name, buf, len); - CHIPC_UNLOCK(sc); - return (error); - - case BHND_NVRAM_SRC_OTP: - case BHND_NVRAM_SRC_NFLASH: - /* Currently unsupported */ - return (ENXIO); - - case BHND_NVRAM_SRC_UNKNOWN: - default: - return (ENODEV); - } - - /* Unknown NVRAM source */ - return (ENODEV); -} - static void chipc_write_chipctrl(device_t dev, uint32_t value, uint32_t mask) { @@ -513,14 +1216,40 @@ static device_method_t chipc_methods[] = { DEVMETHOD(device_detach, chipc_detach), DEVMETHOD(device_suspend, chipc_suspend), DEVMETHOD(device_resume, chipc_resume), - + + /* Bus interface */ + DEVMETHOD(bus_probe_nomatch, chipc_probe_nomatch), + DEVMETHOD(bus_print_child, chipc_print_child), + DEVMETHOD(bus_child_pnpinfo_str, chipc_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, chipc_child_location_str), + + DEVMETHOD(bus_add_child, chipc_add_child), + DEVMETHOD(bus_child_deleted, chipc_child_deleted), + + 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, chipc_alloc_resource), + DEVMETHOD(bus_release_resource, chipc_release_resource), + DEVMETHOD(bus_adjust_resource, chipc_adjust_resource), + DEVMETHOD(bus_activate_resource, chipc_activate_resource), + DEVMETHOD(bus_deactivate_resource, chipc_deactivate_resource), + DEVMETHOD(bus_get_resource_list, chipc_get_resource_list), + + 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), + + /* BHND bus inteface */ + DEVMETHOD(bhnd_bus_activate_resource, chipc_activate_bhnd_resource), + /* ChipCommon interface */ DEVMETHOD(bhnd_chipc_nvram_src, chipc_nvram_src), DEVMETHOD(bhnd_chipc_write_chipctrl, chipc_write_chipctrl), - - /* NVRAM interface */ - DEVMETHOD(bhnd_nvram_getvar, chipc_nvram_getvar), - DEVMETHOD(bhnd_nvram_setvar, chipc_nvram_setvar), + DEVMETHOD(bhnd_chipc_enable_sprom, chipc_enable_sprom_pins), + DEVMETHOD(bhnd_chipc_disable_sprom, chipc_disable_sprom_pins), DEVMETHOD_END }; diff --git a/sys/dev/bhnd/cores/chipc/chipc.h b/sys/dev/bhnd/cores/chipc/chipc.h index 2757f7965c76..7a37aaaa6930 100644 --- a/sys/dev/bhnd/cores/chipc/chipc.h +++ b/sys/dev/bhnd/cores/chipc/chipc.h @@ -48,4 +48,4 @@ bhnd_chipc_nvram_src(device_t dev) return (BHND_CHIPC_NVRAM_SRC(dev)); } -#endif /* _BHND_CORES_CHIPC_CHIPC_H_ */ \ No newline at end of file +#endif /* _BHND_CORES_CHIPC_CHIPC_H_ */ diff --git a/sys/dev/bhnd/cores/chipc/chipc_private.h b/sys/dev/bhnd/cores/chipc/chipc_private.h new file mode 100644 index 000000000000..d58e1fd36bf6 --- /dev/null +++ b/sys/dev/bhnd/cores/chipc/chipc_private.h @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2015-2016 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_CORES_CHIPC_CHIPC_PRIVATE_H_ +#define _BHND_CORES_CHIPC_CHIPC_PRIVATE_H_ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * Private bhnd_chipc(4) driver definitions. + */ + +struct chipc_caps; +struct chipc_region; +struct chipc_softc; + +int chipc_init_child_resource(struct resource *r, + struct resource *parent, + bhnd_size_t offset, bhnd_size_t size); + +struct chipc_region *chipc_alloc_region(struct chipc_softc *sc, + bhnd_port_type type, u_int port, + u_int region); +void chipc_free_region(struct chipc_softc *sc, + struct chipc_region *cr); +struct chipc_region *chipc_find_region(struct chipc_softc *sc, + rman_res_t start, rman_res_t end); +struct chipc_region *chipc_find_region_by_rid(struct chipc_softc *sc, + int rid); + +int chipc_retain_region(struct chipc_softc *sc, + struct chipc_region *cr, int flags); +int chipc_release_region(struct chipc_softc *sc, + struct chipc_region *cr, int flags); + +void chipc_print_caps(device_t dev, + struct chipc_caps *caps); + +/** + * chipc SYS_RES_MEMORY region allocation record. + */ +struct chipc_region { + bhnd_port_type cr_port_type; /**< bhnd port type */ + u_int cr_port_num; /**< bhnd port number */ + u_int cr_region_num; /**< bhnd region number */ + + bhnd_addr_t cr_addr; /**< region base address */ + bhnd_addr_t cr_end; /**< region end address */ + bhnd_size_t cr_count; /**< region count */ + int cr_rid; /**< rid, or -1 if no rid + * is allocated by the bus for + * this region */ + + struct bhnd_resource *cr_res; /**< bus resource, or NULL */ + u_int cr_refs; /**< RF_ALLOCATED refcount */ + u_int cr_act_refs; /**< RF_ACTIVE refcount */ + + STAILQ_ENTRY(chipc_region) cr_link; +}; + +#endif /* _BHND_CORES_CHIPC_CHIPC_PRIVATE_H_ */ diff --git a/sys/dev/bhnd/cores/chipc/chipc_subr.c b/sys/dev/bhnd/cores/chipc/chipc_subr.c new file mode 100644 index 000000000000..3485bf339cd2 --- /dev/null +++ b/sys/dev/bhnd/cores/chipc/chipc_subr.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 2016 Michael Zhilin + * Copyright (c) 2015-2016 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 "chipc_private.h" +#include "chipcvar.h" + +/** + * 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. + */ +int +chipc_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 bus values */ + vaddr = (uintptr_t) rman_get_virtual(parent); + bt = rman_get_bustag(parent); + bh = rman_get_bushandle(parent); + + /* Configure child resource with offset-adjusted 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); +} + + +/* + * Print a capability structure. + */ +void +chipc_print_caps(device_t dev, struct chipc_caps *caps) +{ +#define CC_TFS(_flag) (caps->_flag ? "yes" : "no") + + device_printf(dev, "MIPSEB: %-3s | BP64: %s\n", + CC_TFS(mipseb), CC_TFS(backplane_64)); + device_printf(dev, "UARTs: %-3hhu | UGPIO: %s\n", + caps->num_uarts, CC_TFS(uart_gpio)); + // XXX: hitting a kvprintf bug with '%#02x' not prefixing '0x' in + // some cases, and not apply the field width in others + device_printf(dev, "UARTClk: 0x%02x | Flash: %u\n", + caps->uart_clock, caps->flash_type); + device_printf(dev, "SPROM: %-3s | OTP: %s\n", + CC_TFS(sprom), CC_TFS(otp_size)); + device_printf(dev, "CFIsz: 0x%02x | OTPsz: 0x%02x\n", + caps->cfi_width, caps->otp_size); + device_printf(dev, "ExtBus: 0x%02x | PwCtl: %s\n", + caps->extbus_type, CC_TFS(power_control)); + device_printf(dev, "PLL: 0x%02x | JTAGM: %s\n", + caps->pll_type, CC_TFS(jtag_master)); + device_printf(dev, "PMU: %-3s | ECI: %s\n", + CC_TFS(pmu), CC_TFS(eci)); + device_printf(dev, "SECI: %-3s | GSIO: %s\n", + CC_TFS(seci), CC_TFS(gsio)); + device_printf(dev, "AOB: %-3s | BootROM: %s\n", + CC_TFS(aob), CC_TFS(boot_rom)); + +#undef CC_TFS +} + +/** + * Allocate and initialize new region record. + * + * @param sc Driver instance state. + * @param type The port type to query. + * @param port The port number to query. + * @param region The region number to query. + */ +struct chipc_region * +chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type, + u_int port, u_int region) +{ + struct chipc_region *cr; + int error; + + /* Don't bother allocating a chipc_region if init will fail */ + if (!bhnd_is_region_valid(sc->dev, type, port, region)) + return (NULL); + + /* Allocate and initialize region info */ + cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT); + if (cr == NULL) + return (NULL); + + cr->cr_port_type = type; + cr->cr_port_num = port; + cr->cr_region_num = region; + cr->cr_res = NULL; + cr->cr_refs = 0; + cr->cr_act_refs = 0; + + error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr, + &cr->cr_count); + if (error) { + device_printf(sc->dev, + "fetching chipc region address failed: %d\n", error); + goto failed; + } + + cr->cr_end = cr->cr_addr + cr->cr_count - 1; + + /* Note that not all regions have an assigned rid, in which case + * this will return -1 */ + cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region); + return (cr); + +failed: + device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n", + bhnd_port_type_name(type), port, region); + free(cr, M_BHND); + return (NULL); +} + +/** + * Deallocate the given region record and its associated resource, if any. + * + * @param sc Driver instance state. + * @param cr Region record to be deallocated. + */ +void +chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr) +{ + KASSERT(cr->cr_refs == 0, + ("chipc %s%u.%u region has %u active references", + bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num, + cr->cr_region_num, cr->cr_refs)); + + if (cr->cr_res != NULL) { + bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_rid, + cr->cr_res); + } + + free(cr, M_BHND); +} + +/** + * Locate the region mapping the given range, if any. Returns NULL if no + * valid region is found. + * + * @param sc Driver instance state. + * @param start start of address range. + * @param end end of address range. + */ +struct chipc_region * +chipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end) +{ + struct chipc_region *cr; + + if (start > end) + return (NULL); + + STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) { + if (start < cr->cr_addr || end > cr->cr_end) + continue; + + /* Found */ + return (cr); + } + + /* Not found */ + return (NULL); +} + +/** + * Locate a region mapping by its bhnd-assigned resource id (as returned by + * bhnd_get_port_rid). + * + * @param sc Driver instance state. + * @param rid Resource ID to query for. + */ +struct chipc_region * +chipc_find_region_by_rid(struct chipc_softc *sc, int rid) +{ + struct chipc_region *cr; + int port_rid; + + STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) { + port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type, + cr->cr_port_num, cr->cr_region_num); + if (port_rid == -1 || port_rid != rid) + continue; + + /* Found */ + return (cr); + } + + /* Not found */ + return (NULL); +} + +/** + * Retain a reference to a chipc_region, allocating and activating the + * backing resource as required. + * + * @param sc chipc driver instance state + * @param cr region to retain. + * @param flags specify RF_ALLOCATED to retain an allocation reference, + * RF_ACTIVE to retain an activation reference. + */ +int +chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags) +{ + int error; + + KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags")); + + CHIPC_LOCK(sc); + + /* Handle allocation */ + if (flags & RF_ALLOCATED) { + /* If this is the first reference, allocate the resource */ + if (cr->cr_refs == 0) { + KASSERT(cr->cr_res == NULL, + ("non-NULL resource has refcount")); + + cr->cr_res = bhnd_alloc_resource(sc->dev, + SYS_RES_MEMORY, &cr->cr_rid, cr->cr_addr, + cr->cr_end, cr->cr_count, 0); + + if (cr->cr_res == NULL) { + CHIPC_UNLOCK(sc); + return (ENXIO); + } + } + + /* Increment allocation refcount */ + cr->cr_refs++; + } + + + /* Handle activation */ + if (flags & RF_ACTIVE) { + KASSERT(cr->cr_refs > 0, + ("cannot activate unallocated resource")); + + /* If this is the first reference, activate the resource */ + if (cr->cr_act_refs == 0) { + error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY, + cr->cr_rid, cr->cr_res); + if (error) { + /* Drop any allocation reference acquired + * above */ + CHIPC_UNLOCK(sc); + chipc_release_region(sc, cr, + flags &~ RF_ACTIVE); + return (error); + } + } + + /* Increment activation refcount */ + cr->cr_act_refs++; + } + + CHIPC_UNLOCK(sc); + return (0); +} + +/** + * Release a reference to a chipc_region, deactivating and releasing the + * backing resource if the reference count hits zero. + * + * @param sc chipc driver instance state + * @param cr region to retain. + * @param flags specify RF_ALLOCATED to release an allocation reference, + * RF_ACTIVE to release an activation reference. + */ +int +chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr, + int flags) +{ + int error; + + CHIPC_LOCK(sc); + error = 0; + + if (flags & RF_ACTIVE) { + KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released")); + KASSERT(cr->cr_act_refs <= cr->cr_refs, + ("RF_ALLOCATED released with RF_ACTIVE held")); + + /* If this is the last reference, deactivate the resource */ + if (cr->cr_act_refs == 1) { + error = bhnd_deactivate_resource(sc->dev, + SYS_RES_MEMORY, cr->cr_rid, cr->cr_res); + if (error) + goto done; + } + + /* Drop our activation refcount */ + cr->cr_act_refs--; + } + + if (flags & RF_ALLOCATED) { + KASSERT(cr->cr_refs > 0, ("overrelease of refs")); + + /* If this is the last reference, release the resource */ + if (cr->cr_refs == 1) { + error = bhnd_release_resource(sc->dev, + SYS_RES_MEMORY, cr->cr_rid, cr->cr_res); + if (error) + goto done; + + cr->cr_res = NULL; + cr->cr_rid = -1; + } + + /* Drop our allocation refcount */ + cr->cr_refs--; + } + +done: + CHIPC_UNLOCK(sc); + return (error); +} diff --git a/sys/dev/bhnd/cores/chipc/chipcreg.h b/sys/dev/bhnd/cores/chipc/chipcreg.h index b67eb34b9646..815c1b2636aa 100644 --- a/sys/dev/bhnd/cores/chipc/chipcreg.h +++ b/sys/dev/bhnd/cores/chipc/chipcreg.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2010 Broadcom Corporation * All rights reserved. * @@ -25,66 +25,189 @@ #ifndef _BHND_CORES_CHIPC_CHIPCREG_H_ #define _BHND_CORES_CHIPC_CHIPCREG_H_ -#define CHIPC_CHIPID_SIZE 0x100 /**< size of the register block - containing the chip - identification registers. */ +#define CHIPC_CHIPID_SIZE 0x100 /**< size of the register block + containing the chip + identification registers + required during bus + enumeration */ /** Evaluates to true if the given ChipCommon core revision provides * the core count via the chip identification register. */ #define CHIPC_NCORES_MIN_HWREV(hwrev) ((hwrev) == 4 || (hwrev) >= 6) -#define CHIPC_GET_ATTR(_entry, _attr) \ - ((_entry & CHIPC_ ## _attr ## _MASK) >> CHIPC_ ## _attr ## _SHIFT) +#define CHIPC_GET_FLAG(_value, _flag) (((_value) & _flag) != 0) +#define CHIPC_GET_BITS(_value, _field) \ + ((_value & _field ## _MASK) >> _field ## _SHIFT) -#define CHIPC_ID 0x0 +#define CHIPC_ID 0x00 #define CHIPC_CAPABILITIES 0x04 -#define CHIPC_OTPST 0x10 -#define CHIPC_CHIPCTRL 0x28 /**< chip control */ -#define CHIPC_CHIPST 0x2c /**< chip status */ +#define CHIPC_CORECTRL 0x08 /* rev >= 1 */ +#define CHIPC_BIST 0x0C + +#define CHIPC_OTPST 0x10 /**< otp status */ +#define CHIPC_OTPCTRL 0x14 /**< otp control */ +#define CHIPC_OTPPROG 0x18 +#define CHIPC_OTPLAYOUT 0x1C /**< otp layout (rev >= 23) */ + +#define CHIPC_INTST 0x20 /**< interrupt status */ +#define CHIPC_INTM 0x24 /**< interrupt mask */ + +#define CHIPC_CHIPCTRL 0x28 /**< chip control (rev >= 11) */ +#define CHIPC_CHIPST 0x2C /**< chip status (rev >= 11) */ + #define CHIPC_JTAGCMD 0x30 #define CHIPC_JTAGIR 0x34 #define CHIPC_JTAGDR 0x38 #define CHIPC_JTAGCTRL 0x3c -#define CHIPC_GPIOPU 0x58 -#define CHIPC_GPIOPD 0x5c + +#define CHIPC_SFLASHCTRL 0x40 +#define CHIPC_SFLASHADDR 0x44 +#define CHIPC_SFLASHDATA 0x48 + +/* siba backplane configuration broadcast (siba-only) */ +#define CHIPC_SBBCAST_ADDR 0x50 +#define CHIPC_SBBCAST_DATA 0x54 + +#define CHIPC_GPIOPU 0x58 /**< pull-up mask (rev >= 20) */ +#define CHIPC_GPIOPD 0x5C /**< pull down mask (rev >= 20) */ #define CHIPC_GPIOIN 0x60 #define CHIPC_GPIOOUT 0x64 #define CHIPC_GPIOOUTEN 0x68 -#define CHIPC_GPIOCTRL 0x6c +#define CHIPC_GPIOCTRL 0x6C #define CHIPC_GPIOPOL 0x70 -#define CHIPC_GPIOINTM 0x74 -#define CHIPC_WATCHDOG 0x80 +#define CHIPC_GPIOINTM 0x74 /**< gpio interrupt mask */ + +#define CHIPC_GPIOEVENT 0x78 /**< gpio event (rev >= 11) */ +#define CHIPC_GPIOEVENT_INTM 0x7C /**< gpio event interrupt mask (rev >= 11) */ + +#define CHIPC_WATCHDOG 0x80 /**< watchdog timer */ + +#define CHIPC_GPIOEVENT_INTPOLARITY 0x84 /**< gpio even interrupt polarity (rev >= 11) */ + +#define CHIPC_GPIOTIMERVAL 0x88 /**< gpio-based LED duty cycle (rev >= 16) */ +#define CHIPC_GPIOTIMEROUTMASK 0x8C + +/* clock control block */ #define CHIPC_CLKC_N 0x90 -#define CHIPC_CLKC_M0 0x94 -#define CHIPC_CLKC_M1 0x98 -#define CHIPC_CLKC_M2 0x9c -#define CHIPC_CLKC_M3 0xa0 -#define CHIPC_CLKDIV 0xa4 -#define CHIPC_SYS_CLK_CTL 0xc0 -#define CHIPC_EROMPTR 0xfc /**< 32-bit EROM base address +#define CHIPC_CLKC_SB 0x94 /* m0 (backplane) */ +#define CHIPC_CLKC_PCI 0x98 /* m1 */ +#define CHIPC_CLKC_M2 0x9C /* mii/uart/mipsref */ +#define CHIPC_CLKC_M3 0xA0 /* cpu */ +#define CHIPC_CLKDIV 0xA4 /* rev >= 3 */ +#define CHIPC_GPIODEBUGSEL 0xA8 /* rev >= 28 */ +#define CHIPC_CAPABILITIES_EXT 0xAC + +/* pll delay (registers rev >= 4) */ +#define CHIPC_PLL_ON_DELAY 0xB0 +#define CHIPC_PLL_FREFSEL_DELAY 0xB4 +#define CHIPC_PLL_SLOWCLK_CTL 0xB8 /* revs 6-9 */ + +/* "instaclock" registers */ +#define CHIPC_SYS_CLK_CTL 0xC0 /* rev >= 10 */ +#define CHIPC_SYS_CLKSTATESTRETCH 0xC4 /* rev >= 10 */ + +/* indirect backplane access (rev >= 10) */ +#define CHIPC_BP_ADDRLOW 0xD0 +#define CHIPC_BP_ADDRHIGH 0xD4 +#define CHIPC_BP_DATA 0xD8 +#define CHIPC_BP_INDACCESS 0xE0 + +/* SPI/I2C (rev >= 37) */ +#define CHIPC_GSIO_CTRL 0xE4 +#define CHIPC_GSIO_ADDR 0xE8 +#define CHIPC_GSIO_DATA 0xEC + +/* More clock dividers (corerev >= 32) */ +#define CHIPC_CLKDIV2 0xF0 + +#define CHIPC_EROMPTR 0xFC /**< 32-bit EROM base address * on BCMA devices */ + +/* ExtBus control registers (rev >= 3) */ +#define CHIPC_PCMCIA_CFG 0x100 +#define CHIPC_PCMCIA_MEMWAIT 0x104 +#define CHIPC_PCMCIA_ATTRWAIT 0x108 +#define CHIPC_PCMCIA_IOWAIT 0x10C +#define CHIPC_IDE_CFG 0x110 +#define CHIPC_IDE_MEMWAIT 0x114 +#define CHIPC_IDE_ATTRWAIT 0x118 +#define CHIPC_IDE_IOWAIT 0x11C +#define CHIPC_PROG_CFG 0x120 +#define CHIPC_PROG_WAITCOUNT 0x124 +#define CHIPC_FLASH_CFG 0x128 +#define CHIPC_FLASH_WAITCOUNT 0x12C +#define CHIPC_SECI_CFG 0x130 +#define CHIPC_SECI_ST 0x134 +#define CHIPC_SECI_STM 0x138 +#define CHIPC_SECI_RXNBC 0x13C + +/* Enhanced Coexistence Interface (ECI) registers (rev 21-34) */ +#define CHIPC_ECI_OUTPUT 0x140 +#define CHIPC_ECI_CTRL 0x144 +#define CHIPC_ECI_INPUTLO 0x148 +#define CHIPC_ECI_INPUTMI 0x14C +#define CHIPC_ECI_INPUTHI 0x150 +#define CHIPC_ECI_INPUTINTPOLARITYLO 0x154 +#define CHIPC_ECI_INPUTINTPOLARITYMI 0x158 +#define CHIPC_ECI_INPUTINTPOLARITYHI 0x15C +#define CHIPC_ECI_INTMASKLO 0x160 +#define CHIPC_ECI_INTMASKMI 0x164 +#define CHIPC_ECI_INTMASKHI 0x168 +#define CHIPC_ECI_EVENTLO 0x16C +#define CHIPC_ECI_EVENTMI 0x170 +#define CHIPC_ECI_EVENTHI 0x174 +#define CHIPC_ECI_EVENTMASKLO 0x178 +#define CHIPC_ECI_EVENTMASKMI 0x17C +#define CHIPC_ECI_EVENTMASKHI 0x180 + +#define CHIPC_FLASHSTRCFG 0x18C /**< BCM4706 NAND flash config */ + #define CHIPC_SPROM_CTRL 0x190 /**< SPROM interface (rev >= 32) */ #define CHIPC_SPROM_ADDR 0x194 #define CHIPC_SPROM_DATA 0x198 -#define CHIPC_CLK_CTL_ST SI_CLK_CTL_ST -#define CHIPC_PMU_CTL 0x600 + +/* Clock control and hardware workarounds (corerev >= 20) */ +#define CHIPC_CLK_CTL_ST 0x1E0 +#define CHIPC_SPROM_HWWAR 0x19 + +#define CHIPC_UART0 0x300 +#define CHIPC_UART1 0x400 + +/* PMU registers (rev >= 20) */ +#define CHIPC_PMU_BASE 0x600 +#define CHIPC_PMU_CTRL 0x600 #define CHIPC_PMU_CAP 0x604 #define CHIPC_PMU_ST 0x608 #define CHIPC_PMU_RES_STATE 0x60c +#define CHIPC_PMU_RES_PENDING 0x610 #define CHIPC_PMU_TIMER 0x614 #define CHIPC_PMU_MIN_RES_MASK 0x618 #define CHIPC_PMU_MAX_RES_MASK 0x61c +#define CHIPC_PMU_RES_TABLE_SEL 0x620 +#define CHIPC_PMU_RES_DEP_MASK 0x624 +#define CHIPC_PMU_RES_UPDN_TIMER 0x628 +#define CHIPC_PMU_RES_TIMER 0x62C +#define CHIPC_PMU_CLKSTRETCH 0x630 +#define CHIPC_PMU_WATCHDOG 0x634 +#define CHIPC_PMU_GPIOSEL 0x638 /* pmu rev >= 1 ? */ +#define CHIPC_PMU_GPIOEN 0x63C /* pmu rev >= 1 ? */ +#define CHIPC_PMU_RES_REQ_TIMER_SEL 0x640 +#define CHIPC_PMU_RES_REQ_TIMER 0x644 +#define CHIPC_PMU_RES_REQ_MASK 0x648 #define CHIPC_CHIPCTL_ADDR 0x650 #define CHIPC_CHIPCTL_DATA 0x654 #define CHIPC_PMU_REG_CONTROL_ADDR 0x658 #define CHIPC_PMU_REG_CONTROL_DATA 0x65C #define CHIPC_PMU_PLL_CONTROL_ADDR 0x660 #define CHIPC_PMU_PLL_CONTROL_DATA 0x664 +#define CHIPC_PMU_STRAPOPT 0x668 /* chipc rev >= 28 */ +#define CHIPC_PMU_XTALFREQ 0x66C /* pmu rev >= 10 */ + #define CHIPC_SPROM_OTP 0x800 /* SPROM/OTP address space */ +#define CHIPC_SPROM_OTP_SIZE 0x400 /** chipid */ -#define CHIPC_ID 0x0 /**< identification register */ #define CHIPC_ID_CHIP_MASK 0x0000FFFF /**< chip id */ #define CHIPC_ID_CHIP_SHIFT 0 #define CHIPC_ID_REV_MASK 0x000F0000 /**< chip revision */ @@ -97,30 +220,49 @@ #define CHIPC_ID_BUS_SHIFT 28 /* capabilities */ -#define CHIPC_CAP_UARTS_MASK 0x00000003 /* Number of UARTs */ +#define CHIPC_CAP_NUM_UART_MASK 0x00000003 /* Number of UARTs (1-3) */ +#define CHIPC_CAP_NUM_UART_SHIFT 0 #define CHIPC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */ -#define CHIPC_CAP_UCLKSEL 0x00000018 /* UARTs clock select */ -#define CHIPC_CAP_UINTCLK 0x00000008 /* UARTs are driven by internal divided clock */ +#define CHIPC_CAP_UCLKSEL_MASK 0x00000018 /* UARTs clock select */ +#define CHIPC_CAP_UCLKSEL_SHIFT 3 +#define CHIPC_CAP_UCLKSEL_UINTCLK 0x1 /* UARTs are driven by internal divided clock */ #define CHIPC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */ #define CHIPC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */ -#define CHIPC_CAP_EXTBUS_NONE 0x00000000 /* No ExtBus present */ -#define CHIPC_CAP_EXTBUS_FULL 0x00000040 /* ExtBus: PCMCIA, IDE & Prog */ -#define CHIPC_CAP_EXTBUS_PROG 0x00000080 /* ExtBus: ProgIf only */ +#define CHIPC_CAP_EXTBUS_SHIFT 6 +#define CHIPC_CAP_EXTBUS_NONE 0x0 /* No ExtBus present */ +#define CHIPC_CAP_EXTBUS_FULL 0x1 /* ExtBus: PCMCIA, IDE & Prog */ +#define CHIPC_CAP_EXTBUS_PROG 0x2 /* ExtBus: ProgIf only */ #define CHIPC_CAP_FLASH_MASK 0x00000700 /* Type of flash */ +#define CHIPC_CAP_FLASH_SHIFT 8 +#define CHIPC_CAP_FLASH_NONE 0x000 /* No flash */ +#define CHIPC_CAP_SFLASH_ST 0x100 /* ST serial flash */ +#define CHIPC_CAP_SFLASH_AT 0x200 /* Atmel serial flash */ +#define CHIPC_CAP_NFLASH 0x300 /* NAND flash */ +#define CHIPC_CAP_PFLASH 0x700 /* Parallel flash */ #define CHIPC_CAP_PLL_MASK 0x00038000 /* Type of PLL */ +#define CHIPC_CAP_PLL_SHIFT 15 #define CHIPC_CAP_PWR_CTL 0x00040000 /* Power control */ -#define CHIPC_CAP_OTP_SIZE 0x00380000 /* OTP Size (0 = none) */ +#define CHIPC_CAP_OTP_SIZE_MASK 0x00380000 /* OTP Size (0 = none) */ #define CHIPC_CAP_OTP_SIZE_SHIFT 19 /* OTP Size shift */ #define CHIPC_CAP_OTP_SIZE_BASE 5 /* OTP Size base */ #define CHIPC_CAP_JTAGP 0x00400000 /* JTAG Master Present */ #define CHIPC_CAP_ROM 0x00800000 /* Internal boot rom active */ #define CHIPC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */ #define CHIPC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */ +#define CHIPC_CAP_ECI 0x20000000 /* Enhanced Coexistence Interface */ #define CHIPC_CAP_SPROM 0x40000000 /* SPROM Present, rev >= 32 */ -#define CHIPC_CAP_NFLASH 0x80000000 /* Nand flash present, rev >= 35 */ +#define CHIPC_CAP_4706_NFLASH 0x80000000 /* NAND flash present, BCM4706 or chipc rev38 (BCM5357)? */ #define CHIPC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */ #define CHIPC_CAP2_GSIO 0x00000002 /* GSIO (spi/i2c) present, rev >= 37 */ +#define CHIPC_CAP2_GCI 0x00000004 /* GCI present (rev >= ??) */ +#define CHIPC_CAP2_AOB 0x00000040 /* Always on Bus present (rev >= 49) + * + * If set, PMU and GCI registers + * are found in dedicated cores. + * + * This appears to be a lower power + * APB bus, bridged via ARM APB IP. */ /* * ChipStatus (Common) @@ -215,8 +357,10 @@ enum { #define CHIPC_OTPP_START_BUSY 0x80000000 #define CHIPC_OTPP_READ 0x40000000 /* HND OTP */ -/* otplayout reg corerev >= 36 */ -#define CHIPC_OTP_CISFORMAT_NEW 0x80000000 +/* otplayout */ +#define CHIPC_OTPL_SIZE_MASK 0x0000f000 /* rev >= 49 */ +#define CHIPC_OTPL_SIZE_SHIFT 12 +#define CHIPC_OTPL_CISFORMAT_NEW 0x80000000 /* rev >= 36 */ /* Opcodes for OTPP_OC field */ #define CHIPC_OTPPOC_READ 0 @@ -488,12 +632,6 @@ enum { #define CHIPC_CLKC_5350_N 0x0311 #define CHIPC_CLKC_5350_M 0x04020009 -/* Flash types in the chipcommon capabilities register */ -#define CHIPC_FLASH_NONE 0x000 /* No flash */ -#define CHIPC_SFLASH_ST 0x100 /* ST serial flash */ -#define CHIPC_SFLASH_AT 0x200 /* Atmel serial flash */ -#define CHIPC_PFLASH 0x700 /* Parallel flash */ - /* Bits in the ExtBus config registers */ #define CHIPC_CFG_EN 0x0001 /* Enable */ #define CHIPC_CFG_EM_MASK 0x000e /* Extif Mode */ @@ -501,11 +639,11 @@ enum { #define CHIPC_CFG_EM_SYNC 0x0002 /* Synchronous */ #define CHIPC_CFG_EM_PCMCIA 0x0004 /* PCMCIA */ #define CHIPC_CFG_EM_IDE 0x0006 /* IDE */ -#define CHIPC_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */ -#define CHIPC_CFG_CD_MASK 0x00e0 /* Sync: Clock divisor, rev >= 20 */ -#define CHIPC_CFG_CE 0x0100 /* Sync: Clock enable, rev >= 20 */ -#define CHIPC_CFG_SB 0x0200 /* Sync: Size/Bytestrobe, rev >= 20 */ -#define CHIPC_CFG_IS 0x0400 /* Extif Sync Clk Select, rev >= 20 */ +#define CHIPC_FLASH_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */ +#define CHIPC_FLASH_CFG_CD_MASK 0x00e0 /* Sync: Clock divisor, rev >= 20 */ +#define CHIPC_FLASH_CFG_CE 0x0100 /* Sync: Clock enable, rev >= 20 */ +#define CHIPC_FLASH_CFG_SB 0x0200 /* Sync: Size/Bytestrobe, rev >= 20 */ +#define CHIPC_FLASH_CFG_IS 0x0400 /* Extif Sync Clk Select, rev >= 20 */ /* ExtBus address space */ #define CHIPC_EB_BASE 0x1a000000 /* Chipc ExtBus base address */ diff --git a/sys/dev/bhnd/cores/chipc/chipcvar.h b/sys/dev/bhnd/cores/chipc/chipcvar.h index fe8dd0ac6917..14001ff61f2c 100644 --- a/sys/dev/bhnd/cores/chipc/chipcvar.h +++ b/sys/dev/bhnd/cores/chipc/chipcvar.h @@ -39,8 +39,56 @@ DECLARE_CLASS(bhnd_chipc); extern devclass_t bhnd_chipc_devclass; -#define CHIPC_MAX_RES 1 -#define CHIPC_MAX_RSPEC (CHIPC_MAX_RES+1) +struct chipc_region; + +/** + * Supported ChipCommon flash types. + */ +typedef enum { + CHIPC_FLASH_NONE = 0, /**< No flash, or a type unrecognized + by the ChipCommon driver */ + CHIPC_PFLASH_CFI = 1, /**< CFI-compatible parallel flash */ + CHIPC_SFLASH_ST = 2, /**< ST serial flash */ + CHIPC_SFLASH_AT = 3, /**< Atmel serial flash */ + CHIPC_QSFLASH_ST = 4, /**< ST quad-SPI flash */ + CHIPC_QSFLASH_AT = 5, /**< Atmel quad-SPI flash */ + CHIPC_NFLASH = 6, /**< NAND flash */ + CHIPC_NFLASH_4706 = 7 /**< BCM4706 NAND flash */ +} chipc_flash; + +/** + * ChipCommon capability flags; + */ +struct chipc_caps { + uint8_t num_uarts; /**< Number of attached UARTS (1-3) */ + bool mipseb; /**< MIPS is big-endian */ + uint8_t uart_clock; /**< UART clock source (see CHIPC_CAP_UCLKSEL_*) */ + uint8_t uart_gpio; /**< UARTs own GPIO pins 12-15 */ + + uint8_t extbus_type; /**< ExtBus type (CHIPC_CAP_EXTBUS_*) */ + chipc_flash flash_type; /**< Flash type */ + uint8_t otp_size; /**< OTP (row?) size, 0 if not present */ + uint8_t cfi_width; /**< CFI bus width, 0 if unknown or CFI not present */ + + uint8_t pll_type; /**< PLL type */ + bool power_control; /**< Power control available */ + bool jtag_master; /**< JTAG Master present */ + bool boot_rom; /**< Internal boot ROM is active */ + uint8_t backplane_64; /**< Backplane supports 64-bit addressing. + Note that this does not gaurantee + the CPU itself supports 64-bit + addressing. */ + bool pmu; /**< PMU is present. */ + bool eci; /**< ECI (enhanced coexistence inteface) is present. */ + bool seci; /**< SECI (serial ECI) is present */ + bool sprom; /**< SPROM is present */ + bool gsio; /**< GSIO (SPI/I2C) present */ + bool aob; /**< AOB (always on bus) present. + If set, PMU and GCI registers are + not accessible via ChipCommon, + and are instead accessible via + dedicated cores on the bhnd bus */ +}; /* * ChipCommon device quirks / features @@ -56,10 +104,10 @@ enum { CHIPC_QUIRK_SUPPORTS_SPROM = (1<<1), /** - * External NAND NVRAM is supported, along with the CHIPC_CAP_NFLASH - * capability flag. + * The BCM4706 NAND flash interface is supported, along with the + * CHIPC_CAP_4706_NFLASH capability flag. */ - CHIPC_QUIRK_SUPPORTS_NFLASH = (1<<2), + CHIPC_QUIRK_4706_NFLASH = (1<<2), /** * The SPROM is attached via muxed pins. The pins must be switched @@ -104,25 +152,47 @@ enum { * device. The muxed pins must be switched to allow reading/writing * the SPROM. */ - CHIPC_QUIRK_4360_FEM_MUX_SPROM = (1<<5) | CHIPC_QUIRK_MUX_SPROM + CHIPC_QUIRK_4360_FEM_MUX_SPROM = (1<<5) | + CHIPC_QUIRK_MUX_SPROM, + + /** Supports CHIPC_CAPABILITIES_EXT register */ + CHIPC_QUIRK_SUPPORTS_CAP_EXT = (1<<6), + + /** OTP size is defined via CHIPC_OTPLAYOUT register in later + * ChipCommon revisions using the 'IPX' OTP controller. */ + CHIPC_QUIRK_IPX_OTPLAYOUT_SIZE = (1<<7), }; +/** + * chipc child device info. + */ +struct chipc_devinfo { + struct resource_list resources; /**< child resources */ +}; + +/** + * chipc driver instance state. + */ struct chipc_softc { device_t dev; - struct resource_spec rspec[CHIPC_MAX_RSPEC]; - struct bhnd_resource *res[CHIPC_MAX_RES]; - struct bhnd_resource *core; /**< core registers. */ + struct chipc_region *core_region; /**< region containing core registers */ + struct bhnd_chipid ccid; /**< chip identification */ - uint32_t quirks; /**< CHIPC_QUIRK_* quirk flags */ - uint32_t caps; /**< CHIPC_CAP_* capability register flags */ - uint32_t cst; /**< CHIPC_CST* status register flags */ - bhnd_nvram_src_t nvram_src; /**< NVRAM source */ - + uint32_t quirks; /**< chipc quirk flags */ + struct chipc_caps caps; /**< chipc capabilities */ + + bhnd_nvram_src_t nvram_src; /**< identified NVRAM source */ + struct mtx mtx; /**< state mutex. */ struct bhnd_sprom sprom; /**< OTP/SPROM shadow, if any */ + size_t sprom_refcnt; /**< SPROM hardware refcount */ + + struct rman mem_rman; /**< port memory manager */ + + STAILQ_HEAD(, chipc_region) mem_regions;/**< memory allocation records */ }; #define CHIPC_LOCK_INIT(sc) \ @@ -133,4 +203,4 @@ struct chipc_softc { #define CHIPC_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) #define CHIPC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) -#endif /* _BHND_CORES_CHIPC_CHIPCVAR_H_ */ \ No newline at end of file +#endif /* _BHND_CORES_CHIPC_CHIPCVAR_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_nvram.h b/sys/dev/bhnd/nvram/bhnd_nvram.h index 2c0c36b74319..5b61538f834a 100644 --- a/sys/dev/bhnd/nvram/bhnd_nvram.h +++ b/sys/dev/bhnd/nvram/bhnd_nvram.h @@ -40,11 +40,7 @@ typedef enum { BHND_NVRAM_SRC_OTP, /**< On-chip one-time-programmable * memory. */ - BHND_NVRAM_SRC_NFLASH, /**< External flash device accessible - * via on-chip flash core, such - * as the NAND/QSPI controller cores - * used on Northstar devices to access - * NVRAM. */ + BHND_NVRAM_SRC_FLASH, /**< External flash */ BHND_NVRAM_SRC_SPROM, /**< External serial EEPROM. */ BHND_NVRAM_SRC_UNKNOWN /**< No NVRAM source is directly @@ -71,12 +67,4 @@ typedef enum { */ } bhnd_nvram_src_t; -/** - * Evaluates to true if the given NVRAM data source is accessible via - * ChipCommon. - */ -#define BHND_NVRAM_SRC_CC(_src) \ - ((_src) == BHND_NVRAM_SRC_OTP || (_src) == BHND_NVRAM_SRC_SPROM) - - -#endif /* _BHND_NVRAM_BHND_NVRAM_H_ */ \ No newline at end of file +#endif /* _BHND_NVRAM_BHND_NVRAM_H_ */ diff --git a/sys/dev/bhnd/nvram/bhnd_sprom.c b/sys/dev/bhnd/nvram/bhnd_sprom.c index 6cddb60a8253..9b4855f84107 100644 --- a/sys/dev/bhnd/nvram/bhnd_sprom.c +++ b/sys/dev/bhnd/nvram/bhnd_sprom.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,539 +30,180 @@ #include __FBSDID("$FreeBSD$"); +/* + * BHND SPROM driver. + * + * Abstract driver for memory-mapped SPROM devices. + */ + #include +#include #include -#include -#include +#include +#include +#include #include #include +#include #include -#include +#include -#include "nvramvar.h" +#include "bhnd_nvram_if.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) -}; +#define SPROM_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "BHND SPROM lock", MTX_DEF) +#define SPROM_LOCK(sc) mtx_lock(&(sc)->mtx) +#define SPROM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define SPROM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) +#define SPROM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) /** - * 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. + * Default bhnd sprom driver implementation of DEVICE_PROBE(). */ int -bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, - bus_size_t offset) +bhnd_sprom_probe(device_t dev) { - bus_size_t res_size; - int error; + /* Quiet by default */ + if (!bootverbose) + device_quiet(dev); + device_set_desc(dev, "Broadcom SPROM/OTP"); - sprom->dev = rman_get_device(r->res); - sprom->sp_res = r; - sprom->sp_res_off = offset; + /* Refuse wildcard attachments */ + return (BUS_PROBE_NOWILDCARD); +} - /* Determine maximum possible SPROM image size */ - res_size = rman_get_size(r->res); - if (offset >= res_size) - return (EINVAL); +/** + * Default bhnd sprom driver implementation of DEVICE_ATTACH(). + * + * Assumes sprom is mapped via YS_RES_MEMORY resource with RID 0. + */ +int +bhnd_sprom_attach(device_t dev) +{ + struct bhnd_sprom_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; - sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX); + /* Allocate SPROM resource */ + sc->sprom_rid = 0; + sc->sprom_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sprom_rid, RF_ACTIVE); + if (sc->sprom_res == NULL) { + device_printf(dev, "failed to allocate resources\n"); + return (ENXIO); + } - /* 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); + /* Initialize SPROM shadow */ + if ((error = bhnd_sprom_init(&sc->shadow, sc->sprom_res, 0))) { + device_printf(dev, "unrecognized SPROM format\n"); + goto failed; + } - /* Read and identify SPROM image */ - if ((error = sprom_populate_shadow(sprom))) - return (error); + /* Initialize mutex */ + SPROM_LOCK_INIT(sc); + + return (0); + +failed: + bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, + sc->sprom_res); + return (error); +} + +/** + * Default bhnd sprom driver implementation of DEVICE_DETACH(). + */ +int +bhnd_sprom_resume(device_t dev) +{ + return (0); +} + +/** + * Default bhnd sprom driver implementation of DEVICE_DETACH(). + */ +int +bhnd_sprom_suspend(device_t dev) +{ + return (0); +} + +/** + * Default bhnd sprom driver implementation of DEVICE_DETACH(). + */ +int +bhnd_sprom_detach(device_t dev) +{ + struct bhnd_sprom_softc *sc; + + sc = device_get_softc(dev); + + bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, + sc->sprom_res); + bhnd_sprom_fini(&sc->shadow); + SPROM_LOCK_DESTROY(sc); return (0); } /** - * Release all resources held by @p sprom. - * - * @param sprom A SPROM instance previously initialized via bhnd_sprom_init(). + * Default bhnd sprom driver implementation of BHND_NVRAM_GETVAR(). */ -void -bhnd_sprom_fini(struct bhnd_sprom *sprom) +static int +bhnd_sprom_getvar_meth(device_t dev, const char *name, void *buf, size_t *len) { - 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; + struct bhnd_sprom_softc *sc; int error; - if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) - return (error); + sc = device_get_softc(dev); - /* Provide required size */ - if (buf == NULL) { - *len = req_size; - return (0); - } + SPROM_LOCK(sc); + error = bhnd_sprom_getvar(&sc->shadow, name, buf, len); + SPROM_UNLOCK(sc); - /* 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); + return (error); } -/* 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. + * Default bhnd sprom driver implementation of BHND_NVRAM_SETVAR(). */ -int -bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf, +static int +bhnd_sprom_setvar_meth(device_t dev, 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; + struct bhnd_sprom_softc *sc; int error; - uint8_t crc; - if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) - return (error); + sc = device_get_softc(dev); - /* Provide required size */ - if (len != req_size) - return (EINVAL); + SPROM_LOCK(sc); + error = bhnd_sprom_setvar(&sc->shadow, name, buf, len); + SPROM_UNLOCK(sc); - /* 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); + return (error); } -/* 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; +static device_method_t bhnd_sprom_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_sprom_probe), + DEVMETHOD(device_attach, bhnd_sprom_attach), + DEVMETHOD(device_resume, bhnd_sprom_resume), + DEVMETHOD(device_suspend, bhnd_sprom_suspend), + DEVMETHOD(device_detach, bhnd_sprom_detach), - crc = BHND_NVRAM_CRC8_INITIAL; + /* NVRAM interface */ + DEVMETHOD(bhnd_nvram_getvar, bhnd_sprom_getvar_meth), + DEVMETHOD(bhnd_nvram_setvar, bhnd_sprom_setvar_meth), - /* Identify the SPROM revision (and populate the SPROM shadow) */ - for (size_t i = 0; i < nitems(sprom_fmts); i++) { - fmt = &sprom_fmts[i]; + DEVMETHOD_END +}; - /* 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; - 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; - - 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); -} +DEFINE_CLASS_0(bhnd_nvram, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc)); +MODULE_VERSION(bhnd_sprom, 1); diff --git a/sys/dev/bhnd/nvram/bhnd_sprom_subr.c b/sys/dev/bhnd/nvram/bhnd_sprom_subr.c new file mode 100644 index 000000000000..dee09411872c --- /dev/null +++ b/sys/dev/bhnd/nvram/bhnd_sprom_subr.c @@ -0,0 +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 Parser + * + * Provides identification, decoding, and encoding 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; + 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; + + 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); +} diff --git a/sys/dev/bhnd/nvram/bhnd_spromvar.h b/sys/dev/bhnd/nvram/bhnd_spromvar.h index 15cfa79187d7..9abc8af22ea5 100644 --- a/sys/dev/bhnd/nvram/bhnd_spromvar.h +++ b/sys/dev/bhnd/nvram/bhnd_spromvar.h @@ -29,9 +29,31 @@ * $FreeBSD$ */ -#ifndef _BHND_NVRAM_BHND_SPROM_H_ -#define _BHND_NVRAM_BHND_SPROM_H_ +#ifndef _BHND_NVRAM_BHND_SPROMVAR_H_ +#define _BHND_NVRAM_BHND_SPROMVAR_H_ +#include + +DECLARE_CLASS(bhnd_sprom_driver); +struct bhnd_sprom; + +int bhnd_sprom_probe(device_t dev); +int bhnd_sprom_attach(device_t dev); +int bhnd_sprom_resume(device_t dev); +int bhnd_sprom_suspend(device_t dev); +int bhnd_sprom_detach(device_t dev); + +int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, + bus_size_t offset); +void bhnd_sprom_fini(struct bhnd_sprom *sprom); +int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, + size_t *len); +int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, + const void *buf, size_t len); + +/** + * bhnd sprom parser instance state. + */ struct bhnd_sprom { device_t dev; /**< sprom parent device */ @@ -46,13 +68,17 @@ struct bhnd_sprom { size_t sp_capacity; /**< shadow buffer capacity */ }; -int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, - bus_size_t offset); -void bhnd_sprom_fini(struct bhnd_sprom *sprom); -int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, - size_t *len); -int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, - const void *buf, size_t len); +/** + * bhnd_sprom driver instance state. Must be first member of all subclass + * softc structures. + */ +struct bhnd_sprom_softc { + device_t dev; + struct bhnd_resource *sprom_res; /**< SPROM resource */ + int sprom_rid; /**< SPROM RID */ + struct bhnd_sprom shadow; /**< SPROM shadow */ + struct mtx mtx; /**< SPROM shadow mutex */ +}; -#endif /* _BHND_NVRAM_BHND_SPROM_H_ */ +#endif /* _BHND_NVRAM_BHND_SPROMVAR_H_ */ diff --git a/sys/dev/bhnd/siba/siba.c b/sys/dev/bhnd/siba/siba.c index c34fc4fca7d2..a508d3b39ae9 100644 --- a/sys/dev/bhnd/siba/siba.c +++ b/sys/dev/bhnd/siba/siba.c @@ -409,7 +409,7 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, continue; *addr = addrspace->sa_base; - *size = addrspace->sa_size; + *size = addrspace->sa_size - addrspace->sa_bus_reserved; return (0); } diff --git a/sys/dev/bhnd/siba/siba_subr.c b/sys/dev/bhnd/siba/siba_subr.c index e688006c2c66..5d9fc72b1dcd 100644 --- a/sys/dev/bhnd/siba/siba_subr.c +++ b/sys/dev/bhnd/siba/siba_subr.c @@ -243,11 +243,16 @@ siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type, { struct siba_addrspace *sa; struct siba_port *port; + rman_res_t r_size; /* Verify that base + size will not overflow */ if (UINT32_MAX - size < base) return (ERANGE); + /* Verify that size - bus_reserved will not underflow */ + if (size < bus_reserved) + return (ERANGE); + /* Must not be 0-length */ if (size == 0) return (EINVAL); @@ -266,11 +271,12 @@ siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type, sa->sa_size = size; sa->sa_sid = sid; sa->sa_region_num = region_num; - + sa->sa_bus_reserved = bus_reserved; + /* Populate the resource list */ - size -= bus_reserved; + r_size = size - bus_reserved; sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY, - base, base + size - 1, size); + base, base + r_size - 1, r_size); /* Append to target port */ STAILQ_INSERT_TAIL(&port->sp_addrs, sa, sa_link); diff --git a/sys/dev/bhnd/siba/sibavar.h b/sys/dev/bhnd/siba/sibavar.h index 784803c6cee4..2c9f14c50e78 100644 --- a/sys/dev/bhnd/siba/sibavar.h +++ b/sys/dev/bhnd/siba/sibavar.h @@ -97,6 +97,8 @@ struct siba_addrspace { u_int sa_region_num; /**< bhnd region id */ uint8_t sa_sid; /**< siba-assigned address space ID */ int sa_rid; /**< bus resource id */ + uint32_t sa_bus_reserved;/**< number of bytes at high end of + * address space reserved for the bus */ STAILQ_ENTRY(siba_addrspace) sa_link; }; diff --git a/sys/modules/bhnd/Makefile b/sys/modules/bhnd/Makefile index 9ef9d13cd96a..b9678cbed1c4 100644 --- a/sys/modules/bhnd/Makefile +++ b/sys/modules/bhnd/Makefile @@ -5,7 +5,8 @@ KMOD= bhnd SRCS= bhnd.c bhnd_subr.c \ - bhnd_sprom.c nvram_subr.c \ + bhnd_sprom.c bhnd_sprom_subr.c \ + nvram_subr.c \ bhnd_nvram_map.h bhnd_nvram_map_data.h SRCS+= bhnd_bus_if.c bhnd_bus_if.h \ diff --git a/sys/modules/bhnd/cores/bhnd_chipc/Makefile b/sys/modules/bhnd/cores/bhnd_chipc/Makefile index bb75aad6b8d1..b4ca2a64bd57 100644 --- a/sys/modules/bhnd/cores/bhnd_chipc/Makefile +++ b/sys/modules/bhnd/cores/bhnd_chipc/Makefile @@ -3,7 +3,8 @@ .PATH: ${.CURDIR}/../../../../dev/bhnd/cores/chipc KMOD= bhnd_chipc -SRCS= chipc.c +SRCS= chipc.c chipc_subr.c \ + bhnd_sprom_chipc.c SRCS+= device_if.h bus_if.h bhnd_bus_if.h \ bhnd_chipc_if.h bhnd_nvram_if.h