The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/flash/n25q.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2009 Oleksandr Tymoshenko.  All rights reserved.
    3  * Copyright (c) 2017 Ruslan Bukin <br@bsdpad.com>
    4  * Copyright (c) 2018 Ian Lepore.  All rights reserved.
    5  * Copyright (c) 2006 M. Warner Losh <imp@FreeBSD.org>
    6  *
    7  * This software was developed by SRI International and the University of
    8  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
    9  * ("CTSRD"), as part of the DARPA CRASH research programme.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 
   33 /* n25q Quad SPI Flash driver. */
   34 
   35 #include <sys/cdefs.h>
   36 __FBSDID("$FreeBSD$");
   37 
   38 #include "opt_platform.h"
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/bio.h>
   43 #include <sys/bus.h>
   44 #include <sys/conf.h>
   45 #include <sys/kernel.h>
   46 #include <sys/kthread.h>
   47 #include <sys/lock.h>
   48 #include <sys/mbuf.h>
   49 #include <sys/malloc.h>
   50 #include <sys/module.h>
   51 #include <sys/mutex.h>
   52 #include <geom/geom_disk.h>
   53 
   54 #include <machine/bus.h>
   55 
   56 #include <dev/fdt/fdt_common.h>
   57 #include <dev/ofw/ofw_bus_subr.h>
   58 #include <dev/ofw/openfirm.h>
   59 
   60 #include <dev/flash/mx25lreg.h>
   61 
   62 #include "qspi_if.h"
   63 
   64 #define N25Q_DEBUG
   65 #undef N25Q_DEBUG
   66 
   67 #ifdef N25Q_DEBUG
   68 #define dprintf(fmt, ...)  printf(fmt, ##__VA_ARGS__)
   69 #else
   70 #define dprintf(fmt, ...)
   71 #endif
   72 
   73 #define FL_NONE                 0x00
   74 #define FL_ERASE_4K             0x01
   75 #define FL_ERASE_32K            0x02
   76 #define FL_ENABLE_4B_ADDR       0x04
   77 #define FL_DISABLE_4B_ADDR      0x08
   78 
   79 /*
   80  * Define the sectorsize to be a smaller size rather than the flash
   81  * sector size. Trying to run FFS off of a 64k flash sector size
   82  * results in a completely un-usable system.
   83  */
   84 #define FLASH_SECTORSIZE        512
   85 
   86 struct n25q_flash_ident {
   87         const char      *name;
   88         uint8_t         manufacturer_id;
   89         uint16_t        device_id;
   90         unsigned int    sectorsize;
   91         unsigned int    sectorcount;
   92         unsigned int    flags;
   93 };
   94 
   95 struct n25q_softc {
   96         device_t                dev;
   97         bus_space_tag_t         bst;
   98         bus_space_handle_t      bsh;
   99         void                    *ih;
  100         struct resource         *res[3];
  101 
  102         uint8_t                 sc_manufacturer_id;
  103         uint16_t                device_id;
  104         unsigned int            sc_sectorsize;
  105         struct mtx              sc_mtx;
  106         struct disk             *sc_disk;
  107         struct proc             *sc_p;
  108         struct bio_queue_head   sc_bio_queue;
  109         unsigned int            sc_flags;
  110         unsigned int            sc_taskstate;
  111 };
  112 
  113 #define TSTATE_STOPPED  0
  114 #define TSTATE_STOPPING 1
  115 #define TSTATE_RUNNING  2
  116 
  117 #define N25Q_LOCK(_sc)          mtx_lock(&(_sc)->sc_mtx)
  118 #define N25Q_UNLOCK(_sc)        mtx_unlock(&(_sc)->sc_mtx)
  119 #define N25Q_LOCK_INIT(_sc)                                     \
  120         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev),   \
  121             "n25q", MTX_DEF)
  122 #define N25Q_LOCK_DESTROY(_sc)  mtx_destroy(&_sc->sc_mtx);
  123 #define N25Q_ASSERT_LOCKED(_sc)                         \
  124         mtx_assert(&_sc->sc_mtx, MA_OWNED);
  125 #define N25Q_ASSERT_UNLOCKED(_sc)                               \
  126         mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
  127 
  128 static struct ofw_compat_data compat_data[] = {
  129         { "n25q00aa",           1 },
  130         { NULL,                 0 },
  131 };
  132 
  133 /* disk routines */
  134 static int n25q_open(struct disk *dp);
  135 static int n25q_close(struct disk *dp);
  136 static int n25q_ioctl(struct disk *, u_long, void *, int, struct thread *);
  137 static void n25q_strategy(struct bio *bp);
  138 static int n25q_getattr(struct bio *bp);
  139 static void n25q_task(void *arg);
  140 
  141 static struct n25q_flash_ident flash_devices[] = {
  142         { "n25q00", 0x20, 0xbb21, (64 * 1024), 2048, FL_ENABLE_4B_ADDR},
  143 };
  144 
  145 static int
  146 n25q_wait_for_device_ready(device_t dev)
  147 {
  148         device_t pdev;
  149         uint8_t status;
  150         int err;
  151 
  152         pdev = device_get_parent(dev);
  153 
  154         do {
  155                 err = QSPI_READ_REG(pdev, dev, CMD_READ_STATUS, &status, 1);
  156         } while (err == 0 && (status & STATUS_WIP));
  157 
  158         return (err);
  159 }
  160 
  161 static struct n25q_flash_ident*
  162 n25q_get_device_ident(struct n25q_softc *sc)
  163 {
  164         uint8_t manufacturer_id;
  165         uint16_t dev_id;
  166         device_t pdev;
  167         uint8_t data[4];
  168         int i;
  169 
  170         pdev = device_get_parent(sc->dev);
  171 
  172         QSPI_READ_REG(pdev, sc->dev, CMD_READ_IDENT, (uint8_t *)&data[0], 4);
  173 
  174         manufacturer_id = data[0];
  175         dev_id = (data[1] << 8) | (data[2]);
  176 
  177         for (i = 0; i < nitems(flash_devices); i++) {
  178                 if ((flash_devices[i].manufacturer_id == manufacturer_id) &&
  179                     (flash_devices[i].device_id == dev_id))
  180                         return &flash_devices[i];
  181         }
  182 
  183         printf("Unknown SPI flash device. Vendor: %02x, device id: %04x\n",
  184             manufacturer_id, dev_id);
  185 
  186         return (NULL);
  187 }
  188 
  189 static int
  190 n25q_write(device_t dev, struct bio *bp, off_t offset, caddr_t data, off_t count)
  191 {
  192         device_t pdev;
  193         int err;
  194 
  195         pdev = device_get_parent(dev);
  196 
  197         dprintf("%s: offset 0x%llx count %lld bytes\n", __func__, offset, count);
  198 
  199         err = QSPI_ERASE(pdev, dev, offset);
  200         if (err != 0) {
  201                 return (err);
  202         }
  203 
  204         err = QSPI_WRITE(pdev, dev, bp, offset, data, count);
  205 
  206         return (err);
  207 }
  208 
  209 static int
  210 n25q_read(device_t dev, struct bio *bp, off_t offset, caddr_t data, off_t count)
  211 {
  212         struct n25q_softc *sc;
  213         device_t pdev;
  214         int err;
  215 
  216         pdev = device_get_parent(dev);
  217         sc = device_get_softc(dev);
  218 
  219         dprintf("%s: offset 0x%llx count %lld bytes\n", __func__, offset, count);
  220 
  221         /*
  222          * Enforce the disk read sectorsize not the erase sectorsize.
  223          * In this way, smaller read IO is possible,dramatically
  224          * speeding up filesystem/geom_compress access.
  225          */
  226         if (count % sc->sc_disk->d_sectorsize != 0
  227             || offset % sc->sc_disk->d_sectorsize != 0) {
  228                 printf("EIO\n");
  229                 return (EIO);
  230         }
  231 
  232         err = QSPI_READ(pdev, dev, bp, offset, data, count);
  233 
  234         return (err);
  235 }
  236 
  237 static int
  238 n25q_set_4b_mode(device_t dev, uint8_t command)
  239 {
  240         device_t pdev;
  241         int err;
  242 
  243         pdev = device_get_parent(dev);
  244 
  245         err = QSPI_WRITE_REG(pdev, dev, command, 0, 0);
  246 
  247         return (err);
  248 }
  249 
  250 static int
  251 n25q_probe(device_t dev)
  252 {
  253         int i;
  254 
  255         if (!ofw_bus_status_okay(dev))
  256                 return (ENXIO);
  257 
  258         /* First try to match the compatible property to the compat_data */
  259         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 1)
  260                 goto found;
  261 
  262         /*
  263          * Next, try to find a compatible device using the names in the
  264          * flash_devices structure
  265          */
  266         for (i = 0; i < nitems(flash_devices); i++)
  267                 if (ofw_bus_is_compatible(dev, flash_devices[i].name))
  268                         goto found;
  269 
  270         return (ENXIO);
  271 found:
  272         device_set_desc(dev, "Micron n25q");
  273 
  274         return (0);
  275 }
  276 
  277 static int
  278 n25q_attach(device_t dev)
  279 {
  280         struct n25q_flash_ident *ident;
  281         struct n25q_softc *sc;
  282 
  283         sc = device_get_softc(dev);
  284         sc->dev = dev;
  285 
  286         N25Q_LOCK_INIT(sc);
  287 
  288         ident = n25q_get_device_ident(sc);
  289         if (ident == NULL) {
  290                 return (ENXIO);
  291         }
  292 
  293         n25q_wait_for_device_ready(sc->dev);
  294 
  295         sc->sc_disk = disk_alloc();
  296         sc->sc_disk->d_open = n25q_open;
  297         sc->sc_disk->d_close = n25q_close;
  298         sc->sc_disk->d_strategy = n25q_strategy;
  299         sc->sc_disk->d_getattr = n25q_getattr;
  300         sc->sc_disk->d_ioctl = n25q_ioctl;
  301         sc->sc_disk->d_name = "flash/qspi";
  302         sc->sc_disk->d_drv1 = sc;
  303         sc->sc_disk->d_maxsize = DFLTPHYS;
  304         sc->sc_disk->d_sectorsize = FLASH_SECTORSIZE;
  305         sc->sc_disk->d_mediasize = (ident->sectorsize * ident->sectorcount);
  306         sc->sc_disk->d_unit = device_get_unit(sc->dev);
  307         sc->sc_disk->d_dump = NULL;
  308         /* Sectorsize for erase operations */
  309         sc->sc_sectorsize =  ident->sectorsize;
  310         sc->sc_flags = ident->flags;
  311 
  312         if (sc->sc_flags & FL_ENABLE_4B_ADDR)
  313                 n25q_set_4b_mode(dev, CMD_ENTER_4B_MODE);
  314 
  315         if (sc->sc_flags & FL_DISABLE_4B_ADDR)
  316                 n25q_set_4b_mode(dev, CMD_EXIT_4B_MODE);
  317 
  318         /* NB: use stripesize to hold the erase/region size for RedBoot */
  319         sc->sc_disk->d_stripesize = ident->sectorsize;
  320 
  321         disk_create(sc->sc_disk, DISK_VERSION);
  322         bioq_init(&sc->sc_bio_queue);
  323 
  324         kproc_create(&n25q_task, sc, &sc->sc_p, 0, 0, "task: n25q flash");
  325         sc->sc_taskstate = TSTATE_RUNNING;
  326 
  327         device_printf(sc->dev, "%s, sector %d bytes, %d sectors\n", 
  328             ident->name, ident->sectorsize, ident->sectorcount);
  329 
  330         return (0);
  331 }
  332 
  333 static int
  334 n25q_detach(device_t dev)
  335 {
  336         struct n25q_softc *sc;
  337         int err;
  338 
  339         sc = device_get_softc(dev);
  340         err = 0;
  341 
  342         N25Q_LOCK(sc);
  343         if (sc->sc_taskstate == TSTATE_RUNNING) {
  344                 sc->sc_taskstate = TSTATE_STOPPING;
  345                 wakeup(sc);
  346                 while (err == 0 && sc->sc_taskstate != TSTATE_STOPPED) {
  347                         err = msleep(sc, &sc->sc_mtx, 0, "n25q", hz * 3);
  348                         if (err != 0) {
  349                                 sc->sc_taskstate = TSTATE_RUNNING;
  350                                 device_printf(sc->dev,
  351                                     "Failed to stop queue task\n");
  352                         }
  353                 }
  354         }
  355         N25Q_UNLOCK(sc);
  356 
  357         if (err == 0 && sc->sc_taskstate == TSTATE_STOPPED) {
  358                 disk_destroy(sc->sc_disk);
  359                 bioq_flush(&sc->sc_bio_queue, NULL, ENXIO);
  360                 N25Q_LOCK_DESTROY(sc);
  361         }
  362         return (err);
  363 }
  364 
  365 static int
  366 n25q_open(struct disk *dp)
  367 {
  368 
  369         return (0);
  370 }
  371 
  372 static int
  373 n25q_close(struct disk *dp)
  374 {
  375 
  376         return (0);
  377 }
  378 
  379 static int
  380 n25q_ioctl(struct disk *dp, u_long cmd, void *data,
  381     int fflag, struct thread *td)
  382 {
  383 
  384         return (EINVAL);
  385 }
  386 
  387 static void
  388 n25q_strategy(struct bio *bp)
  389 {
  390         struct n25q_softc *sc;
  391 
  392         sc = (struct n25q_softc *)bp->bio_disk->d_drv1;
  393 
  394         N25Q_LOCK(sc);
  395         bioq_disksort(&sc->sc_bio_queue, bp);
  396         wakeup(sc);
  397         N25Q_UNLOCK(sc);
  398 }
  399 
  400 static int
  401 n25q_getattr(struct bio *bp)
  402 {
  403         struct n25q_softc *sc;
  404         device_t dev;
  405 
  406         if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) {
  407                 return (ENXIO);
  408         }
  409 
  410         sc = bp->bio_disk->d_drv1;
  411         dev = sc->dev;
  412 
  413         if (strcmp(bp->bio_attribute, "SPI::device") == 0) {
  414                 if (bp->bio_length != sizeof(dev)) {
  415                         return (EFAULT);
  416                 }
  417                 bcopy(&dev, bp->bio_data, sizeof(dev));
  418                 return (0);
  419         }
  420 
  421         return (-1);
  422 }
  423 
  424 static void
  425 n25q_task(void *arg)
  426 {
  427         struct n25q_softc *sc;
  428         struct bio *bp;
  429         device_t dev;
  430 
  431         sc = (struct n25q_softc *)arg;
  432 
  433         dev = sc->dev;
  434 
  435         for (;;) {
  436                 N25Q_LOCK(sc);
  437                 do {
  438                         if (sc->sc_taskstate == TSTATE_STOPPING) {
  439                                 sc->sc_taskstate = TSTATE_STOPPED;
  440                                 N25Q_UNLOCK(sc);
  441                                 wakeup(sc);
  442                                 kproc_exit(0);
  443                         }
  444                         bp = bioq_first(&sc->sc_bio_queue);
  445                         if (bp == NULL)
  446                                 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", hz);
  447                 } while (bp == NULL);
  448                 bioq_remove(&sc->sc_bio_queue, bp);
  449                 N25Q_UNLOCK(sc);
  450 
  451                 switch (bp->bio_cmd) {
  452                 case BIO_READ:
  453                         bp->bio_error = n25q_read(dev, bp, bp->bio_offset, 
  454                             bp->bio_data, bp->bio_bcount);
  455                         break;
  456                 case BIO_WRITE:
  457                         bp->bio_error = n25q_write(dev, bp, bp->bio_offset, 
  458                             bp->bio_data, bp->bio_bcount);
  459                         break;
  460                 default:
  461                         bp->bio_error = EOPNOTSUPP;
  462                 }
  463 
  464                 biodone(bp);
  465         }
  466 }
  467 
  468 static device_method_t n25q_methods[] = {
  469         /* Device interface */
  470         DEVMETHOD(device_probe,         n25q_probe),
  471         DEVMETHOD(device_attach,        n25q_attach),
  472         DEVMETHOD(device_detach,        n25q_detach),
  473 
  474         { 0, 0 }
  475 };
  476 
  477 static driver_t n25q_driver = {
  478         "n25q",
  479         n25q_methods,
  480         sizeof(struct n25q_softc),
  481 };
  482 
  483 DRIVER_MODULE(n25q, simplebus, n25q_driver, 0, 0);

Cache object: 7fefac24ce9d4315ba220edc3a1f071f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.