i2c: increase BUFLEN/CMDLEN to 128, add page flag.

128 byte reads are much more common than 32 byte reads. The message
passing + setup/teardown for a read is much more expensive, in terms
of time, than the reading itself. A slightly bigger struct is well
worth the time savings. This reduces read times for /dev/eeprom
from 57 seconds per 4KB to 14 seconds.

Additionally, make sending the page address in the eeprom driver
and utility optional. This can save a little time when reading
within the same page and allows support for smaller devices that
don't support pages (example: chips containing EDID).

Change-Id: Ie48087caee40c11fa241d1555fce9309ddd27b43
This commit is contained in:
Thomas Cort 2013-07-26 19:44:52 -04:00
parent 11be35a165
commit 437177b028
7 changed files with 94 additions and 59 deletions

View file

@ -18,7 +18,7 @@
* In the future, this could be expanded to support cape EEPROMs. * In the future, this could be expanded to support cape EEPROMs.
*/ */
static int board_info_beaglebone(int fd, i2c_addr_t address); static int board_info_beaglebone(int fd, i2c_addr_t address, int flags);
/* Memory Layout of the BeagleBone and BeagleBone Black EEPROM */ /* Memory Layout of the BeagleBone and BeagleBone Black EEPROM */
typedef struct beaglebone_info typedef struct beaglebone_info
@ -32,7 +32,7 @@ typedef struct beaglebone_info
} beaglebone_info_t; } beaglebone_info_t;
static int static int
board_info_beaglebone(int fd, i2c_addr_t address) board_info_beaglebone(int fd, i2c_addr_t address, int flags)
{ {
int r; int r;
int i, j; int i, j;
@ -40,7 +40,7 @@ board_info_beaglebone(int fd, i2c_addr_t address)
beaglebone_info_t boneinfo; beaglebone_info_t boneinfo;
r = eeprom_read(fd, address, 0x0000, &boneinfo, r = eeprom_read(fd, address, 0x0000, &boneinfo,
sizeof(beaglebone_info_t)); sizeof(beaglebone_info_t), flags);
if (r == -1) { if (r == -1) {
fprintf(stderr, "Failed to read BeagleBone info r=%d\n", r); fprintf(stderr, "Failed to read BeagleBone info r=%d\n", r);
return -1; return -1;
@ -65,12 +65,12 @@ board_info_beaglebone(int fd, i2c_addr_t address)
} }
int int
board_info(int fd, i2c_addr_t address) board_info(int fd, i2c_addr_t address, int flags)
{ {
int r; int r;
uint8_t magic_number[4]; uint8_t magic_number[4];
r = eeprom_read(fd, address, 0x0000, &magic_number, 4); r = eeprom_read(fd, address, 0x0000, &magic_number, 4, flags);
if (r == -1) { if (r == -1) {
printf("%-16s: %s\n", "BOARD_NAME", "UNKNOWN"); printf("%-16s: %s\n", "BOARD_NAME", "UNKNOWN");
return 0; return 0;
@ -78,7 +78,7 @@ board_info(int fd, i2c_addr_t address)
if (magic_number[0] == 0xaa && magic_number[1] == 0x55 && if (magic_number[0] == 0xaa && magic_number[1] == 0x55 &&
magic_number[2] == 0x33 && magic_number[3] == 0xee) { magic_number[2] == 0x33 && magic_number[3] == 0xee) {
board_info_beaglebone(fd, address); board_info_beaglebone(fd, address, flags);
} else { } else {
printf("%-16s: %s\n", "BOARD_NAME", "UNKNOWN"); printf("%-16s: %s\n", "BOARD_NAME", "UNKNOWN");
} }

View file

@ -3,6 +3,7 @@
eepromread \- read data from an EEPROM eepromread \- read data from an EEPROM
.SH SYNOPSIS .SH SYNOPSIS
\fBeepromread\fR [\fB\-i\fR] [\fB\-f\fR \fIdev\fR] [\fB\-a\fR \fIslave_addr\fR] \fBeepromread\fR [\fB\-i\fR] [\fB\-f\fR \fIdev\fR] [\fB\-a\fR \fIslave_addr\fR]
[\fB\-n\fR]
.br .br
.de FL .de FL
.TP .TP
@ -24,6 +25,11 @@ eepromread \- read data from an EEPROM
.TP 5 .TP 5
.B \-a .B \-a
# Use \fIslave_address\fR instead of \fI0x50\fR. # Use \fIslave_address\fR instead of \fI0x50\fR.
.TP 5
.B \-n
# Do not send the page number when addressing the memory on the EEPROM. Some
smaller EEPROM chips aren't organized into pages and get confused if a page
number is sent with the memory address. Use this when reading EDID.
.SH EXAMPLES .SH EXAMPLES
.TP 20 .TP 20
.B eepromread -i .B eepromread -i
@ -34,6 +40,9 @@ eepromread \- read data from an EEPROM
.TP 20 .TP 20
.B eepromread -f /dev/i2c-3 -a 0x54 .B eepromread -f /dev/i2c-3 -a 0x54
# display the first 256 bytes of the EEPROM on I2C bus 3, slave address 0x54. # display the first 256 bytes of the EEPROM on I2C bus 3, slave address 0x54.
.TP 20
.B eepromread -f /dev/i2c-3 -n
# read the EDID info from the display on I2C bus 3 on the BeagleBoard-xM.
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
\fIeepromread\fR is a simple tool for viewing the contents of an EEPROM. \fIeepromread\fR is a simple tool for viewing the contents of an EEPROM.

View file

@ -1,4 +1,5 @@
#include <minix/i2c.h> #include <minix/i2c.h>
#include <minix/com.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -13,17 +14,17 @@
#include "eepromread.h" #include "eepromread.h"
static int __eeprom_read32(int fd, i2c_addr_t addr, uint16_t memaddr, static int __eeprom_read128(int fd, i2c_addr_t addr, uint16_t memaddr,
void *buf, size_t buflen); void *buf, size_t buflen, int flags);
static int eeprom_dump(int fd, i2c_addr_t addr); static int eeprom_dump(int fd, i2c_addr_t addr, int flags);
#define DEFAULT_I2C_DEVICE "/dev/i2c-1" #define DEFAULT_I2C_DEVICE "/dev/i2c-1"
#define DEFAULT_I2C_ADDRESS 0x50 #define DEFAULT_I2C_ADDRESS 0x50
/* /*
* The /dev interface only supports 32 byte reads/writes and the EEPROM is * The /dev interface only supports 128 byte reads/writes and the EEPROM is
* larger, so to read the whole EEPROM, the task is broken down into 32 byte * larger, so to read the whole EEPROM, the task is broken down into 128 byte
* chunks in eeprom_read(). __eeprom_read32() does the actual ioctl() to do * chunks in eeprom_read(). __eeprom_read128() does the actual ioctl() to do
* the read. * the read.
* *
* A future enhancement might be to add support for the /dev/eeprom interface * A future enhancement might be to add support for the /dev/eeprom interface
@ -33,8 +34,8 @@ static int eeprom_dump(int fd, i2c_addr_t addr);
*/ */
static int static int
__eeprom_read32(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf, __eeprom_read128(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
size_t buflen) size_t buflen, int flags)
{ {
int r; int r;
minix_i2c_ioctl_exec_t ioctl_exec; minix_i2c_ioctl_exec_t ioctl_exec;
@ -51,10 +52,16 @@ __eeprom_read32(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
ioctl_exec.iie_addr = addr; ioctl_exec.iie_addr = addr;
/* set the address to read from */ /* set the address to read from */
ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff); if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) {
ioctl_exec.iie_cmd[1] = (memaddr & 0xff); /* reading within the current page */
ioctl_exec.iie_cmdlen = 2; ioctl_exec.iie_cmd[0] = (memaddr & 0xff);
ioctl_exec.iie_cmdlen = 1;
} else {
/* reading from device with multiple pages */
ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff);
ioctl_exec.iie_cmd[1] = (memaddr & 0xff);
ioctl_exec.iie_cmdlen = 2;
}
ioctl_exec.iie_buflen = buflen; ioctl_exec.iie_buflen = buflen;
r = ioctl(fd, MINIX_I2C_IOCTL_EXEC, &ioctl_exec); r = ioctl(fd, MINIX_I2C_IOCTL_EXEC, &ioctl_exec);
@ -70,7 +77,7 @@ __eeprom_read32(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
int int
eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf, eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
size_t buflen) size_t buflen, int flags)
{ {
int r; int r;
uint16_t i; uint16_t i;
@ -80,10 +87,11 @@ eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
return -1; return -1;
} }
for (i = 0; i < buflen; i += 32) {
r = __eeprom_read32(fd, addr, memaddr + i, buf + i, for (i = 0; i < buflen; i += 128) {
((buflen - i) < 32) ? (buflen - i) : 32);
r = __eeprom_read128(fd, addr, memaddr + i, buf + i,
((buflen - i) < 128) ? (buflen - i) : 128, flags);
if (r == -1) { if (r == -1) {
return -1; return -1;
} }
@ -96,14 +104,14 @@ eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
* Read 256 bytes and print it to the screen in HEX and ASCII. * Read 256 bytes and print it to the screen in HEX and ASCII.
*/ */
static int static int
eeprom_dump(int fd, i2c_addr_t addr) eeprom_dump(int fd, i2c_addr_t addr, int flags)
{ {
int i, j, r; int i, j, r;
uint8_t buf[256]; uint8_t buf[256];
memset(buf, '\0', 256); memset(buf, '\0', 256);
r = eeprom_read(fd, addr, 0x0000, buf, 256); r = eeprom_read(fd, addr, 0x0000, buf, 256, flags);
if (r == -1) { if (r == -1) {
return r; return r;
} }
@ -152,13 +160,13 @@ int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int r, fd; int r, fd;
int ch, iflag = 0; int ch, iflag = 0, read_flags = 0;
char *device = DEFAULT_I2C_DEVICE; char *device = DEFAULT_I2C_DEVICE;
i2c_addr_t address = DEFAULT_I2C_ADDRESS; i2c_addr_t address = DEFAULT_I2C_ADDRESS;
setprogname(*argv); setprogname(*argv);
while ((ch = getopt(argc, argv, "a:f:i")) != -1) { while ((ch = getopt(argc, argv, "a:f:in")) != -1) {
switch (ch) { switch (ch) {
case 'a': case 'a':
address = strtol(optarg, NULL, 0x10); address = strtol(optarg, NULL, 0x10);
@ -169,6 +177,9 @@ main(int argc, char *argv[])
case 'i': case 'i':
iflag = 1; iflag = 1;
break; break;
case 'n':
read_flags |= BDEV_NOPAGE;
break;
default: default:
break; break;
} }
@ -181,13 +192,13 @@ main(int argc, char *argv[])
} }
if (iflag == 1) { if (iflag == 1) {
r = board_info(fd, address); r = board_info(fd, address, read_flags);
if (r == -1) { if (r == -1) {
fprintf(stderr, "board_info(): %s\n", strerror(errno)); fprintf(stderr, "board_info(): %s\n", strerror(errno));
return 1; return 1;
} }
} else { } else {
r = eeprom_dump(fd, address); r = eeprom_dump(fd, address, read_flags);
if (r == -1) { if (r == -1) {
fprintf(stderr, "eeprom_dump(): %s\n", strerror(errno)); fprintf(stderr, "eeprom_dump(): %s\n", strerror(errno));
return 1; return 1;

View file

@ -2,7 +2,7 @@
#define __EEPROMREAD_H #define __EEPROMREAD_H
int eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf, int eeprom_read(int fd, i2c_addr_t addr, uint16_t memaddr, void *buf,
size_t buflen); size_t buflen, int flags);
int board_info(int fd, i2c_addr_t address); int board_info(int fd, i2c_addr_t address, int flags);
#endif /* __EEPROMREAD_H */ #endif /* __EEPROMREAD_H */

View file

@ -1,4 +1,5 @@
#include <minix/blockdriver.h> #include <minix/blockdriver.h>
#include <minix/com.h>
#include <minix/drivers.h> #include <minix/drivers.h>
#include <minix/ds.h> #include <minix/ds.h>
#include <minix/i2c.h> #include <minix/i2c.h>
@ -53,10 +54,10 @@ struct blockdriver cat24c256_tab = {
.bdr_device = NULL /* 1 insance per bus, threads not needed */ .bdr_device = NULL /* 1 insance per bus, threads not needed */
}; };
static int cat24c256_read32(uint16_t memaddr, void *buf, size_t buflen); static int cat24c256_read128(uint16_t memaddr, void *buf, size_t buflen, int flags);
static int cat24c256_read(uint16_t memaddr, void *buf, size_t buflen); static int cat24c256_read(uint16_t memaddr, void *buf, size_t buflen, int flags);
static int cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen); static int cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen, int flags);
static int cat24c256_write(uint16_t memaddr, void *buf, size_t buflen); static int cat24c256_write(uint16_t memaddr, void *buf, size_t buflen, int flags);
/* globals */ /* globals */
@ -176,13 +177,13 @@ cat24c256_blk_transfer(dev_t minor, int do_write, u64_t pos64,
return EINVAL; return EINVAL;
} }
r = cat24c256_write(position, copybuf, count); r = cat24c256_write(position, copybuf, count, flags);
if (r != OK) { if (r != OK) {
log_warn(&log, "write failed (r=%d)\n", r); log_warn(&log, "write failed (r=%d)\n", r);
return r; return r;
} }
} else { } else {
r = cat24c256_read(position, copybuf, count); r = cat24c256_read(position, copybuf, count, flags);
if (r != OK) { if (r != OK) {
log_warn(&log, "read failed (r=%d)\n", r); log_warn(&log, "read failed (r=%d)\n", r);
return r; return r;
@ -259,16 +260,16 @@ cat24c256_blk_other(message * m)
return r; return r;
} }
/* The lower level i2c interface can only read/write 32 bytes at a time. /* The lower level i2c interface can only read/write 128 bytes at a time.
* One might want to do more I/O than that at once w/EEPROM, so there is * One might want to do more I/O than that at once w/EEPROM, so there is
* cat24c256_read() and cat24c256_read32(). cat24c256_read32() does the * cat24c256_read() and cat24c256_read128(). cat24c256_read128() does the
* actual reading in chunks up to 32 bytes. cat24c256_read() splits * actual reading in chunks up to 128 bytes. cat24c256_read() splits
* the request up into chunks and repeatedly calls cat24c256_read32() * the request up into chunks and repeatedly calls cat24c256_read128()
* until all of the requested EEPROM locations have been read. * until all of the requested EEPROM locations have been read.
*/ */
static int static int
cat24c256_read32(uint16_t memaddr, void *buf, size_t buflen) cat24c256_read128(uint16_t memaddr, void *buf, size_t buflen, int flags)
{ {
int r; int r;
minix_i2c_ioctl_exec_t ioctl_exec; minix_i2c_ioctl_exec_t ioctl_exec;
@ -286,9 +287,15 @@ cat24c256_read32(uint16_t memaddr, void *buf, size_t buflen)
ioctl_exec.iie_addr = address; ioctl_exec.iie_addr = address;
/* set the memory address to read from */ /* set the memory address to read from */
ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff); if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) {
ioctl_exec.iie_cmd[1] = (memaddr & 0xff); /* reading within the current page */
ioctl_exec.iie_cmdlen = 2; ioctl_exec.iie_cmd[0] = (memaddr & 0xff);
ioctl_exec.iie_cmdlen = 1;
} else {
ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff);
ioctl_exec.iie_cmd[1] = (memaddr & 0xff);
ioctl_exec.iie_cmdlen = 2;
}
ioctl_exec.iie_buflen = buflen; ioctl_exec.iie_buflen = buflen;
@ -307,7 +314,7 @@ cat24c256_read32(uint16_t memaddr, void *buf, size_t buflen)
} }
int int
cat24c256_read(uint16_t memaddr, void *buf, size_t buflen) cat24c256_read(uint16_t memaddr, void *buf, size_t buflen, int flags)
{ {
int r; int r;
uint16_t i; uint16_t i;
@ -317,25 +324,26 @@ cat24c256_read(uint16_t memaddr, void *buf, size_t buflen)
return -1; return -1;
} }
for (i = 0; i < buflen; i += 32) { for (i = 0; i < buflen; i += 128) {
r = cat24c256_read32(memaddr + i, buf + i, r = cat24c256_read128(memaddr + i, buf + i,
((buflen - i) < 32) ? (buflen - i) : 32); ((buflen - i) < 128) ? (buflen - i) : 128, flags);
if (r != OK) { if (r != OK) {
return r; return r;
} }
log_trace(&log, "read %d bytes starting at 0x%x\n", log_trace(&log, "read %d bytes starting at 0x%x\n",
((buflen - i) < 32) ? (buflen - i) : 32, memaddr + i); ((buflen - i) < 128) ? (buflen - i) : 128, memaddr + i);
} }
return OK; return OK;
} }
static int static int
cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen) cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen, int flags)
{ {
int r; int r;
int addrlen;
minix_i2c_ioctl_exec_t ioctl_exec; minix_i2c_ioctl_exec_t ioctl_exec;
if (buflen > (I2C_EXEC_MAX_BUFLEN - 2) || buf == NULL if (buflen > (I2C_EXEC_MAX_BUFLEN - 2) || buf == NULL
@ -352,11 +360,17 @@ cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen)
ioctl_exec.iie_cmdlen = 0; ioctl_exec.iie_cmdlen = 0;
/* set the memory address to write to */ /* set the memory address to write to */
ioctl_exec.iie_buf[0] = ((memaddr >> 8) & 0xff); if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) {
ioctl_exec.iie_buf[1] = (memaddr & 0xff); /* writing within the current page */
ioctl_exec.iie_buf[0] = (memaddr & 0xff); /* address */
memcpy(ioctl_exec.iie_buf + 2, buf, buflen); addrlen = 1;
ioctl_exec.iie_buflen = buflen + 2; } else {
ioctl_exec.iie_buf[0] = ((memaddr >> 8) & 0xff);/* page */
ioctl_exec.iie_buf[1] = (memaddr & 0xff); /* address */
addrlen = 2;
}
memcpy(ioctl_exec.iie_buf + addrlen, buf, buflen);
ioctl_exec.iie_buflen = buflen + addrlen;
r = i2cdriver_exec(bus_endpoint, &ioctl_exec); r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
if (r != OK) { if (r != OK) {
@ -371,7 +385,7 @@ cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen)
} }
int int
cat24c256_write(uint16_t memaddr, void *buf, size_t buflen) cat24c256_write(uint16_t memaddr, void *buf, size_t buflen, int flags)
{ {
int r; int r;
uint16_t i; uint16_t i;
@ -384,7 +398,7 @@ cat24c256_write(uint16_t memaddr, void *buf, size_t buflen)
for (i = 0; i < buflen; i += 16) { for (i = 0; i < buflen; i += 16) {
r = cat24c256_write16(memaddr + i, buf + i, r = cat24c256_write16(memaddr + i, buf + i,
((buflen - i) < 16) ? (buflen - i) : 16); ((buflen - i) < 16) ? (buflen - i) : 16, flags);
if (r != OK) { if (r != OK) {
return r; return r;
} }

View file

@ -1298,6 +1298,7 @@
/* Bits in 'BDEV_FLAGS' field of block device transfer requests. */ /* Bits in 'BDEV_FLAGS' field of block device transfer requests. */
# define BDEV_NOFLAGS 0x00 /* no flags are set */ # define BDEV_NOFLAGS 0x00 /* no flags are set */
# define BDEV_FORCEWRITE 0x01 /* force write to disk immediately */ # define BDEV_FORCEWRITE 0x01 /* force write to disk immediately */
# define BDEV_NOPAGE 0x02 /* eeprom: don't send page address */
/* Field names for GETRUSAGE related calls */ /* Field names for GETRUSAGE related calls */
#define RU_ENDPT m1_i1 /* indicates a process for sys_getrusage */ #define RU_ENDPT m1_i1 /* indicates a process for sys_getrusage */

View file

@ -98,8 +98,8 @@ typedef struct i2c_ioctl_exec {
void *iie_buf; /* pointer to data buffer */ void *iie_buf; /* pointer to data buffer */
size_t iie_buflen; /* length of data buffer */ size_t iie_buflen; /* length of data buffer */
} i2c_ioctl_exec_t; } i2c_ioctl_exec_t;
#define I2C_EXEC_MAX_CMDLEN 32 #define I2C_EXEC_MAX_CMDLEN 128
#define I2C_EXEC_MAX_BUFLEN 32 #define I2C_EXEC_MAX_BUFLEN 128
#define I2C_IOCTL_EXEC _IOW('I', 0, i2c_ioctl_exec_t) #define I2C_IOCTL_EXEC _IOW('I', 0, i2c_ioctl_exec_t)