/* * Copyright (C) 2008-2011 by egnite GmbH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /*! * \file dev/spi_blkio_at45d.c * \brief Low level block I/O routines for Adesto/Atmel AT45D SPI Flash. * * \verbatim * $Id$ * \endverbatim */ #include #include #include #include /*! * \addtogroup xgSpiBlockIoAt45d */ /*@{*/ /*! * \brief Initialize the block I/O interface. * * \param dev Specifies the blcok I/O device. * * \return 0 on success or -1 in case of an error. */ static int At45dBlkIoInit(NUTDEVICE * dev) { NUTBLOCKIO *blkio; NUTSPINODE *node; AT45D_INFO *df; NUTASSERT(dev != NULL); NUTASSERT(dev->dev_dcb != NULL); NUTASSERT(dev->dev_icb != NULL); blkio = dev->dev_dcb; node = dev->dev_icb; df = At45dNodeProbe(node); if (df) { /* Known DataFlash type detected. */ blkio->blkio_info = (void *) df; blkio->blkio_blk_cnt = df->at45d_pages; blkio->blkio_blk_siz = df->at45d_psize; return 0; } /* No known DataFlash type detected. */ return -1; } /*! * \brief Read data from DataFlash memory. * * \param dev Specifies the registered DataFlash device. * \param pgn Page number to read, starting at 0. * \param data Points to a buffer that receives the data. * \param len Number of bytes to read. * * \return The number of bytes actually read. A return value of -1 indicates * an error. */ static int At45dBlkIoRead(NUTDEVICE * dev, uint32_t pgn, void *data, int len) { NUTBLOCKIO *blkio; AT45D_INFO *info; NUTASSERT(dev != NULL); NUTASSERT(dev->dev_dcb != NULL); blkio = dev->dev_dcb; info = (AT45D_INFO *) blkio->blkio_info; NUTASSERT(blkio->blkio_info != NULL); if (pgn >= info->at45d_pages) { return -1; } pgn <<= info->at45d_pshft; if (At45dNodeLock((NUTSPINODE *) dev->dev_icb) || At45dNodeTransfer((NUTSPINODE *) dev->dev_icb, DFCMD_CONT_READ, pgn, 8, NULL, data, len)) { At45dNodeUnlock((NUTSPINODE *) dev->dev_icb); return -1; } At45dNodeUnlock((NUTSPINODE *) dev->dev_icb); return len; } /*! * \brief Write data to DataFlash memory. * * Each page will be automatically erased before writing the data. If the * last page is not completely filled with new data, the contents of * remaining bytes at the end of the page is undetermined. * * \param dev Specifies the registered DataFlash device. * \param pgn The page number. * \param data Points to the buffer that contains the bytes to be written. * \param len Number of bytes available in the buffer. This may be less * than the page size, in which case the remaining bytes of * the page will be set to 0xff. * * \return The number of bytes actually written. A return value of -1 indicates * an error. */ static int At45dBlkIoWrite(NUTDEVICE * dev, uint32_t pgn, const void *data, int len) { int rc = -1; uint8_t *dp = (uint8_t *) data; int step; uint_fast8_t pshft; uint32_t limit; NUTBLOCKIO *blkio; NUTSPINODE *node; /* Sanity check. */ if (len == 0) { return 0; } NUTASSERT(data != NULL); NUTASSERT(dev != NULL); NUTASSERT(dev->dev_dcb != NULL); NUTASSERT(dev->dev_icb != NULL); blkio = (NUTBLOCKIO *) dev->dev_dcb; node = (NUTSPINODE *) dev->dev_icb; NUTASSERT(blkio->blkio_info != NULL); pshft = ((AT45D_INFO *) blkio->blkio_info)->at45d_pshft; step = ((AT45D_INFO *) blkio->blkio_info)->at45d_psize; limit = ((AT45D_INFO *) blkio->blkio_info)->at45d_pages; while (len) { if (step > len) { step = len; } if (At45dNodeLock(node)) { break; } /* Copy data to the internal DataFlash RAM buffer. */ if (At45dNodeTransfer(node, DFCMD_BUF1_WRITE, 0, 4, dp, NULL, step)) { break; } /* Erase the page and flash the RAM buffer contents. */ if (At45dNodeCommand(node, DFCMD_BUF1_FLASH, pgn << pshft, 4)) { break; } if (At45dNodeWaitReady(node, AT45_WRITE_POLLS, 1)) { break; } At45dNodeUnlock(node); if (rc < 0) { rc = 0; } rc += step; dp += step; len -= step; if (++pgn >= limit) { break; } } return rc; } #ifdef __HARVARD_ARCH__ static int At45dBlkIoWrite_P(NUTDEVICE * dev, uint32_t pgn, PGM_P data, int len) { return -1; } #endif /*! * \brief Perform block I/O device control functions. * * This function is called by the ioctl() function of the C runtime * library. * * \param dev Identifies the device that receives the control command. * \param req Requested control command. May be set to one of the * following constants: * - \ref NUTBLKDEV_MEDIAAVAIL * - \ref NUTBLKDEV_MEDIACHANGE * * \param conf Points to a buffer that contains any data required for * the given control function or receives data from that * function. * \return 0 on success, -1 otherwise. */ static int At45dBlkIoCtl(NUTDEVICE * dev, int req, void *conf) { int rc = 0; switch (req) { case NUTBLKDEV_MEDIAAVAIL: /* Modification required for DataFlash cards. */ { int *flg; NUTASSERT(conf != NULL); flg = (int *) conf; *flg = 1; } break; case NUTBLKDEV_MEDIACHANGE: /* Modification required for DataFlash cards. */ { int *flg; NUTASSERT(conf != NULL); flg = (int *) conf; *flg = 0; } break; default: rc = -1; break; } return rc; } #ifndef BLKIO_MOUNT_OFFSET_AT45D0 #ifdef MOUNT_OFFSET_AT45D0 #define BLKIO_MOUNT_OFFSET_AT45D0 MOUNT_OFFSET_AT45D0 #else #define BLKIO_MOUNT_OFFSET_AT45D0 0 #endif #endif #ifndef BLKIO_MOUNT_TOP_RESERVE_AT45D0 #ifdef MOUNT_TOP_RESERVE_AT45D0 #define BLKIO_MOUNT_TOP_RESERVE_AT45D0 MOUNT_TOP_RESERVE_AT45D0 #else #define BLKIO_MOUNT_TOP_RESERVE_AT45D0 1 #endif #endif /*! * \brief First AT45D block I/O control implementation structure. */ static NUTBLOCKIO blkIoAt45d0 = { NULL, /*!< \brief Device specific parameters, blkio_info. */ 0, /*!< \brief Total number of pages, blkio_blk_cnt. */ 0, /*!< \brief Number of bytes per page, blkio_blk_siz. */ BLKIO_MOUNT_OFFSET_AT45D0, /*!< \brief Number of sectors reserved at bottom, blkio_vol_bot. */ BLKIO_MOUNT_TOP_RESERVE_AT45D0, /*!< \brief Number of sectors reserved at top, blkio_vol_top. */ At45dBlkIoRead, /*!< \brief Read from node, blkio_read. */ At45dBlkIoWrite, /*!< \brief Write to node, blkio_write. */ #ifdef __HARVARD_ARCH__ At45dBlkIoWrite_P, /*!< \brief Write program memory to node, blkio_write_P. */ #endif At45dBlkIoCtl /*!< \brief Control functions, blkio_ioctl. */ }; /*! * \brief First AT45D block I/O device implementation structure. */ NUTDEVICE devSpiBlkAt45d0 = { NULL, /*!< \brief Pointer to next device, dev_next. */ {'A', 'T', '4', '5', 'D', '0', 0, 0, 0}, /*!< \brief Unique device name, dev_name. */ IFTYP_BLKIO, /*!< \brief Type of device, dev_type. */ 0, /*!< \brief Base address, dev_base (not used). */ 0, /*!< \brief First interrupt number, dev_irq (not used). */ &nodeAt45d0, /*!< \brief Interface control block, dev_icb. */ &blkIoAt45d0, /*!< \brief Driver control block, dev_dcb. */ At45dBlkIoInit, /*!< \brief Driver initialization routine, dev_init. */ NutBlockDeviceIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */ NutBlockDeviceRead, /*!< \brief Read from device, dev_read. */ NutBlockDeviceWrite, /*!< \brief Write to device, dev_write. */ #ifdef __HARVARD_ARCH__ NutBlockDeviceWrite_P, /*!< \brief Write data from program space to device, dev_write_P. */ #endif NutBlockDeviceOpen, /*!< \brief Mount volume, dev_open. */ NutBlockDeviceClose, /*!< \brief Unmount volume, dev_close. */ NutBlockDeviceSize, /*!< \brief Request file size, dev_size. */ NULL, /*!< \brief Select function, optional, not yet implemented */ }; #ifndef BLKIO_MOUNT_OFFSET_AT45D1 #ifdef MOUNT_OFFSET_AT45D1 #define BLKIO_MOUNT_OFFSET_AT45D1 MOUNT_OFFSET_AT45D1 #else #define BLKIO_MOUNT_OFFSET_AT45D1 0 #endif #endif #ifndef BLKIO_MOUNT_TOP_RESERVE_AT45D1 #ifdef MOUNT_TOP_RESERVE_AT45D1 #define BLKIO_MOUNT_TOP_RESERVE_AT45D1 MOUNT_TOP_RESERVE_AT45D1 #else #define BLKIO_MOUNT_TOP_RESERVE_AT45D1 1 #endif #endif /*! * \brief Second AT45D block I/O control implementation structure. */ static NUTBLOCKIO blkIoAt45d1 = { NULL, /*!< \brief Device specific parameters, blkio_info. */ 0, /*!< \brief Total number of pages, blkio_blk_cnt. */ 0, /*!< \brief Number of bytes per page, blkio_blk_siz. */ BLKIO_MOUNT_OFFSET_AT45D1, /*!< \brief Number of sectors reserved at bottom, blkio_vol_bot. */ BLKIO_MOUNT_TOP_RESERVE_AT45D1, /*!< \brief Number of sectors reserved at top, blkio_vol_top. */ At45dBlkIoRead, /*!< \brief Read from node, blkio_read. */ At45dBlkIoWrite, /*!< \brief Write to node, blkio_write. */ #ifdef __HARVARD_ARCH__ At45dBlkIoWrite_P, /*!< \brief Write program memory to node, blkio_write_P. */ #endif At45dBlkIoCtl /*!< \brief Control functions, blkio_ioctl. */ }; /*! * \brief Second AT45D block I/O device implementation structure. */ NUTDEVICE devSpiBlkAt45d1 = { NULL, /*!< \brief Pointer to next device, dev_next. */ {'A', 'T', '4', '5', 'D', '1', 0, 0, 0}, /*!< \brief Unique device name, dev_name. */ IFTYP_BLKIO, /*!< \brief Type of device, dev_type. */ 0, /*!< \brief Base address, dev_base (not used). */ 0, /*!< \brief First interrupt number, dev_irq (not used). */ &nodeAt45d1, /*!< \brief Interface control block, dev_icb. */ &blkIoAt45d1, /*!< \brief Driver control block, dev_dcb. */ At45dBlkIoInit, /*!< \brief Driver initialization routine, dev_init. */ NutBlockDeviceIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */ NutBlockDeviceRead, /*!< \brief Read from device, dev_read. */ NutBlockDeviceWrite, /*!< \brief Write to device, dev_write. */ #ifdef __HARVARD_ARCH__ NutBlockDeviceWrite_P, /*!< \brief Write data from program space to device, dev_write_P. */ #endif NutBlockDeviceOpen, /*!< \brief Mount volume, dev_open. */ NutBlockDeviceClose, /*!< \brief Unmount volume, dev_close. */ NutBlockDeviceSize, /*!< \brief Request file size, dev_size. */ NULL, /*!< \brief Select function, optional, not yet implemented */ }; #ifndef BLKIO_MOUNT_OFFSET_AT45D2 #ifdef MOUNT_OFFSET_AT45D2 #define BLKIO_MOUNT_OFFSET_AT45D2 MOUNT_OFFSET_AT45D2 #else #define BLKIO_MOUNT_OFFSET_AT45D2 0 #endif #endif #ifndef BLKIO_MOUNT_TOP_RESERVE_AT45D2 #ifdef MOUNT_TOP_RESERVE_AT45D2 #define BLKIO_MOUNT_TOP_RESERVE_AT45D2 MOUNT_TOP_RESERVE_AT45D2 #else #define BLKIO_MOUNT_TOP_RESERVE_AT45D2 1 #endif #endif /*! * \brief Third AT45D block I/O control implementation structure. */ static NUTBLOCKIO blkIoAt45d2 = { NULL, /*!< \brief Device specific parameters, blkio_info. */ 0, /*!< \brief Total number of pages, blkio_blk_cnt. */ 0, /*!< \brief Number of bytes per page, blkio_blk_siz. */ BLKIO_MOUNT_OFFSET_AT45D2, /*!< \brief Number of sectors reserved at bottom, blkio_vol_bot. */ BLKIO_MOUNT_TOP_RESERVE_AT45D2, /*!< \brief Number of sectors reserved at top, blkio_vol_top. */ At45dBlkIoRead, /*!< \brief Read from node, blkio_read. */ At45dBlkIoWrite, /*!< \brief Write to node, blkio_write. */ #ifdef __HARVARD_ARCH__ At45dBlkIoWrite_P, /*!< \brief Write program memory to node, blkio_write_P. */ #endif At45dBlkIoCtl /*!< \brief Control functions, blkio_ioctl. */ }; /*! * \brief Third AT45D block I/O device implementation structure. */ NUTDEVICE devSpiBlkAt45d2 = { NULL, /*!< \brief Pointer to next device, dev_next. */ {'A', 'T', '4', '5', 'D', '2', 0, 0, 0}, /*!< \brief Unique device name, dev_name. */ IFTYP_BLKIO, /*!< \brief Type of device, dev_type. */ 0, /*!< \brief Base address, dev_base (not used). */ 0, /*!< \brief First interrupt number, dev_irq (not used). */ &nodeAt45d2, /*!< \brief Interface control block, dev_icb. */ &blkIoAt45d2, /*!< \brief Driver control block, dev_dcb. */ At45dBlkIoInit, /*!< \brief Driver initialization routine, dev_init. */ NutBlockDeviceIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */ NutBlockDeviceRead, /*!< \brief Read from device, dev_read. */ NutBlockDeviceWrite, /*!< \brief Write to device, dev_write. */ #ifdef __HARVARD_ARCH__ NutBlockDeviceWrite_P, /*!< \brief Write data from program space to device, dev_write_P. */ #endif NutBlockDeviceOpen, /*!< \brief Mount volume, dev_open. */ NutBlockDeviceClose, /*!< \brief Unmount volume, dev_close. */ NutBlockDeviceSize, /*!< \brief Request file size, dev_size. */ NULL, /*!< \brief Select function, optional, not yet implemented */ }; #ifndef BLKIO_MOUNT_OFFSET_AT45D3 #ifdef MOUNT_OFFSET_AT45D3 #define BLKIO_MOUNT_OFFSET_AT45D3 MOUNT_OFFSET_AT45D3 #else #define BLKIO_MOUNT_OFFSET_AT45D3 0 #endif #endif #ifndef BLKIO_MOUNT_TOP_RESERVE_AT45D3 #ifdef MOUNT_TOP_RESERVE_AT45D3 #define BLKIO_MOUNT_TOP_RESERVE_AT45D3 MOUNT_TOP_RESERVE_AT45D3 #else #define BLKIO_MOUNT_TOP_RESERVE_AT45D3 1 #endif #endif /*! * \brief Forth AT45D block I/O control implementation structure. */ static NUTBLOCKIO blkIoAt45d3 = { NULL, /*!< \brief Device specific parameters, blkio_info. */ 0, /*!< \brief Total number of pages, blkio_blk_cnt. */ 0, /*!< \brief Number of bytes per page, blkio_blk_siz. */ BLKIO_MOUNT_OFFSET_AT45D3, /*!< \brief Number of sectors reserved at bottom, blkio_vol_bot. */ BLKIO_MOUNT_TOP_RESERVE_AT45D3, /*!< \brief Number of sectors reserved at top, blkio_vol_top. */ At45dBlkIoRead, /*!< \brief Read from node, blkio_read. */ At45dBlkIoWrite, /*!< \brief Write to node, blkio_write. */ #ifdef __HARVARD_ARCH__ At45dBlkIoWrite_P, /*!< \brief Write program memory to node, blkio_write_P. */ #endif At45dBlkIoCtl /*!< \brief Control functions, blkio_ioctl. */ }; /*! * \brief Forth AT45D block I/O device implementation structure. */ NUTDEVICE devSpiBlkAt45d3 = { NULL, /*!< \brief Pointer to next device, dev_next. */ {'A', 'T', '4', '5', 'D', '3', 0, 0, 0}, /*!< \brief Unique device name, dev_name. */ IFTYP_BLKIO, /*!< \brief Type of device, dev_type. */ 0, /*!< \brief Base address, dev_base (not used). */ 0, /*!< \brief First interrupt number, dev_irq (not used). */ &nodeAt45d3, /*!< \brief Interface control block, dev_icb. */ &blkIoAt45d3, /*!< \brief Driver control block, dev_dcb. */ At45dBlkIoInit, /*!< \brief Driver initialization routine, dev_init. */ NutBlockDeviceIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */ NutBlockDeviceRead, /*!< \brief Read from device, dev_read. */ NutBlockDeviceWrite, /*!< \brief Write to device, dev_write. */ #ifdef __HARVARD_ARCH__ NutBlockDeviceWrite_P, /*!< \brief Write data from program space to device, dev_write_P. */ #endif NutBlockDeviceOpen, /*!< \brief Mount volume, dev_open. */ NutBlockDeviceClose, /*!< \brief Unmount volume, dev_close. */ NutBlockDeviceSize, /*!< \brief Request file size, dev_size. */ NULL, /*!< \brief Select function, optional, not yet implemented */ }; /*@}*/