/**************************************************************************** * drivers/mtd/at25ee.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTD_AT25EE /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifndef CONFIG_AT25EE_SPIMODE # define CONFIG_AT25EE_SPIMODE 0 #endif /* EEPROM commands * High bit of low nibble used for A8 in 25xx040/at25040 products */ #define AT25EE_CMD_WRSR 0x01 #define AT25EE_CMD_WRITE 0x02 #define AT25EE_CMD_READ 0x03 #define AT25EE_CMD_WRDIS 0x04 #define AT25EE_CMD_RDSR 0x05 #define AT25EE_CMD_WREN 0x06 /* Following commands will be available some day via IOCTLs * PE 0x42 Page erase (25xx512/1024) * SE 0xD8 Sector erase (25xx512/1024) * CE 0xC7 Chip erase (25xx512/1024) * RDID 0xAB Wake up and read electronic signature (25xx512/1024) * DPD 0xB9 Sleep (25xx512/1024) * * Identification page access for ST devices * RDID/RDLS 0x83 Read identification page / Read ID page lock status * WRID/LID 0x82 Write identification page / Lock ID page */ /* SR bits definitions */ #define AT25EE_SR_WIP 0x01 /* Write in Progress */ #define AT25EE_SR_WEL 0x02 /* Write Enable Latch */ #define AT25EE_SR_BP0 0x04 /* First Block Protect bit */ #define AT25EE_SR_BP1 0x08 /* Second Block Protect bit */ #define AT25EE_SR_WPEN 0x80 /* Write Protect Enable */ #define AT25EE_DUMMY 0xFF /* For applications where a file system is used on the AT25EE, the tiny page * sizes will result in very inefficient EEPROM usage. In such cases, it is * better if blocks are comprised of "clusters" of pages so that the file * system block size is, say, 256 or 512 bytes. * In any event, the block size *must* be an even multiple of the pages. */ /**************************************************************************** * Private Types ****************************************************************************/ /* Device geometry description, compact form (2 bytes per entry) */ struct at25ee_geom_s { uint8_t bytes : 4; /* Power of 2 of 128 bytes (0:128 1:256 2:512 etc) */ uint8_t pagesize : 4; /* Power of 2 of 8 bytes (0:8 1:16 2:32 3:64 etc) */ uint8_t addrlen : 4; /* Number of bytes in command address field */ uint8_t flags : 4; /* Addr. management for 25xx040, 1=A8 in inst */ }; /* This type represents the state of the MTD device. The struct mtd_dev_s * must appear at the beginning of the definition so that you can freely * cast between pointers to struct mtd_dev_s and struct at25ee_dev_s. */ struct at25ee_dev_s { struct mtd_dev_s mtd; /* MTD interface */ struct spi_dev_s *spi; /* SPI device where the EEPROM is attached */ uint32_t size; /* in bytes, expanded from geometry */ uint16_t pgsize; /* write block size, in bytes, expanded from * geometry */ uint16_t npages; /* numpages, derived from geometry */ uint16_t addrlen; /* number of BITS in data addresses */ uint16_t blocksize; /* Block sized to report */ mutex_t lock; /* file access serialization */ uint8_t readonly; /* Flags */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void at25ee_lock(FAR struct spi_dev_s *dev); /* MTD driver methods */ static int at25ee_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks); static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, FAR uint8_t *buf); static ssize_t at25ee_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, FAR const uint8_t *buf); static ssize_t at25ee_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR uint8_t *buf); static ssize_t at25ee_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR const uint8_t *buf); static int at25ee_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); static void at25ee_writepage(FAR struct at25ee_dev_s *priv, uint32_t devaddr, FAR const uint8_t *data, size_t len); static void at25ee_writeenable(FAR struct at25ee_dev_s *priv, int enable); static void at25ee_waitwritecomplete(struct at25ee_dev_s *priv); static void at25ee_sendcmd(FAR struct spi_dev_s *spi, uint8_t cmd, uint8_t addrlen, uint32_t addr); static inline void at25ee_unlock(FAR struct spi_dev_s *dev); static void at25ee_lock(FAR struct spi_dev_s *dev); /**************************************************************************** * Private Data ****************************************************************************/ /* Supported device geometries. * One geometry can fit more than one device. * The user will use an enum'd index from include/eeprom/spi_xx25xx.h */ static const struct at25ee_geom_s g_at25ee_devices[] = { /* Microchip devices */ { 0, 1, 1, 0 }, /* 25xx010A 128 16 1 */ { 1, 1, 1, 0 }, /* 25xx020A 256 16 1 */ { 2, 1, 1, 1 }, /* 25xx040 512 16 1+bit */ { 3, 1, 1, 0 }, /* 25xx080 1024 16 1 */ { 3, 2, 2, 0 }, /* 25xx080B 1024 32 2 */ { 4, 1, 2, 0 }, /* 25xx160 2048 16 2 */ { 4, 2, 2, 0 }, /* 25xx160B/D 2048 32 2 */ { 5, 2, 2, 0 }, /* 25xx320 4096 32 2 */ { 6, 2, 2, 0 }, /* 25xx640 8192 32 2 */ { 7, 3, 2, 0 }, /* 25xx128 16384 64 2 */ { 8, 3, 2, 0 }, /* 25xx256 32768 64 2 */ { 9, 4, 2, 0 }, /* 25xx512 65536 128 2 */ { 10, 5, 3, 0 }, /* 25xx1024 131072 256 3 */ /* Atmel devices */ { 0, 0, 1, 0 }, /* AT25010B 128 8 1 */ { 1, 0, 1, 0 }, /* AT25020B 256 8 1 */ { 2, 0, 1, 1 }, /* AT25040B 512 8 1+bit */ /* STM devices */ { 11, 5, 3, 0 }, /* M95M02 262144 256 3 */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: at25ee_lock * * Description: * On SPI buses where there are multiple devices, it will be necessary to * lock SPI to have exclusive access to the buses for a sequence of * transfers. The bus should be locked before the chip is selected. * * This is a blocking call and will not return until we have exclusive * access to the SPI bus. We will retain that exclusive access until the * bus is unlocked. * * After locking the SPI bus, the we also need call the setfrequency, * setbits, and setmode methods to make sure that the SPI is properly * configured for the device. If the SPI bus is being shared, then it may * have been left in an incompatible state. * * Input Parameters: * dev - pointer to device structure * Returned Value: * none * ****************************************************************************/ static void at25ee_lock(FAR struct spi_dev_s *dev) { SPI_LOCK(dev, true); SPI_SETMODE(dev, CONFIG_AT25EE_SPIMODE); SPI_SETBITS(dev, 8); SPI_HWFEATURES(dev, 0); SPI_SETFREQUENCY(dev, CONFIG_AT25EE_SPIFREQUENCY); #ifdef CONFIG_SPI_DELAY_CONTROL SPI_SETDELAY(dev, CONFIG_AT25EE_START_DELAY, CONFIG_AT25EE_STOP_DELAY, CONFIG_AT25EE_CS_DELAY, CONFIG_AT25EE_IFDELAY); #endif } /**************************************************************************** * Name: at25ee_unlock * * Description: * Unlocks the SPI bus * * Input Parameters: * dev - pointer to device structure * Returned Value: * none * ****************************************************************************/ static inline void at25ee_unlock(FAR struct spi_dev_s *dev) { SPI_LOCK(dev, false); } /**************************************************************************** * Name: at25ee_sendcmd * * Description: * Send command and address as one transaction to take advantage * of possible faster DMA transfers. * Sending byte per byte is MUCH slower. * * Input Parameters: * spi - a reference to the spi device * cmd - SPI command to send * addrlen - length of the address, in bits * addr - address to write to * * Returned Value: * none * ****************************************************************************/ static void at25ee_sendcmd(FAR struct spi_dev_s *spi, uint8_t cmd, uint8_t addrlen, uint32_t addr) { uint8_t buf[4]; int cmdlen = 1; /* Store command */ buf[0] = cmd; /* Store address according to its length */ if (addrlen == 9) { buf[0] |= (((addr >> 8) & 1) << 3); } if (addrlen > 16) { buf[cmdlen++] = (addr >> 16) & 0xff; } if (addrlen > 9) { buf[cmdlen++] = (addr >> 8) & 0xff; } buf[cmdlen++] = addr & 0xff; SPI_SNDBLOCK(spi, buf, cmdlen); } /**************************************************************************** * Name: at25ee_waitwritecomplete * * Description: * loop until the write operation is done. * * Input Parameters: * priv - a reference to the device structure * * Returned Value: * none * ****************************************************************************/ static void at25ee_waitwritecomplete(struct at25ee_dev_s *priv) { uint8_t status; /* Loop as long as the memory is busy with a write cycle */ do { /* Select this FLASH part */ at25ee_lock(priv->spi); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), true); /* Send "Read Status Register (RDSR)" command */ SPI_SEND(priv->spi, AT25EE_CMD_RDSR); /* Send a dummy byte to generate the clock needed to shift out the * status */ status = SPI_SEND(priv->spi, AT25EE_DUMMY); /* Deselect the FLASH */ SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), false); at25ee_unlock(priv->spi); /* Given that writing could take up to a few milliseconds, * the following short delay in the "busy" case will allow * other peripherals to access the SPI bus. */ if ((status & AT25EE_SR_WIP) != 0) { nxsig_usleep(1000); } } while ((status & AT25EE_SR_WIP) != 0); } /**************************************************************************** * Name: at25ee_writeenable * * Description: * Enable or disable write operations. * This is required before any write, since a lot of operations * automatically disable the write latch. * * Input Parameters: * priv - a reference to the device structure * enable - enable (true) or disable(false) write operations * * Returned Value: * none * ****************************************************************************/ static void at25ee_writeenable(FAR struct at25ee_dev_s *priv, int enable) { at25ee_lock(priv->spi); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), true); SPI_SEND(priv->spi, enable ? AT25EE_CMD_WREN : AT25EE_CMD_WRDIS); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), false); at25ee_unlock(priv->spi); } /**************************************************************************** * Name: at25ee_writepage * * Description: * Write data to the EEPROM, NOT crossing page boundaries. * * Input Parameters: * priv - a reference to the device structure * devaddr - the address to start the write * data - pointer to data buffer to write * len - length of the data to write * * Returned Value: * none * ****************************************************************************/ static void at25ee_writepage(FAR struct at25ee_dev_s *priv, uint32_t devaddr, FAR const uint8_t *data, size_t len) { at25ee_lock(priv->spi); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), true); at25ee_sendcmd(priv->spi, AT25EE_CMD_WRITE, priv->addrlen, devaddr); SPI_SNDBLOCK(priv->spi, data, len); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), false); at25ee_unlock(priv->spi); } /**************************************************************************** * Name: at25ee_eraseall * * Description: * Erase all data in the device * * Input Parameters: * priv - a reference to the device structure * devaddr - the address to start the write * data - pointer to data buffer to write * len - length of the data to write * * Returned Value: * none * ****************************************************************************/ static int at25ee_eraseall(FAR struct at25ee_dev_s *priv) { uint8_t *buf; int startblock = 0; DEBUGASSERT(priv); buf = kmm_malloc(priv->pgsize); if (!buf) { ferr("ERROR: Failed to alloc memory for at25ee eraseall!\n"); return -ENOMEM; } memset(buf, 0xff, priv->pgsize); for (startblock = 0; startblock < priv->npages; startblock++) { uint16_t offset = startblock * priv->pgsize; at25ee_write(&priv->mtd, offset, priv->pgsize, buf); } kmm_free(buf); return OK; } /**************************************************************************** * Name: at25ee_erase * * Description: * Erase a number of blocks of data. * * Input Parameters: * dev - a reference to the device structure * startblock - start block of the erase * nblocks - nblocks to erase * * Returned Value: * Success (OK) or fail (negated error code) ****************************************************************************/ static int at25ee_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks) { #ifndef CONFIG_AT25EE_ENABLE_BLOCK_ERASE return (int)nblocks; #else FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; uint8_t *buf; size_t blocksleft; DEBUGASSERT(dev); if (priv->blocksize > priv->pgsize) { startblock *= (priv->blocksize / priv->pgsize); nblocks *= (priv->blocksize / priv->pgsize); } blocksleft = nblocks; if (startblock >= priv->npages) { return -E2BIG; } buf = kmm_malloc(priv->pgsize); if (!buf) { ferr("ERROR: Failed to alloc memory for at25ee erase!\n"); return -ENOMEM; } memset(buf, 0xff, priv->pgsize); if (startblock + nblocks > priv->npages) { nblocks = priv->npages - startblock; } finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); while (blocksleft-- > 0) { off_t offset = startblock * priv->pgsize; finfo("startblock: %08lx offset: %d\n", (long)startblock, (int)offset); at25ee_write(dev, offset, priv->pgsize, buf); startblock++; } kmm_free(buf); if (priv->blocksize > priv->pgsize) { return (int)(nblocks / (priv->blocksize / priv->pgsize)); } else { return (int)nblocks; } #endif } /**************************************************************************** * Name: at25ee_read * * Description: * Read a number of bytes of data. * * Input Parameters: * dev - a reference to the device structure * offset - start of the memory to read * nbytes - number of bytes to read * buffer - pointer to variable to store the read data * * Returned Value: * Size of the data read ****************************************************************************/ static ssize_t at25ee_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR uint8_t *buf) { int ret; FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; DEBUGASSERT(buf); DEBUGASSERT(dev); ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } if ((offset + nbytes) > priv->size) { return 0; /* end-of-file */ } at25ee_lock(priv->spi); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), true); /* STM32F4Disco: There is a 25 us delay here */ at25ee_sendcmd(priv->spi, AT25EE_CMD_READ, priv->addrlen, offset); SPI_RECVBLOCK(priv->spi, buf, nbytes); SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), false); at25ee_unlock(priv->spi); nxmutex_unlock(&priv->lock); return nbytes; } /**************************************************************************** * Name: at25ee_write * * Description: * Write a number of bytes of data. * * Input Parameters: * dev - a reference to the device structure * offset - start of the memory to write * nbytes - number of bytes to write * buf - pointer to buffer of data to write * * Returned Value: * Size of the data written ****************************************************************************/ static ssize_t at25ee_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR const uint8_t *buf) { int ret = -EACCES; FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; int pageoff; size_t cnt; DEBUGASSERT(buf); DEBUGASSERT(dev); if (priv->readonly) { return -EPERM; } /* Forbid writes past the end of the device */ if (nbytes + offset >= priv->size) { return 0; } ret = nxmutex_lock(&priv->lock); if (ret < 0) { return 0; } /* From this point no failure cannot be detected anymore. * The user should verify the write by rereading memory. */ ret = nbytes; /* save number of bytes written */ /* Writes can't happen in a row like the read does. * The EEPROM is made of pages, and write sequences * cannot cross page boundaries. So every time the last * byte of a page is programmed, the SPI transaction is * stopped, and the status register is read until the * write operation has completed. */ /* First, write some page-unaligned data */ pageoff = offset & (priv->pgsize - 1); cnt = priv->pgsize - pageoff; if (cnt > nbytes) { cnt = nbytes; } if (pageoff > 0) { at25ee_writeenable(priv, true); at25ee_writepage(priv, offset, buf, cnt); at25ee_waitwritecomplete(priv); nbytes -= cnt; buf += cnt; offset += cnt; } /* Then, write remaining bytes at page-aligned addresses */ while (nbytes > 0) { cnt = nbytes; if (cnt > priv->pgsize) { cnt = priv->pgsize; } at25ee_writeenable(priv, true); at25ee_writepage(priv, offset, buf, cnt); at25ee_waitwritecomplete(priv); nbytes -= cnt; buf += cnt; offset += cnt; } nxmutex_unlock(&priv->lock); return ret; } /**************************************************************************** * Name: at25ee_bread * * Description: * Read a number of blocks of data. * * Input Parameters: * dev - a reference to the device structure * startblock - start block of the read * nblocks - nblocks to read * buf - pointer to variable to store the read data * * Returned Value: * Number of blocks written ****************************************************************************/ static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, FAR uint8_t *buf) { FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; off_t offset; ssize_t nread; size_t i; DEBUGASSERT(dev); DEBUGASSERT(buf); if (priv->blocksize > priv->pgsize) { startblock *= (priv->blocksize / priv->pgsize); nblocks *= (priv->blocksize / priv->pgsize); } finfo("startblock: %08lx nblocks: %lu\n", (unsigned long)startblock, (unsigned long)nblocks); if (startblock >= priv->npages) { return 0; } if (startblock + nblocks > priv->npages) { nblocks = priv->npages - startblock; } /* Convert the access from startblock and number of blocks to a byte * offset and number of bytes. */ offset = startblock * priv->pgsize; /* Then perform the byte-oriented read for each block separately */ for (i = 0; i < nblocks; i++) { nread = at25ee_read(dev, offset, priv->pgsize, buf); if (nread < 0) { return nread; } offset += priv->pgsize; buf += priv->pgsize; } if (priv->blocksize > priv->pgsize) { return nblocks / (priv->blocksize / priv->pgsize); } else { return nblocks; } } /**************************************************************************** * Name: at25ee_bwrite * * Description: * Write a number of blocks of data. * * Input Parameters: * dev - a reference to the device structure * startblock - starting block to write to * nblocks - nblocks to write * buf - pointer to data buffer to write * * Returned Value: * Size of the data written ****************************************************************************/ static ssize_t at25ee_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, FAR const uint8_t *buf) { FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; size_t blocksleft; DEBUGASSERT(dev); DEBUGASSERT(buf); if (priv->blocksize > priv->pgsize) { startblock *= (priv->blocksize / priv->pgsize); nblocks *= (priv->blocksize / priv->pgsize); } blocksleft = nblocks; if (startblock >= priv->npages) { return 0; } if (startblock + nblocks > priv->npages) { nblocks = priv->npages - startblock; } finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); while (blocksleft-- > 0) { off_t offset = startblock * priv->pgsize; finfo("startblock: %08lx offset: %d\n", (long)startblock, (int)offset); at25ee_write(dev, offset, priv->pgsize, buf); startblock++; buf += priv->pgsize; } if (priv->blocksize > priv->pgsize) { return nblocks / (priv->blocksize / priv->pgsize); } else { return nblocks; } } /**************************************************************************** * Name: at25ee_ioctl * * Description: * IOCTLS relating to the EEPROM mtd device * * Input Parameters: * dev - a reference to the device structure * cmd - ioctl command * arg - ioctl argument * * Returned Value: * Success (OK) or fail (negated error code) ****************************************************************************/ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) { FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; int ret = -EINVAL; /* Assume good command with bad parameters */ DEBUGASSERT(dev); finfo("cmd: %d\n", cmd); switch (cmd) { case MTDIOC_GEOMETRY: { FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *) ((uintptr_t)arg); if (geo) { memset(geo, 0, sizeof(*geo)); /* Populate the geometry structure with information need to * know the capacity and how to access the device. * * NOTE: * that the device is treated as though it where just an array * of fixed size blocks. * That is most likely not true, but the client will expect the * device logic to do whatever is necessary to make it appear * so. * * blocksize: * May be user defined. * The block size for the at24XX devices may be larger than * the page size in order to better support file systems. * The read and write functions translate BLOCKS to pages * for the small flash devices * erasesize: * It has to be at least as big as the blocksize, bigger * serves no purpose. * neraseblocks * Note that the device size is in kilobits and must be * scaled by 1024 / 8 */ if (priv->blocksize > priv->pgsize) { geo->blocksize = priv->blocksize; geo->erasesize = priv->blocksize; geo->neraseblocks = priv->size / priv->blocksize; } else { geo->blocksize = priv->pgsize; geo->erasesize = priv->pgsize; geo->neraseblocks = priv->npages; } ret = OK; finfo("blocksize: %" PRId32 " erasesize: %" PRId32 " neraseblocks: %" PRId32 "\n", geo->blocksize, geo->erasesize, geo->neraseblocks); } } break; case BIOC_PARTINFO: { FAR struct partition_info_s *info = (FAR struct partition_info_s *)arg; if (info != NULL) { if (priv->blocksize > priv->pgsize) { info->numsectors = priv->size / priv->blocksize; info->sectorsize = priv->blocksize; } else { info->numsectors = priv->npages; info->sectorsize = priv->pgsize; } info->startsector = 0; info->parent[0] = '\0'; ret = OK; } } break; case MTDIOC_BULKERASE: ret = at25ee_eraseall(priv); break; default: ret = -ENOTTY; /* Bad command */ break; } return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: at25ee_initialize * * Description: * Create an initialized MTD device instance for an AT25 SPI EEPROM * MTD devices are not registered in the file system, but are created * as instances that can be bound to other functions * (such as a block or character driver front end). * * Input Parameters: * dev - a reference to the spi device structure * devtype - device type, from include/nuttx/eeprom/spi_xx25xx.h * readonly - sets block driver to be readonly * * Returned Value: * Initialised device instance (success) or NULL (fail) * ****************************************************************************/ FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, int devtype, int readonly) { FAR struct at25ee_dev_s *priv; DEBUGASSERT(dev); /* Check device type early */ if ((devtype < 0) || (devtype >= sizeof(g_at25ee_devices) / sizeof(g_at25ee_devices[0]))) { return NULL; } priv = kmm_zalloc(sizeof(struct at25ee_dev_s)); if (priv == NULL) { ferr("ERROR: Failed to allocate device structure\n"); return NULL; } /* Initialize the allocated structure */ nxmutex_init(&priv->lock); priv->spi = dev; priv->size = 128 << g_at25ee_devices[devtype].bytes; priv->pgsize = 8 << g_at25ee_devices[devtype].pagesize; priv->addrlen = g_at25ee_devices[devtype].addrlen << 3; priv->npages = priv->size / priv->pgsize; #ifdef CONFIG_USE_NATIVE_AT25EE_BLOCK_SIZE priv->blocksize = priv->pgsize; #else if ((CONFIG_MANUAL_AT25EE_BLOCK_SIZE % priv->pgsize) || (CONFIG_MANUAL_AT25EE_BLOCK_SIZE > priv->size)) { ferr("ERROR: Configured block size is incorrect!\n"); DEBUGASSERT(0); priv->blocksize = priv->pgsize; } else { priv->blocksize = CONFIG_MANUAL_AT25EE_BLOCK_SIZE; } #endif if ((g_at25ee_devices[devtype].flags & 1)) { priv->addrlen = 9; } priv->readonly = !!readonly; finfo("EEPROM device, %"PRIu32" bytes, " "%u per page, addrlen %u, readonly %d\n", priv->size, priv->pgsize, priv->addrlen, priv->readonly); priv->mtd.erase = at25ee_erase; priv->mtd.bread = at25ee_bread; priv->mtd.bwrite = at25ee_bwrite; priv->mtd.read = at25ee_read; priv->mtd.write = at25ee_write; priv->mtd.ioctl = at25ee_ioctl; priv->mtd.name = "at25ee"; /* Return the implementation-specific state structure as the MTD device */ finfo("Return %p\n", priv); return (FAR struct mtd_dev_s *)priv; } #endif /* CONFIG_MTD_AT25EE */