From 526eb341447e5c83ca8035468d3a1987eca81b98 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Mon, 15 Jul 2013 10:29:31 -0400 Subject: [PATCH] cat24c256: driver for the cat24c256 i2c eeprom Change-Id: I682d136c80fc868d3f0a5edb0cf6c33c0f3a28ea --- commands/MAKEDEV/MAKEDEV.sh | 16 +- distrib/sets/lists/minix/md.evbarm | 1 + drivers/Makefile | 2 +- drivers/cat24c256/Makefile | 14 + drivers/cat24c256/README.txt | 16 + drivers/cat24c256/cat24c256.c | 517 +++++++++++++++++++++++++++++ etc/system.conf | 5 + include/minix/dmap.h | 25 +- 8 files changed, 593 insertions(+), 3 deletions(-) create mode 100644 drivers/cat24c256/Makefile create mode 100644 drivers/cat24c256/README.txt create mode 100644 drivers/cat24c256/cat24c256.c diff --git a/commands/MAKEDEV/MAKEDEV.sh b/commands/MAKEDEV/MAKEDEV.sh index 9ca26f8c2..e5eecf5d9 100644 --- a/commands/MAKEDEV/MAKEDEV.sh +++ b/commands/MAKEDEV/MAKEDEV.sh @@ -24,7 +24,13 @@ case $#:$1 in ttyq0 ttyq1 ttyq2 ttyq3 ttyq4 ttyq5 ttyq6 ttyq7 ttyq8 ttyq9 \ ttyqa ttyqb ttyqc ttyqd ttyqe ttyqf \ eth klog random uds filter fbd hello fb0 \ - i2c-1 i2c-2 i2c-3 + i2c-1 i2c-2 i2c-3 \ + eepromb1s50 eepromb1s51 eepromb1s52 eepromb1s53 \ + eepromb1s54 eepromb1s55 eepromb1s56 eepromb1s57 \ + eepromb2s50 eepromb2s51 eepromb2s52 eepromb2s53 \ + eepromb2s54 eepromb2s55 eepromb2s56 eepromb2s57 \ + eepromb3s50 eepromb3s51 eepromb3s52 eepromb3s53 \ + eepromb3s54 eepromb3s55 eepromb3s56 eepromb3s57 ;; 0:|1:-\?) cat >&2 <&2 ex=1 diff --git a/distrib/sets/lists/minix/md.evbarm b/distrib/sets/lists/minix/md.evbarm index 841f92b7a..5d9778808 100644 --- a/distrib/sets/lists/minix/md.evbarm +++ b/distrib/sets/lists/minix/md.evbarm @@ -102,6 +102,7 @@ ./usr/lib/libpadconf.a minix-sys ./usr/lib/libpadconf_pic.a minix-sys ./usr/mdec minix-sys +./usr/sbin/cat24c256 minix-sys ./usr/sbin/fb minix-sys ./usr/sbin/gpio minix-sys ./usr/sbin/i2c minix-sys diff --git a/drivers/Makefile b/drivers/Makefile index d0e2405a2..99fe499c2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \ .endif .if ${MACHINE_ARCH} == "earm" -SUBDIR= fb gpio i2c mmc log tty random +SUBDIR= cat24c256 fb gpio i2c mmc log tty random .endif .endif # ${MKIMAGEONLY} != "yes" diff --git a/drivers/cat24c256/Makefile b/drivers/cat24c256/Makefile new file mode 100644 index 000000000..5c7866352 --- /dev/null +++ b/drivers/cat24c256/Makefile @@ -0,0 +1,14 @@ +# Makefile for the i2c eeprom commonly found on the BeagleBone (CAT24C256) +PROG= cat24c256 +SRCS= cat24c256.c + +DPADD+= ${LIBI2CDRIVER} ${LIBBLOCKDRIVER} ${LIBSYS} ${LIBTIMERS} +LDADD+= -li2cdriver -lblockdriver -lsys -ltimers + +MAN= + +BINDIR?= /usr/sbin + +CPPFLAGS+= -I${NETBSDSRCDIR} + +.include diff --git a/drivers/cat24c256/README.txt b/drivers/cat24c256/README.txt new file mode 100644 index 000000000..0ec53259f --- /dev/null +++ b/drivers/cat24c256/README.txt @@ -0,0 +1,16 @@ +CAT24C256 Driver (EEPROM) +========================= + +Overview +-------- + +This is the driver for the EEPROM chip commonly found on the BeagleBone +and the BeagleBone Black as well as capes and expansion boards. + +Testing the Code +---------------- + +Starting up an instance: + +/bin/service up /usr/sbin/cat24c256 -dev /dev/eepromb1s50 -label cat24c256.1.50 -args 'bus=1 address=0x50' + diff --git a/drivers/cat24c256/cat24c256.c b/drivers/cat24c256/cat24c256.c new file mode 100644 index 000000000..70942c5d0 --- /dev/null +++ b/drivers/cat24c256/cat24c256.c @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* constants */ +#define NR_DEVS 1 /* number of devices this driver handles */ +#define EEPROM_DEV 0 /* index of eeprom device */ + +/* When passing data over a grant one needs to pass + * a buffer to sys_safecopy copybuff is used for that*/ +#define COPYBUF_SIZE 0x1000 /* 4k buff */ +static unsigned char copybuf[COPYBUF_SIZE]; + +static i2c_addr_t valid_addrs[9] = { + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x00 +}; + +/* SEF functions and variables. */ +static void sef_local_startup(void); +static int sef_cb_init(int type, sef_init_info_t * info); +static int sef_cb_lu_state_save(int); +static int lu_state_restore(void); + +/* libblockdriver callbacks */ +static int cat24c256_blk_open(dev_t minor, int access); +static int cat24c256_blk_close(dev_t minor); +static ssize_t cat24c256_blk_transfer(dev_t minor, int do_write, u64_t pos, + endpoint_t endpt, iovec_t * iov, unsigned count, int flags); +static int cat24c256_blk_ioctl(dev_t minor, unsigned int request, + endpoint_t endpt, cp_grant_id_t grant); +static struct device *cat24c256_blk_part(dev_t minor); +static int cat24c256_blk_other(message * m); + +/* Entry points into the device dependent code of block drivers. */ +struct blockdriver cat24c256_tab = { + .bdr_type = BLOCKDRIVER_TYPE_OTHER, + .bdr_open = cat24c256_blk_open, + .bdr_close = cat24c256_blk_close, + .bdr_transfer = cat24c256_blk_transfer, + .bdr_ioctl = cat24c256_blk_ioctl, /* nop -- always returns EINVAL */ + .bdr_cleanup = NULL, /* nothing allocated -- nothing to clean up */ + .bdr_part = cat24c256_blk_part, + .bdr_geometry = NULL, /* no geometry (cylinders, heads, sectors, etc) */ + .bdr_intr = NULL, /* i2c devices don't generate interrupts */ + .bdr_alarm = NULL, /* alarm not needed */ + .bdr_other = cat24c256_blk_other, /* to recv notify events from DS */ + .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_read(uint16_t memaddr, void *buf, size_t buflen); +static int cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen); +static int cat24c256_write(uint16_t memaddr, void *buf, size_t buflen); + +/* globals */ + +/* counts the number of times a device file is open */ +static int openct[NR_DEVS]; + +/* base and size of each device */ +static struct device geom[NR_DEVS]; + +/* the bus that this device is on (counting starting at 1) */ +static uint32_t bus; + +/* slave address of the device */ +static i2c_addr_t address; + +/* endpoint for the driver for the bus itself. */ +static endpoint_t bus_endpoint; + +/* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */ +static struct log log = { + .name = "cat24c256", + .log_level = LEVEL_INFO, + .log_func = default_log +}; + +static int +cat24c256_blk_open(dev_t minor, int access) +{ + log_trace(&log, "cat24c256_blk_open(%d,%d)\n", minor, access); + if (cat24c256_blk_part(minor) == NULL) { + return ENXIO; + } + + openct[minor]++; + + return OK; +} + +static int +cat24c256_blk_close(dev_t minor) +{ + log_trace(&log, "cat24c256_blk_close(%d)\n", minor); + if (cat24c256_blk_part(minor) == NULL) { + return ENXIO; + } + + if (openct[minor] < 1) { + log_warn(&log, "closing unopened device %d\n", minor); + return EINVAL; + } + openct[minor]--; + + return OK; +} + +static ssize_t +cat24c256_blk_transfer(dev_t minor, int do_write, u64_t pos64, + endpoint_t endpt, iovec_t * iov, unsigned nr_req, int flags) +{ + /* Read or write one the driver's block devices. */ + unsigned count; + struct device *dv; + u64_t dv_size; + int r; + u64_t position; + cp_grant_id_t grant; + ssize_t total = 0; + vir_bytes offset; + + log_trace(&log, "cat24c256_blk_transfer()\n"); + + /* Get minor device information. */ + dv = cat24c256_blk_part(minor); + if (dv == NULL) { + return ENXIO; + } + + if (nr_req > NR_IOREQS) { + return EINVAL; + } + + dv_size = dv->dv_size; + if (pos64 > dv_size) { + return OK; /* Beyond EOF */ + } + position = pos64; + offset = 0; + + while (nr_req > 0) { + + /* How much to transfer and where to / from. */ + count = iov->iov_size; + grant = (cp_grant_id_t) iov->iov_addr; + + /* check for EOF */ + if (position >= dv_size) { + return total; + } + + /* don't go past the end of the device */ + if (position + count > dv_size) { + count = dv_size - position; + } + + /* don't overflow copybuf */ + if (count > COPYBUF_SIZE) { + count = COPYBUF_SIZE; + } + + log_trace(&log, "transfering 0x%x bytes\n", count); + + if (do_write) { + r = sys_safecopyfrom(endpt, grant, (vir_bytes) offset, + (vir_bytes) copybuf, count); + if (r != OK) { + log_warn(&log, "safecopyfrom failed\n"); + return EINVAL; + } + + r = cat24c256_write(position, copybuf, count); + if (r != OK) { + log_warn(&log, "write failed (r=%d)\n", r); + return r; + } + } else { + r = cat24c256_read(position, copybuf, count); + if (r != OK) { + log_warn(&log, "read failed (r=%d)\n", r); + return r; + } + + r = sys_safecopyto(endpt, grant, (vir_bytes) offset, + (vir_bytes) copybuf, count); + if (r != OK) { + log_warn(&log, "safecopyto failed\n"); + return EINVAL; + } + } + + /* Book the number of bytes transferred. */ + position += count; + total += count; + offset += count; + + /* only go on to the next iov when this one is full */ + if ((iov->iov_size -= count) == 0) { + iov++; + nr_req--; + offset = 0; + } + } + + return total; +} + +static int +cat24c256_blk_ioctl(dev_t minor, unsigned int request, endpoint_t endpt, + cp_grant_id_t grant) +{ + log_trace(&log, "cat24c256_blk_ioctl(%d)\n", minor); + /* no supported ioctls for this device */ + return EINVAL; +} + +static struct device * +cat24c256_blk_part(dev_t minor) +{ + log_trace(&log, "cat24c256_blk_part(%d)\n", minor); + + if (minor >= NR_DEVS) { + return NULL; + } + + return &geom[minor]; +} + +static int +cat24c256_blk_other(message * m) +{ + int r; + + log_trace(&log, "cat24c256_blk_other(0x%x)\n", m->m_type); + + switch (m->m_type) { + case NOTIFY_MESSAGE: + if (m->m_source == DS_PROC_NR) { + log_debug(&log, + "bus driver changed state, update endpoint\n"); + i2cdriver_handle_bus_update(&bus_endpoint, bus, + address); + } + r = OK; + break; + default: + log_warn(&log, "Invalid message type (0x%x)\n", m->m_type); + r = EINVAL; + break; + } + + return r; +} + +/* The lower level i2c interface can only read/write 32 bytes at a time. + * 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 + * actual reading in chunks up to 32 bytes. cat24c256_read() splits + * the request up into chunks and repeatedly calls cat24c256_read32() + * until all of the requested EEPROM locations have been read. + */ + +static int +cat24c256_read32(uint16_t memaddr, void *buf, size_t buflen) +{ + int r; + minix_i2c_ioctl_exec_t ioctl_exec; + + if (buflen > I2C_EXEC_MAX_BUFLEN || buf == NULL + || (((uint16_t) (memaddr + buflen)) < memaddr)) { + log_warn(&log, + "buflen exceeded max or buf == NULL or would overflow\n"); + return -1; + } + + memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t)); + + ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP; + ioctl_exec.iie_addr = address; + + /* set the memory address to read from */ + 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; + + r = i2cdriver_exec(bus_endpoint, &ioctl_exec); + if (r != OK) { + return r; + } + + /* call was good, copy results to caller's buffer */ + memcpy(buf, ioctl_exec.iie_buf, buflen); + + log_debug(&log, "Read %d bytes from 0x%x 0x%x OK\n", buflen, + ioctl_exec.iie_cmd[0], ioctl_exec.iie_cmd[1]); + + return OK; +} + +int +cat24c256_read(uint16_t memaddr, void *buf, size_t buflen) +{ + int r; + uint16_t i; + + if (buf == NULL || ((memaddr + buflen) < memaddr)) { + log_warn(&log, "buf == NULL or would overflow\n"); + return -1; + } + + for (i = 0; i < buflen; i += 32) { + + r = cat24c256_read32(memaddr + i, buf + i, + ((buflen - i) < 32) ? (buflen - i) : 32); + if (r != OK) { + return r; + } + + log_trace(&log, "read %d bytes starting at 0x%x\n", + ((buflen - i) < 32) ? (buflen - i) : 32, memaddr + i); + } + + return OK; +} + +static int +cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen) +{ + int r; + minix_i2c_ioctl_exec_t ioctl_exec; + + if (buflen > (I2C_EXEC_MAX_BUFLEN - 2) || buf == NULL + || (((uint16_t) (memaddr + buflen + 2)) < memaddr)) { + log_warn(&log, + "buflen exceeded max or buf == NULL or would overflow\n"); + return -1; + } + + memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t)); + + ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP; + ioctl_exec.iie_addr = address; + ioctl_exec.iie_cmdlen = 0; + + /* set the memory address to write to */ + ioctl_exec.iie_buf[0] = ((memaddr >> 8) & 0xff); + ioctl_exec.iie_buf[1] = (memaddr & 0xff); + + memcpy(ioctl_exec.iie_buf + 2, buf, buflen); + ioctl_exec.iie_buflen = buflen + 2; + + r = i2cdriver_exec(bus_endpoint, &ioctl_exec); + if (r != OK) { + return r; + } + + log_debug(&log, "Wrote %d bytes to 0x%x 0x%x OK - First = 0x%x\n", + buflen, ioctl_exec.iie_buf[0], ioctl_exec.iie_buf[1], + ioctl_exec.iie_buf[2]); + + return OK; +} + +int +cat24c256_write(uint16_t memaddr, void *buf, size_t buflen) +{ + int r; + uint16_t i; + + if (buf == NULL || ((memaddr + buflen) < memaddr)) { + log_warn(&log, "buf == NULL or would overflow\n"); + return -1; + } + + for (i = 0; i < buflen; i += 16) { + + r = cat24c256_write16(memaddr + i, buf + i, + ((buflen - i) < 16) ? (buflen - i) : 16); + if (r != OK) { + return r; + } + + log_trace(&log, "wrote %d bytes starting at 0x%x\n", + ((buflen - i) < 16) ? (buflen - i) : 16, memaddr + i); + } + + return OK; +} + +static int +sef_cb_lu_state_save(int UNUSED(state)) +{ + ds_publish_u32("bus", bus, DSF_OVERWRITE); + ds_publish_u32("address", address, DSF_OVERWRITE); + return OK; +} + +static int +lu_state_restore(void) +{ + /* Restore the state. */ + u32_t value; + + ds_retrieve_u32("bus", &value); + ds_delete_u32("bus"); + bus = (int) value; + + ds_retrieve_u32("address", &value); + ds_delete_u32("address"); + address = (int) value; + + return OK; +} + +static int +sef_cb_init(int type, sef_init_info_t * UNUSED(info)) +{ + int r; + + if (type == SEF_INIT_LU) { + /* Restore the state. */ + lu_state_restore(); + } + + geom[EEPROM_DEV].dv_base = ((u64_t) (0)); + geom[EEPROM_DEV].dv_size = ((u64_t) (32768)); + + /* look-up the endpoint for the bus driver */ + bus_endpoint = i2cdriver_bus_endpoint(bus); + if (bus_endpoint == 0) { + log_warn(&log, "Couldn't find bus driver.\n"); + return EXIT_FAILURE; + } + + /* claim the EEPROM device */ + r = i2cdriver_reserve_device(bus_endpoint, address); + if (r != OK) { + log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n", + address, r); + return EXIT_FAILURE; + } + + if (type != SEF_INIT_LU) { + + /* sign up for updates about the i2c bus going down/up */ + r = i2cdriver_subscribe_bus_updates(bus); + if (r != OK) { + log_warn(&log, "Couldn't subscribe to bus updates\n"); + return EXIT_FAILURE; + } + + i2cdriver_announce(bus); + blockdriver_announce(type); + log_debug(&log, "announced\n"); + } + + return OK; +} + +static void +sef_local_startup(void) +{ + /* + * Register init callbacks. Use the same function for all event types + */ + sef_setcb_init_fresh(sef_cb_init); + sef_setcb_init_lu(sef_cb_init); + sef_setcb_init_restart(sef_cb_init); + + /* + * Register live update callbacks. + */ + /* Agree to update immediately when LU is requested in a valid state. */ + sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready); + /* Support live update starting from any standard state. */ + sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard); + /* Register a custom routine to save the state. */ + sef_setcb_lu_state_save(sef_cb_lu_state_save); + + /* Let SEF perform startup. */ + sef_startup(); +} + +int +main(int argc, char *argv[]) +{ + int r; + + env_setargs(argc, argv); + r = i2cdriver_env_parse(&bus, &address, valid_addrs); + if (r < 0) { + log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n"); + log_warn(&log, "Example -args 'bus=3 address=0x54'\n"); + return EXIT_FAILURE; + } else if (r > 0) { + log_warn(&log, + "Invalid slave address for device, expecting 0x50-0x57\n"); + return EXIT_FAILURE; + } + + sef_local_startup(); + + log_debug(&log, "Startup Complete\n"); + blockdriver_task(&cat24c256_tab); + log_debug(&log, "Shutting down\n"); + + return OK; +} diff --git a/etc/system.conf b/etc/system.conf index 6e111b368..209a45435 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -608,6 +608,11 @@ service i2c ipc SYSTEM RS DS; }; +service cat24c256 +{ + ipc SYSTEM RS DS i2c; +}; + service vbox { system diff --git a/include/minix/dmap.h b/include/minix/dmap.h index 71b30db53..5adc17cbd 100644 --- a/include/minix/dmap.h +++ b/include/minix/dmap.h @@ -42,7 +42,30 @@ enum dev_style { STYLE_NDEV, STYLE_DEV, STYLE_DEVA, STYLE_TTY, STYLE_CTTY, #define I2C0_MAJOR 20 /* 20 = /dev/i2c-1 (i2c-dev) */ #define I2C1_MAJOR 21 /* 21 = /dev/i2c-2 (i2c-dev) */ #define I2C2_MAJOR 22 /* 22 = /dev/i2c-3 (i2c-dev) */ - +#define EEPROMB1S50_MAJOR 23 /* 23 = /dev/eepromb1s50 (cat24c256) */ +#define EEPROMB1S51_MAJOR 24 /* 24 = /dev/eepromb1s51 (cat24c256) */ +#define EEPROMB1S52_MAJOR 25 /* 25 = /dev/eepromb1s52 (cat24c256) */ +#define EEPROMB1S53_MAJOR 26 /* 26 = /dev/eepromb1s53 (cat24c256) */ +#define EEPROMB1S54_MAJOR 27 /* 27 = /dev/eepromb1s54 (cat24c256) */ +#define EEPROMB1S55_MAJOR 28 /* 28 = /dev/eepromb1s55 (cat24c256) */ +#define EEPROMB1S56_MAJOR 29 /* 29 = /dev/eepromb1s56 (cat24c256) */ +#define EEPROMB1S57_MAJOR 30 /* 30 = /dev/eepromb1s57 (cat24c256) */ +#define EEPROMB2S50_MAJOR 31 /* 31 = /dev/eepromb2s50 (cat24c256) */ +#define EEPROMB2S51_MAJOR 32 /* 32 = /dev/eepromb2s51 (cat24c256) */ +#define EEPROMB2S52_MAJOR 33 /* 33 = /dev/eepromb2s52 (cat24c256) */ +#define EEPROMB2S53_MAJOR 34 /* 34 = /dev/eepromb2s53 (cat24c256) */ +#define EEPROMB2S54_MAJOR 35 /* 35 = /dev/eepromb2s54 (cat24c256) */ +#define EEPROMB2S55_MAJOR 36 /* 36 = /dev/eepromb2s55 (cat24c256) */ +#define EEPROMB2S56_MAJOR 37 /* 37 = /dev/eepromb2s56 (cat24c256) */ +#define EEPROMB2S57_MAJOR 38 /* 38 = /dev/eepromb2s57 (cat24c256) */ +#define EEPROMB3S50_MAJOR 39 /* 39 = /dev/eepromb3s50 (cat24c256) */ +#define EEPROMB3S51_MAJOR 40 /* 40 = /dev/eepromb3s51 (cat24c256) */ +#define EEPROMB3S52_MAJOR 41 /* 41 = /dev/eepromb3s52 (cat24c256) */ +#define EEPROMB3S53_MAJOR 42 /* 42 = /dev/eepromb3s53 (cat24c256) */ +#define EEPROMB3S54_MAJOR 43 /* 43 = /dev/eepromb3s54 (cat24c256) */ +#define EEPROMB3S55_MAJOR 44 /* 44 = /dev/eepromb3s55 (cat24c256) */ +#define EEPROMB3S56_MAJOR 45 /* 45 = /dev/eepromb3s56 (cat24c256) */ +#define EEPROMB3S57_MAJOR 46 /* 46 = /dev/eepromb3s57 (cat24c256) */ /* Minor device numbers for memory driver. */ # define RAM_DEV_OLD 0 /* minor device for /dev/ram */