# HG changeset patch
# User Matt Mackall <mpm@selenic.com>
# Date 1249942085 18000
# Node ID 953d9a779cd8d3ef2b30803ee70cd83c8806830f
# Parent  4ac22168c4f00cf68d851fc60b996d14d898071a
imported patch comcerto-i2c-stm24

diff -r 4ac22168c4f0 -r 953d9a779cd8 drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig	Mon Aug 10 17:08:05 2009 -0500
+++ b/drivers/i2c/chips/Kconfig	Mon Aug 10 17:08:05 2009 -0500
@@ -6,6 +6,12 @@
 
 menu "Miscellaneous I2C Chip support"
 
+config EEPROM_STM24
+	tristate "I2c-eeprom-stm24"
+	depends on I2C 
+	help
+	  This provides an interface to read and write the EEPROM and reset the chip.
+
 config DS1682
 	tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
 	depends on EXPERIMENTAL
diff -r 4ac22168c4f0 -r 953d9a779cd8 drivers/i2c/chips/Makefile
--- a/drivers/i2c/chips/Makefile	Mon Aug 10 17:08:05 2009 -0500
+++ b/drivers/i2c/chips/Makefile	Mon Aug 10 17:08:05 2009 -0500
@@ -15,6 +15,7 @@
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
 obj-$(CONFIG_PCF8575)		+= pcf8575.o
+obj-$(CONFIG_EEPROM_STM24)	+= st-m24-eeprom.o
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
diff -r 4ac22168c4f0 -r 953d9a779cd8 drivers/i2c/chips/st-m24-eeprom.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/i2c/chips/st-m24-eeprom.c	Mon Aug 10 17:08:05 2009 -0500
@@ -0,0 +1,279 @@
+/*
+ *  drivers/i2c/chips/st-m24-eeprom.c
+ *
+ *  Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/autoconf.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+MODULE_DESCRIPTION("ST M24 I2C EEPROM driver");
+MODULE_LICENSE("GPL");
+
+#define CONFIG_ST_M24_PAGE_SIZE 64
+#define CONFIG_ST_M24_CHIP_SIZE 32768
+#define CONFIG_ST_M24_WRITE_TIME 5
+
+#define M24_PAGE	(CONFIG_ST_M24_PAGE_SIZE)
+#define M24_SIZE	(CONFIG_ST_M24_CHIP_SIZE)
+#define M24_WAIT	(CONFIG_ST_M24_WRITE_TIME)
+
+static unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD_1(st_m24_eeprom);
+
+static struct i2c_driver m24_driver;
+
+static inline int m24_set_address(struct i2c_msg *msg, u32 addr)
+{
+	int addr_len = 1;
+
+	msg->buf[0] = addr >> 8;
+	msg->len++;
+	if (M24_SIZE > 256) {
+		msg->buf[1] = addr & 255;
+		msg->len++;
+		addr_len++;
+	}
+
+	return addr_len;
+}
+
+static int m24_set_pointer(struct i2c_client *client, u32 addr)
+{
+	int err;
+	u8 buf[2];
+	struct i2c_msg msg =
+	{
+		.addr = client->addr,
+		.flags = 0,
+		.len = 0,
+		.buf = buf,
+	};
+
+	m24_set_address(&msg, addr);
+	if ((err = i2c_transfer(client->adapter, &msg, 1)) != 1) {
+		dev_err(&client->dev, "read transaction failed - couldn't set address, code: %d\n", err);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int m24_read_data(struct i2c_client *client, void *buf, int len)
+{
+	int err;
+	struct i2c_msg	msg =
+	{
+		.addr = client->addr,
+		.flags = I2C_M_RD,
+		.len = len,
+		.buf = buf,
+	};
+
+	if ((err = i2c_transfer(client->adapter, &msg, 1)) != 1) {
+		dev_err(&client->dev, "read transaction failed, code: %d\n", err);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int m24_write_data(struct i2c_client *client, u32 addr, void *buf, void *src, int len)
+{
+	int err;
+	struct i2c_msg msg =
+	{
+		.addr = client->addr,
+		.flags = 0,
+		.len = len,
+		.buf = buf,
+	};
+
+	/* set address and copy data just after address bytes */
+	memcpy(buf + m24_set_address(&msg, addr), src, len);
+
+	if ((err = i2c_transfer(client->adapter, &msg, 1)) != 1) {
+		dev_err(&client->dev, "write transaction failed, code: %d\n", err);
+		return -EIO;
+	}
+
+	mdelay(M24_WAIT);
+
+	return 0;
+}
+
+static int m24_read_write(struct kobject *kobj, char *buf, loff_t offs, size_t len, int read)
+{
+	struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
+	int count;
+
+	if (offs + len > M24_SIZE)
+		len = M24_SIZE - offs;
+	if (len < 0)
+		return -EINVAL;
+	if (len == 0)
+		return 0;
+
+	if (read) {
+		count = m24_set_pointer(client, offs);	/* for the read transaction set address once at start */
+		if (count == 0)
+			count = m24_read_data(client, buf, len);
+		if (count == 0)
+			count = len;
+	}
+	else {
+		u8 *txbuf = kmalloc(M24_PAGE + 2, GFP_KERNEL);
+		u32 tmp;
+		int req, err;
+
+		if (txbuf != NULL) {
+			/* write all data in no more than one page size transaction */
+			count = 0;
+
+			do {
+				tmp = (offs + count + M24_PAGE) & ~(M24_PAGE - 1); 
+				req = tmp - offs - count;
+				if (req + count > len)
+					req = len - count;
+
+				err = m24_write_data(client, offs+count, txbuf, buf+count, req);
+				if (err < 0) {
+					count = err;
+					break;
+				}
+
+				count += req;
+			} while (count < len);
+
+			kfree(txbuf);
+		} else {
+			dev_err(&client->dev, "failed to allocate memory\n");
+			count = -ENOMEM;
+		}
+	}
+
+	return count;
+}
+
+static int m24_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	return m24_read_write(kobj, buf, off, count, 1);
+}
+
+static int m24_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	return m24_read_write(kobj, buf, off, count, 0);
+}
+
+static struct bin_attribute m24_attr =
+{
+	.attr = {
+		.name	= "eeprom",
+		.mode	= 0664,
+		.owner	= THIS_MODULE,
+	},
+	.size = M24_SIZE,
+	.read = m24_read,
+	.write = m24_write,
+};
+
+static int m24_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *client;
+	int err = 0;
+
+	printk("i2c:st-m24-eeprom: in m24_detect\n");
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (client == NULL) {
+		printk(KERN_ERR "st-m24-eeprom: failed to allocate memory\n");
+		err = -ENOMEM;
+		goto err;
+	}
+
+	client->addr = address;
+	client->adapter = adapter;
+	client->driver = &m24_driver;
+	client->flags = 0;
+
+	strlcpy(client->name, "ST M24 I2C EEPROM", I2C_NAME_SIZE);
+
+	err = i2c_attach_client(client);
+	if (err) {
+		printk(KERN_ERR "st-m24-eeprom: failed to attach I2C client\n");
+		goto err_free;
+	}
+
+	err = sysfs_create_bin_file(&client->dev.kobj, &m24_attr);
+	if (err) {
+		dev_err(&client->dev, "failed to create sysfs node\n");
+		goto err_detach;
+	}
+
+	return 0;
+
+err_detach:
+	i2c_detach_client(client);
+
+err_free:
+	kfree(client);
+
+err:
+	return err;
+}
+
+static int m24_attach_adapter(struct i2c_adapter *adapter)
+{
+	printk("i2c:m24_attach_adapter\n");
+	return i2c_probe(adapter, &addr_data, m24_detect);
+}
+
+static int m24_detach_client(struct i2c_client *client)
+{
+	sysfs_remove_bin_file(&client->dev.kobj, &m24_attr);
+
+	i2c_detach_client(client);
+
+	kfree(client);
+
+	return 0;
+}
+
+static struct i2c_driver m24_driver = {
+	.driver = {
+		.name	= "st-m24-eeprom",
+	},
+	.attach_adapter	= m24_attach_adapter,
+	.detach_client	= m24_detach_client,
+};
+
+static int __init m24_init(void)
+{
+	printk("i2c:m24_init\n");
+	return i2c_add_driver(&m24_driver);
+}
+
+static void __exit m24_exit(void)
+{
+	i2c_del_driver(&m24_driver);
+}
+
+module_init(m24_init);
+module_exit(m24_exit);
