1e00251b7SMarcel Moolenaar /*- 2e00251b7SMarcel Moolenaar * Copyright (c) 2007, Juniper Networks, Inc. 3e00251b7SMarcel Moolenaar * All rights reserved. 4e00251b7SMarcel Moolenaar * 5e00251b7SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 6e00251b7SMarcel Moolenaar * modification, are permitted provided that the following conditions 7e00251b7SMarcel Moolenaar * are met: 8e00251b7SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 9e00251b7SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 10e00251b7SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 11e00251b7SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 12e00251b7SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 13e00251b7SMarcel Moolenaar * 3. Neither the name of the author nor the names of any co-contributors 14e00251b7SMarcel Moolenaar * may be used to endorse or promote products derived from this software 15e00251b7SMarcel Moolenaar * without specific prior written permission. 16e00251b7SMarcel Moolenaar * 17e00251b7SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18e00251b7SMarcel Moolenaar * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19e00251b7SMarcel Moolenaar * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20e00251b7SMarcel Moolenaar * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21e00251b7SMarcel Moolenaar * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22e00251b7SMarcel Moolenaar * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23e00251b7SMarcel Moolenaar * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24e00251b7SMarcel Moolenaar * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25e00251b7SMarcel Moolenaar * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26e00251b7SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27e00251b7SMarcel Moolenaar * SUCH DAMAGE. 28e00251b7SMarcel Moolenaar */ 29e00251b7SMarcel Moolenaar 30e00251b7SMarcel Moolenaar #include <sys/cdefs.h> 31e00251b7SMarcel Moolenaar __FBSDID("$FreeBSD$"); 32e00251b7SMarcel Moolenaar 33e00251b7SMarcel Moolenaar #include <sys/param.h> 34e00251b7SMarcel Moolenaar #include <sys/systm.h> 35e00251b7SMarcel Moolenaar #include <sys/bus.h> 36e00251b7SMarcel Moolenaar #include <sys/conf.h> 37e00251b7SMarcel Moolenaar #include <sys/ioccom.h> 38e00251b7SMarcel Moolenaar #include <sys/kernel.h> 39e00251b7SMarcel Moolenaar #include <sys/malloc.h> 40e00251b7SMarcel Moolenaar #include <sys/proc.h> 41e00251b7SMarcel Moolenaar #include <sys/sysctl.h> 42e00251b7SMarcel Moolenaar #include <sys/types.h> 43e00251b7SMarcel Moolenaar #include <sys/uio.h> 44e00251b7SMarcel Moolenaar 45e00251b7SMarcel Moolenaar #include <sys/cfictl.h> 46e00251b7SMarcel Moolenaar 47e00251b7SMarcel Moolenaar #include <machine/atomic.h> 48e00251b7SMarcel Moolenaar #include <machine/bus.h> 49e00251b7SMarcel Moolenaar 50e00251b7SMarcel Moolenaar #include <dev/cfi/cfi_var.h> 51e00251b7SMarcel Moolenaar 52e00251b7SMarcel Moolenaar static d_open_t cfi_devopen; 53e00251b7SMarcel Moolenaar static d_close_t cfi_devclose; 54e00251b7SMarcel Moolenaar static d_read_t cfi_devread; 55e00251b7SMarcel Moolenaar static d_write_t cfi_devwrite; 56e00251b7SMarcel Moolenaar static d_ioctl_t cfi_devioctl; 57e00251b7SMarcel Moolenaar 58e00251b7SMarcel Moolenaar struct cdevsw cfi_cdevsw = { 59e00251b7SMarcel Moolenaar .d_version = D_VERSION, 60e00251b7SMarcel Moolenaar .d_flags = 0, 61e00251b7SMarcel Moolenaar .d_name = cfi_driver_name, 62e00251b7SMarcel Moolenaar .d_open = cfi_devopen, 63e00251b7SMarcel Moolenaar .d_close = cfi_devclose, 64e00251b7SMarcel Moolenaar .d_read = cfi_devread, 65e00251b7SMarcel Moolenaar .d_write = cfi_devwrite, 66e00251b7SMarcel Moolenaar .d_ioctl = cfi_devioctl, 67e00251b7SMarcel Moolenaar }; 68e00251b7SMarcel Moolenaar 69e00251b7SMarcel Moolenaar /* 70e00251b7SMarcel Moolenaar * Begin writing into a new block/sector. We read the sector into 71e00251b7SMarcel Moolenaar * memory and keep updating that, until we move into another sector 72e00251b7SMarcel Moolenaar * or the process stops writing. At that time we write the whole 73e00251b7SMarcel Moolenaar * sector to flash (see cfi_block_finish). 74e00251b7SMarcel Moolenaar */ 75e00251b7SMarcel Moolenaar static int 76e00251b7SMarcel Moolenaar cfi_block_start(struct cfi_softc *sc, u_int ofs) 77e00251b7SMarcel Moolenaar { 78e00251b7SMarcel Moolenaar union { 79e00251b7SMarcel Moolenaar uint8_t *x8; 80e00251b7SMarcel Moolenaar uint16_t *x16; 81e00251b7SMarcel Moolenaar uint32_t *x32; 82e00251b7SMarcel Moolenaar } ptr; 83e00251b7SMarcel Moolenaar u_int rofs, rsz; 84e00251b7SMarcel Moolenaar uint32_t val; 85e00251b7SMarcel Moolenaar int r; 86e00251b7SMarcel Moolenaar 87e00251b7SMarcel Moolenaar rofs = 0; 88e00251b7SMarcel Moolenaar for (r = 0; r < sc->sc_regions; r++) { 89e00251b7SMarcel Moolenaar rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz; 90e00251b7SMarcel Moolenaar if (ofs < rofs + rsz) 91e00251b7SMarcel Moolenaar break; 92e00251b7SMarcel Moolenaar rofs += rsz; 93e00251b7SMarcel Moolenaar } 94e00251b7SMarcel Moolenaar if (r == sc->sc_regions) 95e00251b7SMarcel Moolenaar return (EFAULT); 96e00251b7SMarcel Moolenaar 97e00251b7SMarcel Moolenaar sc->sc_wrbufsz = sc->sc_region[r].r_blksz; 98e00251b7SMarcel Moolenaar sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK); 99e00251b7SMarcel Moolenaar sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz; 100e00251b7SMarcel Moolenaar 101e00251b7SMarcel Moolenaar /* Read the block from flash for byte-serving. */ 102e00251b7SMarcel Moolenaar ptr.x8 = sc->sc_wrbuf; 103e00251b7SMarcel Moolenaar for (r = 0; r < sc->sc_wrbufsz; r += sc->sc_width) { 104e00251b7SMarcel Moolenaar val = cfi_read(sc, sc->sc_wrofs + r); 105e00251b7SMarcel Moolenaar switch (sc->sc_width) { 106e00251b7SMarcel Moolenaar case 1: 107e00251b7SMarcel Moolenaar *(ptr.x8)++ = val; 108e00251b7SMarcel Moolenaar break; 109e00251b7SMarcel Moolenaar case 2: 110e00251b7SMarcel Moolenaar *(ptr.x16)++ = val; 111e00251b7SMarcel Moolenaar break; 112e00251b7SMarcel Moolenaar case 4: 113e00251b7SMarcel Moolenaar *(ptr.x32)++ = val; 114e00251b7SMarcel Moolenaar break; 115e00251b7SMarcel Moolenaar } 116e00251b7SMarcel Moolenaar } 117e00251b7SMarcel Moolenaar sc->sc_writing = 1; 118e00251b7SMarcel Moolenaar return (0); 119e00251b7SMarcel Moolenaar } 120e00251b7SMarcel Moolenaar 121e00251b7SMarcel Moolenaar /* 122e00251b7SMarcel Moolenaar * Finish updating the current block/sector by writing the compound 123e00251b7SMarcel Moolenaar * set of changes to the flash. 124e00251b7SMarcel Moolenaar */ 125e00251b7SMarcel Moolenaar static int 126e00251b7SMarcel Moolenaar cfi_block_finish(struct cfi_softc *sc) 127e00251b7SMarcel Moolenaar { 128e00251b7SMarcel Moolenaar int error; 129e00251b7SMarcel Moolenaar 130e00251b7SMarcel Moolenaar error = cfi_write_block(sc); 131e00251b7SMarcel Moolenaar free(sc->sc_wrbuf, M_TEMP); 132e00251b7SMarcel Moolenaar sc->sc_wrbuf = NULL; 133e00251b7SMarcel Moolenaar sc->sc_wrbufsz = 0; 134e00251b7SMarcel Moolenaar sc->sc_wrofs = 0; 135e00251b7SMarcel Moolenaar sc->sc_writing = 0; 136e00251b7SMarcel Moolenaar return (error); 137e00251b7SMarcel Moolenaar } 138e00251b7SMarcel Moolenaar 139e00251b7SMarcel Moolenaar static int 140e00251b7SMarcel Moolenaar cfi_devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 141e00251b7SMarcel Moolenaar { 142e00251b7SMarcel Moolenaar struct cfi_softc *sc; 143e00251b7SMarcel Moolenaar 144e00251b7SMarcel Moolenaar sc = dev->si_drv1; 145e00251b7SMarcel Moolenaar /* We allow only 1 open. */ 146e00251b7SMarcel Moolenaar if (!atomic_cmpset_acq_ptr(&sc->sc_opened, NULL, td->td_proc)) 147e00251b7SMarcel Moolenaar return (EBUSY); 148e00251b7SMarcel Moolenaar return (0); 149e00251b7SMarcel Moolenaar } 150e00251b7SMarcel Moolenaar 151e00251b7SMarcel Moolenaar static int 152e00251b7SMarcel Moolenaar cfi_devclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 153e00251b7SMarcel Moolenaar { 154e00251b7SMarcel Moolenaar struct cfi_softc *sc; 155e00251b7SMarcel Moolenaar int error; 156e00251b7SMarcel Moolenaar 157e00251b7SMarcel Moolenaar sc = dev->si_drv1; 158e00251b7SMarcel Moolenaar /* Sanity. Not really necessary. */ 159e00251b7SMarcel Moolenaar if (sc->sc_opened != td->td_proc) 160e00251b7SMarcel Moolenaar return (ENXIO); 161e00251b7SMarcel Moolenaar 162e00251b7SMarcel Moolenaar error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; 163e00251b7SMarcel Moolenaar sc->sc_opened = NULL; 164e00251b7SMarcel Moolenaar return (error); 165e00251b7SMarcel Moolenaar } 166e00251b7SMarcel Moolenaar 167e00251b7SMarcel Moolenaar static int 168e00251b7SMarcel Moolenaar cfi_devread(struct cdev *dev, struct uio *uio, int ioflag) 169e00251b7SMarcel Moolenaar { 170e00251b7SMarcel Moolenaar union { 171e00251b7SMarcel Moolenaar uint8_t x8[4]; 172e00251b7SMarcel Moolenaar uint16_t x16[2]; 173e00251b7SMarcel Moolenaar uint32_t x32[1]; 174e00251b7SMarcel Moolenaar } buf; 175e00251b7SMarcel Moolenaar struct cfi_softc *sc; 176e00251b7SMarcel Moolenaar u_int ofs; 177e00251b7SMarcel Moolenaar uint32_t val; 178e00251b7SMarcel Moolenaar int error; 179e00251b7SMarcel Moolenaar 180e00251b7SMarcel Moolenaar sc = dev->si_drv1; 181e00251b7SMarcel Moolenaar 182e00251b7SMarcel Moolenaar error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; 183e00251b7SMarcel Moolenaar if (!error) 184e00251b7SMarcel Moolenaar error = (uio->uio_offset > sc->sc_size) ? EIO : 0; 185e00251b7SMarcel Moolenaar 186e00251b7SMarcel Moolenaar while (error == 0 && uio->uio_resid > 0 && 187e00251b7SMarcel Moolenaar uio->uio_offset < sc->sc_size) { 188e00251b7SMarcel Moolenaar ofs = uio->uio_offset; 189e00251b7SMarcel Moolenaar val = cfi_read(sc, ofs); 190e00251b7SMarcel Moolenaar switch (sc->sc_width) { 191e00251b7SMarcel Moolenaar case 1: 192e00251b7SMarcel Moolenaar buf.x8[0] = val; 193e00251b7SMarcel Moolenaar break; 194e00251b7SMarcel Moolenaar case 2: 195e00251b7SMarcel Moolenaar buf.x16[0] = val; 196e00251b7SMarcel Moolenaar break; 197e00251b7SMarcel Moolenaar case 4: 198e00251b7SMarcel Moolenaar buf.x32[0] = val; 199e00251b7SMarcel Moolenaar break; 200e00251b7SMarcel Moolenaar } 201e00251b7SMarcel Moolenaar ofs &= sc->sc_width - 1; 202e00251b7SMarcel Moolenaar error = uiomove(buf.x8 + ofs, 203e00251b7SMarcel Moolenaar MIN(uio->uio_resid, sc->sc_width - ofs), uio); 204e00251b7SMarcel Moolenaar } 205e00251b7SMarcel Moolenaar return (error); 206e00251b7SMarcel Moolenaar } 207e00251b7SMarcel Moolenaar 208e00251b7SMarcel Moolenaar static int 209e00251b7SMarcel Moolenaar cfi_devwrite(struct cdev *dev, struct uio *uio, int ioflag) 210e00251b7SMarcel Moolenaar { 211e00251b7SMarcel Moolenaar struct cfi_softc *sc; 212e00251b7SMarcel Moolenaar u_int ofs, top; 213e00251b7SMarcel Moolenaar int error; 214e00251b7SMarcel Moolenaar 215e00251b7SMarcel Moolenaar sc = dev->si_drv1; 216e00251b7SMarcel Moolenaar 217e00251b7SMarcel Moolenaar error = (uio->uio_offset > sc->sc_size) ? EIO : 0; 218e00251b7SMarcel Moolenaar while (error == 0 && uio->uio_resid > 0 && 219e00251b7SMarcel Moolenaar uio->uio_offset < sc->sc_size) { 220e00251b7SMarcel Moolenaar ofs = uio->uio_offset; 221e00251b7SMarcel Moolenaar 222e00251b7SMarcel Moolenaar /* 223e00251b7SMarcel Moolenaar * Finish the current block if we're about to write 224e00251b7SMarcel Moolenaar * to a different block. 225e00251b7SMarcel Moolenaar */ 226e00251b7SMarcel Moolenaar if (sc->sc_writing) { 227e00251b7SMarcel Moolenaar top = sc->sc_wrofs + sc->sc_wrbufsz; 228e00251b7SMarcel Moolenaar if (ofs < sc->sc_wrofs || ofs >= top) 229e00251b7SMarcel Moolenaar cfi_block_finish(sc); 230e00251b7SMarcel Moolenaar } 231e00251b7SMarcel Moolenaar 232e00251b7SMarcel Moolenaar /* Start writing to a (new) block if applicable. */ 233e00251b7SMarcel Moolenaar if (!sc->sc_writing) { 234e00251b7SMarcel Moolenaar error = cfi_block_start(sc, uio->uio_offset); 235e00251b7SMarcel Moolenaar if (error) 236e00251b7SMarcel Moolenaar break; 237e00251b7SMarcel Moolenaar } 238e00251b7SMarcel Moolenaar 239e00251b7SMarcel Moolenaar top = sc->sc_wrofs + sc->sc_wrbufsz; 240e00251b7SMarcel Moolenaar error = uiomove(sc->sc_wrbuf + ofs - sc->sc_wrofs, 241e00251b7SMarcel Moolenaar MIN(top - ofs, uio->uio_resid), uio); 242e00251b7SMarcel Moolenaar } 243e00251b7SMarcel Moolenaar return (error); 244e00251b7SMarcel Moolenaar } 245e00251b7SMarcel Moolenaar 246e00251b7SMarcel Moolenaar static int 247e00251b7SMarcel Moolenaar cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 248e00251b7SMarcel Moolenaar struct thread *td) 249e00251b7SMarcel Moolenaar { 250e00251b7SMarcel Moolenaar struct cfi_softc *sc; 251e00251b7SMarcel Moolenaar struct cfiocqry *rq; 252e00251b7SMarcel Moolenaar int error; 253e00251b7SMarcel Moolenaar u_char val; 254e00251b7SMarcel Moolenaar 255e00251b7SMarcel Moolenaar if (cmd != CFIOCQRY) 256e00251b7SMarcel Moolenaar return (ENOIOCTL); 257e00251b7SMarcel Moolenaar 258e00251b7SMarcel Moolenaar sc = dev->si_drv1; 259e00251b7SMarcel Moolenaar 260e00251b7SMarcel Moolenaar error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; 261e00251b7SMarcel Moolenaar if (error) 262e00251b7SMarcel Moolenaar return (error); 263e00251b7SMarcel Moolenaar 264e00251b7SMarcel Moolenaar rq = (struct cfiocqry *)data; 265e00251b7SMarcel Moolenaar 266e00251b7SMarcel Moolenaar if (rq->offset >= sc->sc_size / sc->sc_width) 267e00251b7SMarcel Moolenaar return (ESPIPE); 268e00251b7SMarcel Moolenaar if (rq->offset + rq->count > sc->sc_size / sc->sc_width) 269e00251b7SMarcel Moolenaar return (ENOSPC); 270e00251b7SMarcel Moolenaar 271e00251b7SMarcel Moolenaar while (!error && rq->count--) { 272e00251b7SMarcel Moolenaar val = cfi_read_qry(sc, rq->offset++); 273e00251b7SMarcel Moolenaar error = copyout(&val, rq->buffer++, 1); 274e00251b7SMarcel Moolenaar } 275e00251b7SMarcel Moolenaar 276e00251b7SMarcel Moolenaar return (error); 277e00251b7SMarcel Moolenaar } 278