Nut/OS  4.10.3
API Reference
spi_at45d.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008-2009 by egnite GmbH
00003  * Copyright (C) 2006 by egnite Software GmbH
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00045 #include <cfg/os.h>
00046 #include <cfg/memory.h>
00047 
00048 #include <dev/blockdev.h>
00049 
00050 #include <sys/nutdebug.h>
00051 #include <sys/timer.h>
00052 
00053 #include <string.h>
00054 
00055 #include <dev/at45d.h>
00056 #include <dev/spi_at45d.h>
00057 
00058 #ifndef AT45_WRITE_POLLS
00059 #define AT45_WRITE_POLLS        1000
00060 #endif
00061 
00062 #if 0
00063 
00064 AT45D_INFO at45d_info[] = {
00065     {8, 512, 256, 0x0D},    /* AT45DB011B - 128kB */
00066     {9, 512, 264, 0x0C},    /* AT45DB011B - 128kB */
00067     {8, 1025, 256, 0x15},   /* AT45DB021B - 256kB */
00068     {9, 1025, 264, 0x14},   /* AT45DB021B - 256kB */
00069     {8, 2048, 256, 0x1D},   /* AT45DB041B - 512kB */
00070     {9, 2048, 264, 0x1C},   /* AT45DB041B - 512kB */
00071     {8, 4096, 256, 0x25},   /* AT45DB081B - 1MB */
00072     {9, 4096, 264, 0x24},   /* AT45DB081B - 1MB */
00073     {9, 4096, 512, 0x2D},   /* AT45DB0161B - 2MB */
00074     {10, 4096, 528, 0x2C},  /* AT45DB0161B - 2MB */
00075     {9, 8192, 512, 0x35},   /* AT45DB0321B - 4MB */
00076     {10, 8192, 528, 0x34},  /* AT45DB0321B - 4MB */
00077     {10, 8192, 1024, 0x39}, /* AT45DB0642 - 8MB */
00078     {11, 8192, 1056, 0x38}, /* AT45DB0642 - 8MB */
00079     {10, 8192, 1024, 0x3D}, /* AT45DB0642D - 8MB */
00080     {11, 8192, 1056, 0x3C}  /* AT45DB0642D - 8MB */
00081 };
00082 
00084 uint_fast8_t at45d_known_types = sizeof(at45d_info) / sizeof(AT45D_INFO);
00085 #endif
00086 
00098 static int At45dCommand(NUTSPINODE * node, uint8_t op, uint32_t parm, int oplen, CONST void *txbuf, void *rxbuf, int xlen)
00099 {
00100     int rc = -1;
00101     NUTSPIBUS *bus;
00102     uint8_t cmd[8];
00103 
00104     NUTASSERT(node != NULL);
00105     bus = (NUTSPIBUS *) node->node_bus;
00106     NUTASSERT(bus != NULL);
00107     NUTASSERT(bus->bus_alloc != NULL);
00108     NUTASSERT(bus->bus_transfer != NULL);
00109     NUTASSERT(bus->bus_release != NULL);
00110 
00111     NUTASSERT(oplen <= 8);
00112     memset(cmd, 0, oplen);
00113     cmd[0] = op;
00114     if (parm) {
00115         cmd[1] = (uint8_t) (parm >> 16);
00116         cmd[2] = (uint8_t) (parm >> 8);
00117         cmd[3] = (uint8_t) parm;
00118     }
00119     rc = (*bus->bus_alloc) (node, 1000);
00120     if (rc == 0) {
00121         rc = (*bus->bus_transfer) (node, cmd, NULL, oplen);
00122         if (rc == 0 && xlen) {
00123             rc = (*bus->bus_transfer) (node, txbuf, rxbuf, xlen);
00124         }
00125         (*bus->bus_release) (node);
00126     }
00127     return rc;
00128 }
00129 
00137 static uint8_t At45dStatus(NUTSPINODE * node)
00138 {
00139     int rc;
00140     uint8_t cmd[2] = { DFCMD_READ_STATUS, 0xFF };
00141     NUTSPIBUS *bus;
00142 
00143     NUTASSERT(node != NULL);
00144     NUTASSERT(node->node_bus != NULL);
00145     bus = (NUTSPIBUS *) node->node_bus;
00146 
00147     NUTASSERT(bus->bus_alloc != NULL);
00148     NUTASSERT(bus->bus_transfer != NULL);
00149     NUTASSERT(bus->bus_wait != NULL);
00150     NUTASSERT(bus->bus_release != NULL);
00151 
00152     rc = (*bus->bus_alloc) (node, 1000);
00153     if (rc == 0) {
00154         rc = (*bus->bus_transfer) (node, cmd, cmd, 2);
00155         if (rc == 0) {
00156             (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
00157             rc = cmd[1];
00158         }
00159         (*bus->bus_release) (node);
00160     }
00161     return (uint8_t) rc;
00162 }
00163 
00171 static int At45dWaitReady(NUTSPINODE * node, uint32_t tmo, int poll)
00172 {
00173     uint8_t sr;
00174 
00175     while (((sr = At45dStatus(node)) & 0x80) == 0) {
00176         if (!poll) {
00177             NutSleep(1);
00178         }
00179         if (tmo-- == 0) {
00180             return -1;
00181         }
00182     }
00183     return 0;
00184 }
00185 
00197 int SpiAt45dCommand(NUTDEVICE * dev, uint8_t op, uint32_t parm, int oplen, CONST void *txbuf, void *rxbuf, int xlen)
00198 {
00199     NUTASSERT(dev != NULL);
00200     return At45dCommand(dev->dev_icb, op, parm, oplen, txbuf, rxbuf, xlen);
00201 }
00202 
00210 uint8_t SpiAt45dStatus(NUTDEVICE * dev)
00211 {
00212     NUTASSERT(dev != NULL);
00213     return At45dStatus(dev->dev_icb);
00214 }
00215 
00223 int SpiAt45dWaitReady(NUTDEVICE * dev, uint32_t tmo, int poll)
00224 {
00225     NUTASSERT(dev != NULL);
00226     return At45dWaitReady(dev->dev_icb, tmo, poll);
00227 }
00228 
00237 int SpiAt45dPageErase(NUTDEVICE * dev, uint32_t pgn)
00238 {
00239     NUTBLOCKIO *blkio;
00240     AT45D_INFO *info;
00241 
00242     NUTASSERT(dev != NULL);
00243     NUTASSERT(dev->dev_dcb != NULL);
00244     blkio = dev->dev_dcb;
00245     
00246     info = (AT45D_INFO *) blkio->blkio_info;
00247     NUTASSERT(blkio->blkio_info != NULL);
00248 
00249     if (pgn >= info->at45d_pages) {
00250         return -1;
00251     }
00252     pgn <<= info->at45d_pshft;
00253     return At45dCommand((NUTSPINODE *) dev->dev_icb, DFCMD_PAGE_ERASE, pgn, 4, NULL, NULL, 0);
00254 }
00255 
00259 int SpiAt45dChipErase(NUTDEVICE * dev)
00260 {
00261     return -1;
00262 }
00263 
00276 int SpiAt45dInit(NUTDEVICE * dev)
00277 {
00278     NUTBLOCKIO *blkio;
00279     NUTSPINODE *node;
00280     uint8_t sr;
00281     int_fast8_t i;
00282 
00283     NUTASSERT(dev != NULL);
00284     NUTASSERT(dev->dev_dcb != NULL);
00285     NUTASSERT(dev->dev_icb != NULL);
00286     blkio = dev->dev_dcb;
00287     node = dev->dev_icb;
00288 
00289     if (At45dWaitReady(node, 10, 1) == 0) {
00290         /* Read the status byte and locate the related table entry. */
00291         sr = At45dStatus(node);
00292         if (sr != 0xff) {
00293             sr &= AT45D_STATUS_DENSITY | AT45D_STATUS_PAGE_SIZE;
00294             for (i = at45d_known_types; --i >= 0;) {
00295                 if (sr == at45d_info[i].at45d_srval) {
00296                     /* Known DataFlash type. */
00297                     blkio->blkio_info = (void *) &at45d_info[i];
00298                     blkio->blkio_blk_cnt = at45d_info[i].at45d_pages;
00299                     blkio->blkio_blk_siz = at45d_info[i].at45d_psize;
00300                     return 0;
00301                 }
00302             }
00303         }
00304     }
00305     /* Unknown DataFlash type. */
00306     return -1;
00307 }
00308 
00320 int SpiAt45dPageRead(NUTDEVICE * dev, uint32_t pgn, void *data, int len)
00321 {
00322     NUTBLOCKIO *blkio;
00323     AT45D_INFO *info;
00324 
00325     NUTASSERT(dev != NULL);
00326     NUTASSERT(dev->dev_dcb != NULL);
00327     blkio = dev->dev_dcb;
00328     
00329     info = (AT45D_INFO *) blkio->blkio_info;
00330     NUTASSERT(blkio->blkio_info != NULL);
00331 
00332     if (pgn >= info->at45d_pages) {
00333         return -1;
00334     }
00335     pgn <<= info->at45d_pshft;
00336 
00337     if (At45dCommand((NUTSPINODE *) dev->dev_icb, DFCMD_CONT_READ, pgn, 8, NULL, data, len)) {
00338         return -1;
00339     }
00340     return len;
00341 }
00342 
00360 int SpiAt45dPageWrite(NUTDEVICE * dev, uint32_t pgn, CONST void *data, int len)
00361 {
00362     int rc = -1;
00363     uint8_t *dp = (uint8_t *) data;
00364     int step;
00365     uint_fast8_t pshft;
00366     uint32_t limit;
00367     NUTBLOCKIO *blkio;
00368     NUTSPINODE *node;
00369 
00370     /* Sanity check. */
00371     if (len == 0) {
00372         return 0;
00373     }
00374     NUTASSERT(data != NULL);
00375     NUTASSERT(dev != NULL);
00376     NUTASSERT(dev->dev_dcb != NULL);
00377     NUTASSERT(dev->dev_icb != NULL);
00378     blkio = (NUTBLOCKIO *) dev->dev_dcb;
00379     node = (NUTSPINODE *) dev->dev_icb;
00380     NUTASSERT(blkio->blkio_info != NULL);
00381     pshft = ((AT45D_INFO *) blkio->blkio_info)->at45d_pshft;
00382     step = ((AT45D_INFO *) blkio->blkio_info)->at45d_psize;
00383     limit = ((AT45D_INFO *) blkio->blkio_info)->at45d_pages;
00384 
00385     while (len) {
00386         if (step > len) {
00387             step = len;
00388         }
00389         /* Copy data to the internal DataFlash RAM buffer. */
00390         if (At45dCommand(node, DFCMD_BUF1_WRITE, 0, 4, dp, NULL, step)) {
00391             break;
00392         }
00393         /* Erase the page and flash the RAM buffer contents. */
00394         if (At45dCommand(node, DFCMD_BUF1_FLASH, pgn << pshft, 4, NULL, NULL, 0)) {
00395             break;
00396         }
00397         if (At45dWaitReady(node, AT45_WRITE_POLLS, 1)) {
00398             break;
00399         }
00400         if (rc < 0) {
00401             rc = 0;
00402         }
00403         rc += step;
00404         dp += step;
00405         len -= step;
00406         if (++pgn >= limit) {
00407             break;
00408         }
00409     }
00410     return rc;
00411 }
00412 
00413 #ifdef __HARVARD_ARCH__
00414 int SpiAt45dPageWrite_P(NUTDEVICE * dev, uint32_t pgn, PGM_P data, int len)
00415 {
00416     return -1;
00417 }
00418 #endif
00419 
00427 uint32_t SpiAt45dPages(NUTDEVICE * dev)
00428 {
00429     NUTBLOCKIO *blkio;
00430 
00431     NUTASSERT(dev != NULL);
00432     NUTASSERT(dev->dev_dcb != NULL);
00433     blkio = dev->dev_dcb;
00434 
00435     NUTASSERT(blkio->blkio_info != NULL);
00436     return ((AT45D_INFO *) blkio->blkio_info)->at45d_pages;
00437 }
00438 
00446 int SpiAt45dPageSize(NUTDEVICE * dev)
00447 {
00448     NUTBLOCKIO *blkio;
00449 
00450     NUTASSERT(dev != NULL);
00451     NUTASSERT(dev->dev_dcb != NULL);
00452     blkio = dev->dev_dcb;
00453 
00454     NUTASSERT(blkio->blkio_info != NULL);
00455     return ((AT45D_INFO *) blkio->blkio_info)->at45d_psize;
00456 }
00457 
00475 int SpiAt45dIOCtl(NUTDEVICE * dev, int req, void *conf)
00476 {
00477     int rc = 0;
00478 
00479     switch (req) {
00480     case NUTBLKDEV_MEDIAAVAIL:
00481         /* Modification required for DataFlash cards. */
00482         {
00483             int *flg;
00484             NUTASSERT(conf != NULL);
00485             flg = (int *) conf;
00486             *flg = 1;
00487         }
00488         break;
00489     case NUTBLKDEV_MEDIACHANGE:
00490         /* Modification required for DataFlash cards. */
00491         {
00492             int *flg;
00493             NUTASSERT(conf != NULL);
00494             flg = (int *) conf;
00495             *flg = 0;
00496         }
00497         break;
00498     default:
00499         rc = -1;
00500         break;
00501     }
00502     return rc;
00503 }