[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 <landonf@landonf.org> Reviewed by: mizkha@gmail.com Differential Revision: https://reviews.freebsd.org/D6471
This commit is contained in:
parent
95320acebc
commit
f4a3eb0297
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -53,29 +53,15 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/bhnd/nvram/bhnd_spromvar.h>
|
||||
|
||||
#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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
101
sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c
Normal file
101
sys/dev/bhnd/cores/chipc/bhnd_sprom_chipc.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*-
|
||||
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* ChipCommon SPROM driver.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/nvram/bhnd_nvram.h>
|
||||
#include <dev/bhnd/nvram/bhnd_spromvar.h>
|
||||
|
||||
#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);
|
File diff suppressed because it is too large
Load Diff
@ -48,4 +48,4 @@ bhnd_chipc_nvram_src(device_t dev)
|
||||
return (BHND_CHIPC_NVRAM_SRC(dev));
|
||||
}
|
||||
|
||||
#endif /* _BHND_CORES_CHIPC_CHIPC_H_ */
|
||||
#endif /* _BHND_CORES_CHIPC_CHIPC_H_ */
|
||||
|
98
sys/dev/bhnd/cores/chipc/chipc_private.h
Normal file
98
sys/dev/bhnd/cores/chipc/chipc_private.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*-
|
||||
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* 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 <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
#include <dev/bhnd/bhndvar.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
365
sys/dev/bhnd/cores/chipc/chipc_subr.c
Normal file
365
sys/dev/bhnd/cores/chipc/chipc_subr.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
|
||||
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
|
||||
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
|
||||
* 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 */
|
||||
|
@ -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_ */
|
||||
#endif /* _BHND_CORES_CHIPC_CHIPCVAR_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_ */
|
||||
#endif /* _BHND_NVRAM_BHND_NVRAM_H_ */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
|
||||
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -30,539 +30,180 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* BHND SPROM driver.
|
||||
*
|
||||
* Abstract driver for memory-mapped SPROM devices.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/bhnd/bhndvar.h>
|
||||
#include <dev/bhnd/bhnd.h>
|
||||
|
||||
#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);
|
||||
|
568
sys/dev/bhnd/nvram/bhnd_sprom_subr.c
Normal file
568
sys/dev/bhnd/nvram/bhnd_sprom_subr.c
Normal file
@ -0,0 +1,568 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Landon Fuller <landon@landonf.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/bhnd/bhndvar.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -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 <dev/bhnd/bhnd.h>
|
||||
|
||||
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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user