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/at45d.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) 2011-2012 Ian Lepore All rights reserved.
    5  * Copyright (c) 2012 Marius Strobl <marius@FreeBSD.org> All rights reserved.
    6  * Copyright (c) 2006 M. Warner Losh <imp@FreeBSD.org>
    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$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bio.h>
   35 #include <sys/bus.h>
   36 #include <sys/conf.h>
   37 #include <sys/endian.h>
   38 #include <sys/kernel.h>
   39 #include <sys/kthread.h>
   40 #include <sys/lock.h>
   41 #include <sys/mbuf.h>
   42 #include <sys/malloc.h>
   43 #include <sys/module.h>
   44 #include <sys/mutex.h>
   45 #include <geom/geom_disk.h>
   46 
   47 #include <dev/spibus/spi.h>
   48 #include "spibus_if.h"
   49 
   50 #include "opt_platform.h"
   51 
   52 #ifdef FDT
   53 #include <dev/fdt/fdt_common.h>
   54 #include <dev/ofw/ofw_bus_subr.h>
   55 #include <dev/ofw/openfirm.h>
   56 
   57 static struct ofw_compat_data compat_data[] = {
   58         { "atmel,at45",         1 },
   59         { "atmel,dataflash",    1 },
   60         { NULL,                 0 },
   61 };
   62 #endif
   63 
   64 /* This is the information returned by the MANUFACTURER_ID command. */
   65 struct at45d_mfg_info {
   66         uint32_t        jedec_id; /* Mfg ID, DevId1, DevId2, ExtLen */
   67         uint16_t        ext_id;   /* ExtId1, ExtId2 */
   68 };
   69 
   70 /*
   71  * This is an entry in our table of metadata describing the chips.  We match on
   72  * both jedec id and extended id info returned by the MANUFACTURER_ID command.
   73  */
   74 struct at45d_flash_ident
   75 {
   76         const char      *name;
   77         uint32_t        jedec;
   78         uint16_t        extid;
   79         uint16_t        extmask;
   80         uint16_t        pagecount;
   81         uint16_t        pageoffset;
   82         uint16_t        pagesize;
   83         uint16_t        pagesize2n;
   84 };
   85 
   86 struct at45d_softc
   87 {
   88         struct bio_queue_head   bio_queue;
   89         struct mtx              sc_mtx;
   90         struct disk             *disk;
   91         struct proc             *p;
   92         device_t                dev;
   93         u_int                   taskstate;
   94         uint16_t                pagecount;
   95         uint16_t                pageoffset;
   96         uint16_t                pagesize;
   97         void                    *dummybuf;
   98 };
   99 
  100 #define TSTATE_STOPPED  0
  101 #define TSTATE_STOPPING 1
  102 #define TSTATE_RUNNING  2
  103 
  104 #define AT45D_LOCK(_sc)                 mtx_lock(&(_sc)->sc_mtx)
  105 #define AT45D_UNLOCK(_sc)               mtx_unlock(&(_sc)->sc_mtx)
  106 #define AT45D_LOCK_INIT(_sc) \
  107         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
  108             "at45d", MTX_DEF)
  109 #define AT45D_LOCK_DESTROY(_sc)         mtx_destroy(&_sc->sc_mtx);
  110 #define AT45D_ASSERT_LOCKED(_sc)        mtx_assert(&_sc->sc_mtx, MA_OWNED);
  111 #define AT45D_ASSERT_UNLOCKED(_sc)      mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
  112 
  113 /* bus entry points */
  114 static device_attach_t at45d_attach;
  115 static device_detach_t at45d_detach;
  116 static device_probe_t at45d_probe;
  117 
  118 /* disk routines */
  119 static int at45d_close(struct disk *dp);
  120 static int at45d_open(struct disk *dp);
  121 static int at45d_getattr(struct bio *bp);
  122 static void at45d_strategy(struct bio *bp);
  123 static void at45d_task(void *arg);
  124 
  125 /* helper routines */
  126 static void at45d_delayed_attach(void *xsc);
  127 static int at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp);
  128 static int at45d_get_status(device_t dev, uint8_t *status);
  129 static int at45d_wait_ready(device_t dev, uint8_t *status);
  130 
  131 #define PAGE_TO_BUFFER_TRANSFER         0x53
  132 #define PAGE_TO_BUFFER_COMPARE          0x60
  133 #define PROGRAM_THROUGH_BUFFER          0x82
  134 #define MANUFACTURER_ID                 0x9f
  135 #define STATUS_REGISTER_READ            0xd7
  136 #define CONTINUOUS_ARRAY_READ           0xe8
  137 
  138 #define STATUS_READY                    (1u << 7)
  139 #define STATUS_CMPFAIL                  (1u << 6)
  140 #define STATUS_PAGE2N                   (1u << 0)
  141 
  142 /*
  143  * Metadata for supported chips.
  144  *
  145  * The jedec id in this table includes the extended id length byte.  A match is
  146  * based on both jedec id and extended id matching.  The chip's extended id (not
  147  * present in most chips) is ANDed with ExtMask and the result is compared to
  148  * ExtId.  If a chip only returns 1 ext id byte it will be in the upper 8 bits
  149  * of ExtId in this table.
  150  *
  151  * A sectorsize2n != 0 is used to indicate that a device optionally supports
  152  * 2^N byte pages.  If support for the latter is enabled, the sector offset
  153  * has to be reduced by one.
  154  */
  155 static const struct at45d_flash_ident at45d_flash_devices[] = {
  156         /* Part Name    Jedec ID    ExtId   ExtMask PgCnt Offs PgSz PgSz2n */
  157         { "AT45DB011B", 0x1f220000, 0x0000, 0x0000,   512,  9,  264,  256 },
  158         { "AT45DB021B", 0x1f230000, 0x0000, 0x0000,  1024,  9,  264,  256 },
  159         { "AT45DB041x", 0x1f240000, 0x0000, 0x0000,  2028,  9,  264,  256 },
  160         { "AT45DB081B", 0x1f250000, 0x0000, 0x0000,  4096,  9,  264,  256 },
  161         { "AT45DB161x", 0x1f260000, 0x0000, 0x0000,  4096, 10,  528,  512 },
  162         { "AT45DB321x", 0x1f270000, 0x0000, 0x0000,  8192, 10,  528,    0 },
  163         { "AT45DB321x", 0x1f270100, 0x0000, 0x0000,  8192, 10,  528,  512 },
  164         { "AT45DB641E", 0x1f280001, 0x0000, 0xff00, 32768,  9,  264,  256 },
  165         { "AT45DB642x", 0x1f280000, 0x0000, 0x0000,  8192, 11, 1056, 1024 },
  166 };
  167 
  168 static int
  169 at45d_get_status(device_t dev, uint8_t *status)
  170 {
  171         uint8_t rxBuf[8], txBuf[8];
  172         struct spi_command cmd;
  173         int err;
  174 
  175         memset(&cmd, 0, sizeof(cmd));
  176         memset(txBuf, 0, sizeof(txBuf));
  177         memset(rxBuf, 0, sizeof(rxBuf));
  178 
  179         txBuf[0] = STATUS_REGISTER_READ;
  180         cmd.tx_cmd = txBuf;
  181         cmd.rx_cmd = rxBuf;
  182         cmd.rx_cmd_sz = cmd.tx_cmd_sz = 2;
  183         err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
  184         *status = rxBuf[1];
  185         return (err);
  186 }
  187 
  188 static int
  189 at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp)
  190 {
  191         uint8_t rxBuf[8], txBuf[8];
  192         struct spi_command cmd;
  193         int err;
  194 
  195         memset(&cmd, 0, sizeof(cmd));
  196         memset(txBuf, 0, sizeof(txBuf));
  197         memset(rxBuf, 0, sizeof(rxBuf));
  198 
  199         txBuf[0] = MANUFACTURER_ID;
  200         cmd.tx_cmd = &txBuf;
  201         cmd.rx_cmd = &rxBuf;
  202         cmd.tx_cmd_sz = cmd.rx_cmd_sz = 7;
  203         err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
  204         if (err)
  205                 return (err);
  206 
  207         resp->jedec_id = be32dec(rxBuf + 1);
  208         resp->ext_id   = be16dec(rxBuf + 5);
  209 
  210         return (0);
  211 }
  212 
  213 static int
  214 at45d_wait_ready(device_t dev, uint8_t *status)
  215 {
  216         struct timeval now, tout;
  217         int err;
  218 
  219         getmicrouptime(&tout);
  220         tout.tv_sec += 3;
  221         do {
  222                 getmicrouptime(&now);
  223                 if (now.tv_sec > tout.tv_sec)
  224                         err = ETIMEDOUT;
  225                 else
  226                         err = at45d_get_status(dev, status);
  227         } while (err == 0 && !(*status & STATUS_READY));
  228         return (err);
  229 }
  230 
  231 static int
  232 at45d_probe(device_t dev)
  233 {
  234         int rv;
  235 
  236 #ifdef FDT
  237         if (!ofw_bus_status_okay(dev))
  238                 return (ENXIO);
  239 
  240         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  241                 return (ENXIO);
  242 
  243         rv = BUS_PROBE_DEFAULT;
  244 #else
  245         rv = BUS_PROBE_NOWILDCARD;
  246 #endif
  247 
  248         device_set_desc(dev, "AT45D Flash Family");
  249         return (rv);
  250 }
  251 
  252 static int
  253 at45d_attach(device_t dev)
  254 {
  255         struct at45d_softc *sc;
  256 
  257         sc = device_get_softc(dev);
  258         sc->dev = dev;
  259         AT45D_LOCK_INIT(sc);
  260 
  261         config_intrhook_oneshot(at45d_delayed_attach, sc);
  262         return (0);
  263 }
  264 
  265 static int
  266 at45d_detach(device_t dev)
  267 {
  268         struct at45d_softc *sc;
  269         int err;
  270 
  271         sc = device_get_softc(dev);
  272         err = 0;
  273 
  274         AT45D_LOCK(sc);
  275         if (sc->taskstate == TSTATE_RUNNING) {
  276                 sc->taskstate = TSTATE_STOPPING;
  277                 wakeup(sc);
  278                 while (err == 0 && sc->taskstate != TSTATE_STOPPED) {
  279                         err = msleep(sc, &sc->sc_mtx, 0, "at45dt", hz * 3);
  280                         if (err != 0) {
  281                                 sc->taskstate = TSTATE_RUNNING;
  282                                 device_printf(sc->dev,
  283                                     "Failed to stop queue task\n");
  284                         }
  285                 }
  286         }
  287         AT45D_UNLOCK(sc);
  288 
  289         if (err == 0 && sc->taskstate == TSTATE_STOPPED) {
  290                 if (sc->disk) {
  291                         disk_destroy(sc->disk);
  292                         bioq_flush(&sc->bio_queue, NULL, ENXIO);
  293                         free(sc->dummybuf, M_DEVBUF);
  294                 }
  295                 AT45D_LOCK_DESTROY(sc);
  296         }
  297         return (err);
  298 }
  299 
  300 static void
  301 at45d_delayed_attach(void *xsc)
  302 {
  303         struct at45d_softc *sc;
  304         struct at45d_mfg_info mfginfo;
  305         const struct at45d_flash_ident *ident;
  306         u_int i;
  307         int sectorsize;
  308         uint32_t jedec;
  309         uint16_t pagesize;
  310         uint8_t status;
  311 
  312         sc = xsc;
  313         ident = NULL;
  314         jedec = 0;
  315 
  316         if (at45d_wait_ready(sc->dev, &status) != 0) {
  317                 device_printf(sc->dev, "Error waiting for device-ready.\n");
  318                 return;
  319         }
  320         if (at45d_get_mfg_info(sc->dev, &mfginfo) != 0) {
  321                 device_printf(sc->dev, "Failed to get ID.\n");
  322                 return;
  323         }
  324         for (i = 0; i < nitems(at45d_flash_devices); i++) {
  325                 ident = &at45d_flash_devices[i];
  326                 if (mfginfo.jedec_id == ident->jedec && 
  327                     (mfginfo.ext_id & ident->extmask) == ident->extid) {
  328                         break;
  329                 }
  330         }
  331         if (i == nitems(at45d_flash_devices)) {
  332                 device_printf(sc->dev, "JEDEC 0x%x not in list.\n", jedec);
  333                 return;
  334         }
  335 
  336         sc->pagecount = ident->pagecount;
  337         sc->pageoffset = ident->pageoffset;
  338         if (ident->pagesize2n != 0 && (status & STATUS_PAGE2N)) {
  339                 sc->pageoffset -= 1;
  340                 pagesize = ident->pagesize2n;
  341         } else
  342                 pagesize = ident->pagesize;
  343         sc->pagesize = pagesize;
  344 
  345         /*
  346          * By default we set up a disk with a sector size that matches the
  347          * device page size.  If there is a device hint or fdt property
  348          * requesting a different size, use that, as long as it is a multiple of
  349          * the device page size).
  350          */
  351         sectorsize = pagesize;
  352 #ifdef FDT
  353         {
  354                 pcell_t size;
  355                 if (OF_getencprop(ofw_bus_get_node(sc->dev),
  356                     "freebsd,sectorsize", &size, sizeof(size)) > 0)
  357                         sectorsize = size;
  358         }
  359 #endif
  360         resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev),
  361             "sectorsize", &sectorsize);
  362 
  363         if ((sectorsize % pagesize) != 0) {
  364                 device_printf(sc->dev, "Invalid sectorsize %d, "
  365                     "must be a multiple of %d\n", sectorsize, pagesize);
  366                 return;
  367         }
  368 
  369         sc->dummybuf = malloc(pagesize, M_DEVBUF, M_WAITOK | M_ZERO);
  370 
  371         sc->disk = disk_alloc();
  372         sc->disk->d_open = at45d_open;
  373         sc->disk->d_close = at45d_close;
  374         sc->disk->d_strategy = at45d_strategy;
  375         sc->disk->d_getattr = at45d_getattr;
  376         sc->disk->d_name = "flash/at45d";
  377         sc->disk->d_drv1 = sc;
  378         sc->disk->d_maxsize = DFLTPHYS;
  379         sc->disk->d_sectorsize = sectorsize;
  380         sc->disk->d_mediasize = pagesize * ident->pagecount;
  381         sc->disk->d_unit = device_get_unit(sc->dev);
  382         disk_create(sc->disk, DISK_VERSION);
  383         bioq_init(&sc->bio_queue);
  384         kproc_create(&at45d_task, sc, &sc->p, 0, 0, "task: at45d flash");
  385         sc->taskstate = TSTATE_RUNNING;
  386         device_printf(sc->dev,
  387             "%s, %d bytes per page, %d pages; %d KBytes; disk sector size %d\n",
  388             ident->name, pagesize, ident->pagecount,
  389             (pagesize * ident->pagecount) / 1024, sectorsize);
  390 }
  391 
  392 static int
  393 at45d_open(struct disk *dp)
  394 {
  395 
  396         return (0);
  397 }
  398 
  399 static int
  400 at45d_close(struct disk *dp)
  401 {
  402 
  403         return (0);
  404 }
  405 
  406 static int
  407 at45d_getattr(struct bio *bp)
  408 {
  409         struct at45d_softc *sc;
  410 
  411         /*
  412          * This function exists to support geom_flashmap and fdt_slicer.
  413          */
  414 
  415         if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
  416                 return (ENXIO);
  417         if (strcmp(bp->bio_attribute, "SPI::device") != 0)
  418                 return (-1);
  419         sc = bp->bio_disk->d_drv1;
  420         if (bp->bio_length != sizeof(sc->dev))
  421                 return (EFAULT);
  422         bcopy(&sc->dev, bp->bio_data, sizeof(sc->dev));
  423         return (0);
  424 }
  425 
  426 static void
  427 at45d_strategy(struct bio *bp)
  428 {
  429         struct at45d_softc *sc;
  430 
  431         sc = (struct at45d_softc *)bp->bio_disk->d_drv1;
  432         AT45D_LOCK(sc);
  433         bioq_disksort(&sc->bio_queue, bp);
  434         wakeup(sc);
  435         AT45D_UNLOCK(sc);
  436 }
  437 
  438 static void
  439 at45d_task(void *arg)
  440 {
  441         uint8_t rxBuf[8], txBuf[8];
  442         struct at45d_softc *sc;
  443         struct bio *bp;
  444         struct spi_command cmd;
  445         device_t dev, pdev;
  446         caddr_t buf;
  447         u_long len, resid;
  448         u_int addr, berr, err, offset, page;
  449         uint8_t status;
  450 
  451         sc = (struct at45d_softc*)arg;
  452         dev = sc->dev;
  453         pdev = device_get_parent(dev);
  454         memset(&cmd, 0, sizeof(cmd));
  455         memset(txBuf, 0, sizeof(txBuf));
  456         memset(rxBuf, 0, sizeof(rxBuf));
  457         cmd.tx_cmd = txBuf;
  458         cmd.rx_cmd = rxBuf;
  459 
  460         for (;;) {
  461                 AT45D_LOCK(sc);
  462                 do {
  463                         if (sc->taskstate == TSTATE_STOPPING) {
  464                                 sc->taskstate = TSTATE_STOPPED;
  465                                 AT45D_UNLOCK(sc);
  466                                 wakeup(sc);
  467                                 kproc_exit(0);
  468                         }
  469                         bp = bioq_takefirst(&sc->bio_queue);
  470                         if (bp == NULL)
  471                                 msleep(sc, &sc->sc_mtx, PRIBIO, "at45dq", 0);
  472                 } while (bp == NULL);
  473                 AT45D_UNLOCK(sc);
  474 
  475                 berr = 0;
  476                 buf = bp->bio_data;
  477                 len = resid = bp->bio_bcount;
  478                 page = bp->bio_offset / sc->pagesize;
  479                 offset = bp->bio_offset % sc->pagesize;
  480 
  481                 switch (bp->bio_cmd) {
  482                 case BIO_READ:
  483                         txBuf[0] = CONTINUOUS_ARRAY_READ;
  484                         cmd.tx_cmd_sz = cmd.rx_cmd_sz = 8;
  485                         cmd.tx_data = sc->dummybuf;
  486                         cmd.rx_data = buf;
  487                         break;
  488                 case BIO_WRITE:
  489                         cmd.tx_cmd_sz = cmd.rx_cmd_sz = 4;
  490                         cmd.tx_data = buf;
  491                         cmd.rx_data = sc->dummybuf;
  492                         if (resid + offset > sc->pagesize)
  493                                 len = sc->pagesize - offset;
  494                         break;
  495                 default:
  496                         berr = EOPNOTSUPP;
  497                         goto out;
  498                 }
  499 
  500                 /*
  501                  * NB: for BIO_READ, this loop is only traversed once.
  502                  */
  503                 while (resid > 0) {
  504                         if (page > sc->pagecount) {
  505                                 berr = EINVAL;
  506                                 goto out;
  507                         }
  508                         addr = page << sc->pageoffset;
  509                         if (bp->bio_cmd == BIO_WRITE) {
  510                                 /*
  511                                  * If writing less than a full page, transfer
  512                                  * the existing page to the buffer, so that our
  513                                  * PROGRAM_THROUGH_BUFFER below will preserve
  514                                  * the parts of the page we're not writing.
  515                                  */
  516                                 if (len != sc->pagesize) {
  517                                         txBuf[0] = PAGE_TO_BUFFER_TRANSFER;
  518                                         txBuf[1] = ((addr >> 16) & 0xff);
  519                                         txBuf[2] = ((addr >> 8) & 0xff);
  520                                         txBuf[3] = 0;
  521                                         cmd.tx_data_sz = cmd.rx_data_sz = 0;
  522                                         err = SPIBUS_TRANSFER(pdev, dev, &cmd);
  523                                         if (err == 0)
  524                                                 err = at45d_wait_ready(dev,
  525                                                     &status);
  526                                         if (err != 0) {
  527                                                 berr = EIO;
  528                                                 goto out;
  529                                         }
  530                                 }
  531                                 txBuf[0] = PROGRAM_THROUGH_BUFFER;
  532                         }
  533 
  534                         addr += offset;
  535                         txBuf[1] = ((addr >> 16) & 0xff);
  536                         txBuf[2] = ((addr >> 8) & 0xff);
  537                         txBuf[3] = (addr & 0xff);
  538                         cmd.tx_data_sz = cmd.rx_data_sz = len;
  539                         err = SPIBUS_TRANSFER(pdev, dev, &cmd);
  540                         if (err == 0 && bp->bio_cmd != BIO_READ)
  541                                 err = at45d_wait_ready(dev, &status);
  542                         if (err != 0) {
  543                                 berr = EIO;
  544                                 goto out;
  545                         }
  546                         if (bp->bio_cmd == BIO_WRITE) {
  547                                 addr = page << sc->pageoffset;
  548                                 txBuf[0] = PAGE_TO_BUFFER_COMPARE;
  549                                 txBuf[1] = ((addr >> 16) & 0xff);
  550                                 txBuf[2] = ((addr >> 8) & 0xff);
  551                                 txBuf[3] = 0;
  552                                 cmd.tx_data_sz = cmd.rx_data_sz = 0;
  553                                 err = SPIBUS_TRANSFER(pdev, dev, &cmd);
  554                                 if (err == 0)
  555                                         err = at45d_wait_ready(dev, &status);
  556                                 if (err != 0 || (status & STATUS_CMPFAIL)) {
  557                                         device_printf(dev, "comparing page "
  558                                             "%d failed (status=0x%x)\n", page,
  559                                             status);
  560                                         berr = EIO;
  561                                         goto out;
  562                                 }
  563                         }
  564                         page++;
  565                         buf += len;
  566                         offset = 0;
  567                         resid -= len;
  568                         if (resid > sc->pagesize)
  569                                 len = sc->pagesize;
  570                         else
  571                                 len = resid;
  572                         if (bp->bio_cmd == BIO_READ)
  573                                 cmd.rx_data = buf;
  574                         else
  575                                 cmd.tx_data = buf;
  576                 }
  577  out:
  578                 if (berr != 0) {
  579                         bp->bio_flags |= BIO_ERROR;
  580                         bp->bio_error = berr;
  581                 }
  582                 bp->bio_resid = resid;
  583                 biodone(bp);
  584         }
  585 }
  586 
  587 static device_method_t at45d_methods[] = {
  588         /* Device interface */
  589         DEVMETHOD(device_probe,         at45d_probe),
  590         DEVMETHOD(device_attach,        at45d_attach),
  591         DEVMETHOD(device_detach,        at45d_detach),
  592 
  593         DEVMETHOD_END
  594 };
  595 
  596 static driver_t at45d_driver = {
  597         "at45d",
  598         at45d_methods,
  599         sizeof(struct at45d_softc),
  600 };
  601 
  602 DRIVER_MODULE(at45d, spibus, at45d_driver, NULL, NULL);
  603 MODULE_DEPEND(at45d, spibus, 1, 1, 1);
  604 #ifdef FDT
  605 MODULE_DEPEND(at45d, fdt_slicer, 1, 1, 1);
  606 SPIBUS_FDT_PNP_INFO(compat_data);
  607 #endif
  608 

Cache object: d5778020006d1f04c8a1b10eaee19827


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