aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc/eeprom/at24.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/eeprom/at24.c')
-rw-r--r--drivers/misc/eeprom/at24.c155
1 files changed, 107 insertions, 48 deletions
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 2cb2736d65a..d87f77f790d 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -20,8 +20,9 @@
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
+#include <linux/of.h>
#include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
@@ -54,7 +55,7 @@
struct at24_data {
struct at24_platform_data chip;
struct memory_accessor macc;
- bool use_smbus;
+ int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
@@ -184,11 +185,19 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
if (count > io_limit)
count = io_limit;
- if (at24->use_smbus) {
+ switch (at24->use_smbus) {
+ case I2C_SMBUS_I2C_BLOCK_DATA:
/* Smaller eeproms can work given some SMBus extension calls */
if (count > I2C_SMBUS_BLOCK_MAX)
count = I2C_SMBUS_BLOCK_MAX;
- } else {
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ count = 2;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ count = 1;
+ break;
+ default:
/*
* When we have a better choice than SMBus calls, use a
* combined I2C message. Write address; then read up to
@@ -219,10 +228,27 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
- if (at24->use_smbus) {
+ switch (at24->use_smbus) {
+ case I2C_SMBUS_I2C_BLOCK_DATA:
status = i2c_smbus_read_i2c_block_data(client, offset,
count, buf);
- } else {
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ status = i2c_smbus_read_word_data(client, offset);
+ if (status >= 0) {
+ buf[0] = status & 0xff;
+ buf[1] = status >> 8;
+ status = count;
+ }
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ status = i2c_smbus_read_byte_data(client, offset);
+ if (status >= 0) {
+ buf[0] = status;
+ status = count;
+ }
+ break;
+ default:
status = i2c_transfer(client->adapter, msg, 2);
if (status == 2)
status = count;
@@ -274,7 +300,8 @@ static ssize_t at24_read(struct at24_data *at24,
return retval;
}
-static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
+static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
@@ -395,11 +422,15 @@ static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
return retval;
}
-static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
+static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
+ if (unlikely(off >= attr->size))
+ return -EFBIG;
+
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
@@ -430,11 +461,32 @@ static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_OF
+static void at24_get_ofdata(struct i2c_client *client,
+ struct at24_platform_data *chip)
+{
+ const __be32 *val;
+ struct device_node *node = client->dev.of_node;
+
+ if (node) {
+ if (of_get_property(node, "read-only", NULL))
+ chip->flags |= AT24_FLAG_READONLY;
+ val = of_get_property(node, "pagesize", NULL);
+ if (val)
+ chip->page_size = be32_to_cpup(val);
+ }
+}
+#else
+static void at24_get_ofdata(struct i2c_client *client,
+ struct at24_platform_data *chip)
+{ }
+#endif /* CONFIG_OF */
+
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
- bool use_smbus = false;
+ int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
@@ -443,10 +495,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
- if (!id->driver_data) {
- err = -ENODEV;
- goto err_out;
- }
+ if (!id->driver_data)
+ return -ENODEV;
+
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
@@ -458,6 +509,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
*/
chip.page_size = 1;
+ /* update chipdata if OF is present */
+ at24_get_ofdata(client, &chip);
+
chip.setup = NULL;
chip.context = NULL;
}
@@ -465,22 +519,31 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
+ if (!chip.page_size) {
+ dev_err(&client->dev, "page_size must not be 0!\n");
+ return -EINVAL;
+ }
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- if (chip.flags & AT24_FLAG_ADDR16) {
- err = -EPFNOSUPPORT;
- goto err_out;
- }
- if (!i2c_check_functionality(client->adapter,
+ if (chip.flags & AT24_FLAG_ADDR16)
+ return -EPFNOSUPPORT;
+
+ if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
- err = -EPFNOSUPPORT;
- goto err_out;
+ use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ use_smbus = I2C_SMBUS_WORD_DATA;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+ use_smbus = I2C_SMBUS_BYTE_DATA;
+ } else {
+ return -EPFNOSUPPORT;
}
- use_smbus = true;
}
if (chip.flags & AT24_FLAG_TAKE8ADDR)
@@ -489,12 +552,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
num_addresses = DIV_ROUND_UP(chip.byte_len,
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
- at24 = kzalloc(sizeof(struct at24_data) +
+ at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
- if (!at24) {
- err = -ENOMEM;
- goto err_out;
- }
+ if (!at24)
+ return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
@@ -505,6 +566,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
+ sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
@@ -531,11 +593,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
- at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
- if (!at24->writebuf) {
- err = -ENOMEM;
- goto err_struct;
- }
+ at24->writebuf = devm_kzalloc(&client->dev,
+ write_max + 2, GFP_KERNEL);
+ if (!at24->writebuf)
+ return -ENOMEM;
} else {
dev_warn(&client->dev,
"cannot write due to controller restrictions.");
@@ -562,14 +623,15 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_set_clientdata(client, at24);
- dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
+ dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
at24->bin.size, client->name,
- writable ? "(writable)" : "(read-only)");
- dev_dbg(&client->dev,
- "page_size %d, num_addresses %d, write_max %d%s\n",
- chip.page_size, num_addresses,
- at24->write_max,
- use_smbus ? ", use_smbus" : "");
+ writable ? "writable" : "read-only", at24->write_max);
+ if (use_smbus == I2C_SMBUS_WORD_DATA ||
+ use_smbus == I2C_SMBUS_BYTE_DATA) {
+ dev_notice(&client->dev, "Falling back to %s reads, "
+ "performance will suffer\n", use_smbus ==
+ I2C_SMBUS_WORD_DATA ? "word" : "byte");
+ }
/* export data to kernel code */
if (chip.setup)
@@ -582,15 +644,10 @@ err_clients:
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
- kfree(at24->writebuf);
-err_struct:
- kfree(at24);
-err_out:
- dev_dbg(&client->dev, "probe error %d\n", err);
return err;
}
-static int __devexit at24_remove(struct i2c_client *client)
+static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
@@ -601,9 +658,6 @@ static int __devexit at24_remove(struct i2c_client *client)
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
- kfree(at24->writebuf);
- kfree(at24);
- i2c_set_clientdata(client, NULL);
return 0;
}
@@ -615,12 +669,17 @@ static struct i2c_driver at24_driver = {
.owner = THIS_MODULE,
},
.probe = at24_probe,
- .remove = __devexit_p(at24_remove),
+ .remove = at24_remove,
.id_table = at24_ids,
};
static int __init at24_init(void)
{
+ if (!io_limit) {
+ pr_err("at24: io_limit must not be 0!\n");
+ return -EINVAL;
+ }
+
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver);
}