Nut/OS  4.10.3
API Reference
spi_at45dib.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008-2010 by egnite GmbH
00003  *
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  * 3. Neither the name of the copyright holders nor the names of
00016  *    contributors may be used to endorse or promote products derived
00017  *    from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00026  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00027  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00028  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00029  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00030  * SUCH DAMAGE.
00031  *
00032  * For additional information see http://www.ethernut.de/
00033  */
00034 
00044 #include <cfg/memory.h>
00045 
00046 #include <sys/nutdebug.h>
00047 #include <dev/at45d.h>
00048 #ifndef DFCMD_BUF1_LOAD_PAGE
00049 #define DFCMD_BUF1_LOAD_PAGE    0x53
00050 #endif
00051 #ifndef DFCMD_BUF2_LOAD_PAGE
00052 #define DFCMD_BUF2_LOAD_PAGE    0x55
00053 #endif
00054 #include <stdlib.h>
00055 
00056 #include <dev/spi_at45dib.h>
00057 
00062 
00064 #define AT45DIB_FDIRTY      0x0001
00065 
00072 typedef struct _AT45DIB {
00077     sf_unit_t dib_page[2];
00078 
00084     uint_fast8_t flags[2];
00085 
00091     int dib_locks[2];
00092 
00097     HANDLE dib_lque;
00098 
00104     uint_fast8_t dib_pshft;
00105 } AT45DIB;
00106 
00117 static int At45dibTransmitCmd(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen)
00118 {
00119     uint8_t cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
00120 
00121     NUTASSERT(oplen <= sizeof(cmd));
00122 
00123     cmd[0] = op;
00124     if (parm) {
00125         cmd[1] = (uint8_t) (parm >> 16);
00126         cmd[2] = (uint8_t) (parm >> 8);
00127         cmd[3] = (uint8_t) parm;
00128     }
00129     return (*((NUTSPIBUS *) node->node_bus)->bus_transfer) (node, cmd, NULL, oplen);
00130 }
00131 
00145 static int At45dibTransfer(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, 
00146                            CONST void *txbuf, void *rxbuf, int xlen)
00147 {
00148     int rc;
00149     NUTSPIBUS *bus;
00150 
00151     /* Sanity checks. */
00152     NUTASSERT(node != NULL);
00153     bus = (NUTSPIBUS *) node->node_bus;
00154     NUTASSERT(bus != NULL);
00155     NUTASSERT(bus->bus_alloc != NULL);
00156     NUTASSERT(bus->bus_transfer != NULL);
00157     NUTASSERT(bus->bus_release != NULL);
00158 
00159     rc = (*bus->bus_alloc) (node, 0);
00160     if (rc == 0) {
00161         rc = At45dibTransmitCmd(node, op, parm, oplen);
00162         if (rc == 0 && xlen) {
00163             rc = (*bus->bus_transfer) (node, txbuf, rxbuf, xlen);
00164         }
00165         (*bus->bus_release) (node);
00166     }
00167     return rc;
00168 }
00169 
00180 static int At45dibCommand(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen)
00181 {
00182     return At45dibTransfer(node, op, parm, oplen, NULL, NULL, 0);
00183 }
00184 
00197 static int At45dibCompare(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, CONST void *buf, int xlen)
00198 {
00199     int rc;
00200     NUTSPIBUS *bus;
00201 
00202     /* Sanity checks. */
00203     NUTASSERT(node != NULL);
00204     bus = (NUTSPIBUS *) node->node_bus;
00205     NUTASSERT(bus != NULL);
00206     NUTASSERT(bus->bus_alloc != NULL);
00207     NUTASSERT(bus->bus_transfer != NULL);
00208     NUTASSERT(bus->bus_release != NULL);
00209 
00210     rc = (*bus->bus_alloc) (node, 0);
00211     if (rc == 0) {
00212         rc = At45dibTransmitCmd(node, op, parm, oplen);
00213         if (rc == 0 && xlen) {
00214             int i;
00215             uint8_t c;
00216             uint8_t *cp = (uint8_t *) buf;
00217 
00218             for (i = 0; i < xlen; i++) {
00219                 rc = (*bus->bus_transfer) (node, NULL, &c, 1);
00220                 if (rc) {
00221                     break;
00222                 }
00223                 if (c != cp[i]) {
00224                     rc = (int) c - (int) cp[i];
00225                     break;
00226                 }
00227             }
00228         }
00229         (*bus->bus_release) (node);
00230     }
00231     return rc;
00232 }
00233 
00248 static int At45dibUsed(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, int xlen)
00249 {
00250     int rc;
00251     NUTSPIBUS *bus;
00252 
00253     /* Sanity checks. */
00254     NUTASSERT(node != NULL);
00255 
00256     bus = (NUTSPIBUS *) node->node_bus;
00257     NUTASSERT(bus != NULL);
00258     NUTASSERT(bus->bus_alloc != NULL);
00259     NUTASSERT(bus->bus_transfer != NULL);
00260     NUTASSERT(bus->bus_release != NULL);
00261 
00262     rc = (*bus->bus_alloc) (node, 0);
00263     if (rc == 0) {
00264         rc = At45dibTransmitCmd(node, op, parm, oplen);
00265         if (rc == 0 && xlen) {
00266             int i;
00267             int n = -1;
00268             uint8_t c;
00269 
00270             for (i = 0; i < xlen; i++) {
00271                 rc = (*bus->bus_transfer) (node, NULL, &c, 1);
00272                 if (rc) {
00273                     break;
00274                 }
00275                 if (c != 0xff) {
00276                     n = i;
00277                 }
00278             }
00279             if (rc == 0) {
00280                 rc = n + 1;
00281             }
00282         }
00283         (*bus->bus_release) (node);
00284     }
00285     return rc;
00286 }
00287 
00288 
00289 #ifdef AT45D_CRC_PAGE
00290 
00291 /*
00292  * http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
00293  */
00294 static uint16_t crc_ccitt_update(uint16_t crc, uint8_t data)
00295 {
00296     data ^= (uint8_t) (crc);
00297     data ^= data << 4;
00298 
00299     return ((((uint16_t) data << 8) | (crc >> 8)) ^ (uint8_t) (data >> 4) ^ ((uint16_t) data << 3));
00300 }
00301 
00314 static int At45dibChecksum(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, uint16_t * crc16, int xlen)
00315 {
00316     int rc;
00317     NUTSPIBUS *bus;
00318 
00319     /* Sanity checks. */
00320     NUTASSERT(node != NULL);
00321     bus = (NUTSPIBUS *) node->node_bus;
00322     NUTASSERT(bus != NULL);
00323     NUTASSERT(bus->bus_alloc != NULL);
00324     NUTASSERT(bus->bus_transfer != NULL);
00325     NUTASSERT(bus->bus_release != NULL);
00326 
00327     *crc16 = 0xffff;
00328     rc = (*bus->bus_alloc) (node, 0);
00329     if (rc == 0) {
00330         rc = At45dibTransmitCmd(node, op, parm, oplen);
00331         if (rc == 0 && xlen) {
00332             int i;
00333             uint8_t c;
00334             uint_fast8_t ne = 0;
00335 
00336             for (i = 0; i < xlen; i++) {
00337                 rc = (*bus->bus_transfer) (node, NULL, &c, 1);
00338                 if (rc) {
00339                     break;
00340                 }
00341                 if (ne || c != 0xff) {
00342                     ne = 1;
00343                     *crc16 = crc_ccitt_update(*crc16, c);
00344                 }
00345             }
00346             if (*crc16 == 0) {
00347                 *crc16 = 0xffff;
00348             }
00349         }
00350         (*bus->bus_release) (node);
00351     }
00352     return rc;
00353 }
00354 
00355 #endif /* AT45D_CRC_PAGE */
00356 
00357 
00365 static uint8_t At45dibStatus(NUTSPINODE * node)
00366 {
00367     int rc;
00368     uint8_t cmd[2] = { DFCMD_READ_STATUS, 0xFF };
00369     NUTSPIBUS *bus;
00370 
00371     /* Sanity checks. */
00372     NUTASSERT(node != NULL);
00373     NUTASSERT(node->node_bus != NULL);
00374 
00375     bus = (NUTSPIBUS *) node->node_bus;
00376     NUTASSERT(bus->bus_alloc != NULL);
00377     NUTASSERT(bus->bus_transfer != NULL);
00378     NUTASSERT(bus->bus_wait != NULL);
00379     NUTASSERT(bus->bus_release != NULL);
00380 
00381     rc = (*bus->bus_alloc) (node, 0);
00382     if (rc == 0) {
00383         rc = (*bus->bus_transfer) (node, cmd, cmd, 2);
00384         if (rc == 0) {
00385             (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
00386             rc = cmd[1];
00387         }
00388         (*bus->bus_release) (node);
00389     }
00390     return (uint8_t) rc;
00391 }
00392 
00401 static int At45dibWaitReady(NUTSPINODE * node, uint32_t tmo)
00402 {
00403     uint8_t sr;
00404 
00405     while (((sr = At45dibStatus(node)) & 0x80) == 0) {
00406         if (tmo-- == 0) {
00407             return -1;
00408         }
00409     }
00410     return 0;
00411 }
00412 
00421 static int At45dibFlash(NUTSERIALFLASH * sfi, int_fast8_t b)
00422 {
00423     int rc;
00424     AT45DIB *at;
00425     uint32_t pga;
00426 
00427 #ifdef AT45D_CRC_PAGE
00428     /* Store a 16 bit CRC at the end of the page, if configured. */
00429     {
00430         uint16_t crc16;
00431 
00432         if (At45dibChecksum(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, 0, 5, &crc16, sfi->sf_unit_size)) {
00433             return -1;
00434         }
00435         if (At45dibTransfer
00436             (sfi->sf_node, b ? DFCMD_BUF2_WRITE : DFCMD_BUF1_WRITE, sfi->sf_unit_size, 4, &crc16, NULL, sizeof(crc16))) {
00437             return -1;
00438         }
00439     }
00440 #endif
00441 
00442     at = (AT45DIB *) sfi->sf_info;
00443     pga = at->dib_page[b];
00444     pga <<= at->dib_pshft;
00445     rc = At45dibCommand(sfi->sf_node, b ? DFCMD_BUF2_FLASH_NE : DFCMD_BUF1_FLASH_NE, pga, 4);
00446     if (rc == 0) {
00447         rc = At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS);
00448         at->flags[b] &= ~AT45DIB_FDIRTY;
00449     }
00450     return rc;
00451 }
00452 
00459 static void At45dibRelease(NUTSERIALFLASH * sfi, int b)
00460 {
00461     AT45DIB *at = (AT45DIB *) sfi->sf_info;
00462 
00463     at->dib_locks[b]--;
00464     NutEventPost(&at->dib_lque);
00465 }
00466 
00475 static int_fast8_t At45dibAllocate(NUTSERIALFLASH * sfi, sf_unit_t pgn)
00476 {
00477     int_fast8_t b;
00478     AT45DIB *at;
00479 
00480     at = (AT45DIB *) sfi->sf_info;
00481     for (;;) {
00482         /* 
00483         ** Check, if the page is already loaded.
00484         */
00485         for (b = 0; b < 2; b++) {
00486             if (at->dib_page[b] == pgn) {
00487                 at->dib_locks[b]++;
00488                 at->flags[b] |= AT45DIB_FDIRTY;
00489                 return b;
00490             }
00491         }
00492 
00493         /*
00494         ** Search for a clean unlocked buffer and load the page.
00495         */
00496         for (b = 0; b < 2; b++) {
00497             if (at->dib_locks[b] == 0 && (at->flags[b] & AT45DIB_FDIRTY) == 0) {
00498                 int rc;
00499                 uint32_t pga = pgn;
00500 
00501                 pga <<= at->dib_pshft;
00502                 at->dib_locks[b]++;
00503                 if (b) {
00504                     rc = At45dibCommand(sfi->sf_node, DFCMD_BUF2_LOAD_PAGE, pga, 4);
00505                 } else {
00506                     rc = At45dibCommand(sfi->sf_node, DFCMD_BUF1_LOAD_PAGE, pga, 4);
00507                 }
00508                 if (rc == 0) {
00509                     rc = At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS);
00510                 }
00511                 if (rc == 0) {
00512                     at->dib_page[b] = pgn;
00513                     at->flags[b] |= AT45DIB_FDIRTY;
00514 
00515                     return b;
00516                 }
00517                 At45dibRelease(sfi, b);
00518 
00519                 return -1;
00520             }
00521         }
00522 
00523         /*
00524         ** No clean buffer. If not locked, save one of them.
00525         */
00526         for (b = 0; b < 2; b++) {
00527             if (at->dib_locks[b] == 0 && (at->flags[b] & AT45DIB_FDIRTY) == AT45DIB_FDIRTY) {
00528                 if (At45dibFlash(sfi, b)) {
00529                     return -1;
00530                 }
00531                 break;
00532             }
00533         }
00534 
00535         /*
00536         ** All buffers are locked. Wait for an unlock event.
00537         */
00538         if (b >= 2) {
00539             NutEventWait(&at->dib_lque, 0);
00540         }
00541     }
00542 }
00543 
00551 static int SpiAt45dibInit(NUTSERIALFLASH * sfi)
00552 {
00553     int_fast8_t b;
00554     uint8_t sr;
00555     int_fast8_t i;
00556 
00557     /* Sanity checks. */
00558     NUTASSERT(sfi != NULL);
00559     NUTASSERT(sfi->sf_node != NULL);
00560 
00561     sr = At45dibStatus(sfi->sf_node);
00562     sr &= AT45D_STATUS_DENSITY | AT45D_STATUS_PAGE_SIZE;
00563     for (i = at45d_known_types; --i >= 0;) {
00564         if (sr == at45d_info[i].at45d_srval) {
00565             /* Known DataFlash type. */
00566             AT45DIB *at = calloc(1, sizeof(AT45DIB));
00567             if (at == NULL) {
00568                 return -1;
00569             }
00570             at->dib_pshft = at45d_info[i].at45d_pshft;
00571             for (b = 0; b < 2; b++) {
00572                 at->dib_page[b] = SERIALFLASH_MAX_UNITS;
00573             }
00574             sfi->sf_info = (void *) at;
00575             sfi->sf_units = (sf_unit_t) at45d_info[i].at45d_pages;
00576             sfi->sf_unit_size = at45d_info[i].at45d_psize;
00577 #ifdef AT45D_CRC_PAGE
00578             sfi->sf_unit_size -= 2;
00579 #endif
00580             return 0;
00581         }
00582     }
00583     /* Unknown DataFlash type. */
00584     return -1;
00585 }
00586 
00594 static void SpiAt45dibExit(NUTSERIALFLASH * sfi)
00595 {
00596     NUTASSERT(sfi != NULL);
00597     NUTASSERT(sfi->sf_info != NULL);
00598 
00599     free(sfi->sf_info);
00600 }
00601 
00611 static int SpiAt45dibCheck(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
00612 {
00613     int rc = 0;
00614 
00615 #ifdef AT45D_CRC_PAGE
00616     AT45DIB *at;
00617     int_fast8_t b;
00618     uint16_t crc16;
00619 
00620     /* Sanity checks. */
00621     NUTASSERT(sfi != NULL);
00622     NUTASSERT(sfi->sf_info != NULL);
00623     NUTASSERT(pgn + cnt <= sfi->sf_units);
00624     NUTASSERT(cnt >= 0);
00625 
00626     at = (AT45DIB *) sfi->sf_info;
00627     while (cnt--) {
00628         /* If this page is buffered, then read from the buffer. */
00629         for (b = 0; b < 2 && at->dib_page[b] != pgn + cnt; b++);
00630         if (b < 2) {
00631             At45dibChecksum(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, 0, 5, &crc16, sfi->sf_unit_size + 2);
00632         }
00633         /* If not buffered, then directly read from the flash. */
00634         else {
00635             uint32_t pga = pgn + cnt;
00636             pga <<= at->dib_pshft;
00637             At45dibChecksum(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, &crc16, sfi->sf_unit_size + 2);
00638         }
00639         if (crc16 != 0xFFFF) {
00640             rc = -1;
00641             break;
00642         }
00643     }
00644 #endif
00645     return rc;
00646 }
00647 
00660 static int SpiAt45dibRead(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, void *data, int len)
00661 {
00662     int rc = 0;
00663 
00664     /* Sanity checks. */
00665     NUTASSERT(sfi != NULL);
00666     NUTASSERT(sfi->sf_info != NULL);
00667     NUTASSERT(pgn < sfi->sf_units);
00668     NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
00669     NUTASSERT(data != NULL);
00670     NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
00671 
00672     if (len) {
00673         int_fast8_t b;
00674         AT45DIB *at = (AT45DIB *) sfi->sf_info;
00675 
00676         /* Normalize the offset. */
00677         if (off < 0) {
00678             off += sfi->sf_unit_size;
00679         }
00680         NUTASSERT(off + len <= (int) sfi->sf_unit_size);
00681 
00682         /* If this page is buffered, then read from the buffer. */
00683         for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
00684         if (b < 2) {
00685             rc = At45dibTransfer(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, off, 5, NULL, data, len);
00686         }
00687         /* If not buffered, then directly read from the flash. */
00688         else {
00689             uint32_t pga = pgn;
00690 
00691             pga <<= at->dib_pshft;
00692             pga |= off;
00693             rc = At45dibTransfer(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, NULL, data, len);
00694         }
00695     }
00696     return rc;
00697 }
00698 
00714 static int SpiAt45dibCompare(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, CONST void *data, int len)
00715 {
00716     int rc = 0;
00717 
00718     /* Sanity checks. */
00719     NUTASSERT(sfi != NULL);
00720     NUTASSERT(sfi->sf_info != NULL);
00721     NUTASSERT(pgn < sfi->sf_units);
00722     NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
00723     NUTASSERT(data != NULL);
00724     NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
00725 
00726     if (len) {
00727         int_fast8_t b;
00728         AT45DIB *at = (AT45DIB *) sfi->sf_info;
00729 
00730         /* Normalize the offset. */
00731         if (off < 0) {
00732             off += sfi->sf_unit_size;
00733         }
00734         NUTASSERT(off + len <= (int) sfi->sf_unit_size);
00735 
00736         /* If this page is buffered, then read from the buffer. */
00737         for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
00738         if (b < 2) {
00739             rc = At45dibCompare(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, off, 5, data, len);
00740         }
00741         /* If not buffered, then directly read from the flash. */
00742         else {
00743             uint32_t pga = pgn;
00744 
00745             pga <<= at->dib_pshft;
00746             pga |= off;
00747             rc = At45dibCompare(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, data, len);
00748         }
00749     }
00750     return rc;
00751 }
00752 
00766 static int SpiAt45dibUsed(NUTSERIALFLASH * sfi, sf_unit_t pgn, int skip)
00767 {
00768     int rc;
00769     int len;
00770     AT45DIB *at;
00771     int_fast8_t b;
00772 
00773     /* Sanity checks. */
00774     NUTASSERT(sfi != NULL);
00775     NUTASSERT(pgn < sfi->sf_units);
00776     NUTASSERT(skip <= (int) sfi->sf_unit_size);
00777 
00778     at = (AT45DIB *) sfi->sf_info;
00779 
00780     /* Determine length and offset. */
00781     len = (int) sfi->sf_unit_size;
00782     if (skip < 0) {
00783         len += skip;
00784         skip = 0;
00785     } else {
00786         len -= skip;
00787     }
00788 
00789     /* If this page is buffered, then read from the buffer. */
00790     for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
00791     if (b < 2) {
00792         rc = At45dibUsed(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, skip, 5, len);
00793     }
00794     /* If not buffered, then directly read from the flash. */
00795     else {
00796         uint32_t pga = pgn;
00797 
00798         pga <<= at->dib_pshft;
00799         pga |= skip;
00800         rc = At45dibUsed(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, len);
00801     }
00802     return rc;
00803 }
00804 
00821 static int SpiAt45dibWrite(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, CONST void *data, int len)
00822 {
00823     int rc = 0;
00824 
00825     /* Sanity checks. */
00826     NUTASSERT(sfi != NULL);
00827     NUTASSERT(pgn < sfi->sf_units);
00828     NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
00829     NUTASSERT(len == 0 || data != NULL);
00830     NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
00831 
00832     if (len) {
00833         int_fast8_t b;
00834 
00835         /* Normalize the offset. */
00836         if (off < 0) {
00837             off += sfi->sf_unit_size;
00838         }
00839         NUTASSERT(off + len <= (int) sfi->sf_unit_size);
00840 
00841         /* Load the page. */
00842         b = At45dibAllocate(sfi, pgn);
00843         if (b < 0) {
00844             return -1;
00845         }
00846         /* Transfer the data and release the page. */
00847         rc = At45dibTransfer(sfi->sf_node, b ? DFCMD_BUF2_WRITE : DFCMD_BUF1_WRITE, off, 4, data, NULL, len);
00848         At45dibRelease(sfi, b);
00849     }
00850     return rc;
00851 }
00852 
00866 static int SpiAt45dibCopy(NUTSERIALFLASH * sfi, sf_unit_t spg, sf_unit_t dpg)
00867 {
00868     int_fast8_t b;
00869 
00870     /* Sanity checks. */
00871     NUTASSERT(sfi != NULL);
00872     NUTASSERT(sfi->sf_info != NULL);
00873     NUTASSERT(spg < sfi->sf_units);
00874     NUTASSERT(dpg < sfi->sf_units);
00875 
00876     /* If source and destination page numbers are equal, just load it. */
00877     if (spg == dpg) {
00878         b = At45dibAllocate(sfi, spg);
00879     } else {
00880         AT45DIB *at = (AT45DIB *) sfi->sf_info;
00881 
00882         /* Invalidate any buffered destination. */
00883         for (b = 0; b < 2; b++) {
00884             if (at->dib_page[b] == dpg) {
00885                 /* But do not touch locked pages. */
00886                 if (at->dib_locks[b]) {
00887                     return -1;
00888                 }
00889                 at->dib_page[b] = SERIALFLASH_MAX_UNITS;
00890             }
00891         }
00892         /* Try to load the source page and make it the destination page. */
00893         b = At45dibAllocate(sfi, spg);
00894         if (b >= 0) {
00895             at->dib_page[b] = dpg;
00896         }
00897     }
00898     if (b >= 0) {
00899         At45dibRelease(sfi, b);
00900         return 0;
00901     }
00902     return -1;
00903 }
00904 
00917 static int SpiAt45dibCommit(NUTSERIALFLASH * sfi, sf_unit_t pgn)
00918 {
00919     int rc = 0;
00920     int_fast8_t b;
00921     AT45DIB *at;
00922 
00923     /* Sanity checks. */
00924     NUTASSERT(sfi != NULL);
00925     NUTASSERT(pgn < sfi->sf_units);
00926 
00927     at = (AT45DIB *) sfi->sf_info;
00928     for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
00929     if (b < 2 && (at->flags[b] & AT45DIB_FDIRTY) == AT45DIB_FDIRTY) {
00930         rc = At45dibFlash(sfi, b);
00931     }
00932     return rc;
00933 }
00934 
00944 static int SpiAt45dibErase(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
00945 {
00946     int_fast8_t b;
00947     AT45DIB *at;
00948 
00949     /* Sanity checks. */
00950     NUTASSERT(sfi != NULL);
00951     NUTASSERT(pgn + cnt <= sfi->sf_units);
00952     NUTASSERT(cnt >= 0);
00953 
00954     at = (AT45DIB *) sfi->sf_info;
00955     /* Invalidate any buffered page. */
00956     for (b = 0; b < 2; b++) {
00957         if (at->dib_page[b] >= pgn && at->dib_page[b] < pgn + cnt) {
00958             at->dib_page[b] = SERIALFLASH_MAX_UNITS;
00959             at->flags[b] &= ~AT45DIB_FDIRTY;
00960         }
00961     }
00962     while (cnt--) {
00963         uint32_t pga = pgn + cnt;
00964 
00965         pga <<= at->dib_pshft;
00966         if (At45dibCommand(sfi->sf_node, DFCMD_PAGE_ERASE, pga, 4)) {
00967             return -1;
00968         }
00969         if (At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS)) {
00970             return -1;
00971         }
00972     }
00973     return 0;
00974 }
00975 
00976 #ifndef SPI_RATE_AT45D0
00977 #define SPI_RATE_AT45D0  33000000
00978 #endif
00979 
00980 #ifndef SPI_MODE_AT45D0
00981 
00982 #ifdef SPI_CSHIGH_AT45D0
00983 #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH)
00984 #else
00985 #define SPI_MODE_AT45D0 SPI_MODE_3
00986 #endif
00987 
00988 #elif defined(SPI_CSHIGH_AT45D0)
00989 
00990 /* This is a tricky problem. Originally we didn't provide mode settings
00991 ** in the Configurator, but used mode 3 only. After experiencing problems
00992 ** with mode switching on the EIR, we need to set mode 0 for that board,
00993 ** which spoils our chip select polarity setting. */
00994 #if SPI_MODE_AT45D0 == SPI_MODE_0
00995 #undef SPI_MODE_AT45D0
00996 #define SPI_MODE_AT45D0 (SPI_MODE_0 | SPI_MODE_CSHIGH)
00997 #elif SPI_MODE_AT45D0 == SPI_MODE_3
00998 #undef SPI_MODE_AT45D0
00999 #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH)
01000 #endif
01001 
01002 #endif /* SPI_MODE_AT45D0 */
01003 
01007 static NUTSPINODE spiNode0 = {
01008     NULL,                       
01009     NULL,                       
01010     SPI_RATE_AT45D0,            
01011     SPI_MODE_AT45D0,            
01012     8,                          
01013     0                           
01014 };
01015 
01016 #ifndef MOUNT_OFFSET_AT45DIB0
01017 #ifdef MOUNT_OFFSET_AT45D0
01018 #define MOUNT_OFFSET_AT45DIB0       MOUNT_OFFSET_AT45D0
01019 #else
01020 #define MOUNT_OFFSET_AT45DIB0       0
01021 #endif
01022 #endif
01023 
01024 #ifndef MOUNT_TOP_RESERVE_AT45DIB0
01025 #ifdef MOUNT_TOP_RESERVE_AT45D0
01026 #define MOUNT_TOP_RESERVE_AT45DIB0  MOUNT_TOP_RESERVE_AT45D0
01027 #else
01028 #define MOUNT_TOP_RESERVE_AT45DIB0  1
01029 #endif
01030 #endif
01031 
01035 NUTSERIALFLASH flashAt45dib0 = {
01036     &spiNode0,                  
01037     NULL,                       
01038     0,                          
01039     0,                          
01040     MOUNT_OFFSET_AT45DIB0,      
01041     MOUNT_TOP_RESERVE_AT45DIB0, 
01042     SpiAt45dibInit,             
01043     SpiAt45dibExit,             
01044     SpiAt45dibCheck,            
01045     SpiAt45dibRead,             
01046     SpiAt45dibCompare,          
01047     SpiAt45dibUsed,             
01048     SpiAt45dibWrite,            
01049     SpiAt45dibCopy,             
01050     SpiAt45dibCommit,           
01051     SpiAt45dibErase             
01052 };
01053 
01054 #ifndef SPI_RATE_AT45D1
01055 #define SPI_RATE_AT45D1  33000000
01056 #endif
01057 
01058 #ifndef SPI_MODE_AT45D1
01059 
01060 #ifdef SPI_CSHIGH_AT45D1
01061 #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH)
01062 #else
01063 #define SPI_MODE_AT45D1 SPI_MODE_3
01064 #endif
01065 
01066 #elif defined(SPI_CSHIGH_AT45D1)
01067 
01068 /* Same problem problem as above. */
01069 #if SPI_MODE_AT45D1 == SPI_MODE_0
01070 #undef SPI_MODE_AT45D1
01071 #define SPI_MODE_AT45D1 (SPI_MODE_0 | SPI_MODE_CSHIGH)
01072 #elif SPI_MODE_AT45D1 == SPI_MODE_3
01073 #undef SPI_MODE_AT45D1
01074 #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH)
01075 #endif
01076 
01077 #endif /* SPI_MODE_AT45D0 */
01078 
01082 static NUTSPINODE spiNode1 = {
01083     NULL,                       
01084     NULL,                       
01085     SPI_RATE_AT45D1,            
01086     SPI_MODE_AT45D1,            
01087     8,                          
01088     1                           
01089 };
01090 
01091 #ifndef MOUNT_OFFSET_AT45DIB1
01092 #ifdef MOUNT_OFFSET_AT45D1
01093 #define MOUNT_OFFSET_AT45DIB1       MOUNT_OFFSET_AT45D1
01094 #else
01095 #define MOUNT_OFFSET_AT45DIB1       0
01096 #endif
01097 #endif
01098 
01099 #ifndef MOUNT_TOP_RESERVE_AT45DIB1
01100 #ifdef MOUNT_TOP_RESERVE_AT45D1
01101 #define MOUNT_TOP_RESERVE_AT45DIB1  MOUNT_TOP_RESERVE_AT45D1
01102 #else
01103 #define MOUNT_TOP_RESERVE_AT45DIB1  1
01104 #endif
01105 #endif
01106 
01110 NUTSERIALFLASH flashAt45dib1 = {
01111     &spiNode1,                  
01112     NULL,                       
01113     0,                          
01114     0,                          
01115     MOUNT_OFFSET_AT45DIB1,      
01116     MOUNT_TOP_RESERVE_AT45DIB1, 
01117     SpiAt45dibInit,             
01118     SpiAt45dibExit,             
01119     SpiAt45dibCheck,            
01120     SpiAt45dibRead,             
01121     SpiAt45dibCompare,          
01122     SpiAt45dibUsed,             
01123     SpiAt45dibWrite,            
01124     SpiAt45dibCopy,             
01125     SpiAt45dibCommit,           
01126     SpiAt45dibErase             
01127 };
01128