diff options
author | marcel <marcel@FreeBSD.org> | 2008-10-25 06:18:12 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2008-10-25 06:18:12 +0000 |
commit | b53efb78faed8c781f00c65853b3da6eeb585a42 (patch) | |
tree | 44068880cbf92d230e6fc6299fb8d840a3951fc0 | |
parent | b40a52a5c254d7d7498acc56a38ca50e4cc67105 (diff) | |
download | FreeBSD-src-b53efb78faed8c781f00c65853b3da6eeb585a42.zip FreeBSD-src-b53efb78faed8c781f00c65853b3da6eeb585a42.tar.gz |
Add a driver for flash memory that implements to the Common Flash
Memory Interface (CFI). The flash memory can be read and written
to through /dev/cfi# and an ioctl() exists so processes can read
the query information.
The driver supports the AMD and Intel command set, though only
the AMD command has been tested.
Obtained from: Juniper Networks, Inc.
-rw-r--r-- | sys/conf/files | 2 | ||||
-rw-r--r-- | sys/conf/files.powerpc | 1 | ||||
-rw-r--r-- | sys/dev/cfi/cfi_bus_lbc.c | 83 | ||||
-rw-r--r-- | sys/dev/cfi/cfi_core.c | 419 | ||||
-rw-r--r-- | sys/dev/cfi/cfi_dev.c | 277 | ||||
-rw-r--r-- | sys/dev/cfi/cfi_reg.h | 117 | ||||
-rw-r--r-- | sys/dev/cfi/cfi_var.h | 77 | ||||
-rw-r--r-- | sys/sys/cfictl.h | 47 |
8 files changed, 1023 insertions, 0 deletions
diff --git a/sys/conf/files b/sys/conf/files index 2ea7457..b14d4e1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -532,6 +532,8 @@ dev/buslogic/bt_pci.c optional bt pci dev/cardbus/cardbus.c optional cardbus dev/cardbus/cardbus_cis.c optional cardbus dev/cardbus/cardbus_device.c optional cardbus +dev/cfi/cfi_core.c optional cfi +dev/cfi/cfi_dev.c optional cfi dev/ciss/ciss.c optional ciss dev/cm/smc90cx6.c optional cm dev/cmx/cmx.c optional cmx diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 93b6cbf..25b193e 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -28,6 +28,7 @@ opt_ah.h optional ath_hal \ crypto/blowfish/bf_enc.c optional crypto | ipsec crypto/des/des_enc.c optional crypto | ipsec | netsmb dev/bm/if_bm.c optional bm powermac +dev/cfi/cfi_bus_lbc.c optional cfi dev/fb/fb.c optional sc dev/hwpmc/hwpmc_powerpc.c optional hwpmc dev/kbd/kbd.c optional sc diff --git a/sys/dev/cfi/cfi_bus_lbc.c b/sys/dev/cfi/cfi_bus_lbc.c new file mode 100644 index 0000000..df24f38 --- /dev/null +++ b/sys/dev/cfi/cfi_bus_lbc.c @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2007, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> + +#include <dev/cfi/cfi_var.h> + +#include <powerpc/mpc85xx/lbc.h> + +static int cfi_lbc_probe(device_t); + +static device_method_t cfi_lbc_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, cfi_lbc_probe), + DEVMETHOD(device_attach, cfi_attach), + DEVMETHOD(device_detach, cfi_detach), + + {0, 0} +}; + +static driver_t cfi_lbc_driver = { + cfi_driver_name, + cfi_lbc_methods, + sizeof(struct cfi_softc), +}; + +DRIVER_MODULE (cfi, lbc, cfi_lbc_driver, cfi_devclass, 0, 0); + +static int +cfi_lbc_probe(device_t dev) +{ + uintptr_t devtype; + int error; + + error = BUS_READ_IVAR(device_get_parent(dev), dev, LBC_IVAR_DEVTYPE, + &devtype); + if (error) + return (error); + + if (devtype != LBC_DEVTYPE_CFI) + return (EINVAL); + + return (cfi_probe(dev)); +} diff --git a/sys/dev/cfi/cfi_core.c b/sys/dev/cfi/cfi_core.c new file mode 100644 index 0000000..4df3190 --- /dev/null +++ b/sys/dev/cfi/cfi_core.c @@ -0,0 +1,419 @@ +/*- + * Copyright (c) 2007, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> + +#include <dev/cfi/cfi_reg.h> +#include <dev/cfi/cfi_var.h> + +extern struct cdevsw cfi_cdevsw; + +char cfi_driver_name[] = "cfi"; +devclass_t cfi_devclass; + +uint32_t +cfi_read(struct cfi_softc *sc, u_int ofs) +{ + uint32_t val; + + ofs &= ~(sc->sc_width - 1); + switch (sc->sc_width) { + case 1: + val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); + break; + case 2: + val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); + break; + case 4: + val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); + break; + default: + val = ~0; + break; + } + + return (val); +} + +static void +cfi_write(struct cfi_softc *sc, u_int ofs, u_int val) +{ + + ofs &= ~(sc->sc_width - 1); + switch (sc->sc_width) { + case 1: + bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); + break; + case 2: + bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val); + break; + case 4: + bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val); + break; + } +} + +uint8_t +cfi_read_qry(struct cfi_softc *sc, u_int ofs) +{ + uint8_t val; + + cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); + val = cfi_read(sc, ofs * sc->sc_width); + cfi_write(sc, 0, CFI_BCS_READ_ARRAY); + return (val); +} + +static void +cfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) +{ + + cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); + cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); + cfi_write(sc, ofs + addr, data); +} + +static char * +cfi_fmtsize(uint32_t sz) +{ + static char buf[8]; + static const char *sfx[] = { "", "K", "M", "G" }; + int sfxidx; + + sfxidx = 0; + while (sfxidx < 3 && sz > 1023) { + sz /= 1024; + sfxidx++; + } + + sprintf(buf, "%u%sB", sz, sfx[sfxidx]); + return (buf); +} + +int +cfi_probe(device_t dev) +{ + char desc[80]; + struct cfi_softc *sc; + char *vend_str; + int error; + uint16_t iface, vend; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_rid = 0; + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, + RF_ACTIVE); + if (sc->sc_res == NULL) + return (ENXIO); + + sc->sc_tag = rman_get_bustag(sc->sc_res); + sc->sc_handle = rman_get_bushandle(sc->sc_res); + + sc->sc_width = 1; + while (sc->sc_width <= 4) { + if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') + break; + sc->sc_width <<= 1; + } + if (sc->sc_width > 4) { + error = ENXIO; + goto out; + } + + /* We got a Q. Check if we also have the R and the Y. */ + if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || + cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { + error = ENXIO; + goto out; + } + + /* Get the vendor and command set. */ + vend = cfi_read_qry(sc, CFI_QRY_VEND) | + (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); + + sc->sc_cmdset = vend; + + switch (vend) { + case CFI_VEND_AMD_ECS: + case CFI_VEND_AMD_SCS: + vend_str = "AMD/Fujitsu"; + break; + case CFI_VEND_INTEL_ECS: + vend_str = "Intel/Sharp"; + break; + case CFI_VEND_INTEL_SCS: + vend_str = "Intel"; + break; + case CFI_VEND_MITSUBISHI_ECS: + case CFI_VEND_MITSUBISHI_SCS: + vend_str = "Mitsubishi"; + break; + default: + vend_str = "Unknown vendor"; + break; + } + + /* Get the device size. */ + sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); + + /* Sanity-check the I/F */ + iface = cfi_read_qry(sc, CFI_QRY_IFACE) | + (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); + + /* + * Adding 1 to iface will give us a bit-wise "switch" + * that allows us to test for the interface width by + * testing a single bit. + */ + iface++; + + error = (iface & sc->sc_width) ? 0 : EINVAL; + if (error) + goto out; + + snprintf(desc, sizeof(desc), "%s - %s", vend_str, + cfi_fmtsize(sc->sc_size)); + device_set_desc_copy(dev, desc); + + out: + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); + return (error); +} + +int +cfi_attach(device_t dev) +{ + struct cfi_softc *sc; + u_int blksz, blocks; + u_int r, u; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_rid = 0; + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, + RF_ACTIVE); + if (sc->sc_res == NULL) + return (ENXIO); + + sc->sc_tag = rman_get_bustag(sc->sc_res); + sc->sc_handle = rman_get_bushandle(sc->sc_res); + + /* Get time-out values for erase and write. */ + sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); + sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); + sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); + sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); + + /* Get erase regions. */ + sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); + sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), + M_TEMP, M_WAITOK | M_ZERO); + for (r = 0; r < sc->sc_regions; r++) { + blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | + (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); + sc->sc_region[r].r_blocks = blocks + 1; + + blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | + (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); + sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : + blksz * 256; + } + + /* Reset the device to a default state. */ + cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); + + if (bootverbose) { + device_printf(dev, "["); + for (r = 0; r < sc->sc_regions; r++) { + printf("%ux%s%s", sc->sc_region[r].r_blocks, + cfi_fmtsize(sc->sc_region[r].r_blksz), + (r == sc->sc_regions - 1) ? "]\n" : ","); + } + } + + u = device_get_unit(dev); + sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, + "%s%u", cfi_driver_name, u); + sc->sc_nod->si_drv1 = sc; + + return (0); +} + +int +cfi_detach(device_t dev) +{ + struct cfi_softc *sc; + + sc = device_get_softc(dev); + + destroy_dev(sc->sc_nod); + free(sc->sc_region, M_TEMP); + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); + return (0); +} + +static int +cfi_wait_ready(struct cfi_softc *sc, u_int timeout) +{ + int done, error; + uint32_t st0, st; + + done = 0; + error = 0; + timeout *= 10; + while (!done && !error && timeout) { + DELAY(100); + timeout--; + + switch (sc->sc_cmdset) { + case CFI_VEND_INTEL_ECS: + case CFI_VEND_INTEL_SCS: + st = cfi_read(sc, sc->sc_wrofs); + done = (st & 0x80); + if (done) { + if (st & 0x02) + error = EPERM; + else if (st & 0x10) + error = EIO; + else if (st & 0x20) + error = ENXIO; + } + break; + case CFI_VEND_AMD_SCS: + case CFI_VEND_AMD_ECS: + st0 = cfi_read(sc, sc->sc_wrofs); + st = cfi_read(sc, sc->sc_wrofs); + done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; + break; + } + } + if (!done && !error) + error = ETIMEDOUT; + if (error) + printf("\nerror=%d\n", error); + return (error); +} + +int +cfi_write_block(struct cfi_softc *sc) +{ + union { + uint8_t *x8; + uint16_t *x16; + uint32_t *x32; + } ptr; + register_t intr; + int error, i; + + /* Erase the block. */ + switch (sc->sc_cmdset) { + case CFI_VEND_INTEL_ECS: + case CFI_VEND_INTEL_SCS: + cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); + cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); + break; + case CFI_VEND_AMD_SCS: + case CFI_VEND_AMD_ECS: + cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, + CFI_AMD_ERASE_SECTOR); + cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); + break; + default: + /* Better safe than sorry... */ + return (ENODEV); + } + error = cfi_wait_ready(sc, sc->sc_erase_timeout); + if (error) + goto out; + + /* Write the block. */ + ptr.x8 = sc->sc_wrbuf; + for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { + + /* + * Make sure the command to start a write and the + * actual write happens back-to-back without any + * excessive delays. + */ + intr = intr_disable(); + + switch (sc->sc_cmdset) { + case CFI_VEND_INTEL_ECS: + case CFI_VEND_INTEL_SCS: + cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); + break; + case CFI_VEND_AMD_SCS: + case CFI_VEND_AMD_ECS: + cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); + break; + } + switch (sc->sc_width) { + case 1: + bus_space_write_1(sc->sc_tag, sc->sc_handle, + sc->sc_wrofs + i, *(ptr.x8)++); + break; + case 2: + bus_space_write_2(sc->sc_tag, sc->sc_handle, + sc->sc_wrofs + i, *(ptr.x16)++); + break; + case 4: + bus_space_write_4(sc->sc_tag, sc->sc_handle, + sc->sc_wrofs + i, *(ptr.x32)++); + break; + } + + intr_restore(intr); + + error = cfi_wait_ready(sc, sc->sc_write_timeout); + if (error) + goto out; + } + + /* error is 0. */ + + out: + cfi_write(sc, 0, CFI_BCS_READ_ARRAY); + return (error); +} diff --git a/sys/dev/cfi/cfi_dev.c b/sys/dev/cfi/cfi_dev.c new file mode 100644 index 0000000..aad1de2 --- /dev/null +++ b/sys/dev/cfi/cfi_dev.c @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 2007, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/ioccom.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/uio.h> + +#include <sys/cfictl.h> + +#include <machine/atomic.h> +#include <machine/bus.h> + +#include <dev/cfi/cfi_var.h> + +static d_open_t cfi_devopen; +static d_close_t cfi_devclose; +static d_read_t cfi_devread; +static d_write_t cfi_devwrite; +static d_ioctl_t cfi_devioctl; + +struct cdevsw cfi_cdevsw = { + .d_version = D_VERSION, + .d_flags = 0, + .d_name = cfi_driver_name, + .d_open = cfi_devopen, + .d_close = cfi_devclose, + .d_read = cfi_devread, + .d_write = cfi_devwrite, + .d_ioctl = cfi_devioctl, +}; + +/* + * Begin writing into a new block/sector. We read the sector into + * memory and keep updating that, until we move into another sector + * or the process stops writing. At that time we write the whole + * sector to flash (see cfi_block_finish). + */ +static int +cfi_block_start(struct cfi_softc *sc, u_int ofs) +{ + union { + uint8_t *x8; + uint16_t *x16; + uint32_t *x32; + } ptr; + u_int rofs, rsz; + uint32_t val; + int r; + + rofs = 0; + for (r = 0; r < sc->sc_regions; r++) { + rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz; + if (ofs < rofs + rsz) + break; + rofs += rsz; + } + if (r == sc->sc_regions) + return (EFAULT); + + sc->sc_wrbufsz = sc->sc_region[r].r_blksz; + sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK); + sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz; + + /* Read the block from flash for byte-serving. */ + ptr.x8 = sc->sc_wrbuf; + for (r = 0; r < sc->sc_wrbufsz; r += sc->sc_width) { + val = cfi_read(sc, sc->sc_wrofs + r); + switch (sc->sc_width) { + case 1: + *(ptr.x8)++ = val; + break; + case 2: + *(ptr.x16)++ = val; + break; + case 4: + *(ptr.x32)++ = val; + break; + } + } + sc->sc_writing = 1; + return (0); +} + +/* + * Finish updating the current block/sector by writing the compound + * set of changes to the flash. + */ +static int +cfi_block_finish(struct cfi_softc *sc) +{ + int error; + + error = cfi_write_block(sc); + free(sc->sc_wrbuf, M_TEMP); + sc->sc_wrbuf = NULL; + sc->sc_wrbufsz = 0; + sc->sc_wrofs = 0; + sc->sc_writing = 0; + return (error); +} + +static int +cfi_devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct cfi_softc *sc; + + sc = dev->si_drv1; + /* We allow only 1 open. */ + if (!atomic_cmpset_acq_ptr(&sc->sc_opened, NULL, td->td_proc)) + return (EBUSY); + return (0); +} + +static int +cfi_devclose(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct cfi_softc *sc; + int error; + + sc = dev->si_drv1; + /* Sanity. Not really necessary. */ + if (sc->sc_opened != td->td_proc) + return (ENXIO); + + error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; + sc->sc_opened = NULL; + return (error); +} + +static int +cfi_devread(struct cdev *dev, struct uio *uio, int ioflag) +{ + union { + uint8_t x8[4]; + uint16_t x16[2]; + uint32_t x32[1]; + } buf; + struct cfi_softc *sc; + u_int ofs; + uint32_t val; + int error; + + sc = dev->si_drv1; + + error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; + if (!error) + error = (uio->uio_offset > sc->sc_size) ? EIO : 0; + + while (error == 0 && uio->uio_resid > 0 && + uio->uio_offset < sc->sc_size) { + ofs = uio->uio_offset; + val = cfi_read(sc, ofs); + switch (sc->sc_width) { + case 1: + buf.x8[0] = val; + break; + case 2: + buf.x16[0] = val; + break; + case 4: + buf.x32[0] = val; + break; + } + ofs &= sc->sc_width - 1; + error = uiomove(buf.x8 + ofs, + MIN(uio->uio_resid, sc->sc_width - ofs), uio); + } + return (error); +} + +static int +cfi_devwrite(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct cfi_softc *sc; + u_int ofs, top; + int error; + + sc = dev->si_drv1; + + error = (uio->uio_offset > sc->sc_size) ? EIO : 0; + while (error == 0 && uio->uio_resid > 0 && + uio->uio_offset < sc->sc_size) { + ofs = uio->uio_offset; + + /* + * Finish the current block if we're about to write + * to a different block. + */ + if (sc->sc_writing) { + top = sc->sc_wrofs + sc->sc_wrbufsz; + if (ofs < sc->sc_wrofs || ofs >= top) + cfi_block_finish(sc); + } + + /* Start writing to a (new) block if applicable. */ + if (!sc->sc_writing) { + error = cfi_block_start(sc, uio->uio_offset); + if (error) + break; + } + + top = sc->sc_wrofs + sc->sc_wrbufsz; + error = uiomove(sc->sc_wrbuf + ofs - sc->sc_wrofs, + MIN(top - ofs, uio->uio_resid), uio); + } + return (error); +} + +static int +cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, + struct thread *td) +{ + struct cfi_softc *sc; + struct cfiocqry *rq; + int error; + u_char val; + + if (cmd != CFIOCQRY) + return (ENOIOCTL); + + sc = dev->si_drv1; + + error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; + if (error) + return (error); + + rq = (struct cfiocqry *)data; + + if (rq->offset >= sc->sc_size / sc->sc_width) + return (ESPIPE); + if (rq->offset + rq->count > sc->sc_size / sc->sc_width) + return (ENOSPC); + + while (!error && rq->count--) { + val = cfi_read_qry(sc, rq->offset++); + error = copyout(&val, rq->buffer++, 1); + } + + return (error); +} diff --git a/sys/dev/cfi/cfi_reg.h b/sys/dev/cfi/cfi_reg.h new file mode 100644 index 0000000..fe4d730 --- /dev/null +++ b/sys/dev/cfi/cfi_reg.h @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 2007, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_CFI_REG_H_ +#define _DEV_CFI_REG_H_ + +struct cfi_qry { + u_char reserved[16]; + u_char ident[3]; /* "QRY" */ + u_char pri_vend[2]; + u_char pri_vend_eqt[2]; + u_char alt_vend[2]; + u_char alt_vend_eqt[2]; + /* System Interface Information. */ + u_char min_vcc; + u_char max_vcc; + u_char min_vpp; + u_char max_vpp; + u_char tto_byte_write; /* 2**n milliseconds. */ + u_char tto_buf_write; /* 2**n milliseconds. */ + u_char tto_block_erase; /* 2**n milliseconds. */ + u_char tto_chip_erase; /* 2**n milliseconds. */ + u_char mto_byte_write; /* 2**n times typical t/o. */ + u_char mto_buf_write; /* 2**n times typical t/o. */ + u_char mto_block_erase; /* 2**n times typical t/o. */ + u_char mto_chip_erase; /* 2**n times typical t/o. */ + /* Device Geometry Definition. */ + u_char size; /* 2**n bytes. */ + u_char iface[2]; + u_char max_buf_write_size[2]; /* 2**n. */ + u_char nregions; /* Number of erase regions. */ + u_char region[4]; /* Single entry. */ + /* Additional entries follow. */ + /* Primary Vendor-specific Extended Query table follows. */ + /* Alternate Vendor-specific Extended Query table follows. */ +}; + +#define CFI_QRY_CMD_ADDR 0x55 +#define CFI_QRY_CMD_DATA 0x98 + +#define CFI_QRY_IDENT offsetof(struct cfi_qry, ident) +#define CFI_QRY_VEND offsetof(struct cfi_qry, pri_vend) + +#define CFI_QRY_TTO_WRITE offsetof(struct cfi_qry, tto_byte_write) +#define CFI_QRY_TTO_ERASE offsetof(struct cfi_qry, tto_block_erase) +#define CFI_QRY_MTO_WRITE offsetof(struct cfi_qry, mto_byte_write) +#define CFI_QRY_MTO_ERASE offsetof(struct cfi_qry, mto_block_erase) + +#define CFI_QRY_SIZE offsetof(struct cfi_qry, size) +#define CFI_QRY_IFACE offsetof(struct cfi_qry, iface) +#define CFI_QRY_NREGIONS offsetof(struct cfi_qry, nregions) +#define CFI_QRY_REGION0 offsetof(struct cfi_qry, region) +#define CFI_QRY_REGION(x) (CFI_QRY_REGION0 + (x) * 4) + +#define CFI_VEND_NONE 0x0000 +#define CFI_VEND_INTEL_ECS 0x0001 +#define CFI_VEND_AMD_SCS 0x0002 +#define CFI_VEND_INTEL_SCS 0x0003 +#define CFI_VEND_AMD_ECS 0x0004 +#define CFI_VEND_MITSUBISHI_SCS 0x0100 +#define CFI_VEND_MITSUBISHI_ECS 0x0101 + +#define CFI_IFACE_X8 0x0000 +#define CFI_IFACE_X16 0x0001 +#define CFI_IFACE_X8X16 0x0002 +#define CFI_IFACE_X32 0x0003 +#define CFI_IFACE_X16X32 0x0005 + +/* Standard Command Set (aka Basic Command Set) */ +#define CFI_BCS_BLOCK_ERASE 0x20 +#define CFI_BCS_PROGRAM 0x40 +#define CFI_BCS_CLEAR_STATUS 0x50 +#define CFI_BCS_READ_STATUS 0x70 +#define CFI_BCS_ERASE_SUSPEND 0xb0 +#define CFI_BCS_ERASE_RESUME 0xd0 /* Equals CONFIRM */ +#define CFI_BCS_CONFIRM 0xd0 +#define CFI_BCS_READ_ARRAY 0xff + +/* AMD commands. */ +#define CFI_AMD_BLOCK_ERASE 0x30 +#define CFI_AMD_UNLOCK_ACK 0x55 +#define CFI_AMD_ERASE_SECTOR 0x80 +#define CFI_AMD_PROGRAM 0xa0 +#define CFI_AMD_UNLOCK 0xaa + +#define AMD_ADDR_START 0xaaa +#define AMD_ADDR_ACK 0x555 + +#endif /* _DEV_CFI_REG_H_ */ diff --git a/sys/dev/cfi/cfi_var.h b/sys/dev/cfi/cfi_var.h new file mode 100644 index 0000000..5c99702 --- /dev/null +++ b/sys/dev/cfi/cfi_var.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2007, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_CFI_VAR_H_ +#define _DEV_CFI_VAR_H_ + +struct cfi_region { + u_int r_blocks; + u_int r_blksz; +}; + +struct cfi_softc { + device_t sc_dev; + + struct resource *sc_res; + bus_space_handle_t sc_handle; + bus_space_tag_t sc_tag; + int sc_rid; + + u_int sc_size; /* Flash size. */ + u_int sc_width; /* Interface width. */ + u_int sc_regions; /* Erase regions. */ + struct cfi_region *sc_region; /* Array of region info. */ + + u_int sc_cmdset; + u_int sc_erase_timeout; + u_int sc_write_timeout; + + struct cdev *sc_nod; + struct proc *sc_opened; /* Process that has us opened. */ + + u_char *sc_wrbuf; + u_int sc_wrbufsz; + u_int sc_wrofs; + u_int sc_writing; +}; + +extern char cfi_driver_name[]; +extern devclass_t cfi_devclass; + +int cfi_probe(device_t); +int cfi_attach(device_t); +int cfi_detach(device_t); + +uint32_t cfi_read(struct cfi_softc *, u_int); +uint8_t cfi_read_qry(struct cfi_softc *, u_int); +int cfi_write_block(struct cfi_softc *); + +#endif /* _DEV_CFI_VAR_H_ */ diff --git a/sys/sys/cfictl.h b/sys/sys/cfictl.h new file mode 100644 index 0000000..edef2a8 --- /dev/null +++ b/sys/sys/cfictl.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2007, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_CFICTL_H_ +#define _SYS_CFICTL_H_ + +/* + * Allow reading of the CFI query structure. + */ + +struct cfiocqry { + unsigned long offset; + unsigned long count; + u_char *buffer; +}; + +#define CFIOCQRY _IOWR('q', 0, struct cfiocqry) + +#endif /* _SYS_CFICTL_H_ */ |