@@ -22,7 +22,9 @@ config EEPROM_AT24
If you use this with an SMBus adapter instead of an I2C adapter,
full functionality is not available. Only smaller devices are
- supported (24c16 and below, max 4 kByte).
+ supported via block reads (24c16 and below, max 4 kByte).
+ Larger devices that use 16-bit addresses will only work with
+ individual byte reads, which is very slow.
This driver can also be built as a module. If so, the module
will be called at24.
@@ -268,7 +268,26 @@ 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) {
+ if (at24->use_smbus && (at24->chip.flags & AT24_FLAG_ADDR16)) {
+ /*
+ * Emulate I2C multi-byte read by using SMBus
+ * "write byte" and "receive byte". This isn't optimal
+ * since there is an unnecessary STOP involved, but
+ * it's the only way to work on many SMBus controllers
+ * when talking to EEPROMs with multi-byte addresses.
+ */
+ status = i2c_smbus_write_byte_data(client,
+ ((offset >> 8) & 0xff), (offset & 0xff));
+ if (status < 0)
+ continue;
+
+ status = i2c_smbus_read_byte(client);
+ if (status < 0)
+ continue;
+
+ buf[0] = status;
+ count = status = 1;
+ } else if (at24->use_smbus) {
status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset,
count, buf);
} else {
@@ -559,10 +578,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* 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)
- return -EPFNOSUPPORT;
-
- if (i2c_check_functionality(client->adapter,
+ if (chip.flags & AT24_FLAG_ADDR16) {
+ /*
+ * This will be slow, but better than nothing
+ * (e.g. read @ 1.4 KiB/s).
+ */
+ use_smbus = I2C_SMBUS_BYTE_DATA;
+ } else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter,