diff options
| author | Landon J. Fuller <landonf@FreeBSD.org> | 2016-11-26 23:22:32 +0000 |
|---|---|---|
| committer | Landon J. Fuller <landonf@FreeBSD.org> | 2016-11-26 23:22:32 +0000 |
| commit | 77cb4d3e5016a2fd090d07ed3e01a199723641d9 (patch) | |
| tree | eefb722db9e959216f82ba0d0d320df9d68a9a4e /sys/mips/broadcom | |
| parent | b84ef73179e9018f9197ba5293c41459f3b468d3 (diff) | |
Notes
Diffstat (limited to 'sys/mips/broadcom')
| -rw-r--r-- | sys/mips/broadcom/bcm_nvram_cfe.c | 527 | ||||
| -rw-r--r-- | sys/mips/broadcom/bcm_nvram_cfevar.h | 47 | ||||
| -rw-r--r-- | sys/mips/broadcom/files.broadcom | 2 |
3 files changed, 576 insertions, 0 deletions
diff --git a/sys/mips/broadcom/bcm_nvram_cfe.c b/sys/mips/broadcom/bcm_nvram_cfe.c new file mode 100644 index 000000000000..314d38683464 --- /dev/null +++ b/sys/mips/broadcom/bcm_nvram_cfe.c @@ -0,0 +1,527 @@ +/*- + * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * BHND CFE NVRAM driver. + * + * Provides access to device NVRAM via CFE. + */ + +#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 <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/bhnd/bhnd.h> + +#include <dev/cfe/cfe_api.h> +#include <dev/cfe/cfe_error.h> +#include <dev/cfe/cfe_ioctl.h> + +#include <dev/bhnd/nvram/bhnd_nvram_iovar.h> + +#include "bhnd_nvram_if.h" + +#include "bcm_nvram_cfevar.h" + +/** + * CFE-backed bhnd_nvram_io implementation. + */ +struct bhnd_nvram_iocfe { + struct bhnd_nvram_io io; /**< common I/O instance state */ + + char *dname; /**< CFE device name (borrowed) */ + int fd; /**< CFE file descriptor */ + size_t offset; /**< base offset */ + size_t size; /**< device size */ + bool req_blk_erase; /**< flash blocks must be erased + before writing */ +}; + +BHND_NVRAM_IOPS_DEFN(iocfe) + +#define IOCFE_LOG(_io, _fmt, ...) \ + printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__) + +static int bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, + char *dname); + +static struct bhnd_nvram_io *bhnd_nvram_find_cfedev(device_t dev, + char **dname, + bhnd_nvram_data_class_t **cls); + +/** Known CFE NVRAM device names, in probe order. */ +static char *nvram_cfe_devs[] = { + "nflash0.nvram", /* NAND */ + "nflash1.nvram", + "flash0.nvram", + "flash1.nvram", +}; + +/** Supported CFE NVRAM formats, in probe order. */ +static bhnd_nvram_data_class_t * const nvram_cfe_fmts[] = { + &bhnd_nvram_bcm_class, + &bhnd_nvram_tlv_class +}; + + +static int +bhnd_nvram_cfe_probe(device_t dev) +{ + struct bhnd_nvram_io *io; + bhnd_nvram_data_class_t *cls; + const char *cls_desc; + char *dname; + char *desc; + + /* Locate a usable CFE device */ + io = bhnd_nvram_find_cfedev(dev, &dname, &cls); + if (io == NULL) + return (ENXIO); + bhnd_nvram_io_free(io); + + /* Format the device description */ + cls_desc = bhnd_nvram_data_class_desc(cls); + asprintf(&desc, M_DEVBUF, "%s CFE %s", cls_desc, dname); + if (desc != NULL) { + device_set_desc_copy(dev, desc); + free(desc, M_DEVBUF); + } else { + device_set_desc(dev, cls_desc); + } + + /* Refuse wildcard attachments */ + return (BUS_PROBE_NOWILDCARD); +} + + +static int +bhnd_nvram_cfe_attach(device_t dev) +{ + struct bhnd_nvram_cfe_softc *sc; + bhnd_nvram_data_class_t *cls; + struct bhnd_nvram_io *io; + char *dname; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* Locate NVRAM device via CFE */ + io = bhnd_nvram_find_cfedev(dev, &dname, &cls); + if (io == NULL) { + device_printf(dev, "CFE NVRAM device not found\n"); + return (ENXIO); + } + + /* Initialize NVRAM store and free the I/O context */ + error = bhnd_nvram_store_parse_new(&sc->store, io, cls); + bhnd_nvram_io_free(io); + if (error) + return (error); + + return (error); +} + +static int +bhnd_nvram_cfe_resume(device_t dev) +{ + return (0); +} + +static int +bhnd_nvram_cfe_suspend(device_t dev) +{ + return (0); +} + +static int +bhnd_nvram_cfe_detach(device_t dev) +{ + struct bhnd_nvram_cfe_softc *sc; + + sc = device_get_softc(dev); + + bhnd_nvram_store_free(sc->store); + + return (0); +} + +static int +bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len, + bhnd_nvram_type type) +{ + struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev); + + return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type)); +} + +static int +bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf, + size_t len, bhnd_nvram_type type) +{ + struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev); + + return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type)); +} + +/** + * Find, open, identify, and return an I/O context mapping our + * CFE NVRAM device. + * + * @param dev bhnd_nvram_cfe device. + * @param[out] dname On success, the CFE device name. + * @param[out] cls On success, the identified NVRAM data format + * class. + * + * @retval non-NULL success. the caller inherits ownership of the returned + * NVRAM I/O context. + * @retval NULL if no usable CFE NVRAM device could be found. + */ +static struct bhnd_nvram_io * +bhnd_nvram_find_cfedev(device_t dev, char **dname, + bhnd_nvram_data_class_t **cls) +{ + struct bhnd_nvram_io *io; + int devinfo; + int error, result; + + for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) { + *cls = nvram_cfe_fmts[i]; + + for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) { + *dname = nvram_cfe_devs[j]; + + /* Does the device exist? */ + if ((devinfo = cfe_getdevinfo(*dname)) < 0) { + if (devinfo != CFE_ERR_DEVNOTFOUND) { + device_printf(dev, "cfe_getdevinfo(%s) " + "failed: %d\n", *dname, devinfo); + } + + continue; + } + + /* Open for reading */ + if ((error = bhnd_nvram_iocfe_new(&io, *dname))) + continue; + + /* Probe */ + result = bhnd_nvram_data_probe(*cls, io); + if (result <= 0) { + /* Found a supporting NVRAM data class */ + return (io); + } + + /* Keep searching */ + bhnd_nvram_io_free(io); + io = NULL; + } + } + + return (NULL); +} + + +/** + * Allocate and return a new I/O context backed by a CFE device. + * + * The caller is responsible for deallocating the returned I/O context via + * bhnd_nvram_io_free(). + * + * @param[out] io On success, a valid I/O context for @p dname. + * @param dname The name of the CFE device to be opened for reading. + * + * @retval 0 success. + * @retval non-zero if opening @p dname otherwise fails, a standard unix error + * will be returned. + */ +static int +bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, char *dname) +{ + struct bhnd_nvram_iocfe *iocfe; + nvram_info_t nvram_info; + int cerr, devinfo, dtype, rlen; + int64_t nv_offset; + u_int nv_size; + bool req_blk_erase; + int error; + + iocfe = malloc(sizeof(*iocfe), M_DEVBUF, M_WAITOK); + iocfe->io.iops = &bhnd_nvram_iocfe_ops; + iocfe->dname = dname; + + /* Try to open the device */ + iocfe->fd = cfe_open(dname); + if (iocfe->fd <= 0) { + IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd); + + error = ENXIO; + goto failed; + } + + /* Try to fetch device info */ + if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) { + IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo); + error = ENXIO; + goto failed; + } + + /* Verify device type */ + dtype = devinfo & CFE_DEV_MASK; + switch (dtype) { + case CFE_DEV_FLASH: + case CFE_DEV_NVRAM: + /* Valid device type */ + break; + default: + IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype); + error = ENXIO; + goto failed; + } + + /* Try to fetch nvram info from CFE */ + cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO, + (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0); + if (cerr == CFE_OK) { + /* Sanity check the result; must not be a negative integer */ + if (nvram_info.nvram_size < 0 || + nvram_info.nvram_offset < 0) + { + IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n", + nvram_info.nvram_size, nvram_info.nvram_offset); + error = ENXIO; + goto failed; + } + + nv_offset = nvram_info.nvram_offset; + nv_size = nvram_info.nvram_size; + req_blk_erase = (nvram_info.nvram_eraseflg != 0); + } else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) { + IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr); + error = ENXIO; + goto failed; + } + + /* Fall back on flash info. + * + * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, + * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns + * CFE_ERR_INV_COMMAND. + */ + if (cerr == CFE_ERR_INV_COMMAND) { + flash_info_t fi; + + cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO, + (unsigned char *)&fi, sizeof(fi), &rlen, 0); + + if (cerr != CFE_OK) { + IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n", + cerr); + error = ENXIO; + goto failed; + } + + nv_offset = 0x0; + nv_size = fi.flash_size; + req_blk_erase = !(fi.flash_flags & FLASH_FLAG_NOERASE); + } + + + /* Verify that the full NVRAM layout can be represented via size_t */ + if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) { + IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n", + nv_size, (intmax_t)nv_offset); + error = ENXIO; + goto failed; + } + + iocfe->offset = nv_offset; + iocfe->size = nv_size; + iocfe->req_blk_erase = req_blk_erase; + + *io = &iocfe->io; + return (CFE_OK); + +failed: + if (iocfe->fd >= 0) + cfe_close(iocfe->fd); + + free(iocfe, M_DEVBUF); + + *io = NULL; + return (error); +} + +static void +bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io; + + cfe_close(iocfe->fd); + free(io, M_DEVBUF); +} + +static size_t +bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io) +{ + struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io; + return (iocfe->size); +} + +static int +bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset, + const void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset, + void **ptr, size_t nbytes, size_t *navail) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + /* unsupported */ + return (ENODEV); +} + +static int +bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, + size_t nbytes) +{ + struct bhnd_nvram_iocfe *iocfe; + size_t remain; + int64_t cfe_offset; + int nr, nreq; + + iocfe = (struct bhnd_nvram_iocfe *)io; + + /* Determine (and validate) the base CFE offset */ +#if (SIZE_MAX > INT64_MAX) + if (iocfe->offset > INT64_MAX || offset > INT64_MAX) + return (ENXIO); +#endif + + if (INT64_MAX - offset < iocfe->offset) + return (ENXIO); + + cfe_offset = iocfe->offset + offset; + + /* Verify that cfe_offset + nbytes is representable */ + if (INT64_MAX - cfe_offset < nbytes) + return (ENXIO); + + /* Perform the read */ + for (remain = nbytes; remain > 0;) { + void *p; + size_t nread; + int64_t cfe_noff; + + nread = (nbytes - remain); + cfe_noff = cfe_offset + nread; + p = ((uint8_t *)buffer + nread); + nreq = ummin(INT_MAX, remain); + + nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq); + if (nr < 0) { + IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr); + return (ENXIO); + } + + /* Check for unexpected short read */ + if (nr == 0 && remain > 0) { + /* If the request fits entirely within the CFE + * device range, we shouldn't hit EOF */ + if (remain < iocfe->size && + iocfe->size - remain > offset) + { + IOCFE_LOG(iocfe, "cfe_readblk() returned " + "unexpected short read (%d/%d)\n", nr, + nreq); + return (ENXIO); + } + } + + if (nr == 0) + break; + + remain -= nr; + } + + /* Check for short read */ + if (remain > 0) + return (ENXIO); + + return (0); +} + +static device_method_t bhnd_nvram_cfe_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_nvram_cfe_probe), + DEVMETHOD(device_attach, bhnd_nvram_cfe_attach), + DEVMETHOD(device_resume, bhnd_nvram_cfe_resume), + DEVMETHOD(device_suspend, bhnd_nvram_cfe_suspend), + DEVMETHOD(device_detach, bhnd_nvram_cfe_detach), + + /* NVRAM interface */ + DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_cfe_getvar), + DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_cfe_setvar), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods, + sizeof(struct bhnd_nvram_cfe_softc)); +EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe, + bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY); diff --git a/sys/mips/broadcom/bcm_nvram_cfevar.h b/sys/mips/broadcom/bcm_nvram_cfevar.h new file mode 100644 index 000000000000..6044111d5ae2 --- /dev/null +++ b/sys/mips/broadcom/bcm_nvram_cfevar.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ + +#ifndef _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ +#define _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ + +#include <sys/param.h> +#include <sys/bus.h> + +#include <dev/bhnd/nvram/bhnd_nvram.h> +#include <dev/bhnd/nvram/bhnd_nvram_store.h> + +/** bhnd_nvram_cfe driver instance state. */ +struct bhnd_nvram_cfe_softc { + device_t dev; + struct bhnd_nvram_store *store; /**< nvram store */ +}; + +#endif /* _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ */ diff --git a/sys/mips/broadcom/files.broadcom b/sys/mips/broadcom/files.broadcom index 04070411444b..f293c1e327e9 100644 --- a/sys/mips/broadcom/files.broadcom +++ b/sys/mips/broadcom/files.broadcom @@ -7,6 +7,8 @@ mips/broadcom/bcm_machdep.c standard mips/broadcom/bcm_bmips.c optional siba_nexus siba mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma +mips/broadcom/bcm_nvram_cfe.c optional bhnd siba_nexus cfe | \ + bhnd bcma_nexus cfe mips/broadcom/bcm_pmu.c standard mips/mips/tick.c standard |
