xiaoxiang781216 commented on code in PR #11423: URL: https://github.com/apache/nuttx/pull/11423#discussion_r1433800036
########## drivers/mtd/at25ee.c: ########## @@ -0,0 +1,1033 @@ +/**************************************************************************** + * 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 <nuttx/config.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/kmalloc.h> +#include <nuttx/signal.h> +#include <nuttx/fs/ioctl.h> +#include <nuttx/spi/spi.h> +#include <nuttx/mtd/mtd.h> + +#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 */ + bool initd; /* True: The device has been initialize */ +}; + +/**************************************************************************** + * 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; + } + + 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. + * NB - It is not necessary to erase data before writing with EEPROMs + * + * 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) +{ + return (int)nblocks; Review Comment: So, the erase needs fill 0xff to the specified range. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
