/* * Copyright (C) 2008-2009 by egnite GmbH * Copyright (C) 2006 by egnite Software 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/nvmem_at45d.c * \brief Non-volatile memory support using Adesto/Atmel AT45 serial * DataFlash memory chips. * * \verbatim * $Id: nvmem_at45d.c 5227 2013-07-19 08:48:47Z u_bonnes $ * \endverbatim */ #include #include #include #include #include #include #include #include #include static NUTDEVICE *devSysConf; /*! * \brief Mimic initialization without actually registering the device. * * This is a little bit tricky. If we want to use the existing DataFlash * routines for accessing the system configuration, they must be * properly initialized. This is normally done by calling NutRegister... * in the application. However, the system configuration must be know * before entering any application code. * */ static int SpiAt45dConfigDevice(void) { #if !defined(DEV_SPIBUS) return -1; #else /* DEV_SPIBUS */ if (devSysConf == NULL) { NUTSPINODE *node; NUTDEVICE *dev; #if NUT_CONFIG_AT45D == 0 dev = &devSpiAt45d0; #elif NUT_CONFIG_AT45D == 1 dev = &devSpiAt45d1; #elif NUT_CONFIG_AT45D == 2 dev = &devSpiAt45d2; #elif NUT_CONFIG_AT45D == 3 dev = &devSpiAt45d3; #else return -1; #endif node = (NUTSPINODE *) dev->dev_icb; NUTASSERT(node != NULL); if (node->node_bus == NULL) { NUTSPIBUS *bus; bus = &DEV_SPIBUS; node->node_bus = bus; } #ifdef NUT_CONFIG_AT45D_CS node->node_cs = NUT_CONFIG_AT45D_CS; #endif NUTASSERT(node->node_bus->bus_initnode != NULL); if ((*node->node_bus->bus_initnode) (node)) { return -1; } NutEventPost(&node->node_bus->bus_mutex); if (SpiAt45dInit(dev)) { return -1; } devSysConf = dev; } return 0; #endif /* DEV_SPIBUS */ } static uint32_t SpiAt45dConfigPage(void) { #ifdef NUT_CONFIG_AT45D_PAGE return NUT_CONFIG_AT45D_PAGE; #else return SpiAt45dPages(devSysConf) - 1; #endif } /*! * \brief Get size of configuration area. * * A part of the DataFlash may be used to store configuration parameters, * like the network interface MAC address, the local hostname etc. The * size of this area may be configured by defining \ref NUT_CONFIG_AT45D_SIZE. * Otherwise one full page is used. * * \return The number of bytes available for configuration data. In case of * an error, 0 is returned. */ size_t SpiAt45dConfigSize(void) { size_t rc = 0; if (SpiAt45dConfigDevice() == 0) { #ifdef NUT_CONFIG_AT45D_SIZE rc = NUT_CONFIG_AT45D_SIZE; #else rc = (size_t) SpiAt45dPageSize(devSysConf); #endif } return rc; } /*! * \brief Load configuration parameters from flash memory. * * \param pos Start location within configuration sector. * \param data Points to a buffer that receives the contents. * \param len Number of bytes to read. * * \return 0 on success or -1 in case of an error. */ int SpiAt45dConfigRead(size_t pos, void *data, size_t len) { int rc = -1; uint8_t *buff; int csize = SpiAt45dConfigSize(); /* Load the complete configuration area. */ if (csize >= pos + len && (buff = malloc(csize)) != NULL) { uint32_t cpage = SpiAt45dConfigPage(); if (SpiAt45dPageRead(devSysConf, cpage, buff, csize) == csize) { /* Copy requested contents to caller's buffer. */ memcpy(data, buff + pos, len); rc = 0; } free(buff); } return rc; } /*! * \brief Store configuration parameters in flash memory. * * \param pos Start location within configuration sector. * \param data Points to a buffer that contains the bytes to store. * \param len Number of bytes to store. * * \return 0 on success or -1 in case of an error. */ int SpiAt45dConfigWrite(size_t pos, const void *data, size_t len) { int rc = -1; uint8_t *pbuff; uint8_t *data_buff = (uint8_t*) data; int csize = SpiAt45dConfigSize(); int psize = SpiAt45dPageSize(devSysConf); int page = SpiAt45dConfigPage(); int offset; int wsize; int remaining = len; if ((len > 0) && (csize >= pos + len) && (len > 0) && ((pbuff = malloc(psize)) != NULL)) { page += pos / psize; offset = pos % psize; wsize = (offset + len > psize) ? psize - offset : psize; if (remaining < wsize) { wsize = remaining; } while (remaining > 0) { /* Load one full page */ if (SpiAt45dPageRead(devSysConf, page, pbuff, psize) == psize) { /* Compare old with new contents. */ if (memcmp(pbuff + offset, data_buff, wsize)) { /* New contents differs. Copy it into the sector buffer. */ memcpy(pbuff + offset, data_buff, wsize); /* Erase sector and write new data. */ if (SpiAt45dPageWrite(devSysConf, page, pbuff, psize) == psize) { rc = 0; } else { rc = -1; break; } } else { rc = 0; } } else { rc = -1; break; } data_buff += wsize; remaining -= wsize; offset = 0; page ++; wsize = (remaining > psize) ? psize : remaining; } free(pbuff); } return rc; }