From 5c015258478eaac484c2fc91bd1d06babacb1ec4 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:40 +0100 Subject: eeprom: at24: add basic regmap_i2c support This patch adds basic regmap support to be used by subsequent patches of this series. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/Kconfig | 1 + drivers/misc/eeprom/at24.c | 57 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 13 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index de58762097c4..68a1ac929917 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -4,6 +4,7 @@ config EEPROM_AT24 tristate "I2C EEPROMs / RAMs / ROMs from most vendors" depends on I2C && SYSFS select NVMEM + select REGMAP_I2C help Enable this driver to get read/write support to most I2C EEPROMs and compatible devices like FRAMs, SRAMs, ROMs etc. After you diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 4d63ac8a82e0..04c455ba4b77 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,11 @@ * which won't work on pure SMBus systems. */ +struct at24_client { + struct i2c_client *client; + struct regmap *regmap; +}; + struct at24_data { struct at24_platform_data chip; int use_smbus; @@ -81,7 +87,7 @@ struct at24_data { * Some chips tie up multiple I2C addresses; dummy devices reserve * them for us, and we'll use them with SMBus calls. */ - struct i2c_client *client[]; + struct at24_client client[]; }; /* @@ -274,7 +280,7 @@ static struct i2c_client *at24_translate_offset(struct at24_data *at24, *offset &= 0xff; } - return at24->client[i]; + return at24->client[i].client; } static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf, @@ -562,7 +568,7 @@ static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf, static int at24_read(void *priv, unsigned int off, void *val, size_t count) { struct at24_data *at24 = priv; - struct device *dev = &at24->client[0]->dev; + struct device *dev = &at24->client[0].client->dev; char *buf = val; int ret; @@ -608,7 +614,7 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) static int at24_write(void *priv, unsigned int off, void *val, size_t count) { struct at24_data *at24 = priv; - struct device *dev = &at24->client[0]->dev; + struct device *dev = &at24->client[0].client->dev; char *buf = val; int ret; @@ -676,6 +682,16 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) } } +static const struct regmap_config regmap_config_8 = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct regmap_config regmap_config_16 = { + .reg_bits = 16, + .val_bits = 8, +}; + static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct at24_platform_data chip; @@ -686,6 +702,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) struct at24_data *at24; int err; unsigned i, num_addresses; + const struct regmap_config *config; u8 test_byte; if (client->dev.platform_data) { @@ -777,8 +794,13 @@ 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); + if (chip.flags & AT24_FLAG_ADDR16) + config = ®map_config_16; + else + config = ®map_config_8; + at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + - num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); + num_addresses * sizeof(struct at24_client), GFP_KERNEL); if (!at24) return -ENOMEM; @@ -788,6 +810,11 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->chip = chip; at24->num_addresses = num_addresses; + at24->client[0].client = client; + at24->client[0].regmap = devm_regmap_init_i2c(client, config); + if (IS_ERR(at24->client[0].regmap)) + return PTR_ERR(at24->client[0].regmap); + 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."); @@ -835,18 +862,22 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) } } - at24->client[0] = client; - /* use dummy devices for multiple-address chips */ for (i = 1; i < num_addresses; i++) { - at24->client[i] = i2c_new_dummy(client->adapter, - client->addr + i); - if (!at24->client[i]) { + at24->client[i].client = i2c_new_dummy(client->adapter, + client->addr + i); + if (!at24->client[i].client) { dev_err(&client->dev, "address 0x%02x unavailable\n", client->addr + i); err = -EADDRINUSE; goto err_clients; } + at24->client[i].regmap = devm_regmap_init_i2c( + at24->client[i].client, config); + if (IS_ERR(at24->client[i].regmap)) { + err = PTR_ERR(at24->client[i].regmap); + goto err_clients; + } } i2c_set_clientdata(client, at24); @@ -905,8 +936,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) err_clients: for (i = 1; i < num_addresses; i++) - if (at24->client[i]) - i2c_unregister_device(at24->client[i]); + if (at24->client[i].client) + i2c_unregister_device(at24->client[i].client); pm_runtime_disable(&client->dev); @@ -923,7 +954,7 @@ static int at24_remove(struct i2c_client *client) nvmem_unregister(at24->nvmem); for (i = 1; i < at24->num_addresses; i++) - i2c_unregister_device(at24->client[i]); + i2c_unregister_device(at24->client[i].client); pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); -- cgit From 4604948641384555da3ad5fc9f012e939e622c37 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:42 +0100 Subject: eeprom: at24: change at24_translate_offset return type Change return type of at24_translate_offset to *at24_client to make member regmap accessible for subsequent patches of this series. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 04c455ba4b77..d00e9b509546 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -267,8 +267,8 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); * 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 int *offset) +static struct at24_client *at24_translate_offset(struct at24_data *at24, + unsigned int *offset) { unsigned i; @@ -280,17 +280,19 @@ static struct i2c_client *at24_translate_offset(struct at24_data *at24, *offset &= 0xff; } - return at24->client[i].client; + return &at24->client[i]; } static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf, unsigned int offset, size_t count) { unsigned long timeout, read_time; + struct at24_client *at24_client; struct i2c_client *client; int status; - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; if (count > io_limit) count = io_limit; @@ -318,13 +320,15 @@ 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 at24_client *at24_client; struct i2c_client *client; struct i2c_msg msg[2]; int status, i; u8 msgbuf[2]; memset(msg, 0, sizeof(msg)); - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; if (count > io_limit) count = io_limit; @@ -368,12 +372,14 @@ 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 at24_client *at24_client; struct i2c_client *client; struct i2c_msg msg[2]; u8 addrbuf[2]; int status; - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; memset(msg, 0, sizeof(msg)); msg[0].addr = client->addr; @@ -421,12 +427,14 @@ 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 at24_client *at24_client; struct i2c_client *client; struct i2c_msg msg[2]; u8 addrbuf[2]; int status; - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; memset(msg, 0, sizeof(msg)); msg[0].addr = client->addr; @@ -479,10 +487,12 @@ static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24, unsigned int offset, size_t count) { unsigned long timeout, write_time; + struct at24_client *at24_client; struct i2c_client *client; ssize_t status = 0; - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; count = at24_adjust_write_count(at24, offset, count); loop_until_timeout(timeout, write_time) { @@ -506,10 +516,12 @@ static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24, unsigned int offset, size_t count) { unsigned long timeout, write_time; + struct at24_client *at24_client; struct i2c_client *client; ssize_t status = 0; - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; loop_until_timeout(timeout, write_time) { status = i2c_smbus_write_byte_data(client, offset, buf[0]); @@ -530,12 +542,14 @@ 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 at24_client *at24_client; struct i2c_client *client; struct i2c_msg msg; ssize_t status = 0; int i = 0; - client = at24_translate_offset(at24, &offset); + at24_client = at24_translate_offset(at24, &offset); + client = at24_client->client; count = at24_adjust_write_count(at24, offset, count); msg.addr = client->addr; -- cgit From 8e5888e17f48d983eee27bd3ebf36fd03c8bb1e5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:45 +0100 Subject: eeprom: at24: add regmap-based write function Add a regmap-based write function. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index d00e9b509546..fa712279183b 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -538,6 +538,31 @@ static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24, return -ETIMEDOUT; } +static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + count = at24_adjust_write_count(at24, offset, count); + + loop_until_timeout(timeout, write_time) { + ret = regmap_bulk_write(regmap, offset, buf, count); + dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + } + + return -ETIMEDOUT; +} + static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf, unsigned int offset, size_t count) { @@ -653,7 +678,7 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) while (count) { int status; - status = at24->write_func(at24, buf, off, count); + status = at24_regmap_write(at24, buf, off, count); if (status < 0) { mutex_unlock(&at24->lock); pm_runtime_put(dev); -- cgit From d4297d6795779b0713203787eada5b8d9f0ce065 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:47 +0100 Subject: eeprom: at24: remove old write functions Remove the old and now unused write functions. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 117 --------------------------------------------- 1 file changed, 117 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index fa712279183b..5f7d81054c11 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -64,11 +64,8 @@ struct at24_client { struct at24_data { struct at24_platform_data chip; 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, @@ -76,7 +73,6 @@ struct at24_data { */ struct mutex lock; - u8 *writebuf; unsigned write_max; unsigned num_addresses; @@ -482,62 +478,6 @@ static size_t at24_adjust_write_count(struct at24_data *at24, return count; } -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 at24_client *at24_client; - struct i2c_client *client; - ssize_t status = 0; - - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - count = at24_adjust_write_count(at24, offset, 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; - } - - 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 at24_client *at24_client; - struct i2c_client *client; - ssize_t status = 0; - - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - - 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_regmap_write(struct at24_data *at24, const char *buf, unsigned int offset, size_t count) { @@ -563,47 +503,6 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, 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 at24_client *at24_client; - struct i2c_client *client; - struct i2c_msg msg; - ssize_t status = 0; - int i = 0; - - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - 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; - - 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 int at24_read(void *priv, unsigned int off, void *val, size_t count) { struct at24_data *at24 = priv; @@ -845,7 +744,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) mutex_init(&at24->lock); at24->use_smbus = use_smbus; - at24->use_smbus_write = use_smbus_write; at24->chip = chip; at24->num_addresses = num_addresses; @@ -869,15 +767,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) : 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) { @@ -889,12 +778,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) write_max = I2C_SMBUS_BLOCK_MAX; at24->write_max = write_max; - - /* buffer (data + address at the beginning) */ - 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."); -- cgit From 4bb5c13cc23c647c1fb6168d08de1fbda8f2357e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:50 +0100 Subject: eeprom: at24: add regmap-based read function Add regmap-based read function and instead of using three different read functions (standard, mac, serial) use just one and factor out the read offset adjustment for mac and serial to at24_adjust_read_offset. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 57 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 5f7d81054c11..57a317b2e9a8 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -75,6 +75,7 @@ struct at24_data { unsigned write_max; unsigned num_addresses; + unsigned int offset_adj; struct nvmem_config nvmem_config; struct nvmem_device *nvmem; @@ -312,6 +313,36 @@ static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf, return -ETIMEDOUT; } +static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct at24_client *at24_client; + struct i2c_client *client; + struct regmap *regmap; + int ret; + + at24_client = at24_translate_offset(at24, &offset); + regmap = at24_client->regmap; + client = at24_client->client; + + if (count > io_limit) + count = io_limit; + + /* adjust offset for mac and serial read ops */ + offset += at24->offset_adj; + + loop_until_timeout(timeout, read_time) { + ret = regmap_bulk_read(regmap, offset, buf, count); + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, ret, jiffies); + if (!ret) + return count; + } + + return -ETIMEDOUT; +} + static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf, unsigned int offset, size_t count) { @@ -531,7 +562,7 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) while (count) { int status; - status = at24->read_func(at24, buf, off, count); + status = at24_regmap_read(at24, buf, off, count); if (status < 0) { mutex_unlock(&at24->lock); pm_runtime_put(dev); @@ -620,6 +651,29 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) } } +static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) +{ + if (flags & AT24_FLAG_MAC) { + /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ + return 0xa0 - byte_len; + } else if (flags & AT24_FLAG_SERIAL && 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. + */ + return 0x0800; + } else if (flags & AT24_FLAG_SERIAL) { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + return 0x0080; + } else { + return 0; + } +} + static const struct regmap_config regmap_config_8 = { .reg_bits = 8, .val_bits = 8, @@ -746,6 +800,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->use_smbus = use_smbus; at24->chip = chip; at24->num_addresses = num_addresses; + at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len); at24->client[0].client = client; at24->client[0].regmap = devm_regmap_init_i2c(client, config); -- cgit From dd69a9da7120391f46dcf0c76a33e20279792a2f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:52 +0100 Subject: eeprom: at24: remove old read functions Remove the old and now unused read functions. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 186 --------------------------------------------- 1 file changed, 186 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 57a317b2e9a8..8800a38e8d84 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -63,9 +63,6 @@ struct at24_client { struct at24_data { struct at24_platform_data chip; - int use_smbus; - - ssize_t (*read_func)(struct at24_data *, char *, unsigned int, size_t); /* * Lock protects against activities from other Linux tasks, @@ -280,39 +277,6 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24, return &at24->client[i]; } -static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf, - unsigned int offset, size_t count) -{ - unsigned long timeout, read_time; - struct at24_client *at24_client; - struct i2c_client *client; - int status; - - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - - 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_regmap_read(struct at24_data *at24, char *buf, unsigned int offset, size_t count) { @@ -343,146 +307,6 @@ static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, 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 at24_client *at24_client; - struct i2c_client *client; - struct i2c_msg msg[2]; - int status, i; - u8 msgbuf[2]; - - memset(msg, 0, sizeof(msg)); - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - - if (count > io_limit) - count = io_limit; - - /* - * 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; - - 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; - } - - 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 at24_client *at24_client; - struct i2c_client *client; - struct i2c_msg msg[2]; - u8 addrbuf[2]; - int status; - - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - - 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; -} - -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 at24_client *at24_client; - struct i2c_client *client; - struct i2c_msg msg[2]; - u8 addrbuf[2]; - int status; - - at24_client = at24_translate_offset(at24, &offset); - client = at24_client->client; - - memset(msg, 0, sizeof(msg)); - msg[0].addr = client->addr; - msg[0].buf = addrbuf; - /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ - addrbuf[0] = 0xa0 - at24->chip.byte_len + 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 @@ -797,7 +621,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENOMEM; mutex_init(&at24->lock); - at24->use_smbus = use_smbus; at24->chip = chip; at24->num_addresses = num_addresses; at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len); @@ -813,15 +636,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) 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; - } - writable = !(chip.flags & AT24_FLAG_READONLY); if (writable) { if (!use_smbus || use_smbus_write) { -- cgit From a23727cb68e4bcd1eefcdab5459331db32516abd Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Nov 2017 21:51:54 +0100 Subject: eeprom: at24: remove now unneeded smbus-related code Remove remaining now unneeded code dealing with SMBUS details. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 56 +++++++--------------------------------------- 1 file changed, 8 insertions(+), 48 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 8800a38e8d84..3fd26d7cb50e 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -513,8 +513,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) struct at24_platform_data chip; kernel_ulong_t magic = 0; bool writable; - int use_smbus = 0; - int use_smbus_write = 0; struct at24_data *at24; int err; unsigned i, num_addresses; @@ -576,33 +574,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) if (chip.flags & AT24_FLAG_MAC && chip.byte_len == 4) chip.byte_len = 6; - /* 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, - I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { - 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; - } - - if (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { - use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA; - } else if (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { - use_smbus_write = I2C_SMBUS_BYTE_DATA; - chip.page_size = 1; - } - } + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && + !i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) + chip.page_size = 1; if (chip.flags & AT24_FLAG_TAKE8ADDR) num_addresses = 8; @@ -638,19 +613,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) writable = !(chip.flags & AT24_FLAG_READONLY); if (writable) { - if (!use_smbus || use_smbus_write) { - - unsigned write_max = chip.page_size; - - if (write_max > io_limit) - write_max = io_limit; - if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) - write_max = I2C_SMBUS_BLOCK_MAX; - at24->write_max = write_max; - } else { - dev_warn(&client->dev, - "cannot write due to controller restrictions."); - } + at24->write_max = min_t(unsigned int, chip.page_size, io_limit); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && + at24->write_max > I2C_SMBUS_BLOCK_MAX) + at24->write_max = I2C_SMBUS_BLOCK_MAX; } /* use dummy devices for multiple-address chips */ @@ -712,12 +678,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n", chip.byte_len, client->name, 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) -- cgit From e32213fbc5432c28268dced0dc8735dcf8532d36 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Fri, 8 Dec 2017 11:28:30 -0500 Subject: eeprom: at24: support eeproms that do not auto-rollover reads Some multi-address eeproms in the at24 family may not automatically roll-over reads to the next slave address. On those eeproms, reads that straddle slave boundaries will not work correctly. Solution: Mark such eeproms with a flag that prevents reads straddling slave boundaries. Add the AT24_FLAG_NO_RDROL flag to the eeprom entry in the device_id table, or add 'no-read-rollover' to the eeprom devicetree entry. Note that I have not personally enountered an at24 chip that does not support read rollovers. They may or may not exist. However, my hardware requires this functionality because of a quirk. Signed-off-by: Sven Van Asbroeck Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/misc') 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) -- cgit From aa4ce22897e159ec7ecf396c199ba0edcef886c3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 13 Dec 2017 11:56:23 +0100 Subject: eeprom: at24: fix coding style issues Fix issues reported by checkpatch for at24.c. Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 848fda8be314..b30db99cd951 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -70,8 +70,8 @@ struct at24_data { */ struct mutex lock; - unsigned write_max; - unsigned num_addresses; + unsigned int write_max; + unsigned int num_addresses; unsigned int offset_adj; struct nvmem_config nvmem_config; @@ -93,7 +93,7 @@ struct at24_data { * * This value is forced to be a power of two so that writes align on pages. */ -static unsigned io_limit = 128; +static unsigned int io_limit = 128; module_param(io_limit, uint, 0); MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); @@ -101,7 +101,7 @@ MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); * Specs often allow 5 msec for a page write, sometimes 20 msec; * it's important to recover from write timeouts. */ -static unsigned write_timeout = 25; +static unsigned int write_timeout = 25; module_param(write_timeout, uint, 0); MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); @@ -111,8 +111,8 @@ MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); #define AT24_BITMASK(x) (BIT(x) - 1) /* create non-zero magic value for given eeprom parameters */ -#define AT24_DEVICE_MAGIC(_len, _flags) \ - ((1 << AT24_SIZE_FLAGS | (_flags)) \ +#define AT24_DEVICE_MAGIC(_len, _flags) \ + ((1 << AT24_SIZE_FLAGS | (_flags)) \ << AT24_SIZE_BYTELEN | ilog2(_len)) /* @@ -255,7 +255,7 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); static struct at24_client *at24_translate_offset(struct at24_data *at24, unsigned int *offset) { - unsigned i; + unsigned int i; if (at24->chip.flags & AT24_FLAG_ADDR16) { i = *offset >> 16; @@ -332,7 +332,7 @@ static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, static size_t at24_adjust_write_count(struct at24_data *at24, unsigned int offset, size_t count) { - unsigned next_page; + unsigned int next_page; /* write_max is at most a page */ if (count > at24->write_max) @@ -530,7 +530,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) bool writable; struct at24_data *at24; int err; - unsigned i, num_addresses; + unsigned int i, num_addresses; const struct regmap_config *config; u8 test_byte; -- cgit From ec3c2d518b163dd2be48ac1fee8d7f1686cd769f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 Dec 2017 18:16:46 +0100 Subject: eeprom: at24: use a common prefix for all symbols in at24.c There are a couple symbols defined in the driver source file which are missing the at24_ prefix. This patch fixes that. For module params: use module_param_named() in order to not break userspace. Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index b30db99cd951..657074ec0e64 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -93,17 +93,17 @@ struct at24_data { * * This value is forced to be a power of two so that writes align on pages. */ -static unsigned int io_limit = 128; -module_param(io_limit, uint, 0); -MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); +static unsigned int at24_io_limit = 128; +module_param_named(io_limit, at24_io_limit, uint, 0); +MODULE_PARM_DESC(at24_io_limit, "Maximum bytes per I/O (default 128)"); /* * Specs often allow 5 msec for a page write, sometimes 20 msec; * it's important to recover from write timeouts. */ -static unsigned int write_timeout = 25; -module_param(write_timeout, uint, 0); -MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); +static unsigned int at24_write_timeout = 25; +module_param_named(write_timeout, at24_write_timeout, uint, 0); +MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); #define AT24_SIZE_BYTELEN 5 #define AT24_SIZE_FLAGS 8 @@ -126,8 +126,9 @@ MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); * 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; \ +#define at24_loop_until_timeout(tout, op_time) \ + for (tout = jiffies + msecs_to_jiffies(at24_write_timeout), \ + op_time = 0; \ op_time ? time_before(op_time, tout) : true; \ usleep_range(1000, 1500), op_time = jiffies) @@ -286,8 +287,8 @@ static size_t at24_adjust_read_count(struct at24_data *at24, count = remainder; } - if (count > io_limit) - count = io_limit; + if (count > at24_io_limit) + count = at24_io_limit; return count; } @@ -309,7 +310,7 @@ static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, /* adjust offset for mac and serial read ops */ offset += at24->offset_adj; - loop_until_timeout(timeout, read_time) { + at24_loop_until_timeout(timeout, read_time) { ret = regmap_bulk_read(regmap, offset, buf, count); dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, ret, jiffies); @@ -360,7 +361,7 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, client = at24_client->client; count = at24_adjust_write_count(at24, offset, count); - loop_until_timeout(timeout, write_time) { + at24_loop_until_timeout(timeout, write_time) { ret = regmap_bulk_write(regmap, offset, buf, count); dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n", count, offset, ret, jiffies); @@ -628,7 +629,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) writable = !(chip.flags & AT24_FLAG_READONLY); if (writable) { - at24->write_max = min_t(unsigned int, chip.page_size, io_limit); + at24->write_max = min_t(unsigned int, + chip.page_size, at24_io_limit); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && at24->write_max > I2C_SMBUS_BLOCK_MAX) at24->write_max = I2C_SMBUS_BLOCK_MAX; @@ -743,12 +745,12 @@ static struct i2c_driver at24_driver = { static int __init at24_init(void) { - if (!io_limit) { - pr_err("at24: io_limit must not be 0!\n"); + if (!at24_io_limit) { + pr_err("at24: at24_io_limit must not be 0!\n"); return -EINVAL; } - io_limit = rounddown_pow_of_two(io_limit); + at24_io_limit = rounddown_pow_of_two(at24_io_limit); return i2c_add_driver(&at24_driver); } module_init(at24_init); -- cgit From eef6939849b04bb1ba36f35f5c864eb0a66d1d83 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 Dec 2017 18:24:43 +0100 Subject: eeprom: at24: code shrink A regmap_config struct is pretty big and declaring two of them statically just to tweak the reg_bits value adds unnecessary bloat. Declare the regmap config locally in at24_probe() instead. Bloat-o-meter output for ARM: add/remove: 0/2 grow/shrink: 1/0 up/down: 4/-272 (-268) Function old new delta at24_probe 1560 1564 +4 regmap_config_8 136 - -136 regmap_config_16 136 - -136 Total: Before=7012, After=6744, chg -3.82% Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 657074ec0e64..b44a3d2b2b20 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -514,16 +514,6 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) } } -static const struct regmap_config regmap_config_8 = { - .reg_bits = 8, - .val_bits = 8, -}; - -static const struct regmap_config regmap_config_16 = { - .reg_bits = 16, - .val_bits = 8, -}; - static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct at24_platform_data chip; @@ -532,7 +522,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) struct at24_data *at24; int err; unsigned int i, num_addresses; - const struct regmap_config *config; + struct regmap_config regmap_config = { }; u8 test_byte; if (client->dev.platform_data) { @@ -601,10 +591,8 @@ 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); - if (chip.flags & AT24_FLAG_ADDR16) - config = ®map_config_16; - else - config = ®map_config_8; + regmap_config.val_bits = 8; + regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8; at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + num_addresses * sizeof(struct at24_client), GFP_KERNEL); @@ -617,7 +605,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len); at24->client[0].client = client; - at24->client[0].regmap = devm_regmap_init_i2c(client, config); + at24->client[0].regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(at24->client[0].regmap)) return PTR_ERR(at24->client[0].regmap); @@ -647,7 +635,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) goto err_clients; } at24->client[i].regmap = devm_regmap_init_i2c( - at24->client[i].client, config); + at24->client[i].client, + ®map_config); if (IS_ERR(at24->client[i].regmap)) { err = PTR_ERR(at24->client[i].regmap); goto err_clients; -- cgit From b680f4fa74496ac01e0f77e612a16d1ea892fd2a Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Wed, 20 Dec 2017 11:48:56 -0500 Subject: eeprom: at24: convert magic numbers to structs Fundamental properties such as capacity and page size differ among at24-type chips. But these chips do not have an id register, so this can't be discovered at runtime. Traditionally, at24-type eeprom properties were determined in two ways: - by passing a 'struct at24_platform_data' via platform_data, or - by naming the chip type in the devicetree, which passes a 'magic number' to probe(), which is then converted to a 'struct at24_platform_data'. Recently a bug was discovered because the magic number rounds down all chip sizes to the lowest power of two. This was addressed by a work-around commit 5478e478eee3 ("eeprom: at24: correctly set the size for at24mac402"), with the wish that magic numbers should over time be converted to structs. This patch replaces the magic numbers with 'struct at24_chip_data'. Signed-off-by: Sven Van Asbroeck Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 221 ++++++++++++++++++++------------------------- 1 file changed, 100 insertions(+), 121 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index b44a3d2b2b20..b1f78b99f8a1 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -105,16 +105,6 @@ static unsigned int at24_write_timeout = 25; module_param_named(write_timeout, at24_write_timeout, uint, 0); MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); -#define AT24_SIZE_BYTELEN 5 -#define AT24_SIZE_FLAGS 8 - -#define AT24_BITMASK(x) (BIT(x) - 1) - -/* create non-zero magic value for given eeprom parameters */ -#define AT24_DEVICE_MAGIC(_len, _flags) \ - ((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 @@ -132,113 +122,108 @@ MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); op_time ? time_before(op_time, tout) : true; \ usleep_range(1000, 1500), op_time = jiffies) +struct at24_chip_data { + /* + * these fields mirror their equivalents in + * struct at24_platform_data + */ + u32 byte_len; + u8 flags; +}; + +#define AT24_CHIP_DATA(_name, _len, _flags) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + } + +/* needs 8 addresses as A0-A2 are ignored */ +AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); +/* old variants can't be handled with this generic entry! */ +AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs01, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs02, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac402, 48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24mac602, 64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY); +/* spd is a 24c02 in memory DIMMs */ +AT24_CHIP_DATA(at24_data_spd, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO); +AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs04, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +/* 24rf08 quirk is handled at i2c-core */ +AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs08, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0); +AT24_CHIP_DATA(at24_data_24cs16, 16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs32, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24cs64, 16, + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); +AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16); +/* identical to 24c08 ? */ +AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); + 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) }, - /* 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) }, - { "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) }, - { "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) }, - { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, - { "at24", 0 }, + { "24c00", (kernel_ulong_t)&at24_data_24c00 }, + { "24c01", (kernel_ulong_t)&at24_data_24c01 }, + { "24cs01", (kernel_ulong_t)&at24_data_24cs01 }, + { "24c02", (kernel_ulong_t)&at24_data_24c02 }, + { "24cs02", (kernel_ulong_t)&at24_data_24cs02 }, + { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, + { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, + { "spd", (kernel_ulong_t)&at24_data_spd }, + { "24c04", (kernel_ulong_t)&at24_data_24c04 }, + { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, + { "24c08", (kernel_ulong_t)&at24_data_24c08 }, + { "24cs08", (kernel_ulong_t)&at24_data_24cs08 }, + { "24c16", (kernel_ulong_t)&at24_data_24c16 }, + { "24cs16", (kernel_ulong_t)&at24_data_24cs16 }, + { "24c32", (kernel_ulong_t)&at24_data_24c32 }, + { "24cs32", (kernel_ulong_t)&at24_data_24cs32 }, + { "24c64", (kernel_ulong_t)&at24_data_24c64 }, + { "24cs64", (kernel_ulong_t)&at24_data_24cs64 }, + { "24c128", (kernel_ulong_t)&at24_data_24c128 }, + { "24c256", (kernel_ulong_t)&at24_data_24c256 }, + { "24c512", (kernel_ulong_t)&at24_data_24c512 }, + { "24c1024", (kernel_ulong_t)&at24_data_24c1024 }, + { "at24", 0 }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, at24_ids); static const struct of_device_id at24_of_match[] = { - { - .compatible = "atmel,24c00", - .data = (void *)AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) - }, - { - .compatible = "atmel,24c01", - .data = (void *)AT24_DEVICE_MAGIC(1024 / 8, 0) - }, - { - .compatible = "atmel,24c02", - .data = (void *)AT24_DEVICE_MAGIC(2048 / 8, 0) - }, - { - .compatible = "atmel,spd", - .data = (void *)AT24_DEVICE_MAGIC(2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO) - }, - { - .compatible = "atmel,24c04", - .data = (void *)AT24_DEVICE_MAGIC(4096 / 8, 0) - }, - { - .compatible = "atmel,24c08", - .data = (void *)AT24_DEVICE_MAGIC(8192 / 8, 0) - }, - { - .compatible = "atmel,24c16", - .data = (void *)AT24_DEVICE_MAGIC(16384 / 8, 0) - }, - { - .compatible = "atmel,24c32", - .data = (void *)AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) - }, - { - .compatible = "atmel,24c64", - .data = (void *)AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) - }, - { - .compatible = "atmel,24c128", - .data = (void *)AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) - }, - { - .compatible = "atmel,24c256", - .data = (void *)AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) - }, - { - .compatible = "atmel,24c512", - .data = (void *)AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) - }, - { - .compatible = "atmel,24c1024", - .data = (void *)AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) - }, - { }, + { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, + { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, + { .compatible = "atmel,24c02", .data = &at24_data_24c02 }, + { .compatible = "atmel,spd", .data = &at24_data_spd }, + { .compatible = "atmel,24c04", .data = &at24_data_24c04 }, + { .compatible = "atmel,24c08", .data = &at24_data_24c08 }, + { .compatible = "atmel,24c16", .data = &at24_data_24c16 }, + { .compatible = "atmel,24c32", .data = &at24_data_24c32 }, + { .compatible = "atmel,24c64", .data = &at24_data_24c64 }, + { .compatible = "atmel,24c128", .data = &at24_data_24c128 }, + { .compatible = "atmel,24c256", .data = &at24_data_24c256 }, + { .compatible = "atmel,24c512", .data = &at24_data_24c512 }, + { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 }, + { /* END OF LIST */ }, }; MODULE_DEVICE_TABLE(of, at24_of_match); static const struct acpi_device_id at24_acpi_ids[] = { - { "INT3499", AT24_DEVICE_MAGIC(8192 / 8, 0) }, - { } + { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, + { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); @@ -516,8 +501,8 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct at24_platform_data chip; - kernel_ulong_t magic = 0; + struct at24_platform_data chip = { 0 }; + const struct at24_chip_data *cd = NULL; bool writable; struct at24_data *at24; int err; @@ -535,28 +520,22 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) */ if (client->dev.of_node && of_match_device(at24_of_match, &client->dev)) { - magic = (kernel_ulong_t) - of_device_get_match_data(&client->dev); + cd = of_device_get_match_data(&client->dev); } else if (id) { - magic = id->driver_data; + cd = (void *)id->driver_data; } else { const struct acpi_device_id *aid; aid = acpi_match_device(at24_acpi_ids, &client->dev); if (aid) - magic = aid->driver_data; + cd = (void *)aid->driver_data; } - if (!magic) + if (!cd) return -ENODEV; - chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); - magic >>= AT24_SIZE_BYTELEN; - chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); - + chip.byte_len = cd->byte_len; + chip.flags = cd->flags; at24_get_pdata(&client->dev, &chip); - - chip.setup = NULL; - chip.context = NULL; } if (!is_power_of_2(chip.byte_len)) -- cgit From ef542e59d7a11d343149b60609f422effede9d80 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Fri, 8 Dec 2017 16:25:06 -0500 Subject: eeprom: at24: remove temporary fix for at24mac402 size The chip size passed via devicetree, i2c, or acpi device ids is now no longer limited to a power of two. So the temporary fix can be removed. Signed-off-by: Sven Van Asbroeck Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index b1f78b99f8a1..581ba640c741 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -549,16 +549,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_warn(&client->dev, "page_size looks suspicious (no power of 2)!\n"); - /* - * REVISIT: the size of the EUI-48 byte array is 6 in at24mac402, while - * the call to ilog2() in AT24_DEVICE_MAGIC() rounds it down to 4. - * - * Eventually we'll get rid of the magic values altoghether in favor of - * real structs, but for now just manually set the right size. - */ - if (chip.flags & AT24_FLAG_MAC && chip.byte_len == 4) - chip.byte_len = 6; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) -- cgit From 6ce261e87fe14d551aae36e15171c60c823ba10a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 19 Dec 2017 11:28:54 +0100 Subject: eeprom: at24: add support for the write-protect pin AT24 EEPROMs have a write-protect pin, which - when pulled high - inhibits writes to the upper quadrant of memory (although it has been observed that on some chips it disables writing to the entire memory range). On some boards, this pin is connected to a GPIO and pulled high by default, which forces the user to manually change its state before writing. On linux this means that we either need to hog the line all the time, or set the GPIO value before writing from outside of the at24 driver. Make the driver check if the write-protect GPIO was defined in the device tree and pull it low whenever writing to the EEPROM. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/misc/eeprom/at24.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 581ba640c741..e79833d62284 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -27,6 +27,7 @@ #include #include #include +#include /* * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. @@ -77,6 +78,8 @@ struct at24_data { struct nvmem_config nvmem_config; struct nvmem_device *nvmem; + struct gpio_desc *wp_gpio; + /* * Some chips tie up multiple I2C addresses; dummy devices reserve * them for us, and we'll use them with SMBus calls. @@ -427,12 +430,14 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) * from this host, but not from other I2C masters. */ mutex_lock(&at24->lock); + gpiod_set_value_cansleep(at24->wp_gpio, 0); while (count) { int status; status = at24_regmap_write(at24, buf, off, count); if (status < 0) { + gpiod_set_value_cansleep(at24->wp_gpio, 1); mutex_unlock(&at24->lock); pm_runtime_put(dev); return status; @@ -442,6 +447,7 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) count -= status; } + gpiod_set_value_cansleep(at24->wp_gpio, 1); mutex_unlock(&at24->lock); pm_runtime_put(dev); @@ -573,6 +579,11 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->num_addresses = num_addresses; at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len); + at24->wp_gpio = devm_gpiod_get_optional(&client->dev, + "wp", GPIOD_OUT_HIGH); + if (IS_ERR(at24->wp_gpio)) + return PTR_ERR(at24->wp_gpio); + at24->client[0].client = client; at24->client[0].regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(at24->client[0].regmap)) -- cgit From 0f30aca72c3b68f4b6a443193b574f14106cd61e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 28 Dec 2017 11:49:13 +0100 Subject: eeprom: at24: extend the list of chips supported in DT Add all supported at24 variants to the of_match table. Signed-off-by: Bartosz Golaszewski Reviewed-by: Javier Martinez Canillas --- drivers/misc/eeprom/at24.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index e79833d62284..01f9c4921c50 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -209,13 +209,22 @@ MODULE_DEVICE_TABLE(i2c, at24_ids); static const struct of_device_id at24_of_match[] = { { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, + { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 }, { .compatible = "atmel,24c02", .data = &at24_data_24c02 }, + { .compatible = "atmel,24cs02", .data = &at24_data_24cs02 }, + { .compatible = "atmel,24mac402", .data = &at24_data_24mac402 }, + { .compatible = "atmel,24mac602", .data = &at24_data_24mac602 }, { .compatible = "atmel,spd", .data = &at24_data_spd }, { .compatible = "atmel,24c04", .data = &at24_data_24c04 }, + { .compatible = "atmel,24cs04", .data = &at24_data_24cs04 }, { .compatible = "atmel,24c08", .data = &at24_data_24c08 }, + { .compatible = "atmel,24cs08", .data = &at24_data_24cs08 }, { .compatible = "atmel,24c16", .data = &at24_data_24c16 }, + { .compatible = "atmel,24cs16", .data = &at24_data_24cs16 }, { .compatible = "atmel,24c32", .data = &at24_data_24c32 }, + { .compatible = "atmel,24cs32", .data = &at24_data_24cs32 }, { .compatible = "atmel,24c64", .data = &at24_data_24c64 }, + { .compatible = "atmel,24cs64", .data = &at24_data_24cs64 }, { .compatible = "atmel,24c128", .data = &at24_data_24c128 }, { .compatible = "atmel,24c256", .data = &at24_data_24c256 }, { .compatible = "atmel,24c512", .data = &at24_data_24c512 }, -- cgit