From abd3c4a7ea6061a3e6c4c0cc634124d60e560ef8 Mon Sep 17 00:00:00 2001 From: Sebastien Lorquet Date: Wed, 14 Feb 2018 07:53:01 -0600 Subject: [PATCH] drivers/eeprobom: EEPROM driver for AT24xx compatible EEPROMs. --- drivers/eeprom/Kconfig | 22 +- drivers/eeprom/Make.defs | 8 +- drivers/eeprom/i2c_xx24xx.c | 772 ++++++++++++++++++++++++++++++ include/nuttx/eeprom/i2c_xx24xx.h | 113 +++++ 4 files changed, 912 insertions(+), 3 deletions(-) create mode 100644 drivers/eeprom/i2c_xx24xx.c create mode 100644 include/nuttx/eeprom/i2c_xx24xx.h diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index f2ea39ce1b..cbb6747c9e 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -5,11 +5,11 @@ if EEPROM config SPI_EE_25XX - bool "Microchip 25xxNNN / Atmel AT25NNN EEPROM devices" + bool "Microchip 25xxNNN / Atmel AT25NNN / ST M95NNN SPI EEPROM devices" default n depends on SPI ---help--- - This selection enables support for the Microchip/Atmel SPI EEPROM + This selection enables support for the Microchip/Atmel/ST SPI EEPROM devices if SPI_EE_25XX @@ -20,4 +20,22 @@ config EE25XX_SPIMODE depends on SPI_EE_25XX endif # SPI_EE_25XX + +config I2C_EE_24XX + bool "Microchip 24xxNNN / Atmel AT24NNN / ST M24NNN I2C EEPROM devices" + default n + depends on I2C + ---help--- + This selection enables support for the Microchip/Atmel/ST I2C EEPROM + devices + +if I2C_EE_24XX + +config EE24XX_FREQUENCY + int "I2C EEPROM frequency (100000 or 400000)" + default 100000 + depends on I2C_EE_24XX + +endif # I2C_EE_24XX + endif # EEPROM diff --git a/drivers/eeprom/Make.defs b/drivers/eeprom/Make.defs index afbebea625..475399886e 100644 --- a/drivers/eeprom/Make.defs +++ b/drivers/eeprom/Make.defs @@ -37,12 +37,18 @@ ifeq ($(CONFIG_EEPROM),y) -# Include the Microchip/Atmel xx25xx driver +# Include the Microchip/Atmel/ST xx25xx driver for SPI ifeq ($(CONFIG_SPI_EE_25XX),y) CSRCS += spi_xx25xx.c endif +# Include the Microchip/Atmel/ST xx24xx driver for I2C + +ifeq ($(CONFIG_I2C_EE_24XX),y) +CSRCS += i2c_xx24xx.c +endif + # Include build support DEPPATH += --dep-path eeprom diff --git a/drivers/eeprom/i2c_xx24xx.c b/drivers/eeprom/i2c_xx24xx.c new file mode 100644 index 0000000000..944e14e856 --- /dev/null +++ b/drivers/eeprom/i2c_xx24xx.c @@ -0,0 +1,772 @@ +/**************************************************************************** + * drivers/eeprom/i2c_xx24xx.c + * + * Copyright (C) 2018 Sebastien Lorquet. All rights reserved. + * Author: Sebastien Lorquet + * + * 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 NuttX nor the names of its 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. + * + ****************************************************************************/ + +/* This is a driver for I2C EEPROMs that use the same commands as the + * xx24xx. + * + * The following devices should be supported: + * + * Manufacturer Device Bytes PgSize AddrLen DevAddr + * Microchip + * 24xx00 16 1 1 1010000 Special case + * 24xx01 128 8 1 1010000 + * 24xx02 256 8 1 1010000 + * 24xx04 512 16 1 101000P + * 24xx08 1024 16 1 10100PP + * 24xx16 2048 16 1 1010PPP + * 24xx32 4096 32 2 1010AAA + * 24xx64 8192 32 2 1010AAA + * 24xx128 16384 64 2 1010AAA + * 24xx256 32768 64 2 1010AAA + * 24xx512 65536 128 2 1010AAA + * 24xx1025 131072 128 2 1010PAA Special case: address bit is shifted. + * 24xx1026 131072 128 2 1010AAP + * + * Atmel + * AT24C01 128 8 1 1010AAA + * AT24C02 256 8 1 1010AAA + * AT24C04 512 16 1 1010AAP P bits = word address + * AT24C08 1024 16 1 1010APP + * AT24C16 2048 16 1 1010PPP + * AT24C32 4096 32 2 1010AAA + * AT24C64 8192 32 2 1010AAA + * AT24C128 16384 64 2 10100AA + * AT24C256 32768 64 2 10100AA + * AT24C512 65536 128 2 10100AA + * AT24C1024 131072 256 2 10100AP + * + * ST Microelectronics + * M24C01 128 16 1 1010AAA + * M24C02 256 16 1 1010AAA + * M24C04 512 16 1 1010AAP + * M24C08 1024 16 1 1010APP + * M24C16 2048 16 1 1010PPP + * M24C32 4096 32 2 1010AAA ID pages supported as a separate device + * M24C64 8192 32 2 1010AAA + * M24128 16384 64 2 1010AAA + * M24256 32768 64 2 1010AAA + * M24512 65536 128 2 1010AAA + * M24M01 131072 256 2 1010AAP + * M24M02 262144 256 2 1010APP + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_EE24XX_FREQUENCY +#define CONFIG_EE24XX_FREQUENCY 100000 +#endif + +/**************************************************************************** + * Types + ****************************************************************************/ + +/* Device geometry description, compact form (2 bytes per entry) */ + +struct ee24xx_geom_s +{ + uint8_t bytes : 4; /* Power of two of 128 bytes (0:128 ... 11:262144) */ + uint8_t pagesize : 4; /* Power of two of 8 bytes (0:8 1:16 2:32 3:64 etc) */ + uint8_t addrlen : 4; /* Nr of bytes in command address field */ + uint8_t abits : 3; /* Nr of Address MSBits in the i2c device address LSBs*/ + uint8_t special : 1; /* Special device: uchip 24xx00 (total 16 bytes) + * or 24xx1025 (shifted P bits) */ +}; + +/* Private data attached to the inode */ + +struct ee24xx_dev_s +{ + /* Bus management */ + + struct i2c_master_s *i2c; /* I2C device where the EEPROM is attached */ + uint32_t freq; /* I2C bus speed */ + uint8_t addr; /* 7-bit unshifted I2C device address */ + + /* Driver management */ + + sem_t sem; /* file write access serialization */ + uint8_t refs; /* Nr of times the device has been opened */ + bool readonly; /* Flags */ + + /* Expanded from geometry */ + + uint32_t size; /* total bytes in device */ + uint16_t pgsize; /* write block size, in bytes */ + uint16_t addrlen; /* number of bytes in data addresses */ + uint16_t haddrbits; /* Number of bits in high address part */ + uint16_t haddrshift; /* bit-shift of high address part */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int ee24xx_open(FAR struct file *filep); +static int ee24xx_close(FAR struct file *filep); +static off_t ee24xx_seek(FAR struct file *filep, off_t offset, int whence); +static ssize_t ee24xx_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t ee24xx_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int ee24xx_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Supported device geometries. + * One geometry can fit more than one device. + * The user will use an enum'ed index from include/eeprom/i2c_xx24xx.h + */ + +static const struct ee24xx_geom_s g_ee24xx_devices[] = +{ + /* Microchip devices */ + +/* by pg al ab sp device bytes page alen */ + { 0, 1, 1, 0, 1}, /* 24xx00 16 1 1 Ridiculously small device */ + { 0, 0, 1, 0, 0}, /* 24xx01 128 8 1 */ + { 1, 0, 1, 0, 0}, /* 24xx02 256 8 1 */ + { 2, 1, 1, 1, 0}, /* 24xx04 512 16 1 */ + { 3, 1, 1, 2, 0}, /* 24xx08 1024 16 1 */ + { 4, 1, 1, 3, 0}, /* 24xx16 2048 16 1 */ + { 5, 2, 2, 0, 0}, /* 24xx32 4096 32 2 */ + { 6, 2, 2, 0, 0}, /* 24xx64 8192 32 2 */ + { 7, 3, 2, 0, 0}, /* 24xx128 16384 64 2 */ + { 8, 3, 2, 0, 0}, /* 24xx256 32768 64 2 */ + { 9, 4, 2, 0, 0}, /* 24xx512 65536 128 2 */ + {10, 4, 2, 1, 1}, /* 24xx1025 131072 128 2 Shifted address, todo */ + {10, 4, 2, 1, 0}, /* 24xx1026 131072 128 2 */ + + /* STM devices */ + + { 0, 1, 1, 0, 0}, /* M24C01 128 16 1 */ + { 1, 1, 1, 0, 0}, /* M24C02 256 16 1 */ + {11, 5, 2, 2, 0}, /* M24M02 262144 256 2 */ +}; + +/* Driver operations */ + +static const struct file_operations ee24xx_fops = +{ + ee24xx_open, /* open */ + ee24xx_close, /* close */ + ee24xx_read, /* read */ + ee24xx_write, /* write */ + ee24xx_seek, /* seek */ + ee24xx_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ee24xx_waitwritecomplete + * + * Use ACK polling to detect the completion of the write operation. + * Returns TRUE if write is complete (device replies to ACK). + * Note: The device always replies an ACK for the control byte, the polling + * shall be done using the ACK for the memory address byte. Read or write does + * not matter. + * Note: We should sleep a bit between retries, the write time is around 5 ms, + * but the bus is slow, so, a few retries at most will happen. + * + ****************************************************************************/ + +static int ee24xx_waitwritecomplete(FAR struct ee24xx_dev_s *eedev, + uint32_t memaddr) +{ + struct i2c_msg_s msgs[1]; + int ret; + int retries = 100; + uint8_t adr; + uint32_t addr_hi = (memaddr >> (eedev->addrlen << 3)); + + msgs[0].frequency = eedev->freq; + msgs[0].addr = eedev->addr | (addr_hi & ((1 << eedev->haddrbits) - 1)); + msgs[0].flags = I2C_M_READ; + msgs[0].buffer = &adr; + msgs[0].length = 1; + + do + { + ret = I2C_TRANSFER(eedev->i2c, msgs, 1); + retries--; + } + while(ret != 0 && retries > 0); + + return ret; +} + +/**************************************************************************** + * Name: ee24xx_writepage + * + * Description: Write data to the EEPROM, NOT crossing page boundaries. + * To avoid allocating a buffer to prepend the address, we are using 2 I2C + * messages while avoiding beginning the second one with a restart condition. + * + ****************************************************************************/ + +static int ee24xx_writepage(FAR struct ee24xx_dev_s *eedev, uint32_t memaddr, + FAR const char *buffer, size_t len) +{ + + struct i2c_msg_s msgs[2]; + uint8_t maddr[2]; + uint32_t addr_hi = (memaddr >> (eedev->addrlen << 3)); + + /* Write data address */ + + maddr[0] = memaddr >> 8; + maddr[1] = memaddr & 0xFF; + + msgs[0].frequency = eedev->freq; + msgs[0].addr = eedev->addr | (addr_hi & ((1 << eedev->haddrbits) - 1)); + msgs[0].flags = 0; + msgs[0].buffer = eedev->addrlen==2 ? &maddr[0] : &maddr[1]; + msgs[0].length = eedev->addrlen; + + /* Write data without a restart nor a control byte */ + + msgs[1].frequency = msgs[0].frequency; + msgs[1].addr = msgs[0].addr; + msgs[1].flags = I2C_M_NORESTART; + msgs[1].buffer = (uint8_t*)buffer; + msgs[1].length = len; + + return I2C_TRANSFER(eedev->i2c, msgs, 2); +} + +/**************************************************************************** + * Name: ee24xx_semtake + * + * Acquire a resource to access the device. + * The purpose of the semaphore is to block tasks that try to access the + * EEPROM while another task is actively using it. + * + ****************************************************************************/ + +static void ee24xx_semtake(FAR struct ee24xx_dev_s *eedev) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&eedev->sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + DEBUGASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: ee24xx_semgive + * + * Release a resource to access the device. + * + ****************************************************************************/ + +static inline void ee24xx_semgive(FAR struct ee24xx_dev_s *eedev) +{ + sem_post(&eedev->sem); +} + +/**************************************************************************** + * Driver Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ee24xx_open + * + * Description: Open the block device + * + ****************************************************************************/ + +static int ee24xx_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ee24xx_dev_s *eedev; + int ret = OK; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee24xx_dev_s *)inode->i_private; + ee24xx_semtake(eedev); + + /* Increment the reference count */ + + if ((eedev->refs + 1) == 0) + { + ret = -EMFILE; + } + else + { + eedev->refs += 1; + } + + ee24xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee24xx_close + * + * Description: Close the block device + * + ****************************************************************************/ + +static int ee24xx_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ee24xx_dev_s *eedev; + int ret = OK; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee24xx_dev_s *)inode->i_private; + ee24xx_semtake(eedev); + + /* Decrement the reference count. I want the entire close operation + * to be atomic wrt other driver operations. + */ + + if (eedev->refs == 0) + { + ret = -EIO; + } + else + { + eedev->refs -= 1; + } + + ee24xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee24xx_seek + * + * Remark: Copied from bchlib + * + ****************************************************************************/ + +static off_t ee24xx_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct ee24xx_dev_s *eedev; + off_t newpos; + int ret; + FAR struct inode *inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee24xx_dev_s *)inode->i_private; + ee24xx_semtake(eedev); + + /* Determine the new, requested file position */ + + switch (whence) + { + case SEEK_CUR: + newpos = filep->f_pos + offset; + break; + + case SEEK_SET: + newpos = offset; + break; + + case SEEK_END: + newpos = eedev->size + offset; + break; + + default: + /* Return EINVAL if the whence argument is invalid */ + + ee24xx_semgive(eedev); + return -EINVAL; + } + + /* Opengroup.org: + * + * "The lseek() function shall allow the file offset to be set beyond the end + * of the existing data in the file. If data is later written at this point, + * subsequent reads of data in the gap shall return bytes with the value 0 + * until data is actually written into the gap." + * + * We can conform to the first part, but not the second. But return EINVAL if + * + * "...the resulting file offset would be negative for a regular file, block + * special file, or directory." + */ + + if (newpos >= 0) + { + filep->f_pos = newpos; + ret = newpos; + finfo("SEEK newpos %d\n",newpos); + } + else + { + ret = -EINVAL; + } + + ee24xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee24xx_read + ****************************************************************************/ + +static ssize_t ee24xx_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct ee24xx_dev_s *eedev; + FAR struct inode *inode = filep->f_inode; + struct i2c_msg_s msgs[2]; + uint8_t addr[2]; + uint32_t addr_hi; + int ret; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee24xx_dev_s *)inode->i_private; + + ee24xx_semtake(eedev); + + /* trim len if read would go beyond end of device */ + + if ((filep->f_pos + len) > eedev->size) + { + len = eedev->size - filep->f_pos; + } + + if (len == 0) + { + /* We are at end of file */ + + ret = 0; + goto done; + } + + /* Write data address */ + + finfo("READ %d bytes at pos %d\n", len, filep->f_pos); + + addr_hi = (filep->f_pos >> (eedev->addrlen << 3)); + + addr[0] = (filep->f_pos) >> 8; + addr[1] = (filep->f_pos) & 0xFF; + + msgs[0].frequency = eedev->freq; + msgs[0].addr = eedev->addr | (addr_hi & ((1 << eedev->haddrbits) - 1)); + msgs[0].flags = 0; + msgs[0].buffer = eedev->addrlen==2 ? &addr[0] : &addr[1]; + msgs[0].length = eedev->addrlen; + + /* Read data */ + + msgs[1].frequency = msgs[0].frequency; + msgs[1].addr = msgs[0].addr; + msgs[1].flags = I2C_M_READ; + msgs[1].buffer = (uint8_t*)buffer; + msgs[1].length = len; + + ret = I2C_TRANSFER(eedev->i2c, msgs, 2); + if (ret < 0) + { + goto done; + } + + ret = len; + + /* Update the file position */ + + filep->f_pos += len; + +done: + ee24xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee24xx_write + ****************************************************************************/ + +static ssize_t ee24xx_write(FAR struct file *filep, FAR const char *buffer, + size_t len) +{ + FAR struct ee24xx_dev_s *eedev; + size_t cnt; + int pageoff; + FAR struct inode *inode = filep->f_inode; + int ret = -EACCES; + int savelen; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee24xx_dev_s *)inode->i_private; + + if (eedev->readonly) + { + return ret; + } + + /* Forbid writes past the end of the device */ + + if (filep->f_pos >= eedev->size) + { + return -EFBIG; + } + + finfo("Entering with len=%d\n", len); + + /* Clamp len to avoid crossing the end of the memory */ + + if ((len + filep->f_pos) > eedev->size) + { + len = eedev->size - filep->f_pos; + finfo("Len clamped to %d\n", len); + } + + savelen = len; /* save number of bytes written */ + + ee24xx_semtake(eedev); + + /* 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, a separate I2C transaction + * required to continue writing. + */ + + /* First, write some page-unaligned data */ + + pageoff = filep->f_pos & (eedev->pgsize - 1); + cnt = eedev->pgsize - pageoff; + if (cnt > len) + { + cnt = len; + } + + if (pageoff > 0) + { + finfo("First %d unaligned bytes at %d (pageoff %d)\n", cnt, filep->f_pos, + pageoff); + + ret = ee24xx_writepage(eedev, filep->f_pos, buffer, cnt); + if (ret < 0) + { + ferr("write failed, ret = %d\n", ret); + goto done; + } + + ret = ee24xx_waitwritecomplete(eedev, filep->f_pos); + if (ret < 0) + { + ferr("writecomplete failed, ret = %d\n", ret); + goto done; + } + + len -= cnt; + buffer += cnt; + filep->f_pos += cnt; + } + + /* Then, write remaining bytes at page-aligned addresses */ + + while (len > 0) + { + cnt = len; + if (cnt > eedev->pgsize) + { + cnt = eedev->pgsize; + } + + finfo("Aligned page write for %d bytes at %d\n", cnt, filep->f_pos); + + ret = ee24xx_writepage(eedev, filep->f_pos, buffer, cnt); + if (ret < 0) + { + ferr("write failed, ret = %d\n", ret); + goto done; + } + + ret = ee24xx_waitwritecomplete(eedev, filep->f_pos); + if (ret < 0) + { + ferr("writecomplete failed, ret = %d\n", ret); + goto done; + } + + len -= cnt; + buffer += cnt; + filep->f_pos += cnt; + } + + ret = savelen; + +done: + ee24xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee24xx_ioctl + * + * Description: TODO: Erase a sector/page/device or read device ID / MAC. + * This is completely optional. + * + ****************************************************************************/ + +static int ee24xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct ee24xx_dev_s *eedev; + FAR struct inode *inode = filep->f_inode; + int ret = 0; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee24xx_dev_s *)inode->i_private; + + switch (cmd) + { + default: + (void)eedev; + ret = -EINVAL; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ee24xx_initialize + * + * Description: Bind a EEPROM driver to an I2C bus. The user MUST provide + * a description of the device geometry, since it is not possible to read + * this information from the device (contrary to the SPI flash devices). + * + ****************************************************************************/ + +int ee24xx_initialize(FAR struct i2c_master_s *bus, uint8_t devaddr, + FAR char *devname, int devtype, int readonly) +{ + FAR struct ee24xx_dev_s *eedev; + + /* Check device type early */ + + if ((devtype < 0) || + (devtype >= sizeof(g_ee24xx_devices) / sizeof(g_ee24xx_devices[0]))) + { + return -EINVAL; + } + + eedev = kmm_zalloc(sizeof(struct ee24xx_dev_s)); + + if (!eedev) + { + return -ENOMEM; + } + + sem_init(&eedev->sem, 0, 1); + + eedev->freq = CONFIG_EE24XX_FREQUENCY; + eedev->i2c = bus; + eedev->addr = devaddr; + eedev->readonly = !!readonly; + + /* Expand device geometry from compacted info */ + + eedev->size = 128 << g_ee24xx_devices[devtype].bytes; + eedev->pgsize = 8 << g_ee24xx_devices[devtype].pagesize; + eedev->addrlen = g_ee24xx_devices[devtype].addrlen; + eedev->haddrbits = g_ee24xx_devices[devtype].abits; + eedev->haddrshift = 0; + + /* Apply special properties */ + + if (g_ee24xx_devices[devtype].special) + { + if (devtype == EEPROM_24xx00) + { + /* Ultra small 16-byte EEPROM */ + + eedev->size = 16; + + /* The device only has BYTE write, + * which is emulated with 1-byte pages + */ + + eedev->pgsize = 1; + } + else if (devtype == EEPROM_24xx1025) + { + /* Microchip alien part where the address MSB is << 2 bits */ + + ferr("Device 24xx1025 is not supported for the moment, TODO.\n"); + + eedev->haddrshift = 2; + free(eedev); + return -ENODEV; + } + } + + finfo("EEPROM device %s, %d bytes, %d per page, addrlen %d, %s\n", + devname, eedev->size, eedev->pgsize, eedev->addrlen, + eedev->readonly ? "readonly" : ""); + + return register_driver(devname, &ee24xx_fops, 0666, eedev); +} diff --git a/include/nuttx/eeprom/i2c_xx24xx.h b/include/nuttx/eeprom/i2c_xx24xx.h new file mode 100644 index 0000000000..6dfd16ba29 --- /dev/null +++ b/include/nuttx/eeprom/i2c_xx24xx.h @@ -0,0 +1,113 @@ + /**************************************************************************** + * include/nuttx/eeprom/i2c_xx24xx.h + * + * Copyright (C) 2018 Sebastien Lorquet. All rights reserved. + * Author: Sebastien Lorquet + * + * 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 NuttX nor the names of its 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_EEPROM_I2C_XX24XX_H +#define __INCLUDE_NUTTX_EEPROM_I2C_XX24XX_H 1 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* DO NOT CHANGE ORDER, IT MACHES CODE IN drivers/eeprom/i2c_xx24xx.c */ + +enum eeprom_24xx_e +{ + /* Microchip geometries */ + + EEPROM_24xx00, + EEPROM_24xx01, + EEPROM_24xx02, + EEPROM_24xx04, + EEPROM_24xx08, + EEPROM_24xx16, + EEPROM_24xx32, + EEPROM_24xx64, + EEPROM_24xx128, + EEPROM_24xx256, + EEPROM_24xx512, + EEPROM_24xx1025, + EEPROM_24xx1026, + + /* Atmel geometries - none... */ + + /* STM geometries */ + + EEPROM_M24C01, + EEPROM_M24C02, + EEPROM_M24M02, + + /* Aliases (devices similar to previously defined ones) */ + + EEPROM_AT24C01 = EEPROM_24xx01, + EEPROM_AT24C02 = EEPROM_24xx02, + EEPROM_AT24C04 = EEPROM_24xx04, + EEPROM_AT24C08 = EEPROM_24xx08, + EEPROM_AT24C16 = EEPROM_24xx16, + EEPROM_AT24C32 = EEPROM_24xx32, + EEPROM_AT24C64 = EEPROM_24xx64, + EEPROM_AT24C128 = EEPROM_24xx128, + EEPROM_AT24C256 = EEPROM_24xx256, + EEPROM_AT24C512 = EEPROM_24xx512, + EEPROM_AT24C1024 = EEPROM_24xx1026, + + EEPROM_M24C04 = EEPROM_24xx04, + EEPROM_M24C08 = EEPROM_24xx08, + EEPROM_M24C16 = EEPROM_24xx16, + EEPROM_M24C32 = EEPROM_24xx32, + EEPROM_M24C64 = EEPROM_24xx64, + EEPROM_M24128 = EEPROM_24xx128, + EEPROM_M24256 = EEPROM_24xx256, + EEPROM_M24512 = EEPROM_24xx512, + EEPROM_M24M01 = EEPROM_24xx1026, +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ee24xx_initialize + * + * Description: Bind a EEPROM driver to an I2C bus. The user MUST provide + * a description of the device geometry, since it is not possible to read + * this information from the device (contrary to the SPI flash devices). + * + ****************************************************************************/ + +struct i2c_master_s; +int ee24xx_initialize(FAR struct i2c_master_s *bus, uint8_t devaddr, + FAR char *devname, int devtype, int readonly); + +#endif /* __INCLUDE__NUTTX_EEPROM_I2C_XX24XX_H */