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 
00063 AT45D_INFO at45d_info[] = {
00064     {8, 512, 256, 0x0D},    /* AT45DB011B - 128kB */
00065     {9, 512, 264, 0x0C},    /* AT45DB011B - 128kB */
00066     {8, 1025, 256, 0x15},   /* AT45DB021B - 256kB */
00067     {9, 1025, 264, 0x14},   /* AT45DB021B - 256kB */
00068     {8, 2048, 256, 0x1D},   /* AT45DB041B - 512kB */
00069     {9, 2048, 264, 0x1C},   /* AT45DB041B - 512kB */
00070     {8, 4096, 256, 0x25},   /* AT45DB081B - 1MB */
00071     {9, 4096, 264, 0x24},   /* AT45DB081B - 1MB */
00072     {9, 4096, 512, 0x2D},   /* AT45DB0161B - 2MB */
00073     {10, 4096, 528, 0x2C},  /* AT45DB0161B - 2MB */
00074     {9, 8192, 512, 0x35},   /* AT45DB0321B - 4MB */
00075     {10, 8192, 528, 0x34},  /* AT45DB0321B - 4MB */
00076     {10, 8192, 1024, 0x39}, /* AT45DB0642 - 8MB */
00077     {11, 8192, 1056, 0x38}, /* AT45DB0642 - 8MB */
00078     {10, 8192, 1024, 0x3D}, /* AT45DB0642D - 8MB */
00079     {11, 8192, 1056, 0x3C}  /* AT45DB0642D - 8MB */
00080 };
00081 
00083 uint_fast8_t at45d_known_types = sizeof(at45d_info) / sizeof(AT45D_INFO);
00084 
00096 static int At45dCommand(NUTSPINODE * node, uint8_t op, uint32_t parm, int oplen, CONST void *txbuf, void *rxbuf, int xlen)
00097 {
00098     int rc = -1;
00099     NUTSPIBUS *bus;
00100     uint8_t cmd[8];
00101 
00102     NUTASSERT(node != NULL);
00103     bus = (NUTSPIBUS *) node->node_bus;
00104     NUTASSERT(bus != NULL);
00105     NUTASSERT(bus->bus_alloc != NULL);
00106     NUTASSERT(bus->bus_transfer != NULL);
00107     NUTASSERT(bus->bus_release != NULL);
00108 
00109     NUTASSERT(oplen <= 8);
00110     memset(cmd, 0, oplen);
00111     cmd[0] = op;
00112     if (parm) {
00113         cmd[1] = (uint8_t) (parm >> 16);
00114         cmd[2] = (uint8_t) (parm >> 8);
00115         cmd[3] = (uint8_t) parm;
00116     }
00117     rc = (*bus->bus_alloc) (node, 1000);
00118     if (rc == 0) {
00119         rc = (*bus->bus_transfer) (node, cmd, NULL, oplen);
00120         if (rc == 0 && xlen) {
00121             rc = (*bus->bus_transfer) (node, txbuf, rxbuf, xlen);
00122         }
00123         (*bus->bus_release) (node);
00124     }
00125     return rc;
00126 }
00127 
00135 static uint8_t At45dStatus(NUTSPINODE * node)
00136 {
00137     int rc;
00138     uint8_t cmd[2] = { DFCMD_READ_STATUS, 0xFF };
00139     NUTSPIBUS *bus;
00140 
00141     NUTASSERT(node != NULL);
00142     NUTASSERT(node->node_bus != NULL);
00143     bus = (NUTSPIBUS *) node->node_bus;
00144 
00145     NUTASSERT(bus->bus_alloc != NULL);
00146     NUTASSERT(bus->bus_transfer != NULL);
00147     NUTASSERT(bus->bus_wait != NULL);
00148     NUTASSERT(bus->bus_release != NULL);
00149 
00150     rc = (*bus->bus_alloc) (node, 1000);
00151     if (rc == 0) {
00152         rc = (*bus->bus_transfer) (node, cmd, cmd, 2);
00153         if (rc == 0) {
00154             (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
00155             rc = cmd[1];
00156         }
00157         (*bus->bus_release) (node);
00158     }
00159     return (uint8_t) rc;
00160 }
00161 
00169 static int At45dWaitReady(NUTSPINODE * node, uint32_t tmo, int poll)
00170 {
00171     uint8_t sr;
00172 
00173     while (((sr = At45dStatus(node)) & 0x80) == 0) {
00174         if (!poll) {
00175             NutSleep(1);
00176         }
00177         if (tmo-- == 0) {
00178             return -1;
00179         }
00180     }
00181     return 0;
00182 }
00183 
00195 int SpiAt45dCommand(NUTDEVICE * dev, uint8_t op, uint32_t parm, int oplen, CONST void *txbuf, void *rxbuf, int xlen)
00196 {
00197     NUTASSERT(dev != NULL);
00198     return At45dCommand(dev->dev_icb, op, parm, oplen, txbuf, rxbuf, xlen);
00199 }
00200 
00208 uint8_t SpiAt45dStatus(NUTDEVICE * dev)
00209 {
00210     NUTASSERT(dev != NULL);
00211     return At45dStatus(dev->dev_icb);
00212 }
00213 
00221 int SpiAt45dWaitReady(NUTDEVICE * dev, uint32_t tmo, int poll)
00222 {
00223     NUTASSERT(dev != NULL);
00224     return At45dWaitReady(dev->dev_icb, tmo, poll);
00225 }
00226 
00235 int SpiAt45dPageErase(NUTDEVICE * dev, uint32_t pgn)
00236 {
00237     NUTBLOCKIO *blkio;
00238     AT45D_INFO *info;
00239 
00240     NUTASSERT(dev != NULL);
00241     NUTASSERT(dev->dev_dcb != NULL);
00242     blkio = dev->dev_dcb;
00243     
00244     info = (AT45D_INFO *) blkio->blkio_info;
00245     NUTASSERT(blkio->blkio_info != NULL);
00246 
00247     if (pgn >= info->at45d_pages) {
00248         return -1;
00249     }
00250     pgn <<= info->at45d_pshft;
00251     return At45dCommand((NUTSPINODE *) dev->dev_icb, DFCMD_PAGE_ERASE, pgn, 4, NULL, NULL, 0);
00252 }
00253 
00257 int SpiAt45dChipErase(NUTDEVICE * dev)
00258 {
00259     return -1;
00260 }
00261 
00274 int SpiAt45dInit(NUTDEVICE * dev)
00275 {
00276     NUTBLOCKIO *blkio;
00277     NUTSPINODE *node;
00278     uint8_t sr;
00279     int_fast8_t i;
00280 
00281     NUTASSERT(dev != NULL);
00282     NUTASSERT(dev->dev_dcb != NULL);
00283     NUTASSERT(dev->dev_icb != NULL);
00284     blkio = dev->dev_dcb;
00285     node = dev->dev_icb;
00286 
00287     /* Read the status byte and locate the related table entry. */
00288     sr = At45dStatus(node);
00289     sr &= AT45D_STATUS_DENSITY | AT45D_STATUS_PAGE_SIZE;
00290     for (i = at45d_known_types; --i >= 0;) {
00291         if (sr == at45d_info[i].at45d_srval) {
00292             /* Known DataFlash type. */
00293             blkio->blkio_info = &at45d_info[i];
00294             blkio->blkio_blk_cnt = at45d_info[i].at45d_pages;
00295             blkio->blkio_blk_siz = at45d_info[i].at45d_psize;
00296             return 0;
00297         }
00298     }
00299     /* Unknown DataFlash type. */
00300     return -1;
00301 }
00302 
00314 int SpiAt45dPageRead(NUTDEVICE * dev, uint32_t pgn, void *data, int len)
00315 {
00316     NUTBLOCKIO *blkio;
00317     AT45D_INFO *info;
00318 
00319     NUTASSERT(dev != NULL);
00320     NUTASSERT(dev->dev_dcb != NULL);
00321     blkio = dev->dev_dcb;
00322     
00323     info = (AT45D_INFO *) blkio->blkio_info;
00324     NUTASSERT(blkio->blkio_info != NULL);
00325 
00326     if (pgn >= info->at45d_pages) {
00327         return -1;
00328     }
00329     pgn <<= info->at45d_pshft;
00330 
00331     if (At45dCommand((NUTSPINODE *) dev->dev_icb, DFCMD_CONT_READ, pgn, 8, NULL, data, len)) {
00332         return -1;
00333     }
00334     return len;
00335 }
00336 
00354 int SpiAt45dPageWrite(NUTDEVICE * dev, uint32_t pgn, CONST void *data, int len)
00355 {
00356     int rc = -1;
00357     uint8_t *dp = (uint8_t *) data;
00358     int step;
00359     uint_fast8_t pshft;
00360     uint32_t limit;
00361     NUTBLOCKIO *blkio;
00362     NUTSPINODE *node;
00363 
00364     /* Sanity check. */
00365     if (len == 0) {
00366         return 0;
00367     }
00368     NUTASSERT(data != NULL);
00369     NUTASSERT(dev != NULL);
00370     NUTASSERT(dev->dev_dcb != NULL);
00371     NUTASSERT(dev->dev_icb != NULL);
00372     blkio = (NUTBLOCKIO *) dev->dev_dcb;
00373     node = (NUTSPINODE *) dev->dev_icb;
00374     NUTASSERT(blkio->blkio_info != NULL);
00375     pshft = ((AT45D_INFO *) blkio->blkio_info)->at45d_pshft;
00376     step = ((AT45D_INFO *) blkio->blkio_info)->at45d_psize;
00377     limit = ((AT45D_INFO *) blkio->blkio_info)->at45d_pages;
00378 
00379     while (len) {
00380         if (step > len) {
00381             step = len;
00382         }
00383         /* Copy data to the internal DataFlash RAM buffer. */
00384         if (At45dCommand(node, DFCMD_BUF1_WRITE, 0, 4, dp, NULL, step)) {
00385             break;
00386         }
00387         /* Erase the page and flash the RAM buffer contents. */
00388         if (At45dCommand(node, DFCMD_BUF1_FLASH, pgn << pshft, 4, NULL, NULL, 0)) {
00389             break;
00390         }
00391         if (At45dWaitReady(node, AT45_WRITE_POLLS, 1)) {
00392             break;
00393         }
00394         if (rc < 0) {
00395             rc = 0;
00396         }
00397         rc += step;
00398         dp += step;
00399         len -= step;
00400         if (++pgn >= limit) {
00401             break;
00402         }
00403     }
00404     return rc;
00405 }
00406 
00407 #ifdef __HARVARD_ARCH__
00408 int SpiAt45dPageWrite_P(NUTDEVICE * dev, uint32_t pgn, PGM_P data, int len)
00409 {
00410     return -1;
00411 }
00412 #endif
00413 
00421 uint32_t SpiAt45dPages(NUTDEVICE * dev)
00422 {
00423     NUTBLOCKIO *blkio;
00424 
00425     NUTASSERT(dev != NULL);
00426     NUTASSERT(dev->dev_dcb != NULL);
00427     blkio = dev->dev_dcb;
00428 
00429     NUTASSERT(blkio->blkio_info != NULL);
00430     return ((AT45D_INFO *) blkio->blkio_info)->at45d_pages;
00431 }
00432 
00440 int SpiAt45dPageSize(NUTDEVICE * dev)
00441 {
00442     NUTBLOCKIO *blkio;
00443 
00444     NUTASSERT(dev != NULL);
00445     NUTASSERT(dev->dev_dcb != NULL);
00446     blkio = dev->dev_dcb;
00447 
00448     NUTASSERT(blkio->blkio_info != NULL);
00449     return ((AT45D_INFO *) blkio->blkio_info)->at45d_psize;
00450 }
00451 
00469 int SpiAt45dIOCtl(NUTDEVICE * dev, int req, void *conf)
00470 {
00471     int rc = 0;
00472 
00473     switch (req) {
00474     case NUTBLKDEV_MEDIAAVAIL:
00475         /* Modification required for DataFlash cards. */
00476         {
00477             int *flg;
00478             NUTASSERT(conf != NULL);
00479             flg = (int *) conf;
00480             *flg = 1;
00481         }
00482         break;
00483     case NUTBLKDEV_MEDIACHANGE:
00484         /* Modification required for DataFlash cards. */
00485         {
00486             int *flg;
00487             NUTASSERT(conf != NULL);
00488             flg = (int *) conf;
00489             *flg = 0;
00490         }
00491         break;
00492     default:
00493         rc = -1;
00494         break;
00495     }
00496     return rc;
00497 }

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/