summaryrefslogtreecommitdiff
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.c576
1 files changed, 350 insertions, 226 deletions
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 089d6943f..3cdf8e1ca 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -23,7 +23,6 @@
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
-#include <linux/regmap.h>
#include <linux/platform_data/at24.h>
/*
@@ -59,6 +58,10 @@ struct at24_data {
int use_smbus;
int use_smbus_write;
+ ssize_t (*read_func)(struct at24_data *, char *, unsigned int, size_t);
+ ssize_t (*write_func)(struct at24_data *,
+ const char *, unsigned int, size_t);
+
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
@@ -69,7 +72,6 @@ struct at24_data {
unsigned write_max;
unsigned num_addresses;
- struct regmap_config regmap_config;
struct nvmem_config nvmem_config;
struct nvmem_device *nvmem;
@@ -111,25 +113,63 @@ MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
+/*
+ * Both reads and writes fail if the previous write didn't complete yet. This
+ * macro loops a few times waiting at least long enough for one entire page
+ * write to work while making sure that at least one iteration is run before
+ * checking the break condition.
+ *
+ * It takes two parameters: a variable in which the future timeout in jiffies
+ * will be stored and a temporary variable holding the time of the last
+ * iteration of processing the request. Both should be unsigned integers
+ * holding at least 32 bits.
+ */
+#define loop_until_timeout(tout, op_time) \
+ for (tout = jiffies + msecs_to_jiffies(write_timeout), op_time = 0; \
+ op_time ? time_before(op_time, tout) : true; \
+ usleep_range(1000, 1500), op_time = jiffies)
+
static const struct i2c_device_id at24_ids[] = {
/* needs 8 addresses as A0-A2 are ignored */
- { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
+ { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
/* old variants can't be handled with this generic entry! */
- { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
- { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
+ { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
+ { "24cs01", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
+ { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
+ { "24cs02", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
+ { "24mac402", AT24_DEVICE_MAGIC(48 / 8,
+ AT24_FLAG_MAC | AT24_FLAG_READONLY) },
+ { "24mac602", AT24_DEVICE_MAGIC(64 / 8,
+ AT24_FLAG_MAC | AT24_FLAG_READONLY) },
/* spd is a 24c02 in memory DIMMs */
- { "spd", AT24_DEVICE_MAGIC(2048 / 8,
- AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
- { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
+ { "spd", AT24_DEVICE_MAGIC(2048 / 8,
+ AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
+ { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
+ { "24cs04", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
/* 24rf08 quirk is handled at i2c-core */
- { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
- { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
- { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
- { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
- { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
- { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
- { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
- { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
+ { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
+ { "24cs08", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
+ { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
+ { "24cs16", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
+ { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
+ { "24cs32", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_ADDR16 |
+ AT24_FLAG_SERIAL |
+ AT24_FLAG_READONLY) },
+ { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
+ { "24cs64", AT24_DEVICE_MAGIC(16,
+ AT24_FLAG_ADDR16 |
+ AT24_FLAG_SERIAL |
+ AT24_FLAG_READONLY) },
+ { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
+ { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
+ { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
+ { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
{ "at24", 0 },
{ /* END OF LIST */ }
};
@@ -147,9 +187,22 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
+ *
+ * Slave address and byte offset derive from the offset. Always
+ * set the byte address; on a multi-master board, another master
+ * may have changed the chip's "current" address pointer.
+ *
+ * REVISIT some multi-address chips don't rollover page reads to
+ * the next slave address, so we may need to truncate the count.
+ * Those chips might need another quirk flag.
+ *
+ * If the real hardware used four adjacent 24c02 chips and that
+ * were misconfigured as one 24c08, that would be a similar effect:
+ * one "eeprom" file not four, but larger reads would fail when
+ * they crossed certain pages.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
- unsigned *offset)
+ unsigned int *offset)
{
unsigned i;
@@ -164,126 +217,168 @@ static struct i2c_client *at24_translate_offset(struct at24_data *at24,
return at24->client[i];
}
-static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
- unsigned offset, size_t count)
+static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf,
+ unsigned int offset, size_t count)
{
- struct i2c_msg msg[2];
- u8 msgbuf[2];
+ unsigned long timeout, read_time;
struct i2c_client *client;
+ int status;
+
+ client = at24_translate_offset(at24, &offset);
+
+ if (count > io_limit)
+ count = io_limit;
+
+ /* Smaller eeproms can work given some SMBus extension calls */
+ if (count > I2C_SMBUS_BLOCK_MAX)
+ count = I2C_SMBUS_BLOCK_MAX;
+
+ loop_until_timeout(timeout, read_time) {
+ status = i2c_smbus_read_i2c_block_data_or_emulated(client,
+ offset,
+ count, buf);
+
+ dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf,
+ unsigned int offset, size_t count)
+{
unsigned long timeout, read_time;
+ struct i2c_client *client;
+ struct i2c_msg msg[2];
int status, i;
+ u8 msgbuf[2];
memset(msg, 0, sizeof(msg));
-
- /*
- * REVISIT some multi-address chips don't rollover page reads to
- * the next slave address, so we may need to truncate the count.
- * Those chips might need another quirk flag.
- *
- * If the real hardware used four adjacent 24c02 chips and that
- * were misconfigured as one 24c08, that would be a similar effect:
- * one "eeprom" file not four, but larger reads would fail when
- * they crossed certain pages.
- */
-
- /*
- * Slave address and byte offset derive from the offset. Always
- * set the byte address; on a multi-master board, another master
- * may have changed the chip's "current" address pointer.
- */
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
- if (at24->use_smbus) {
- /* Smaller eeproms can work given some SMBus extension calls */
- if (count > I2C_SMBUS_BLOCK_MAX)
- count = I2C_SMBUS_BLOCK_MAX;
- } else {
- /*
- * When we have a better choice than SMBus calls, use a
- * combined I2C message. Write address; then read up to
- * io_limit data bytes. Note that read page rollover helps us
- * here (unlike writes). msgbuf is u8 and will cast to our
- * needs.
- */
- i = 0;
- if (at24->chip.flags & AT24_FLAG_ADDR16)
- msgbuf[i++] = offset >> 8;
- msgbuf[i++] = offset;
-
- msg[0].addr = client->addr;
- msg[0].buf = msgbuf;
- msg[0].len = i;
-
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].buf = buf;
- msg[1].len = count;
- }
-
/*
- * Reads fail if the previous write didn't complete yet. We may
- * loop a few times until this one succeeds, waiting at least
- * long enough for one entire page write to work.
+ * When we have a better choice than SMBus calls, use a combined I2C
+ * message. Write address; then read up to io_limit data bytes. Note
+ * that read page rollover helps us here (unlike writes). msgbuf is
+ * u8 and will cast to our needs.
*/
- timeout = jiffies + msecs_to_jiffies(write_timeout);
- do {
- read_time = jiffies;
- if (at24->use_smbus) {
- status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset,
- count, buf);
- } else {
- status = i2c_transfer(client->adapter, msg, 2);
- if (status == 2)
- status = count;
- }
+ i = 0;
+ if (at24->chip.flags & AT24_FLAG_ADDR16)
+ msgbuf[i++] = offset >> 8;
+ msgbuf[i++] = offset;
+
+ msg[0].addr = client->addr;
+ msg[0].buf = msgbuf;
+ msg[0].len = i;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = count;
+
+ loop_until_timeout(timeout, read_time) {
+ status = i2c_transfer(client->adapter, msg, 2);
+ if (status == 2)
+ status = count;
+
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
-
- /* REVISIT: at HZ=100, this is sloooow */
- msleep(1);
- } while (time_before(read_time, timeout));
+ }
return -ETIMEDOUT;
}
-static ssize_t at24_read(struct at24_data *at24,
- char *buf, loff_t off, size_t count)
+static ssize_t at24_eeprom_read_serial(struct at24_data *at24, char *buf,
+ unsigned int offset, size_t count)
{
- ssize_t retval = 0;
+ unsigned long timeout, read_time;
+ struct i2c_client *client;
+ struct i2c_msg msg[2];
+ u8 addrbuf[2];
+ int status;
- if (unlikely(!count))
- return count;
+ client = at24_translate_offset(at24, &offset);
+
+ memset(msg, 0, sizeof(msg));
+ msg[0].addr = client->addr;
+ msg[0].buf = addrbuf;
/*
- * Read data from chip, protecting against concurrent updates
- * from this host, but not from other I2C masters.
+ * The address pointer of the device is shared between the regular
+ * EEPROM array and the serial number block. The dummy write (part of
+ * the sequential read protocol) ensures the address pointer is reset
+ * to the desired position.
*/
- mutex_lock(&at24->lock);
+ if (at24->chip.flags & AT24_FLAG_ADDR16) {
+ /*
+ * For 16 bit address pointers, the word address must contain
+ * a '10' sequence in bits 11 and 10 regardless of the
+ * intended position of the address pointer.
+ */
+ addrbuf[0] = 0x08;
+ addrbuf[1] = offset;
+ msg[0].len = 2;
+ } else {
+ /*
+ * Otherwise the word address must begin with a '10' sequence,
+ * regardless of the intended address.
+ */
+ addrbuf[0] = 0x80 + offset;
+ msg[0].len = 1;
+ }
- while (count) {
- ssize_t status;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = count;
- status = at24_eeprom_read(at24, buf, off, count);
- if (status <= 0) {
- if (retval == 0)
- retval = status;
- break;
- }
- buf += status;
- off += status;
- count -= status;
- retval += status;
+ loop_until_timeout(timeout, read_time) {
+ status = i2c_transfer(client->adapter, msg, 2);
+ if (status == 2)
+ return count;
}
- mutex_unlock(&at24->lock);
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_eeprom_read_mac(struct at24_data *at24, char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, read_time;
+ struct i2c_client *client;
+ struct i2c_msg msg[2];
+ u8 addrbuf[2];
+ int status;
+
+ client = at24_translate_offset(at24, &offset);
+
+ memset(msg, 0, sizeof(msg));
+ msg[0].addr = client->addr;
+ msg[0].buf = addrbuf;
+ addrbuf[0] = 0x90 + offset;
+ msg[0].len = 1;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = count;
+
+ loop_until_timeout(timeout, read_time) {
+ status = i2c_transfer(client->adapter, msg, 2);
+ if (status == 2)
+ return count;
+ }
- return retval;
+ return -ETIMEDOUT;
}
/*
@@ -291,21 +386,15 @@ static ssize_t at24_read(struct at24_data *at24,
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
- * We only use page mode writes; the alternative is sloooow. This routine
- * writes at most one page.
+ * We only use page mode writes; the alternative is sloooow. These routines
+ * write at most one page.
*/
-static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
- unsigned offset, size_t count)
+
+static size_t at24_adjust_write_count(struct at24_data *at24,
+ unsigned int offset, size_t count)
{
- struct i2c_client *client;
- struct i2c_msg msg;
- ssize_t status = 0;
- unsigned long timeout, write_time;
unsigned next_page;
- /* Get corresponding I2C address and adjust offset */
- client = at24_translate_offset(at24, &offset);
-
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
@@ -315,145 +404,167 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
if (offset + count > next_page)
count = next_page - offset;
- /* If we'll use I2C calls for I/O, set up the message */
- if (!at24->use_smbus) {
- int i = 0;
+ return count;
+}
- msg.addr = client->addr;
- msg.flags = 0;
+static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24,
+ const char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, write_time;
+ struct i2c_client *client;
+ ssize_t status = 0;
- /* msg.buf is u8 and casts will mask the values */
- msg.buf = at24->writebuf;
- if (at24->chip.flags & AT24_FLAG_ADDR16)
- msg.buf[i++] = offset >> 8;
+ client = at24_translate_offset(at24, &offset);
+ count = at24_adjust_write_count(at24, offset, count);
- msg.buf[i++] = offset;
- memcpy(&msg.buf[i], buf, count);
- msg.len = i + count;
+ loop_until_timeout(timeout, write_time) {
+ status = i2c_smbus_write_i2c_block_data(client,
+ offset, count, buf);
+ if (status == 0)
+ status = count;
+
+ dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
}
- /*
- * Writes fail if the previous one didn't complete yet. We may
- * loop a few times until this one succeeds, waiting at least
- * long enough for one entire page write to work.
- */
- timeout = jiffies + msecs_to_jiffies(write_timeout);
- do {
- write_time = jiffies;
- if (at24->use_smbus_write) {
- switch (at24->use_smbus_write) {
- case I2C_SMBUS_I2C_BLOCK_DATA:
- status = i2c_smbus_write_i2c_block_data(client,
- offset, count, buf);
- break;
- case I2C_SMBUS_BYTE_DATA:
- status = i2c_smbus_write_byte_data(client,
- offset, buf[0]);
- break;
- }
-
- if (status == 0)
- status = count;
- } else {
- status = i2c_transfer(client->adapter, &msg, 1);
- if (status == 1)
- status = count;
- }
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24,
+ const char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, write_time;
+ struct i2c_client *client;
+ ssize_t status = 0;
+
+ client = at24_translate_offset(at24, &offset);
+
+ loop_until_timeout(timeout, write_time) {
+ status = i2c_smbus_write_byte_data(client, offset, buf[0]);
+ if (status == 0)
+ status = count;
+
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf,
+ unsigned int offset, size_t count)
+{
+ unsigned long timeout, write_time;
+ struct i2c_client *client;
+ struct i2c_msg msg;
+ ssize_t status = 0;
+ int i = 0;
+
+ client = at24_translate_offset(at24, &offset);
+ count = at24_adjust_write_count(at24, offset, count);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+
+ /* msg.buf is u8 and casts will mask the values */
+ msg.buf = at24->writebuf;
+ if (at24->chip.flags & AT24_FLAG_ADDR16)
+ msg.buf[i++] = offset >> 8;
+
+ msg.buf[i++] = offset;
+ memcpy(&msg.buf[i], buf, count);
+ msg.len = i + count;
- /* REVISIT: at HZ=100, this is sloooow */
- msleep(1);
- } while (time_before(write_time, timeout));
+ loop_until_timeout(timeout, write_time) {
+ status = i2c_transfer(client->adapter, &msg, 1);
+ if (status == 1)
+ status = count;
+
+ dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
+ }
return -ETIMEDOUT;
}
-static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
- size_t count)
+static int at24_read(void *priv, unsigned int off, void *val, size_t count)
{
- ssize_t retval = 0;
+ struct at24_data *at24 = priv;
+ char *buf = val;
if (unlikely(!count))
return count;
/*
- * Write data to chip, protecting against concurrent updates
+ * Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
- ssize_t status;
+ int status;
- status = at24_eeprom_write(at24, buf, off, count);
- if (status <= 0) {
- if (retval == 0)
- retval = status;
- break;
+ status = at24->read_func(at24, buf, off, count);
+ if (status < 0) {
+ mutex_unlock(&at24->lock);
+ return status;
}
buf += status;
off += status;
count -= status;
- retval += status;
}
mutex_unlock(&at24->lock);
- return retval;
+ return 0;
}
-/*-------------------------------------------------------------------------*/
-
-/*
- * Provide a regmap interface, which is registered with the NVMEM
- * framework
-*/
-static int at24_regmap_read(void *context, const void *reg, size_t reg_size,
- void *val, size_t val_size)
+static int at24_write(void *priv, unsigned int off, void *val, size_t count)
{
- struct at24_data *at24 = context;
- off_t offset = *(u32 *)reg;
- int err;
+ struct at24_data *at24 = priv;
+ char *buf = val;
- err = at24_read(at24, val, offset, val_size);
- if (err)
- return err;
- return 0;
-}
+ if (unlikely(!count))
+ return -EINVAL;
-static int at24_regmap_write(void *context, const void *data, size_t count)
-{
- struct at24_data *at24 = context;
- const char *buf;
- u32 offset;
- size_t len;
- int err;
+ /*
+ * Write data to chip, protecting against concurrent updates
+ * from this host, but not from other I2C masters.
+ */
+ mutex_lock(&at24->lock);
- memcpy(&offset, data, sizeof(offset));
- buf = (const char *)data + sizeof(offset);
- len = count - sizeof(offset);
+ while (count) {
+ int status;
- err = at24_write(at24, buf, offset, len);
- if (err)
- return err;
- return 0;
-}
+ status = at24->write_func(at24, buf, off, count);
+ if (status < 0) {
+ mutex_unlock(&at24->lock);
+ return status;
+ }
+ buf += status;
+ off += status;
+ count -= status;
+ }
-static const struct regmap_bus at24_regmap_bus = {
- .read = at24_regmap_read,
- .write = at24_regmap_write,
- .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
-};
+ mutex_unlock(&at24->lock);
-/*-------------------------------------------------------------------------*/
+ return 0;
+}
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
- struct at24_platform_data *chip)
+ struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
@@ -468,7 +579,7 @@ static void at24_get_ofdata(struct i2c_client *client,
}
#else
static void at24_get_ofdata(struct i2c_client *client,
- struct at24_platform_data *chip)
+ struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
@@ -482,7 +593,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
struct at24_data *at24;
int err;
unsigned i, num_addresses;
- struct regmap *regmap;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
@@ -544,10 +654,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
} else {
return -EPFNOSUPPORT;
}
- }
- /* Use I2C operations unless we're stuck with SMBus extensions. */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
@@ -575,6 +682,30 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->chip = chip;
at24->num_addresses = num_addresses;
+ if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) {
+ dev_err(&client->dev,
+ "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
+ return -EINVAL;
+ }
+
+ if (chip.flags & AT24_FLAG_SERIAL) {
+ at24->read_func = at24_eeprom_read_serial;
+ } else if (chip.flags & AT24_FLAG_MAC) {
+ at24->read_func = at24_eeprom_read_mac;
+ } else {
+ at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus
+ : at24_eeprom_read_i2c;
+ }
+
+ if (at24->use_smbus) {
+ if (at24->use_smbus_write == I2C_SMBUS_I2C_BLOCK_DATA)
+ at24->write_func = at24_eeprom_write_smbus_block;
+ else
+ at24->write_func = at24_eeprom_write_smbus_byte;
+ } else {
+ at24->write_func = at24_eeprom_write_i2c;
+ }
+
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || use_smbus_write) {
@@ -612,19 +743,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
}
- at24->regmap_config.reg_bits = 32;
- at24->regmap_config.val_bits = 8;
- at24->regmap_config.reg_stride = 1;
- at24->regmap_config.max_register = chip.byte_len - 1;
-
- regmap = devm_regmap_init(&client->dev, &at24_regmap_bus, at24,
- &at24->regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&client->dev, "regmap init failed\n");
- err = PTR_ERR(regmap);
- goto err_clients;
- }
-
at24->nvmem_config.name = dev_name(&client->dev);
at24->nvmem_config.dev = &client->dev;
at24->nvmem_config.read_only = !writable;
@@ -632,6 +750,12 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->nvmem_config.owner = THIS_MODULE;
at24->nvmem_config.compat = true;
at24->nvmem_config.base_dev = &client->dev;
+ at24->nvmem_config.reg_read = at24_read;
+ at24->nvmem_config.reg_write = at24_write;
+ at24->nvmem_config.priv = at24;
+ at24->nvmem_config.stride = 4;
+ at24->nvmem_config.word_size = 1;
+ at24->nvmem_config.size = chip.byte_len;
at24->nvmem = nvmem_register(&at24->nvmem_config);