diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/eeprom/at24.c | 80 |
1 files changed, 78 insertions, 2 deletions
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 6acf35af271c..3b27ff14876c 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -134,16 +134,34 @@ static const struct i2c_device_id at24_ids[] = { { "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) }, + { "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) }, /* 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) }, + { "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) }, + { "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) }, @@ -276,6 +294,59 @@ static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf, return -ETIMEDOUT; } +static ssize_t at24_eeprom_read_serial(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; + + /* + * 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. + */ + 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; + } + + 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 -ETIMEDOUT; +} + /* * Note that if the hardware write-protect pin is pulled high, the whole * chip is normally write protected. But there are plenty of product @@ -577,8 +648,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->chip = chip; at24->num_addresses = num_addresses; - at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus - : at24_eeprom_read_i2c; + if (chip.flags & AT24_FLAG_SERIAL) { + at24->read_func = at24_eeprom_read_serial; + } 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; |