at24c i2c EEPROM driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> Here's a refreshed version of the patch ... addressing much of
> your feedback and replacing those nasty switch statements with
> some more reasonable table driven logic.  It's more generic.

And here's what will be the last update for a while.  The main
patch I'd like to see someone contribute is SMBUS support so
this can run in PC type hardware too.

- Dave


Add a simple driver for most I2C EEPROMs, giving sysfs read/write
access to their contents.  Tested with 24LC00, AT24C04, and 24LC64
so far; at both 100 and 400 KHz.

Changes from the last snapshot include bugfixes to address chip write
delays (an msleep, making things veeerrry slow at HZ=100), cleanups,
and claiming all a chip's addresses, to lock out any other drivers.
Also Kconfig doesn't headline Atmel AT24C series any more, since
they're far from the only chips supported.

Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>

--- osk.orig/drivers/i2c/chips/Kconfig	2005-07-17 17:28:00.000000000 -0700
+++ osk/drivers/i2c/chips/Kconfig	2005-07-17 17:28:02.000000000 -0700
@@ -406,6 +406,29 @@
 menu "Other I2C Chip support"
 	depends on I2C
 
+config I2C_AT24C
+	tristate "EEPROMs from most vendors"
+	depends on I2C && EXPERIMENTAL
+	help
+	  Enable this driver to get read/write support to most I2C EEPROMs,
+	  after you configure the driver to know about each eeprom on on
+	  your target board.  Use these generic chip names, instead of
+	  vendor-specific ones like at24c64 or 24lc02:
+
+	     24c00, 24c01, 24c02, dimm (readonly 24c02), 24c04, 24c08,
+	     24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024
+
+	  Unless you like data loss puzzles, always be sure that any chip
+	  you configure as a 24c32 (32 Kbits) or larger is NOT really a
+	  24c16 (16 Kbits) or smaller.  (Marking the chip as readonly won't
+	  help recover from this.)
+
+	  The current version of this driver demands full I2C bus support,
+	  so it won't yet work on most PCs (limited to SMBUS).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called at24c.
+
 config SENSORS_DS1337
 	tristate "Dallas Semiconductor DS1337 and DS1339 Real Time Clock"
 	depends on I2C && EXPERIMENTAL
--- osk.orig/drivers/i2c/chips/Makefile	2005-07-17 17:28:00.000000000 -0700
+++ osk/drivers/i2c/chips/Makefile	2005-07-17 17:28:02.000000000 -0700
@@ -7,6 +7,9 @@
 obj-$(CONFIG_SENSORS_W83627HF)	+= w83627hf.o
 obj-$(CONFIG_SENSORS_W83781D)	+= w83781d.o
 
+# let 'at24c' get a chance before the less powerful 'eeprom'
+obj-$(CONFIG_I2C_AT24C)		+= at24c.o
+
 obj-$(CONFIG_SENSORS_ADM1021)	+= adm1021.o
 obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025.o
 obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ osk/drivers/i2c/chips/at24c.c	2005-07-17 17:28:02.000000000 -0700
@@ -0,0 +1,737 @@
+/*
+ * at24c.c -- support many I2C EEPROMs, including Atmel AT24C models
+ *
+ * Copyright (C) 2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+// #define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#ifdef CONFIG_ARM
+#include <asm/mach-types.h>
+#endif
+
+
+/* If all the bus drivers are statically linked, I2C drivers often won't
+ * need hotplug support.  Iff that's true in your config, you can #define
+ * NO_I2C_DYNAMIC_BUSSES and shrink this driver's runtime footprint.
+ */
+#define NO_I2C_DYNAMIC_BUSSES
+
+#ifdef	NO_I2C_DYNAMIC_BUSSES
+#undef	__devinit
+#undef	__devinitdata
+#undef	__devexit
+#undef	__devexit_p
+/* no I2C devices will be hotplugging */
+#define	__devinit	__init
+#define	__devinitdata	__initdata
+#define	__devexit	__exit
+#define	__devexit_p	__exit_p
+#endif
+
+
+/* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
+ * Differences between different vendor product lines (like Atmel AT24C or
+ * MicroChip 24LC, etc) won't much matter for typical read/write access.
+ *
+ * However, misconfiguration can lose data (e.g. "set 16 bit memory address"
+ * could be interpreted as "write data at 8 bit address", or writing with too
+ * big a page size), so board-specific EEPROM configuration should use static
+ * board descriptions rather than dynamic probing when there's much potential
+ * for confusion.
+ *
+ * Using /etc/modprobe.conf you might configure a system with several EEPROMs
+ * something like this:
+ *
+ *   options at24c	chip_names=24c00,dimm,dimm,24c64  \
+ *			probe=0,50,1,50,1,51,2,52 \
+ *			readonly=Y,Y,Y,N
+ *
+ * Or, add some board-specific code at the end of this driver, before
+ * registering the driver.
+ *
+ * CONFIGURATION MECHANISM IS SUBJECT TO CHANGE!!
+ *
+ * Key current(*) differences from "eeprom" driver:
+ *  (a) "at24c" also supports write access
+ *  (b) "at24c" handles all common eeproms, not just 24c02 compatibles
+ *  (c) "eeprom" will probe i2c and set up a 24c02 at each address;
+ *      "at24c" expects a static config, with part types and addresses
+ *  (d) "eeprom" also works on SMBUS-only pc type hardware
+ *
+ * (*) Patches accepted...
+ */
+static unsigned short probe[I2C_CLIENT_MAX_OPTS] __devinitdata
+	= I2C_CLIENT_DEFAULTS;
+static unsigned n_probe __devinitdata;
+module_param_array(probe, ushort, &n_probe, 0);
+MODULE_PARM_DESC(probe, "List of adapter,address pairs for I2C eeproms");
+
+static unsigned short ignore[] __devinitdata = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data __devinitdata = {
+	.normal_i2c	= ignore,
+	.probe		= probe,
+	.ignore		= ignore,
+	.force		= ignore,
+};
+
+static char *chip_names[I2C_CLIENT_MAX_OPTS / 2] __devinitdata;
+static unsigned n_chip_names __devinitdata;
+module_param_array(chip_names, charp, &n_chip_names, 0);
+MODULE_PARM_DESC(chip_names,
+		"Type of chip; one chip name per probe pair");
+
+static int readonly[I2C_CLIENT_MAX_OPTS / 2] __devinitdata;
+static unsigned n_readonly __devinitdata;
+module_param_array(readonly, bool, &n_readonly, 0);
+MODULE_PARM_DESC(chip_names, "Readonly flags, one per probe pair");
+
+
+/* As seen through Linux I2C, differences between the most common types
+ * of I2C memory include:
+ *	- How many I2C addresses the chip consumes: 1, 2, 4, or 8?
+ *	- Memory address space for one I2C address:  256 bytes, or 64 KB?
+ *	- How full that memory space is:  16 bytes, 256, 32Kb, etc?
+ *	- What write page size does it support?
+ */
+struct chip_desc {
+	u32		byte_len;		/* of 1..8 i2c addrs, total */
+	char		name[10];
+	u16		page_size;		/* for writes */
+	u8		i2c_addr_mask;		/* for multi-addr chips */
+	u8		flags;
+#define	EE_ADDR2	0x0080			/* 16 bit addrs; <= 64 KB */
+#define	EE_READONLY	0x0040
+#define	EE_24RF08	0x0001
+};
+
+struct at24c_data {
+	struct i2c_client	client;
+	struct semaphore	lock;
+	struct bin_attribute	bin;
+
+	struct chip_desc	chip;
+
+	/* each chip has an internal "memory address" pointer;
+	 * we remember it for faster read access.
+	 */
+	u32			next_addr;
+
+	/* some chips tie up multiple I2C addresses ... */
+	int			users;
+	struct i2c_client	extras[];
+};
+
+/* Specs often allow 5 msec for a page write, sometimes 20 msec;
+ * it's important to recover from write timeouts.
+ */
+#define	EE_TIMEOUT	25
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct chip_desc chips[] __devinitdata = {
+
+/* this first block of EEPROMS uses 8 bit memory addressing
+ * and can be accessed using standard SMBUS requests.
+ */
+{
+	/* 128 bit chip */
+	.name		= "24c00",
+	.byte_len	= 128 / 8,
+	.i2c_addr_mask	= 0x07,			/* I2C A0-A2 ignored */
+	.page_size	= 1,
+}, {
+	/* 1 Kbit chip */
+	.name		= "24c01",
+	.byte_len	= 1024 / 8,
+	/* some have 16 byte pages: */
+	.page_size	= 8,
+}, {
+	/* 2 Kbit chip */
+	.name		= "24c02",
+	.byte_len	= 2048 / 8,
+	/* some have 16 byte pages: */
+	.page_size	= 8,
+}, {
+	/* 2 Kbit chip */
+	.name		= "dimm",		/* 24c02 in memory DIMMs */
+	.byte_len	= 2048 / 8,
+	.flags		= EE_READONLY,
+	/* some have 16 byte pages: */
+	.page_size	= 8,
+}, {
+	/* 4 Kbit chip */
+	.name		= "24c04",
+	.byte_len	= 4096 / 8,
+	.page_size	= 16,
+	.i2c_addr_mask	= 0x01,			/* I2C A0 is MEM A8 */
+}, {
+	/* 8 Kbit chip */
+	.name		= "24c08",
+	.byte_len	= 8192 / 8,
+	.page_size	= 16,
+	.i2c_addr_mask	= 0x03, 		/* I2C A1-A0 is MEM A9-A8 */
+}, {
+	/* 8 Kbit chip */
+	.name		= "24rf08",
+	.byte_len	= 8192 / 8,
+	.flags		= EE_24RF08,
+	.page_size	= 16,
+	.i2c_addr_mask	= 0x03, 		/* I2C A1-A0 is MEM A9-A8 */
+}, {
+	/* 16 Kbit chip */
+	.name		= "24c16",
+	.byte_len	= 16384 / 8,
+	.page_size	= 16,
+	.i2c_addr_mask	= 0x07,			/* I2C A2-A0 is MEM A10-A8 */
+},
+
+/* this second block of EEPROMS uses 16 bit memory addressing
+ * and can't be accessed using standard SMBUS requests.
+ * REVISIT maybe SMBUS 2.0 requests could work ...
+ */
+{
+	/* 32 Kbits */
+	.name		= "24c32",
+	.byte_len	= 32768 / 8,
+	.flags		= EE_ADDR2,
+	.page_size	= 32,
+}, {
+	/* 64 Kbits */
+	.name		= "24c64",
+	.byte_len	= 65536 / 8,
+	.flags		= EE_ADDR2,
+	.page_size	= 32,
+}, {
+	/* 128 Kbits */
+	.name		= "24c128",
+	.byte_len	= 131072 / 8,
+	.flags		= EE_ADDR2,
+	.page_size	= 64,
+}, {
+	/* 256 Kbits */
+	.name		= "24c256",
+	.byte_len	= 262144 / 8,
+	.flags		= EE_ADDR2,
+	.page_size	= 64,
+}, {
+	/* 512 Kbits */
+	.name		= "24c512",
+	.byte_len	= 524288 / 8,
+	.flags		= EE_ADDR2,
+	.page_size	= 128,
+}, {
+	/* 1 Mbits */
+	.name		= "24c1024",
+	.byte_len	= 1048576 / 8,
+	.flags		= EE_ADDR2,
+	.page_size	= 256,
+	.i2c_addr_mask	= 0x01,			/* I2C A0 is MEM A16 */
+}
+
+};
+
+static inline const struct chip_desc *__devinit
+find_chip(const char *s)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(chips); i++)
+		if (strnicmp(s, chips[i].name, sizeof chips[i].name) == 0)
+			return &chips[i];
+	return NULL;
+}
+
+static inline const struct chip_desc *__devinit
+which_chip(struct i2c_adapter *adapter, int address, int *writable)
+{
+	unsigned		i;
+
+	for (i = 0; i < n_probe; i++) {
+		const struct chip_desc	*chip;
+		char			*name;
+
+		if (probe[i++] != adapter->id)
+			continue;
+		if (probe[i] != address)
+			continue;
+
+		i >>= 1;
+		if ((name = chip_names[i]) == NULL) {
+			dev_err(&adapter->dev, "no chipname for addr %d\n",
+				address);
+			return NULL;
+
+		}
+		chip = find_chip(name);
+		if (chip == NULL)
+			dev_err(&adapter->dev, "unknown chipname %s\n", name);
+
+		/* user specified r/o value will overide the default */
+		if (i < n_readonly)
+			*writable = !readonly[i];
+		else
+			*writable = !(chip->flags & EE_READONLY);
+		return chip;
+	}
+
+	/* "can't happen" */
+	dev_dbg(&adapter->dev, "addr %d not in probe[]\n", address);
+	return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* This parameter is to help this driver avoid blocking other drivers
+ * out of I2C for potentially troublesome amounts of time.  With a
+ * 100 KHz I2C clock, one 256 byte read takes about 1/43 second.
+ * VALUE MUST BE A POWER OF TWO so writes will align on pages!!
+ * Note that SMBUS has a 32 byte ceiling...
+ */
+static unsigned io_limit = 128;
+module_param(io_limit, uint, 0);
+MODULE_PARM_DESC(io_limit, "maximum bytes per i/o (default 128)");
+
+static inline int at24c_ee_address(
+	const struct chip_desc	*chip,
+	struct i2c_msg		*msg,
+	unsigned		*offset,
+	u32			*next_addr
+)
+{
+	unsigned	per_address = 256;
+
+	if (*offset >= chip->byte_len)
+		return -EINVAL;
+
+	/* Nothing to do unless accessing that memory would go to some
+	 * later I2C address.  In that case, modify the output params.
+	 * Yes, it can loop more than once on 24c08 and 24c16.
+	 */
+	if (chip->flags & EE_ADDR2)
+		per_address = 64 * 1024;
+	while (*offset >= per_address) {
+		msg->addr++;
+		*offset -= per_address;
+		*next_addr -= per_address;
+	}
+	return 0;
+}
+
+static ssize_t
+at24c_ee_read(
+	struct at24c_data	*at24c,
+	char			*buf,
+	unsigned		offset,
+	size_t			count
+)
+{
+	struct i2c_msg		msg;
+	ssize_t			status;
+	u32			next_addr;
+	size_t			transferred = 0;
+
+	down(&at24c->lock);
+	msg.addr = at24c->client.addr;
+	msg.flags = 0;
+
+	/* maybe adjust i2c address and offset
+	 * NOTE:  we could now search at24c->extras to choose use
+	 * some i2c_client other than the main one...
+	 */
+	next_addr = at24c->next_addr;
+	status = at24c_ee_address(&at24c->chip, &msg, &offset, &next_addr);
+	if (status < 0)
+		goto done;
+
+	/* REVISIT at least some key cases here can use just the
+	 * SMBUS subset; one issue is 16 byte "register" writes.
+	 */
+
+	/* Maybe change the current on-chip address with a dummy write */
+	if (next_addr != offset) {
+		u8	tmp[2];
+
+		msg.buf = tmp;
+		tmp[1] = (u8) offset;
+		tmp[0] = (u8) (offset >> 8);
+		if (at24c->chip.flags & EE_ADDR2) {
+			msg.len = 2;
+		} else {
+			msg.len = 1;
+			msg.buf++;
+		}
+		status = i2c_transfer(at24c->client.adapter, &msg, 1);
+		dev_dbg(&at24c->client.dev,
+				"addr %02x, set current to %d --> %d\n",
+				msg.addr, offset, status);
+		if (status < 0)
+			goto done;
+		next_addr = at24c->next_addr = offset;
+	}
+
+	/* issue bounded sequential reads for the data bytes, knowing that
+	 * read page rollover goes to the next page and/or memory block.
+	 */
+	while (count > 0) {
+		unsigned	segment;
+
+		if (count > io_limit)
+			segment = io_limit;
+		else
+			segment = count;
+
+		msg.len = segment;
+		msg.buf = buf;
+		msg.flags = I2C_M_RD;
+		status = i2c_transfer(at24c->client.adapter, &msg, 1);
+		dev_dbg(&at24c->client.dev, "read %d, %d --> %d\n",
+				transferred, count, status);
+
+		if (status < 0)
+			break;
+		if (status != segment) {
+			status = -EIO;
+			break;
+		}
+
+		count -= segment;
+
+		at24c->next_addr += segment;
+		offset += segment;
+		buf += segment;
+		transferred += segment;
+	}
+done:
+	if (status <= 0)
+		at24c->next_addr = at24c->bin.size;
+	up(&at24c->lock);
+	return transferred ? transferred : status;
+}
+
+static ssize_t
+at24c_bin_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct i2c_client	*client;
+	struct at24c_data	*at24c;
+
+	client = to_i2c_client(container_of(kobj, struct device, kobj));
+	at24c = i2c_get_clientdata(client);
+
+	if (unlikely(off >= at24c->bin.size))
+		return 0;
+	if ((off + count) > at24c->bin.size)
+		count = at24c->bin.size - off;
+	if (unlikely(!count))
+		return count;
+
+	/* we don't maintain caches for any data:  simpler, cheaper */
+	return at24c_ee_read(at24c, buf, off, count);
+}
+
+
+/* Note that if the hardware write-protect pin is pulled high, the whole
+ * chip is normally write protected.  But there are plenty of product
+ * variants here, including OTP fuses and partial chip protect.
+ */
+static ssize_t
+at24c_ee_write(struct at24c_data *at24c, char *buf, loff_t off, size_t count)
+{
+	struct i2c_msg		msg;
+	ssize_t			status = 0;
+	unsigned		written = 0;
+	u32			scratch;
+	unsigned		buf_size;
+	unsigned long		timeout, retries;
+
+	/* temp buffer lets us stick the address at the beginning */
+	buf_size = at24c->chip.page_size;
+	if (buf_size > io_limit)
+		buf_size = io_limit;
+	msg.buf = kmalloc(buf_size + 2, GFP_KERNEL);
+	if (!msg.buf)
+		return -ENOMEM;
+	msg.flags = 0;
+
+	/* For write, rollover is within the page ... so we write at
+	 * most one page, then manually roll over to the next page.
+	 */
+	down(&at24c->lock);
+	timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
+	retries = 0;
+	do {
+		unsigned	segment;
+		unsigned	offset = (unsigned) off;
+
+		/* maybe adjust i2c address and offset */
+		msg.addr = at24c->client.addr;
+		status = at24c_ee_address(&at24c->chip, &msg,
+						&offset, &scratch);
+		if (status < 0)
+			break;
+
+		/* write as much of a page as we can */
+		segment = buf_size - (offset % buf_size);
+		if (segment > count)
+			segment = count;
+		msg.len = segment + 1;
+		if (at24c->chip.flags & EE_ADDR2) {
+			msg.len++;
+			msg.buf[1] = (u8) offset;
+			msg.buf[0] = (u8) (offset >> 8);
+			memcpy(&msg.buf[2], buf, segment);
+		} else {
+			msg.buf[0] = offset;
+			memcpy(&msg.buf[1], buf, segment);
+		}
+
+		/* The chip may take a while to finish its previous write;
+		 * this write also polls for completion of the last one.
+		 * So we always retry a few times.
+		 */
+		status = i2c_transfer(at24c->client.adapter, &msg, 1);
+		dev_dbg(&at24c->client.dev,
+			"write %d bytes to %02x at %d --> %d (%ld)\n",
+			segment, msg.addr, offset, status, jiffies);
+		if (status < 0) {
+			if (retries++ < 3 || time_after(timeout, jiffies)) {
+				/* REVISIT:  at HZ=100, this is sloooow */
+				msleep(1);
+				continue;
+			}
+			dev_err(&at24c->client.dev,
+				"write %d bytes offset %d to %02x, "
+				"%ld ticks err %d\n",
+				segment, offset, msg.addr,
+				jiffies - (timeout - EE_TIMEOUT),
+				status);
+			status = -ETIMEDOUT;
+			break;
+		}
+
+		off += segment;
+		buf += segment;
+		count -= segment;
+		written += segment;
+		if (status < 0 || count == 0) {
+			if (written != 0)
+				status = written;
+			break;
+		}
+
+		/* yielding may avoid the losing msleep() path above */
+		yield();
+		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
+		retries = 0;
+	} while (count > 0);
+	at24c->next_addr = at24c->bin.size;
+	up(&at24c->lock);
+
+	kfree(msg.buf);
+	return status;
+}
+
+static ssize_t
+at24c_bin_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct i2c_client	*client;
+	struct at24c_data	*at24c;
+
+	client = to_i2c_client(container_of(kobj, struct device, kobj));
+	at24c = i2c_get_clientdata(client);
+
+	if (unlikely(off >= at24c->bin.size))
+		return -EFBIG;
+	if ((off + count) > at24c->bin.size)
+		count = at24c->bin.size - off;
+	if (unlikely(!count))
+		return count;
+
+	return at24c_ee_write(at24c, buf, off, count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver at24c_driver;
+
+static void __devinit
+at24c_activate(
+	struct i2c_adapter	*adapter,
+	int			address,
+	const struct chip_desc	*chip,
+	int			writable
+)
+{
+	struct at24c_data	*at24c;
+	int			err = -ENOMEM;
+
+	if (!(at24c = kcalloc(1, sizeof(struct at24c_data)
+			+ chip->i2c_addr_mask * sizeof(struct i2c_client),
+			GFP_KERNEL)))
+		goto fail;
+
+	init_MUTEX(&at24c->lock);
+	at24c->chip = *chip;
+	snprintf(at24c->client.name, I2C_NAME_SIZE,
+			"%s I2C EEPROM", at24c->chip.name);
+
+	/* Export the EEPROM bytes through sysfs, since that's convenient.
+	 * By default, only root should see the data (maybe passwords etc)
+	 */
+	at24c->bin.attr.name = "eeprom";
+	at24c->bin.attr.mode = S_IRUSR;
+	at24c->bin.attr.owner = THIS_MODULE;
+	at24c->bin.read = at24c_bin_read;
+
+	at24c->bin.size = at24c->chip.byte_len;
+	at24c->next_addr = at24c->bin.size;
+	if (writable) {
+		at24c->bin.write = at24c_bin_write;
+		at24c->bin.attr.mode |= S_IWUSR;
+	}
+
+	/* register the first chip address */
+	address &= ~at24c->chip.i2c_addr_mask;
+
+	at24c->client.addr = address;
+	at24c->client.adapter = adapter;
+	at24c->client.driver = &at24c_driver;
+	at24c->client.flags = 0;
+	i2c_set_clientdata(&at24c->client, at24c);
+
+	/* prevent AT24RF08 corruption, a possible consequence of the
+	 * probe done by the i2c core (verifying a chip is present).
+	 */
+	if (chip->flags & EE_24RF08)
+		i2c_smbus_write_quick(&at24c->client, 0);
+
+	if ((err = i2c_attach_client(&at24c->client)))
+		goto fail;
+	at24c->users++;
+
+	/* then register any other addresses, so other drivers can't */
+	if (chip->i2c_addr_mask) {
+		unsigned		i;
+		struct i2c_client	*c;
+
+		for (i = 0; i < chip->i2c_addr_mask; i++) {
+			c = &at24c->extras[i];
+			c->addr = address + i + 1;
+			c->adapter = adapter;
+			c->driver = &at24c_driver;
+
+			i2c_set_clientdata(c, at24c);
+			snprintf(c->name, sizeof c->name,
+				"%s address %d", chip->name, i + 2);
+			err = i2c_attach_client(c);
+			if (err == 0)
+				at24c->users++;
+		}
+	}
+
+	sysfs_create_bin_file(&at24c->client.dev.kobj, &at24c->bin);
+
+	dev_info(&at24c->client.dev, "%d byte %s%s\n",
+		at24c->bin.size, at24c->client.name,
+		writable ? " (writable)" : "");
+	dev_dbg(&at24c->client.dev, "page_size %d, i2c_addr_mask %d\n",
+		at24c->chip.page_size, at24c->chip.i2c_addr_mask);
+	return;
+fail:
+	dev_dbg(&adapter->dev, "%s probe, err %d\n", at24c_driver.name, err);
+	kfree(at24c);
+}
+
+static int __devinit
+at24c_old_probe(struct i2c_adapter *adapter, int address, int kind)
+{
+	const struct chip_desc	*chip;
+	int			writable;
+
+	chip = which_chip(adapter, address, &writable);
+	if (chip)
+		at24c_activate(adapter, address, chip, writable);
+	return 0;
+}
+
+
+static int __devinit at24c_attach_adapter(struct i2c_adapter *adapter)
+{
+	/* REVISIT: using SMBUS calls would improve portability, though
+	 * maybe at the cost of support for 16 bit memory addressing...
+	 */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
+		dev_dbg(&adapter->dev, "%s probe, no I2C\n",
+			at24c_driver.name);
+		return 0;
+	}
+	return i2c_probe(adapter, &addr_data, at24c_old_probe);
+}
+
+static int __devexit at24c_detach_client(struct i2c_client *client)
+{
+	int			err;
+	struct at24c_data	*at24c;
+
+	err = i2c_detach_client(client);
+	if (err) {
+		dev_err(&client->dev, "deregistration failed, %d\n", err);
+		return err;
+	}
+
+	/* up to eight clients per chip, detached in any order... */
+	at24c = i2c_get_clientdata(client);
+	if (at24c->users-- == 0)
+		kfree(at24c);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver at24c_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "at24c",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= at24c_attach_adapter,
+	.detach_client	= __devexit_p(at24c_detach_client),
+};
+
+static int __init at24c_init(void)
+{
+#ifdef CONFIG_OMAP_OSK_MISTRAL
+	if (machine_is_omap_osk()) {
+		n_chip_names = 1;
+		n_probe = 1;
+		chip_names[0] = "24c04";
+		probe[0] = 0;
+		probe[1] = 0x50;
+	}
+
+#endif
+	return i2c_add_driver(&at24c_driver);
+}
+module_init(at24c_init);
+
+static void __exit at24c_exit(void)
+{
+	i2c_del_driver(&at24c_driver);
+}
+module_exit(at24c_exit);
+
+MODULE_DESCRIPTION("Driver for AT24C series (and compatible) I2C EEPROMs");
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
+




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux