Extend the AT24 EEPROM driver so that it supports: (1) the byte-oriented read() method, (2) parts with extended memory regions, and (3) parts with 8-bit addressing.

This commit is contained in:
Gregory Nutt
2015-03-17 14:27:27 -06:00
parent 26923b39de
commit 495f420978
3 changed files with 200 additions and 43 deletions
+27 -2
View File
@@ -271,9 +271,9 @@ config MTD_AT24XX
if MTD_AT24XX if MTD_AT24XX
config AT24XX_SIZE config AT24XX_SIZE
int "AT24xx size (kbit)" int "AT24xx size (Kbit)"
default 64 default 64
---help--- ---help---
This is the XX in the AT24Cxx part number. For example, if you have a This is the XX in the AT24Cxx part number. For example, if you have a
AT 24C512, then the correct value is 512. This value is also the capacity AT 24C512, then the correct value is 512. This value is also the capacity
of the part in kilobits. For example, the 24C512 supports 512 Kbits or of the part in kilobits. For example, the 24C512 supports 512 Kbits or
@@ -283,6 +283,31 @@ config AT24XX_ADDR
hex "AT24XX I2C address" hex "AT24XX I2C address"
default 0x50 default 0x50
range 0x50 0x57 range 0x50 0x57
---help---
The I2C address of the FLASH part. This is should be 0b01010aaa
(where aaa is determined by board/pin configuration).
For accesses to "extended memory" accesses, the driver will set
bit 3 of this address using 0xb01011aaa as the I2C address.
config AT24XX_EXTENDED
bool "Extended memory"
default n
---help---
If the device supports extended memory, then this operion may be set
to enabled the MTDIOC_EXTENDED ioctl() operation. When the
extended operation is selected, calls to the driver read method will
return data from the extended memory region.
config AT24XX_EXTSIZE
int "Extended memory size (bytes)"
default 0
depends on AT24XX_EXTENDED
---help---
If the device supports extended memory, then this option provides
the size of the memory in bytes.
Other, block-oriented access are not effected by this setting
endif endif
+169 -41
View File
@@ -81,24 +81,34 @@
/* Get the part configuration based on the size configuration */ /* Get the part configuration based on the size configuration */
#if CONFIG_AT24XX_SIZE == 32 /* AT24C32: 32Kbits = 4KiB; 128 * 32 = 4096 */ #if CONFIG_AT24XX_SIZE == 2 /* AT24C32: 2Kbits = 256; 16 * 16 = 256 */
# define AT24XX_NPAGES 16
# define AT24XX_PAGESIZE 16
# define AT24XX_ADDRSIZE 1
#elif CONFIG_AT24XX_SIZE == 32 /* AT24C32: 32Kbits = 4KiB; 128 * 32 = 4096 */
# define AT24XX_NPAGES 128 # define AT24XX_NPAGES 128
# define AT24XX_PAGESIZE 32 # define AT24XX_PAGESIZE 32
# define AT24XX_ADDRSIZE 2
#elif CONFIG_AT24XX_SIZE == 48 /* AT24C48: 48Kbits = 6KiB; 192 * 32 = 6144 */ #elif CONFIG_AT24XX_SIZE == 48 /* AT24C48: 48Kbits = 6KiB; 192 * 32 = 6144 */
# define AT24XX_NPAGES 192 # define AT24XX_NPAGES 192
# define AT24XX_PAGESIZE 32 # define AT24XX_PAGESIZE 32
# define AT24XX_ADDRSIZE 2
#elif CONFIG_AT24XX_SIZE == 64 /* AT24C64: 64Kbits = 8KiB; 256 * 32 = 8192 */ #elif CONFIG_AT24XX_SIZE == 64 /* AT24C64: 64Kbits = 8KiB; 256 * 32 = 8192 */
# define AT24XX_NPAGES 256 # define AT24XX_NPAGES 256
# define AT24XX_PAGESIZE 32 # define AT24XX_PAGESIZE 32
# define AT24XX_ADDRSIZE 2
#elif CONFIG_AT24XX_SIZE == 128 /* AT24C128: 128Kbits = 16KiB; 256 * 64 = 16384 */ #elif CONFIG_AT24XX_SIZE == 128 /* AT24C128: 128Kbits = 16KiB; 256 * 64 = 16384 */
# define AT24XX_NPAGES 256 # define AT24XX_NPAGES 256
# define AT24XX_PAGESIZE 64 # define AT24XX_PAGESIZE 64
# define AT24XX_ADDRSIZE 2
#elif CONFIG_AT24XX_SIZE == 256 /* AT24C256: 256Kbits = 32KiB; 512 * 64 = 32768 */ #elif CONFIG_AT24XX_SIZE == 256 /* AT24C256: 256Kbits = 32KiB; 512 * 64 = 32768 */
# define AT24XX_NPAGES 512 # define AT24XX_NPAGES 512
# define AT24XX_PAGESIZE 64 # define AT24XX_PAGESIZE 64
# define AT24XX_ADDRSIZE 2
#elif CONFIG_AT24XX_SIZE == 512 /* AT24C512: 512Kbits = 64KiB; 512 * 128 = 65536 */ #elif CONFIG_AT24XX_SIZE == 512 /* AT24C512: 512Kbits = 64KiB; 512 * 128 = 65536 */
# define AT24XX_NPAGES 512 # define AT24XX_NPAGES 512
# define AT24XX_PAGESIZE 128 # define AT24XX_PAGESIZE 128
# define AT24XX_ADDRSIZE 2
#endif #endif
/* For applications where a file system is used on the AT24, the tiny page sizes /* For applications where a file system is used on the AT24, the tiny page sizes
@@ -126,6 +136,10 @@ struct at24c_dev_s
{ {
struct mtd_dev_s mtd; /* MTD interface */ struct mtd_dev_s mtd; /* MTD interface */
FAR struct i2c_dev_s *dev; /* Saved I2C interface instance */ FAR struct i2c_dev_s *dev; /* Saved I2C interface instance */
bool initd; /* True: The device has been initialize */
#ifdef CONFIG_AT24XX_EXTENDED
bool extended; /* True: use extended memory region */
#endif
uint8_t addr; /* I2C address */ uint8_t addr; /* I2C address */
uint16_t pagesize; /* 32, 63 */ uint16_t pagesize; /* 32, 63 */
uint16_t npages; /* 128, 256, 512, 1024 */ uint16_t npages; /* 128, 256, 512, 1024 */
@@ -142,10 +156,8 @@ static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf); size_t nblocks, FAR uint8_t *buf);
static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf); size_t nblocks, FAR const uint8_t *buf);
#if 0 /* Not implemented */ static ssize_t at24c_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
static ssize_t at24c_read(FAR struct mtd_dev_s *dev, off_t offset, FAR uint8_t *buffer);
size_t nbytes,FAR uint8_t *buffer);
#endif
static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/************************************************************************************ /************************************************************************************
@@ -165,23 +177,28 @@ static struct at24c_dev_s g_at24c;
static int at24c_eraseall(FAR struct at24c_dev_s *priv) static int at24c_eraseall(FAR struct at24c_dev_s *priv)
{ {
int startblock = 0; int startblock = 0;
uint8_t buf[AT24XX_PAGESIZE + 2]; uint8_t buf[AT24XX_PAGESIZE + AT24XX_ADDRSIZE];
memset(&buf[2],0xff,priv->pagesize); memset(&buf[AT24XX_ADDRSIZE], 0xff, priv->pagesize);
I2C_SETADDRESS(priv->dev,priv->addr,7); I2C_SETADDRESS(priv->dev, priv->addr, 7);
I2C_SETFREQUENCY(priv->dev,100000); I2C_SETFREQUENCY(priv->dev, 100000);
for (startblock = 0; startblock < priv->npages; startblock++) for (startblock = 0; startblock < priv->npages; startblock++)
{ {
#if AT24XX_ADDRSIZE == 2
uint16_t offset = startblock * priv->pagesize; uint16_t offset = startblock * priv->pagesize;
buf[1] = offset & 0xff; buf[1] = offset & 0xff;
buf[0] = (offset >> 8) & 0xff; buf[0] = (offset >> 8) & 0xff;
#else
buf[0] = startblock * priv->pagesize;
#endif
while (I2C_WRITE(priv->dev, buf, 2) < 0) while (I2C_WRITE(priv->dev, buf, AT24XX_ADDRSIZE) < 0)
{ {
usleep(1000); usleep(1000);
} }
I2C_WRITE(priv->dev, buf, priv->pagesize + 2);
I2C_WRITE(priv->dev, buf, AT24XX_PAGESIZE + AT24XX_ADDRSIZE);
} }
return OK; return OK;
@@ -198,6 +215,54 @@ static int at24c_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nbloc
return (int)nblocks; return (int)nblocks;
} }
/************************************************************************************
* Name: at24c_read_internal
************************************************************************************/
static ssize_t at24c_read_internal(FAR struct at24c_dev_s *priv, off_t offset,
size_t nbytes, FAR uint8_t *buffer,
uint8_t address)
{
uint8_t buf[AT24XX_ADDRSIZE];
fvdbg("offset: %lu nbytes: %lu address: %02x\n",
(unsigned long)offset, (unsigned long)nbytes, address);
I2C_SETADDRESS(priv->dev, address, 7);
I2C_SETFREQUENCY(priv->dev, 100000);
/* "Random Read: A Random Read requires a dummy byte write sequence to load in the
* data word address. Once the device address word and data word address are clocked
* in and acknowledged by the EEPROM, the microcontroller must generate another
* Start condition. The microcontroller now initiates a current address read
* by sending a device address with the read/write select bit high. The EEPROM
* acknowledges the device address and serially clocks out the data word. To end the
* random read sequence, the microcontroller does not respond with a zero but does
* generate a Stop condition in the subsequent clock cycle."
*
* A repeated START after the address is suggested, however, this simple driver
* just performs the write as a sepate transfer with an additional, unnecessary STOP.
*/
#if AT24XX_ADDRSIZE == 2
buf[1] = offset & 0xff;
buf[0] = (offset >> 8) & 0xff;
#else
buf[0] = (uint8_t)offset;
#endif
while (I2C_WRITE(priv->dev, buf, AT24XX_ADDRSIZE) < 0)
{
fvdbg("wait\n");
usleep(1000);
}
/* Then transfer the following bytes */
I2C_READ(priv->dev, buffer, nbytes);
return nbytes;
}
/************************************************************************************ /************************************************************************************
* Name: at24c_bread * Name: at24c_bread
************************************************************************************/ ************************************************************************************/
@@ -206,15 +271,17 @@ static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buffer) size_t nblocks, FAR uint8_t *buffer)
{ {
FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev; FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
size_t blocksleft; off_t offset;
ssize_t nread;
size_t i;
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE #if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE); startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
nblocks *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE); nblocks *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
#endif #endif
blocksleft = nblocks;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); fvdbg("startblock: %08lx nblocks: %lu\n",
(unsigned long)startblock, (unsigned long)nblocks);
if (startblock >= priv->npages) if (startblock >= priv->npages)
{ {
@@ -226,31 +293,30 @@ static ssize_t at24c_bread(FAR struct mtd_dev_s *dev, off_t startblock,
nblocks = priv->npages - startblock; nblocks = priv->npages - startblock;
} }
I2C_SETADDRESS(priv->dev,priv->addr,7); /* Convert the access from startblock and number of blocks to a byte
I2C_SETFREQUENCY(priv->dev,100000); * offset and number of bytes.
*/
while (blocksleft-- > 0) offset = startblock * priv->pagesize;
/* Then perform the byte-oriented read for each block separately */
for (i = 0; i < nblocks; i++)
{ {
uint16_t offset = startblock * priv->pagesize; nread = at24c_read_internal(priv, offset, priv->pagesize, buffer,
uint8_t buf[2]; priv->addr);
buf[1] = offset & 0xff; if (nread < 0)
buf[0] = (offset >> 8) & 0xff;
while (I2C_WRITE(priv->dev, buf, 2) < 0)
{ {
fvdbg("wait\n"); return nread;
usleep(1000);
} }
I2C_READ(priv->dev, buffer, priv->pagesize); offset += priv->pagesize;
startblock++;
buffer += priv->pagesize;
} }
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE #if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
return nblocks / (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE); return nblocks / (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
#else #else
return nblocks; return nblocks;
#endif #endif
} }
@@ -266,7 +332,7 @@ static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t
{ {
FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev; FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
size_t blocksleft; size_t blocksleft;
uint8_t buf[AT24XX_PAGESIZE+2]; uint8_t buf[AT24XX_PAGESIZE + AT24XX_ADDRSIZE];
#if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE #if CONFIG_AT24XX_MTD_BLOCKSIZE > AT24XX_PAGESIZE
startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE); startblock *= (CONFIG_AT24XX_MTD_BLOCKSIZE / AT24XX_PAGESIZE);
@@ -290,18 +356,23 @@ static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t
while (blocksleft-- > 0) while (blocksleft-- > 0)
{ {
#if AT24XX_ADDRSIZE
uint16_t offset = startblock * priv->pagesize; uint16_t offset = startblock * priv->pagesize;
while (I2C_WRITE(priv->dev, (uint8_t *)&offset, 2) < 0) buf[1] = offset & 0xff;
buf[0] = (offset >> 8) & 0xff;
#else
buf[0] = startblock * priv->pagesize;
#endif
while (I2C_WRITE(priv->dev, buf, AT24XX_ADDRSIZE) < 0)
{ {
fvdbg("wait\n"); fvdbg("wait\n");
usleep(1000); usleep(1000);
} }
buf[1] = offset & 0xff; memcpy(&buf[AT24XX_ADDRSIZE], buffer, priv->pagesize);
buf[0] = (offset >> 8) & 0xff;
memcpy(&buf[2], buffer, priv->pagesize);
I2C_WRITE(priv->dev, buf, priv->pagesize + 2); I2C_WRITE(priv->dev, buf, priv->pagesize + AT24XX_ADDRSIZE);
startblock++; startblock++;
buffer += priv->pagesize; buffer += priv->pagesize;
} }
@@ -313,6 +384,54 @@ static ssize_t at24c_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t
#endif #endif
} }
/************************************************************************************
* Name: at24c_read
************************************************************************************/
static ssize_t at24c_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer)
{
FAR struct at24c_dev_s *priv = (FAR struct at24c_dev_s *)dev;
size_t memsize;
uint8_t addr;
fvdbg("offset: %lu nbytes: %lu\n", (unsigned long)offset, (unsigned long)nbytes);
/* Don't permit reads beyond the end of the memory region */
#ifdef MTDIOC_EXTENDED
if (priv->extended)
{
memsize = CONFIG_AT24XX_EXTSIZE;
}
else
#endif
{
memsize = AT24XX_NPAGES * AT24XX_PAGESIZE;
}
if (offset + nbytes > memsize)
{
/* Return 0 meaning end-of-file */
return 0;
}
/* Get the I2C address, converting it to the extended I2C if needed */
addr = priv->addr;
#ifdef MTDIOC_EXTENDED
if (priv->extended)
{
addr |= 0x08;
}
#endif
/* Then perform the byte-oriented read using common logic */
return at24c_read_internal(priv, offset, nbytes, buffer, addr);
}
/************************************************************************************ /************************************************************************************
* Name: at24c_ioctl * Name: at24c_ioctl
************************************************************************************/ ************************************************************************************/
@@ -373,6 +492,13 @@ static int at24c_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
ret = at24c_eraseall(priv); ret = at24c_eraseall(priv);
break; break;
#ifdef CONFIG_AT24XX_EXTENDED
case MTDIOC_EXTENDED:
priv->extended = (arg != 0);
ret = OK;
break;
#endif
case MTDIOC_XIPBASE: case MTDIOC_XIPBASE:
default: default:
ret = -ENOTTY; /* Bad command */ ret = -ENOTTY; /* Bad command */
@@ -410,7 +536,7 @@ FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_dev_s *dev)
*/ */
priv = &g_at24c; priv = &g_at24c;
if (priv) if (!priv->initd)
{ {
/* Initialize the allocated structure */ /* Initialize the allocated structure */
@@ -425,15 +551,17 @@ FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_dev_s *dev)
priv->mtd.erase = at24c_erase; priv->mtd.erase = at24c_erase;
priv->mtd.bread = at24c_bread; priv->mtd.bread = at24c_bread;
priv->mtd.bwrite = at24c_bwrite; priv->mtd.bwrite = at24c_bwrite;
priv->mtd.read = at24c_read;
priv->mtd.ioctl = at24c_ioctl; priv->mtd.ioctl = at24c_ioctl;
priv->dev = dev; priv->dev = dev;
}
/* Register the MTD with the procfs system if enabled */ /* Register the MTD with the procfs system if enabled */
#ifdef CONFIG_MTD_REGISTRATION #ifdef CONFIG_MTD_REGISTRATION
mtd_register(&priv->mtd, "at24xx"); mtd_register(&priv->mtd, "at24xx");
#endif #endif
priv->initd = true;
}
/* Return the implementation-specific state structure as the MTD device */ /* Return the implementation-specific state structure as the MTD device */
@@ -441,4 +569,4 @@ FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_dev_s *dev)
return (FAR struct mtd_dev_s *)priv; return (FAR struct mtd_dev_s *)priv;
} }
#endif #endif /* CONFIG_MTD_AT24XX */
+4
View File
@@ -228,6 +228,10 @@
* OUT: None */ * OUT: None */
#define MTDIOC_SETSPEED _MTDIOC(0x0004) /* IN: New bus speed in Hz #define MTDIOC_SETSPEED _MTDIOC(0x0004) /* IN: New bus speed in Hz
* OUT: None */ * OUT: None */
#define MTDIOC_EXTENDED _MTDIOC(0x0005) /* IN: unsigned long
* 0=Use normal memory region
* 1=Use alternate/extended memory
* OUT: None */
/* NuttX ARP driver ioctl definitions (see netinet/arp.h) *******************/ /* NuttX ARP driver ioctl definitions (see netinet/arp.h) *******************/