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/mx25l.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
    5  * Copyright (c) 2009 Oleksandr Tymoshenko.  All rights reserved.
    6  * Copyright (c) 2018 Ian Lepore.  All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD: releng/12.0/sys/dev/flash/mx25l.c 336869 2018-07-29 16:55:28Z ian $");
   31 
   32 #include "opt_platform.h"
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/bio.h>
   37 #include <sys/bus.h>
   38 #include <sys/conf.h>
   39 #include <sys/kernel.h>
   40 #include <sys/kthread.h>
   41 #include <sys/lock.h>
   42 #include <sys/mbuf.h>
   43 #include <sys/malloc.h>
   44 #include <sys/module.h>
   45 #include <sys/mutex.h>
   46 #include <geom/geom_disk.h>
   47 
   48 #ifdef FDT
   49 #include <dev/fdt/fdt_common.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 #include <dev/ofw/openfirm.h>
   52 #endif
   53 
   54 #include <dev/spibus/spi.h>
   55 #include "spibus_if.h"
   56 
   57 #include <dev/flash/mx25lreg.h>
   58 
   59 #define FL_NONE                 0x00
   60 #define FL_ERASE_4K             0x01
   61 #define FL_ERASE_32K            0x02
   62 #define FL_ENABLE_4B_ADDR       0x04
   63 #define FL_DISABLE_4B_ADDR      0x08
   64 
   65 /*
   66  * Define the sectorsize to be a smaller size rather than the flash
   67  * sector size. Trying to run FFS off of a 64k flash sector size
   68  * results in a completely un-usable system.
   69  */
   70 #define MX25L_SECTORSIZE        512
   71 
   72 struct mx25l_flash_ident
   73 {
   74         const char      *name;
   75         uint8_t         manufacturer_id;
   76         uint16_t        device_id;
   77         unsigned int    sectorsize;
   78         unsigned int    sectorcount;
   79         unsigned int    flags;
   80 };
   81 
   82 struct mx25l_softc 
   83 {
   84         device_t        sc_dev;
   85         device_t        sc_parent;
   86         uint8_t         sc_manufacturer_id;
   87         uint16_t        sc_device_id;
   88         unsigned int    sc_erasesize;
   89         struct mtx      sc_mtx;
   90         struct disk     *sc_disk;
   91         struct proc     *sc_p;
   92         struct bio_queue_head sc_bio_queue;
   93         unsigned int    sc_flags;
   94         unsigned int    sc_taskstate;
   95         uint8_t         sc_dummybuf[FLASH_PAGE_SIZE];
   96 };
   97 
   98 #define TSTATE_STOPPED  0
   99 #define TSTATE_STOPPING 1
  100 #define TSTATE_RUNNING  2
  101 
  102 #define M25PXX_LOCK(_sc)                mtx_lock(&(_sc)->sc_mtx)
  103 #define M25PXX_UNLOCK(_sc)              mtx_unlock(&(_sc)->sc_mtx)
  104 #define M25PXX_LOCK_INIT(_sc) \
  105         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
  106             "mx25l", MTX_DEF)
  107 #define M25PXX_LOCK_DESTROY(_sc)        mtx_destroy(&_sc->sc_mtx);
  108 #define M25PXX_ASSERT_LOCKED(_sc)       mtx_assert(&_sc->sc_mtx, MA_OWNED);
  109 #define M25PXX_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
  110 
  111 /* disk routines */
  112 static int mx25l_open(struct disk *dp);
  113 static int mx25l_close(struct disk *dp);
  114 static int mx25l_ioctl(struct disk *, u_long, void *, int, struct thread *);
  115 static void mx25l_strategy(struct bio *bp);
  116 static int mx25l_getattr(struct bio *bp);
  117 static void mx25l_task(void *arg);
  118 
  119 static struct mx25l_flash_ident flash_devices[] = {
  120         { "en25f32",    0x1c, 0x3116, 64 * 1024, 64, FL_NONE },
  121         { "en25p32",    0x1c, 0x2016, 64 * 1024, 64, FL_NONE },
  122         { "en25p64",    0x1c, 0x2017, 64 * 1024, 128, FL_NONE },
  123         { "en25q32",    0x1c, 0x3016, 64 * 1024, 64, FL_NONE },
  124         { "en25q64",    0x1c, 0x3017, 64 * 1024, 128, FL_ERASE_4K },
  125         { "m25p32",     0x20, 0x2016, 64 * 1024, 64, FL_NONE },
  126         { "m25p64",     0x20, 0x2017, 64 * 1024, 128, FL_NONE },
  127         { "mx25l1606e", 0xc2, 0x2015, 64 * 1024, 32, FL_ERASE_4K},
  128         { "mx25ll32",   0xc2, 0x2016, 64 * 1024, 64, FL_NONE },
  129         { "mx25ll64",   0xc2, 0x2017, 64 * 1024, 128, FL_NONE },
  130         { "mx25ll128",  0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K },
  131         { "mx25ll256",  0xc2, 0x2019, 64 * 1024, 512, FL_ERASE_4K | FL_ERASE_32K | FL_ENABLE_4B_ADDR },
  132         { "s25fl032",   0x01, 0x0215, 64 * 1024, 64, FL_NONE },
  133         { "s25fl064",   0x01, 0x0216, 64 * 1024, 128, FL_NONE },
  134         { "s25fl128",   0x01, 0x2018, 64 * 1024, 256, FL_NONE },
  135         { "s25fl256s",  0x01, 0x0219, 64 * 1024, 512, FL_NONE },
  136         { "SST25VF010A", 0xbf, 0x2549, 4 * 1024, 32, FL_ERASE_4K | FL_ERASE_32K },
  137         { "SST25VF032B", 0xbf, 0x254a, 64 * 1024, 64, FL_ERASE_4K | FL_ERASE_32K },
  138 
  139         /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
  140         { "w25x32",     0xef, 0x3016, 64 * 1024, 64, FL_ERASE_4K },
  141         { "w25x64",     0xef, 0x3017, 64 * 1024, 128, FL_ERASE_4K },
  142         { "w25q32",     0xef, 0x4016, 64 * 1024, 64, FL_ERASE_4K },
  143         { "w25q64",     0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
  144         { "w25q64bv",   0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
  145         { "w25q128",    0xef, 0x4018, 64 * 1024, 256, FL_ERASE_4K },
  146         { "w25q256",    0xef, 0x4019, 64 * 1024, 512, FL_ERASE_4K },
  147 
  148          /* Atmel */
  149         { "at25df641",  0x1f, 0x4800, 64 * 1024, 128, FL_ERASE_4K },
  150 
  151         /* GigaDevice */
  152         { "gd25q64",    0xc8, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
  153 };
  154 
  155 static int
  156 mx25l_wait_for_device_ready(struct mx25l_softc *sc)
  157 {
  158         uint8_t txBuf[2], rxBuf[2];
  159         struct spi_command cmd;
  160         int err;
  161 
  162         memset(&cmd, 0, sizeof(cmd));
  163 
  164         do {
  165                 txBuf[0] = CMD_READ_STATUS;
  166                 cmd.tx_cmd = txBuf;
  167                 cmd.rx_cmd = rxBuf;
  168                 cmd.rx_cmd_sz = 2;
  169                 cmd.tx_cmd_sz = 2;
  170                 err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd);
  171         } while (err == 0 && (rxBuf[1] & STATUS_WIP));
  172 
  173         return (err);
  174 }
  175 
  176 static struct mx25l_flash_ident*
  177 mx25l_get_device_ident(struct mx25l_softc *sc)
  178 {
  179         uint8_t txBuf[8], rxBuf[8];
  180         struct spi_command cmd;
  181         uint8_t manufacturer_id;
  182         uint16_t dev_id;
  183         int err, i;
  184 
  185         memset(&cmd, 0, sizeof(cmd));
  186         memset(txBuf, 0, sizeof(txBuf));
  187         memset(rxBuf, 0, sizeof(rxBuf));
  188 
  189         txBuf[0] = CMD_READ_IDENT;
  190         cmd.tx_cmd = &txBuf;
  191         cmd.rx_cmd = &rxBuf;
  192         /*
  193          * Some compatible devices has extended two-bytes ID
  194          * We'll use only manufacturer/deviceid atm
  195          */
  196         cmd.tx_cmd_sz = 4;
  197         cmd.rx_cmd_sz = 4;
  198         err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd);
  199         if (err)
  200                 return (NULL);
  201 
  202         manufacturer_id = rxBuf[1];
  203         dev_id = (rxBuf[2] << 8) | (rxBuf[3]);
  204 
  205         for (i = 0; i < nitems(flash_devices); i++) {
  206                 if ((flash_devices[i].manufacturer_id == manufacturer_id) &&
  207                     (flash_devices[i].device_id == dev_id))
  208                         return &flash_devices[i];
  209         }
  210 
  211         device_printf(sc->sc_dev,
  212             "Unknown SPI flash device. Vendor: %02x, device id: %04x\n",
  213             manufacturer_id, dev_id);
  214         return (NULL);
  215 }
  216 
  217 static int
  218 mx25l_set_writable(struct mx25l_softc *sc, int writable)
  219 {
  220         uint8_t txBuf[1], rxBuf[1];
  221         struct spi_command cmd;
  222         int err;
  223 
  224         memset(&cmd, 0, sizeof(cmd));
  225         memset(txBuf, 0, sizeof(txBuf));
  226         memset(rxBuf, 0, sizeof(rxBuf));
  227 
  228         txBuf[0] = writable ? CMD_WRITE_ENABLE : CMD_WRITE_DISABLE;
  229         cmd.tx_cmd = txBuf;
  230         cmd.rx_cmd = rxBuf;
  231         cmd.rx_cmd_sz = 1;
  232         cmd.tx_cmd_sz = 1;
  233         err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd);
  234         return (err);
  235 }
  236 
  237 static int
  238 mx25l_erase_cmd(struct mx25l_softc *sc, off_t sector)
  239 {
  240         uint8_t txBuf[5], rxBuf[5];
  241         struct spi_command cmd;
  242         int err;
  243 
  244         if ((err = mx25l_set_writable(sc, 1)) != 0)
  245                 return (err);
  246 
  247         memset(&cmd, 0, sizeof(cmd));
  248         memset(txBuf, 0, sizeof(txBuf));
  249         memset(rxBuf, 0, sizeof(rxBuf));
  250 
  251         cmd.tx_cmd = txBuf;
  252         cmd.rx_cmd = rxBuf;
  253 
  254         if (sc->sc_flags & FL_ERASE_4K)
  255                 txBuf[0] = CMD_BLOCK_4K_ERASE;
  256         else if (sc->sc_flags & FL_ERASE_32K)
  257                 txBuf[0] = CMD_BLOCK_32K_ERASE;
  258         else
  259                 txBuf[0] = CMD_SECTOR_ERASE;
  260 
  261         if (sc->sc_flags & FL_ENABLE_4B_ADDR) {
  262                 cmd.rx_cmd_sz = 5;
  263                 cmd.tx_cmd_sz = 5;
  264                 txBuf[1] = ((sector >> 24) & 0xff);
  265                 txBuf[2] = ((sector >> 16) & 0xff);
  266                 txBuf[3] = ((sector >> 8) & 0xff);
  267                 txBuf[4] = (sector & 0xff);
  268         } else {
  269                 cmd.rx_cmd_sz = 4;
  270                 cmd.tx_cmd_sz = 4;
  271                 txBuf[1] = ((sector >> 16) & 0xff);
  272                 txBuf[2] = ((sector >> 8) & 0xff);
  273                 txBuf[3] = (sector & 0xff);
  274         }
  275         if ((err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd)) != 0)
  276                 return (err);
  277         err = mx25l_wait_for_device_ready(sc);
  278         return (err);
  279 }
  280 
  281 static int
  282 mx25l_write(struct mx25l_softc *sc, off_t offset, caddr_t data, off_t count)
  283 {
  284         uint8_t txBuf[8], rxBuf[8];
  285         struct spi_command cmd;
  286         off_t bytes_to_write;
  287         int err = 0;
  288 
  289         if (sc->sc_flags & FL_ENABLE_4B_ADDR) {
  290                 cmd.tx_cmd_sz = 5;
  291                 cmd.rx_cmd_sz = 5;
  292         } else {
  293                 cmd.tx_cmd_sz = 4;
  294                 cmd.rx_cmd_sz = 4;
  295         }
  296 
  297         /*
  298          * Writes must be aligned to the erase sectorsize, since blocks are
  299          * fully erased before they're written to.
  300          */
  301         if (count % sc->sc_erasesize != 0 || offset % sc->sc_erasesize != 0)
  302                 return (EIO);
  303 
  304         /*
  305          * Maximum write size for CMD_PAGE_PROGRAM is FLASH_PAGE_SIZE, so loop
  306          * to write chunks of FLASH_PAGE_SIZE bytes each.
  307          */
  308         while (count != 0) {
  309                 /* If we crossed a sector boundary, erase the next sector. */
  310                 if (((offset) % sc->sc_erasesize) == 0) {
  311                         err = mx25l_erase_cmd(sc, offset);
  312                         if (err)
  313                                 break;
  314                 }
  315 
  316                 txBuf[0] = CMD_PAGE_PROGRAM;
  317                 if (sc->sc_flags & FL_ENABLE_4B_ADDR) {
  318                         txBuf[1] = (offset >> 24) & 0xff;
  319                         txBuf[2] = (offset >> 16) & 0xff;
  320                         txBuf[3] = (offset >> 8) & 0xff;
  321                         txBuf[4] = offset & 0xff;
  322                 } else {
  323                         txBuf[1] = (offset >> 16) & 0xff;
  324                         txBuf[2] = (offset >> 8) & 0xff;
  325                         txBuf[3] = offset & 0xff;
  326                 }
  327 
  328                 bytes_to_write = MIN(FLASH_PAGE_SIZE, count);
  329                 cmd.tx_cmd = txBuf;
  330                 cmd.rx_cmd = rxBuf;
  331                 cmd.tx_data = data;
  332                 cmd.rx_data = sc->sc_dummybuf;
  333                 cmd.tx_data_sz = (uint32_t)bytes_to_write;
  334                 cmd.rx_data_sz = (uint32_t)bytes_to_write;
  335 
  336                 /*
  337                  * Each completed write operation resets WEL (write enable
  338                  * latch) to disabled state, so we re-enable it here.
  339                  */
  340                 if ((err = mx25l_wait_for_device_ready(sc)) != 0)
  341                         break;
  342                 if ((err = mx25l_set_writable(sc, 1)) != 0)
  343                         break;
  344 
  345                 err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd);
  346                 if (err != 0)
  347                         break;
  348                 err = mx25l_wait_for_device_ready(sc);
  349                 if (err)
  350                         break;
  351 
  352                 data   += bytes_to_write;
  353                 offset += bytes_to_write;
  354                 count  -= bytes_to_write;
  355         }
  356 
  357         return (err);
  358 }
  359 
  360 static int
  361 mx25l_read(struct mx25l_softc *sc, off_t offset, caddr_t data, off_t count)
  362 {
  363         uint8_t txBuf[8], rxBuf[8];
  364         struct spi_command cmd;
  365         int err = 0;
  366 
  367         /*
  368          * Enforce that reads are aligned to the disk sectorsize, not the
  369          * erase sectorsize.  In this way, smaller read IO is possible,
  370          * dramatically speeding up filesystem/geom_compress access.
  371          */
  372         if (count % sc->sc_disk->d_sectorsize != 0 ||
  373             offset % sc->sc_disk->d_sectorsize != 0)
  374                 return (EIO);
  375 
  376         txBuf[0] = CMD_FAST_READ;
  377         if (sc->sc_flags & FL_ENABLE_4B_ADDR) {
  378                 cmd.tx_cmd_sz = 6;
  379                 cmd.rx_cmd_sz = 6;
  380 
  381                 txBuf[1] = (offset >> 24) & 0xff;
  382                 txBuf[2] = (offset >> 16) & 0xff;
  383                 txBuf[3] = (offset >> 8) & 0xff;
  384                 txBuf[4] = offset & 0xff;
  385                 /* Dummy byte */
  386                 txBuf[5] = 0;
  387         } else {
  388                 cmd.tx_cmd_sz = 5;
  389                 cmd.rx_cmd_sz = 5;
  390 
  391                 txBuf[1] = (offset >> 16) & 0xff;
  392                 txBuf[2] = (offset >> 8) & 0xff;
  393                 txBuf[3] = offset & 0xff;
  394                 /* Dummy byte */
  395                 txBuf[4] = 0;
  396         }
  397 
  398         cmd.tx_cmd = txBuf;
  399         cmd.rx_cmd = rxBuf;
  400         cmd.tx_data = data;
  401         cmd.rx_data = data;
  402         cmd.tx_data_sz = count;
  403         cmd.rx_data_sz = count;
  404 
  405         err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd);
  406         return (err);
  407 }
  408 
  409 static int
  410 mx25l_set_4b_mode(struct mx25l_softc *sc, uint8_t command)
  411 {
  412         uint8_t txBuf[1], rxBuf[1];
  413         struct spi_command cmd;
  414         int err;
  415 
  416         memset(&cmd, 0, sizeof(cmd));
  417         memset(txBuf, 0, sizeof(txBuf));
  418         memset(rxBuf, 0, sizeof(rxBuf));
  419 
  420         cmd.tx_cmd_sz = cmd.rx_cmd_sz = 1;
  421 
  422         cmd.tx_cmd = txBuf;
  423         cmd.rx_cmd = rxBuf;
  424 
  425         txBuf[0] = command;
  426 
  427         if ((err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd)) == 0)
  428                 err = mx25l_wait_for_device_ready(sc);
  429 
  430         return (err);
  431 }
  432 
  433 #ifdef  FDT
  434 static struct ofw_compat_data compat_data[] = {
  435         { "st,m25p",            1 },
  436         { "jedec,spi-nor",      1 },
  437         { NULL,                 0 },
  438 };
  439 #endif
  440 
  441 static int
  442 mx25l_probe(device_t dev)
  443 {
  444 #ifdef FDT
  445         int i;
  446 
  447         if (!ofw_bus_status_okay(dev))
  448                 return (ENXIO);
  449 
  450         /* First try to match the compatible property to the compat_data */
  451         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 1)
  452                 goto found;
  453 
  454         /*
  455          * Next, try to find a compatible device using the names in the
  456          * flash_devices structure
  457          */
  458         for (i = 0; i < nitems(flash_devices); i++)
  459                 if (ofw_bus_is_compatible(dev, flash_devices[i].name))
  460                         goto found;
  461 
  462         return (ENXIO);
  463 found:
  464 #endif
  465         device_set_desc(dev, "M25Pxx Flash Family");
  466 
  467         return (0);
  468 }
  469 
  470 static int
  471 mx25l_attach(device_t dev)
  472 {
  473         struct mx25l_softc *sc;
  474         struct mx25l_flash_ident *ident;
  475         int err;
  476 
  477         sc = device_get_softc(dev);
  478         sc->sc_dev = dev;
  479         sc->sc_parent = device_get_parent(sc->sc_dev);
  480 
  481         M25PXX_LOCK_INIT(sc);
  482 
  483         ident = mx25l_get_device_ident(sc);
  484         if (ident == NULL)
  485                 return (ENXIO);
  486 
  487         if ((err = mx25l_wait_for_device_ready(sc)) != 0)
  488                 return (err);
  489 
  490         sc->sc_flags = ident->flags;
  491 
  492         if (sc->sc_flags & FL_ERASE_4K)
  493                 sc->sc_erasesize = 4 * 1024;
  494         else if (sc->sc_flags & FL_ERASE_32K)
  495                 sc->sc_erasesize = 32 * 1024;
  496         else
  497                 sc->sc_erasesize = ident->sectorsize;
  498 
  499         if (sc->sc_flags & FL_ENABLE_4B_ADDR) {
  500                 if ((err = mx25l_set_4b_mode(sc, CMD_ENTER_4B_MODE)) != 0)
  501                         return (err);
  502         } else if (sc->sc_flags & FL_DISABLE_4B_ADDR) {
  503                 if ((err = mx25l_set_4b_mode(sc, CMD_EXIT_4B_MODE)) != 0)
  504                         return (err);
  505         }
  506 
  507         sc->sc_disk = disk_alloc();
  508         sc->sc_disk->d_open = mx25l_open;
  509         sc->sc_disk->d_close = mx25l_close;
  510         sc->sc_disk->d_strategy = mx25l_strategy;
  511         sc->sc_disk->d_getattr = mx25l_getattr;
  512         sc->sc_disk->d_ioctl = mx25l_ioctl;
  513         sc->sc_disk->d_name = "flash/spi";
  514         sc->sc_disk->d_drv1 = sc;
  515         sc->sc_disk->d_maxsize = DFLTPHYS;
  516         sc->sc_disk->d_sectorsize = MX25L_SECTORSIZE;
  517         sc->sc_disk->d_mediasize = ident->sectorsize * ident->sectorcount;
  518         sc->sc_disk->d_stripesize = sc->sc_erasesize;
  519         sc->sc_disk->d_unit = device_get_unit(sc->sc_dev);
  520         sc->sc_disk->d_dump = NULL;             /* NB: no dumps */
  521         strlcpy(sc->sc_disk->d_descr, ident->name,
  522             sizeof(sc->sc_disk->d_descr));
  523 
  524         disk_create(sc->sc_disk, DISK_VERSION);
  525         bioq_init(&sc->sc_bio_queue);
  526 
  527         kproc_create(&mx25l_task, sc, &sc->sc_p, 0, 0, "task: mx25l flash");
  528         sc->sc_taskstate = TSTATE_RUNNING;
  529 
  530         device_printf(sc->sc_dev, 
  531             "device type %s, size %dK in %d sectors of %dK, erase size %dK\n",
  532             ident->name,
  533             ident->sectorcount * ident->sectorsize / 1024,
  534             ident->sectorcount, ident->sectorsize / 1024,
  535             sc->sc_erasesize / 1024);
  536 
  537         return (0);
  538 }
  539 
  540 static int
  541 mx25l_detach(device_t dev)
  542 {
  543         struct mx25l_softc *sc;
  544         int err;
  545 
  546         sc = device_get_softc(dev);
  547         err = 0;
  548 
  549         M25PXX_LOCK(sc);
  550         if (sc->sc_taskstate == TSTATE_RUNNING) {
  551                 sc->sc_taskstate = TSTATE_STOPPING;
  552                 wakeup(sc);
  553                 while (err == 0 && sc->sc_taskstate != TSTATE_STOPPED) {
  554                         err = msleep(sc, &sc->sc_mtx, 0, "mx25dt", hz * 3);
  555                         if (err != 0) {
  556                                 sc->sc_taskstate = TSTATE_RUNNING;
  557                                 device_printf(sc->sc_dev,
  558                                     "Failed to stop queue task\n");
  559                         }
  560                 }
  561         }
  562         M25PXX_UNLOCK(sc);
  563 
  564         if (err == 0 && sc->sc_taskstate == TSTATE_STOPPED) {
  565                 disk_destroy(sc->sc_disk);
  566                 bioq_flush(&sc->sc_bio_queue, NULL, ENXIO);
  567                 M25PXX_LOCK_DESTROY(sc);
  568         }
  569         return (err);
  570 }
  571 
  572 static int
  573 mx25l_open(struct disk *dp)
  574 {
  575         return (0);
  576 }
  577 
  578 static int
  579 mx25l_close(struct disk *dp)
  580 {
  581 
  582         return (0);
  583 }
  584 
  585 static int
  586 mx25l_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
  587         struct thread *td)
  588 {
  589 
  590         return (EINVAL);
  591 }
  592 
  593 static void
  594 mx25l_strategy(struct bio *bp)
  595 {
  596         struct mx25l_softc *sc;
  597 
  598         sc = (struct mx25l_softc *)bp->bio_disk->d_drv1;
  599         M25PXX_LOCK(sc);
  600         bioq_disksort(&sc->sc_bio_queue, bp);
  601         wakeup(sc);
  602         M25PXX_UNLOCK(sc);
  603 }
  604 
  605 static int
  606 mx25l_getattr(struct bio *bp)
  607 {
  608         struct mx25l_softc *sc;
  609         device_t dev;
  610 
  611         if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
  612                 return (ENXIO);
  613 
  614         sc = bp->bio_disk->d_drv1;
  615         dev = sc->sc_dev;
  616 
  617         if (strcmp(bp->bio_attribute, "SPI::device") == 0) {
  618                 if (bp->bio_length != sizeof(dev))
  619                         return (EFAULT);
  620                 bcopy(&dev, bp->bio_data, sizeof(dev));
  621         } else
  622                 return (-1);
  623         return (0);
  624 }
  625 
  626 static void
  627 mx25l_task(void *arg)
  628 {
  629         struct mx25l_softc *sc = (struct mx25l_softc*)arg;
  630         struct bio *bp;
  631         device_t dev;
  632 
  633         for (;;) {
  634                 dev = sc->sc_dev;
  635                 M25PXX_LOCK(sc);
  636                 do {
  637                         if (sc->sc_taskstate == TSTATE_STOPPING) {
  638                                 sc->sc_taskstate = TSTATE_STOPPED;
  639                                 M25PXX_UNLOCK(sc);
  640                                 wakeup(sc);
  641                                 kproc_exit(0);
  642                         }
  643                         bp = bioq_first(&sc->sc_bio_queue);
  644                         if (bp == NULL)
  645                                 msleep(sc, &sc->sc_mtx, PRIBIO, "mx25jq", 0);
  646                 } while (bp == NULL);
  647                 bioq_remove(&sc->sc_bio_queue, bp);
  648                 M25PXX_UNLOCK(sc);
  649 
  650                 switch (bp->bio_cmd) {
  651                 case BIO_READ:
  652                         bp->bio_error = mx25l_read(sc, bp->bio_offset, 
  653                             bp->bio_data, bp->bio_bcount);
  654                         break;
  655                 case BIO_WRITE:
  656                         bp->bio_error = mx25l_write(sc, bp->bio_offset, 
  657                             bp->bio_data, bp->bio_bcount);
  658                         break;
  659                 default:
  660                         bp->bio_error = EINVAL;
  661                 }
  662 
  663 
  664                 biodone(bp);
  665         }
  666 }
  667 
  668 static devclass_t mx25l_devclass;
  669 
  670 static device_method_t mx25l_methods[] = {
  671         /* Device interface */
  672         DEVMETHOD(device_probe,         mx25l_probe),
  673         DEVMETHOD(device_attach,        mx25l_attach),
  674         DEVMETHOD(device_detach,        mx25l_detach),
  675 
  676         { 0, 0 }
  677 };
  678 
  679 static driver_t mx25l_driver = {
  680         "mx25l",
  681         mx25l_methods,
  682         sizeof(struct mx25l_softc),
  683 };
  684 
  685 DRIVER_MODULE(mx25l, spibus, mx25l_driver, mx25l_devclass, 0, 0);
  686 MODULE_DEPEND(mx25l, spibus, 1, 1, 1);
  687 #ifdef  FDT
  688 SPIBUS_PNP_INFO(compat_data);
  689 #endif

Cache object: b456937cae18fefcc810f2b0648e5ecd


[ 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.