/* * 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_node_at45d.c * \brief Low level routines for Adesto/Atmel AT45D SPI Flash. * * \verbatim * $Id$ * \endverbatim */ #include #include #include #include #include #include #include /*! * \addtogroup xgSpiNodeAt45d */ /*@{*/ typedef struct _AT45D_DCB { HANDLE dcb_lock; } AT45D_DCB; static AT45D_DCB dcbAt45d0 = { SIGNALED }; static AT45D_DCB dcbAt45d1 = { SIGNALED }; static AT45D_DCB dcbAt45d2 = { SIGNALED }; static AT45D_DCB dcbAt45d3 = { SIGNALED }; int At45dNodeLock(NUTSPINODE * node) { AT45D_DCB *dcb = (AT45D_DCB *) node->node_dcb; return NutEventWait(&dcb->dcb_lock, NUT_WAIT_INFINITE); } void At45dNodeUnlock(NUTSPINODE * node) { AT45D_DCB *dcb = (AT45D_DCB *) node->node_dcb; NutEventPost(&dcb->dcb_lock); } /*! * \brief Transmit DataFlash command. * * \param node Specifies the SPI node. * \param op Command code. * \param parm Command parameter. * \param oplen Command length. * * \return 0 on success, -1 on errors. */ static int At45dNodeTransmitCmd(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen) { uint8_t cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; NUTASSERT(oplen <= sizeof(cmd)); cmd[0] = op; if (parm) { cmd[1] = (uint8_t) (parm >> 16); cmd[2] = (uint8_t) (parm >> 8); cmd[3] = (uint8_t) parm; } return (*((NUTSPIBUS *) node->node_bus)->bus_transfer) (node, cmd, NULL, oplen); } /*! * \brief Execute DataFlash command with data transfer. * * \param node Specifies the SPI node. * \param op Command code. * \param parm Command parameter. * \param oplen Command length. * \param txbuf Pointer to the transmit data buffer, may be set to NULL. * \param rxbuf Pointer to the receive data buffer, may be set to NULL. * \param xlen Number of byte to receive and/or transmit. * * \return 0 on success, -1 on errors. */ int At45dNodeTransfer(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, const void *txbuf, void *rxbuf, int xlen) { int rc; NUTSPIBUS *bus; /* Sanity checks. */ NUTASSERT(node != NULL); bus = (NUTSPIBUS *) node->node_bus; NUTASSERT(bus != NULL); NUTASSERT(bus->bus_alloc != NULL); NUTASSERT(bus->bus_transfer != NULL); NUTASSERT(bus->bus_release != NULL); rc = (*bus->bus_alloc) (node, 0); if (rc == 0) { rc = At45dNodeTransmitCmd(node, op, parm, oplen); if (rc == 0 && xlen) { rc = (*bus->bus_transfer) (node, txbuf, rxbuf, xlen); } (*bus->bus_release) (node); } return rc; } /*! * \brief Execute DataFlash command without data. * * \param node Specifies the SPI node. * \param op Command operation code. * \param parm Optional command parameter. * \param oplen Command length. * * \return 0 on success, -1 on errors. */ int At45dNodeCommand(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen) { return At45dNodeTransfer(node, op, parm, oplen, NULL, NULL, 0); } /*! * \brief Query the status of the serial flash. * * \param node Specifies the SPI node. * * \return 0 on success or -1 in case of an error. */ uint8_t At45dNodeStatus(NUTSPINODE * node) { int rc; uint8_t cmd[2] = { DFCMD_READ_STATUS, 0xFF }; NUTSPIBUS *bus; /* Sanity checks. */ NUTASSERT(node != NULL); NUTASSERT(node->node_bus != NULL); bus = (NUTSPIBUS *) node->node_bus; NUTASSERT(bus->bus_alloc != NULL); NUTASSERT(bus->bus_transfer != NULL); NUTASSERT(bus->bus_wait != NULL); NUTASSERT(bus->bus_release != NULL); rc = (*bus->bus_alloc) (node, 0); if (rc == 0) { rc = (*bus->bus_transfer) (node, cmd, cmd, 2); if (rc == 0) { (*bus->bus_wait) (node, NUT_WAIT_INFINITE); rc = cmd[1]; } (*bus->bus_release) (node); } return (uint8_t) rc; } /*! * \brief Wait until DataFlash memory cycle finished. * * \param node Specifies the SPI node. * \param tmo Number of loops (or milliseconds) to wait at max. * \param poll If 0, the current thread will be suspended for * 1 millisecond between each retry. Otherwise the * polling loop will not sleep, but may be still suspended * by the lower level SPI bus driver. * * \return 0 on success or -1 in case of an error. */ int At45dNodeWaitReady(NUTSPINODE * node, uint32_t tmo, int poll) { uint8_t sr; while (((sr = At45dNodeStatus(node)) & 0x80) == 0) { if (!poll) { NutSleep(1); } if (tmo-- == 0) { return -1; } } return 0; } /*! * \brief Determine the DataFlash type. * * If the board contains a known DataFlash chip, but couldn't be * determined by this routine, then most probably the chip select * configuration is wrong. * * \param node Specifies the SPI node. * * \return Pointer to a \ref AT45D_INFO structure, which contains * the relevant DataFlash parameters. If no known DataFlash * is available, then NULL is returned. */ AT45D_INFO *At45dNodeProbe(NUTSPINODE * node) { int_fast8_t i; uint8_t sr; if (At45dNodeWaitReady(node, 10, 1) == 0) { sr = At45dNodeStatus(node); if (sr != 0xff) { sr &= AT45D_STATUS_DENSITY | AT45D_STATUS_PAGE_SIZE; for (i = at45d_known_types; --i >= 0;) { if (sr == at45d_info[i].at45d_srval) { return &at45d_info[i]; } } } } return NULL; } #ifndef SPI_RATE_AT45D0 #define SPI_RATE_AT45D0 33000000 #endif #ifndef SPI_MODE_AT45D0 #ifdef SPI_CSHIGH_AT45D0 #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH) #else #define SPI_MODE_AT45D0 SPI_MODE_3 #endif #elif defined(SPI_CSHIGH_AT45D0) /* This is a tricky problem. Originally we didn't provide mode settings ** in the Configurator, but used mode 3 only. After experiencing problems ** with mode switching on the EIR, we need to set mode 0 for that board, ** which spoils our chip select polarity setting. */ #if SPI_MODE_AT45D0 == SPI_MODE_0 #undef SPI_MODE_AT45D0 #define SPI_MODE_AT45D0 (SPI_MODE_0 | SPI_MODE_CSHIGH) #elif SPI_MODE_AT45D0 == SPI_MODE_3 #undef SPI_MODE_AT45D0 #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH) #endif #endif /* SPI_MODE_AT45D0 */ /*! * \brief First AT45D DataFlash SPI node implementation structure. */ NUTSPINODE nodeAt45d0 = { NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */ NULL, /*!< \brief Pointer to the bus device driver specific settings, node_stat. */ SPI_RATE_AT45D0, /*!< \brief Initial clock rate, node_rate. */ SPI_MODE_AT45D0, /*!< \brief Initial mode, node_mode. */ 8, /*!< \brief Initial data bits, node_bits. */ 0, /*!< \brief Chip select, node_cs. */ &dcbAt45d0 /*!< \brief Pointer to our private device control block, node_dcb. */ }; #ifndef SPI_RATE_AT45D1 #define SPI_RATE_AT45D1 33000000 #endif #ifndef SPI_MODE_AT45D1 #ifdef SPI_CSHIGH_AT45D1 #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH) #else #define SPI_MODE_AT45D1 SPI_MODE_3 #endif #elif defined(SPI_CSHIGH_AT45D1) /* Same problem problem as above. */ #if SPI_MODE_AT45D1 == SPI_MODE_0 #undef SPI_MODE_AT45D1 #define SPI_MODE_AT45D1 (SPI_MODE_0 | SPI_MODE_CSHIGH) #elif SPI_MODE_AT45D1 == SPI_MODE_3 #undef SPI_MODE_AT45D1 #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH) #endif #endif /* SPI_MODE_AT45D1 */ /*! * \brief Second AT45D DataFlash SPI node implementation structure. */ NUTSPINODE nodeAt45d1 = { NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */ NULL, /*!< \brief Pointer to the bus device driver specific settings, node_stat. */ SPI_RATE_AT45D1, /*!< \brief Initial clock rate, node_rate. */ SPI_MODE_AT45D1, /*!< \brief Initial mode, node_mode. */ 8, /*!< \brief Initial data bits, node_bits. */ 0, /*!< \brief Chip select, node_cs. */ &dcbAt45d1 /*!< \brief Pointer to our private device control block, node_dcb. */ }; #ifndef SPI_RATE_AT45D2 #define SPI_RATE_AT45D2 33000000 #endif #ifndef SPI_MODE_AT45D2 #ifdef SPI_CSHIGH_AT45D2 #define SPI_MODE_AT45D2 (SPI_MODE_3 | SPI_MODE_CSHIGH) #else #define SPI_MODE_AT45D2 SPI_MODE_3 #endif #elif defined(SPI_CSHIGH_AT45D2) /* Same problem problem as above. */ #if SPI_MODE_AT45D2 == SPI_MODE_0 #undef SPI_MODE_AT45D2 #define SPI_MODE_AT45D2 (SPI_MODE_0 | SPI_MODE_CSHIGH) #elif SPI_MODE_AT45D2 == SPI_MODE_3 #undef SPI_MODE_AT45D2 #define SPI_MODE_AT45D2 (SPI_MODE_3 | SPI_MODE_CSHIGH) #endif #endif /* SPI_MODE_AT45D2 */ /*! * \brief Third AT45D DataFlash SPI node implementation structure. */ NUTSPINODE nodeAt45d2 = { NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */ NULL, /*!< \brief Pointer to the bus device driver specific settings, node_stat. */ SPI_RATE_AT45D2, /*!< \brief Initial clock rate, node_rate. */ SPI_MODE_AT45D2, /*!< \brief Initial mode, node_mode. */ 8, /*!< \brief Initial data bits, node_bits. */ 0, /*!< \brief Chip select, node_cs. */ &dcbAt45d2 /*!< \brief Pointer to our private device control block, node_dcb. */ }; #ifndef SPI_RATE_AT45D3 #define SPI_RATE_AT45D3 33000000 #endif #ifndef SPI_MODE_AT45D3 #ifdef SPI_CSHIGH_AT45D3 #define SPI_MODE_AT45D3 (SPI_MODE_3 | SPI_MODE_CSHIGH) #else #define SPI_MODE_AT45D3 SPI_MODE_3 #endif #elif defined(SPI_CSHIGH_AT45D3) /* Same problem problem as above. */ #if SPI_MODE_AT45D3 == SPI_MODE_0 #undef SPI_MODE_AT45D3 #define SPI_MODE_AT45D3 (SPI_MODE_0 | SPI_MODE_CSHIGH) #elif SPI_MODE_AT45D3 == SPI_MODE_3 #undef SPI_MODE_AT45D3 #define SPI_MODE_AT45D3 (SPI_MODE_3 | SPI_MODE_CSHIGH) #endif #endif /* SPI_MODE_AT45D3 */ /*! * \brief Forth AT45D DataFlash SPI node implementation structure. */ NUTSPINODE nodeAt45d3 = { NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */ NULL, /*!< \brief Pointer to the bus device driver specific settings, node_stat. */ SPI_RATE_AT45D3, /*!< \brief Initial clock rate, node_rate. */ SPI_MODE_AT45D3, /*!< \brief Initial mode, node_mode. */ 8, /*!< \brief Initial data bits, node_bits. */ 0, /*!< \brief Chip select, node_cs. */ &dcbAt45d3 /*!< \brief Pointer to our private device control block, node_dcb. */ }; /*@}*/