diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 3fd26d7cb50e..848fda8be314 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -251,15 +251,6 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); * 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 at24_client *at24_translate_offset(struct at24_data *at24, unsigned int *offset) @@ -277,6 +268,30 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24, return &at24->client[i]; } +static size_t at24_adjust_read_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned int bits; + size_t remainder; + + /* + * In case of multi-address chips that don't rollover reads to + * the next slave address: truncate the count to the slave boundary, + * so that the read never straddles slaves. + */ + if (at24->chip.flags & AT24_FLAG_NO_RDROL) { + bits = (at24->chip.flags & AT24_FLAG_ADDR16) ? 16 : 8; + remainder = BIT(bits) - offset; + if (count > remainder) + count = remainder; + } + + if (count > io_limit) + count = io_limit; + + return count; +} + static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, unsigned int offset, size_t count) { @@ -289,9 +304,7 @@ static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, at24_client = at24_translate_offset(at24, &offset); regmap = at24_client->regmap; client = at24_client->client; - - if (count > io_limit) - count = io_limit; + count = at24_adjust_read_count(at24, offset, count); /* adjust offset for mac and serial read ops */ offset += at24->offset_adj; @@ -457,6 +470,8 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) if (device_property_present(dev, "read-only")) chip->flags |= AT24_FLAG_READONLY; + if (device_property_present(dev, "no-read-rollover")) + chip->flags |= AT24_FLAG_NO_RDROL; err = device_property_read_u32(dev, "size", &val); if (!err) diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h index 271a4e25af67..841bb2815a41 100644 --- a/include/linux/platform_data/at24.h +++ b/include/linux/platform_data/at24.h @@ -50,6 +50,8 @@ struct at24_platform_data { #define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ #define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */ #define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */ +#define AT24_FLAG_NO_RDROL BIT(1) /* does not auto-rollover reads to */ + /* the next slave address */ void (*setup)(struct nvmem_device *nvmem, void *context); void *context;