Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 68a1ac9..0f791bf 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,9 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
menu "EEPROM support"
config EEPROM_AT24
tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
depends on I2C && SYSFS
select NVMEM
+ select NVMEM_SYSFS
select REGMAP_I2C
help
Enable this driver to get read/write support to most I2C EEPROMs
@@ -13,7 +15,7 @@
ones like at24c64, 24lc02 or fm24c04:
24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
- 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024
+ 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048
Unless you like data loss puzzles, always be sure that any chip
you configure as a 24c32 (32 kbit) or larger is NOT really a
@@ -33,6 +35,7 @@
tristate "SPI EEPROMs from most vendors"
depends on SPI && SYSFS
select NVMEM
+ select NVMEM_SYSFS
help
Enable this driver to get read/write support to most SPI EEPROMs,
after you configure the board init code to know about each eeprom
@@ -42,13 +45,16 @@
will be called at25.
config EEPROM_LEGACY
- tristate "Old I2C EEPROM reader"
+ tristate "Old I2C EEPROM reader (DEPRECATED)"
depends on I2C && SYSFS
help
If you say yes here you get read-only access to the EEPROM data
available on modern memory DIMMs and Sony Vaio laptops via I2C. Such
EEPROMs could theoretically be available on other devices as well.
+ This driver is deprecated and will be removed soon, please use the
+ better at24 driver instead.
+
This driver can also be built as a module. If so, the module
will be called eeprom.
@@ -79,6 +85,7 @@
depends on SPI && SYSFS
select REGMAP
select NVMEM
+ select NVMEM_SYSFS
help
Driver for the microwire EEPROM chipsets 93xx46x. The driver
supports both read and write commands and also the command to
@@ -111,4 +118,15 @@
This driver can also be built as a module. If so, the module
will be called idt_89hpesx.
+config EEPROM_EE1004
+ tristate "SPD EEPROMs on DDR4 memory modules"
+ depends on I2C && SYSFS
+ help
+ Enable this driver to get read support to SPD EEPROMs following
+ the JEDEC EE1004 standard. These are typically found on DDR4
+ SDRAM memory modules.
+
+ This driver can also be built as a module. If so, the module
+ will be called ee1004.
+
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 2aab60e..a9b4b65 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -7,3 +7,4 @@
obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o
+obj-$(CONFIG_EEPROM_EE1004) += ee1004.o
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 7e50e1d..2cccd82 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -14,7 +14,6 @@
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
-#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
@@ -22,10 +21,24 @@
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
-#include <linux/platform_data/at24.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
+/* Address pointer is 16 bit. */
+#define AT24_FLAG_ADDR16 BIT(7)
+/* sysfs-entry will be read-only. */
+#define AT24_FLAG_READONLY BIT(6)
+/* sysfs-entry will be world-readable. */
+#define AT24_FLAG_IRUGO BIT(5)
+/* Take always 8 addresses (24c00). */
+#define AT24_FLAG_TAKE8ADDR BIT(4)
+/* Factory-programmed serial number. */
+#define AT24_FLAG_SERIAL BIT(3)
+/* Factory-programmed mac address. */
+#define AT24_FLAG_MAC BIT(2)
+/* Does not auto-rollover reads to the next slave address. */
+#define AT24_FLAG_NO_RDROL BIT(1)
+
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
@@ -106,28 +119,7 @@
module_param_named(write_timeout, at24_write_timeout, uint, 0);
MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
-/*
- * 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 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)
-
struct at24_chip_data {
- /*
- * these fields mirror their equivalents in
- * struct at24_platform_data
- */
u32 byte_len;
u8 flags;
};
@@ -173,6 +165,7 @@
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);
+AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16);
/* identical to 24c08 ? */
AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0);
@@ -199,6 +192,7 @@
{ "24c256", (kernel_ulong_t)&at24_data_24c256 },
{ "24c512", (kernel_ulong_t)&at24_data_24c512 },
{ "24c1024", (kernel_ulong_t)&at24_data_24c1024 },
+ { "24c2048", (kernel_ulong_t)&at24_data_24c2048 },
{ "at24", 0 },
{ /* END OF LIST */ }
};
@@ -227,6 +221,7 @@
{ .compatible = "atmel,24c256", .data = &at24_data_24c256 },
{ .compatible = "atmel,24c512", .data = &at24_data_24c512 },
{ .compatible = "atmel,24c1024", .data = &at24_data_24c1024 },
+ { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 },
{ /* END OF LIST */ },
};
MODULE_DEVICE_TABLE(of, at24_of_match);
@@ -308,13 +303,22 @@
/* adjust offset for mac and serial read ops */
offset += at24->offset_adj;
- at24_loop_until_timeout(timeout, read_time) {
+ timeout = jiffies + msecs_to_jiffies(at24_write_timeout);
+ do {
+ /*
+ * The timestamp shall be taken before the actual operation
+ * to avoid a premature timeout in case of high CPU load.
+ */
+ read_time = jiffies;
+
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;
- }
+
+ usleep_range(1000, 1500);
+ } while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
@@ -358,14 +362,23 @@
regmap = at24_client->regmap;
client = at24_client->client;
count = at24_adjust_write_count(at24, offset, count);
+ timeout = jiffies + msecs_to_jiffies(at24_write_timeout);
- at24_loop_until_timeout(timeout, write_time) {
+ do {
+ /*
+ * The timestamp shall be taken before the actual operation
+ * to avoid a premature timeout in case of high CPU load.
+ */
+ write_time = jiffies;
+
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;
- }
+
+ usleep_range(1000, 1500);
+ } while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
@@ -467,63 +480,11 @@
return 0;
}
-static void at24_properties_to_pdata(struct device *dev,
- struct at24_platform_data *chip)
-{
- int err;
- u32 val;
-
- 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, "address-width", &val);
- if (!err) {
- switch (val) {
- case 8:
- if (chip->flags & AT24_FLAG_ADDR16)
- dev_warn(dev, "Override address width to be 8, while default is 16\n");
- chip->flags &= ~AT24_FLAG_ADDR16;
- break;
- case 16:
- chip->flags |= AT24_FLAG_ADDR16;
- break;
- default:
- dev_warn(dev, "Bad \"address-width\" property: %u\n",
- val);
- }
- }
-
- err = device_property_read_u32(dev, "size", &val);
- if (!err)
- chip->byte_len = val;
-
- err = device_property_read_u32(dev, "pagesize", &val);
- if (!err) {
- chip->page_size = val;
- } else {
- /*
- * This is slow, but we can't know all eeproms, so we better
- * play safe. Specifying custom eeprom-types via platform_data
- * is recommended anyhow.
- */
- chip->page_size = 1;
- }
-}
-
-static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
+static const struct at24_chip_data *at24_get_chip_data(struct device *dev)
{
struct device_node *of_node = dev->of_node;
const struct at24_chip_data *cdata;
const struct i2c_device_id *id;
- struct at24_platform_data *pd;
-
- pd = dev_get_platdata(dev);
- if (pd) {
- memcpy(pdata, pd, sizeof(*pdata));
- return 0;
- }
id = i2c_match_id(at24_ids, to_i2c_client(dev));
@@ -540,47 +501,29 @@
cdata = acpi_device_get_match_data(dev);
if (!cdata)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
- pdata->byte_len = cdata->byte_len;
- pdata->flags = cdata->flags;
- at24_properties_to_pdata(dev, pdata);
-
- return 0;
-}
-
-static void at24_remove_dummy_clients(struct at24_data *at24)
-{
- int i;
-
- for (i = 1; i < at24->num_addresses; i++)
- i2c_unregister_device(at24->client[i].client);
+ return cdata;
}
static int at24_make_dummy_client(struct at24_data *at24, unsigned int index,
struct regmap_config *regmap_config)
{
struct i2c_client *base_client, *dummy_client;
- unsigned short int addr;
struct regmap *regmap;
struct device *dev;
base_client = at24->client[0].client;
dev = &base_client->dev;
- addr = base_client->addr + index;
- dummy_client = i2c_new_dummy(base_client->adapter,
- base_client->addr + index);
- if (!dummy_client) {
- dev_err(dev, "address 0x%02x unavailable\n", addr);
- return -EADDRINUSE;
- }
+ dummy_client = devm_i2c_new_dummy_device(dev, base_client->adapter,
+ base_client->addr + index);
+ if (IS_ERR(dummy_client))
+ return PTR_ERR(dummy_client);
regmap = devm_regmap_init_i2c(dummy_client, regmap_config);
- if (IS_ERR(regmap)) {
- i2c_unregister_device(dummy_client);
+ if (IS_ERR(regmap))
return PTR_ERR(regmap);
- }
at24->client[index].client = dummy_client;
at24->client[index].regmap = regmap;
@@ -615,13 +558,13 @@
{
struct regmap_config regmap_config = { };
struct nvmem_config nvmem_config = { };
- struct at24_platform_data pdata = { };
+ u32 byte_len, page_size, flags, addrw;
+ const struct at24_chip_data *cdata;
struct device *dev = &client->dev;
bool i2c_fn_i2c, i2c_fn_block;
unsigned int i, num_addresses;
struct at24_data *at24;
struct regmap *regmap;
- size_t at24_size;
bool writable;
u8 test_byte;
int err;
@@ -630,52 +573,92 @@
i2c_fn_block = i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
- err = at24_get_pdata(dev, &pdata);
+ cdata = at24_get_chip_data(dev);
+ if (IS_ERR(cdata))
+ return PTR_ERR(cdata);
+
+ err = device_property_read_u32(dev, "pagesize", &page_size);
if (err)
- return err;
+ /*
+ * This is slow, but we can't know all eeproms, so we better
+ * play safe. Specifying custom eeprom-types via device tree
+ * or properties is recommended anyhow.
+ */
+ page_size = 1;
+
+ flags = cdata->flags;
+ if (device_property_present(dev, "read-only"))
+ flags |= AT24_FLAG_READONLY;
+ if (device_property_present(dev, "no-read-rollover"))
+ flags |= AT24_FLAG_NO_RDROL;
+
+ err = device_property_read_u32(dev, "address-width", &addrw);
+ if (!err) {
+ switch (addrw) {
+ case 8:
+ if (flags & AT24_FLAG_ADDR16)
+ dev_warn(dev,
+ "Override address width to be 8, while default is 16\n");
+ flags &= ~AT24_FLAG_ADDR16;
+ break;
+ case 16:
+ flags |= AT24_FLAG_ADDR16;
+ break;
+ default:
+ dev_warn(dev, "Bad \"address-width\" property: %u\n",
+ addrw);
+ }
+ }
+
+ err = device_property_read_u32(dev, "size", &byte_len);
+ if (err)
+ byte_len = cdata->byte_len;
if (!i2c_fn_i2c && !i2c_fn_block)
- pdata.page_size = 1;
+ page_size = 1;
- if (!pdata.page_size) {
+ if (!page_size) {
dev_err(dev, "page_size must not be 0!\n");
return -EINVAL;
}
- if (!is_power_of_2(pdata.page_size))
+ if (!is_power_of_2(page_size))
dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
- if (pdata.flags & AT24_FLAG_TAKE8ADDR)
- num_addresses = 8;
- else
- num_addresses = DIV_ROUND_UP(pdata.byte_len,
- (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+ err = device_property_read_u32(dev, "num-addresses", &num_addresses);
+ if (err) {
+ if (flags & AT24_FLAG_TAKE8ADDR)
+ num_addresses = 8;
+ else
+ num_addresses = DIV_ROUND_UP(byte_len,
+ (flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+ }
- if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) {
+ if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) {
dev_err(dev,
"invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
return -EINVAL;
}
regmap_config.val_bits = 8;
- regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+ regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8;
regmap_config.disable_locking = true;
regmap = devm_regmap_init_i2c(client, ®map_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client);
- at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL);
+ at24 = devm_kzalloc(dev, struct_size(at24, client, num_addresses),
+ GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
- at24->byte_len = pdata.byte_len;
- at24->page_size = pdata.page_size;
- at24->flags = pdata.flags;
+ at24->byte_len = byte_len;
+ at24->page_size = page_size;
+ at24->flags = flags;
at24->num_addresses = num_addresses;
- at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len);
+ at24->offset_adj = at24_get_offset_adj(flags, byte_len);
at24->client[0].client = client;
at24->client[0].regmap = regmap;
@@ -683,10 +666,10 @@
if (IS_ERR(at24->wp_gpio))
return PTR_ERR(at24->wp_gpio);
- writable = !(pdata.flags & AT24_FLAG_READONLY);
+ writable = !(flags & AT24_FLAG_READONLY);
if (writable) {
at24->write_max = min_t(unsigned int,
- pdata.page_size, at24_io_limit);
+ page_size, at24_io_limit);
if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
at24->write_max = I2C_SMBUS_BLOCK_MAX;
}
@@ -694,12 +677,28 @@
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
err = at24_make_dummy_client(at24, i, ®map_config);
- if (err) {
- at24_remove_dummy_clients(at24);
+ if (err)
return err;
- }
}
+ nvmem_config.name = dev_name(dev);
+ nvmem_config.dev = dev;
+ nvmem_config.read_only = !writable;
+ nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO);
+ nvmem_config.owner = THIS_MODULE;
+ nvmem_config.compat = true;
+ nvmem_config.base_dev = dev;
+ nvmem_config.reg_read = at24_read;
+ nvmem_config.reg_write = at24_write;
+ nvmem_config.priv = at24;
+ nvmem_config.stride = 1;
+ nvmem_config.word_size = 1;
+ nvmem_config.size = byte_len;
+
+ at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
+ if (IS_ERR(at24->nvmem))
+ return PTR_ERR(at24->nvmem);
+
i2c_set_clientdata(client, at24);
/* enable runtime pm */
@@ -713,54 +712,19 @@
err = at24_read(at24, 0, &test_byte, 1);
pm_runtime_idle(dev);
if (err) {
- err = -ENODEV;
- goto err_clients;
- }
-
- nvmem_config.name = dev_name(dev);
- nvmem_config.dev = dev;
- nvmem_config.read_only = !writable;
- nvmem_config.root_only = true;
- nvmem_config.owner = THIS_MODULE;
- nvmem_config.compat = true;
- nvmem_config.base_dev = dev;
- nvmem_config.reg_read = at24_read;
- nvmem_config.reg_write = at24_write;
- nvmem_config.priv = at24;
- nvmem_config.stride = 1;
- nvmem_config.word_size = 1;
- nvmem_config.size = pdata.byte_len;
-
- at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
- if (IS_ERR(at24->nvmem)) {
- err = PTR_ERR(at24->nvmem);
- goto err_clients;
+ pm_runtime_disable(dev);
+ return -ENODEV;
}
dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
- pdata.byte_len, client->name,
+ byte_len, client->name,
writable ? "writable" : "read-only", at24->write_max);
- /* export data to kernel code */
- if (pdata.setup)
- pdata.setup(at24->nvmem, pdata.context);
-
return 0;
-
-err_clients:
- at24_remove_dummy_clients(at24);
- pm_runtime_disable(dev);
-
- return err;
}
static int at24_remove(struct i2c_client *client)
{
- struct at24_data *at24;
-
- at24 = i2c_get_clientdata(client);
-
- at24_remove_dummy_clients(at24);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 840afb3..cde9a2f 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* at25.c -- support most SPI EEPROMs, such as Atmel AT25 models
*
* Copyright (C) 2006 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/kernel.h>
@@ -366,7 +362,7 @@
at25->nvmem_config.word_size = 1;
at25->nvmem_config.size = chip.byte_len;
- at25->nvmem = nvmem_register(&at25->nvmem_config);
+ at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config);
if (IS_ERR(at25->nvmem))
return PTR_ERR(at25->nvmem);
@@ -379,16 +375,6 @@
return 0;
}
-static int at25_remove(struct spi_device *spi)
-{
- struct at25_data *at25;
-
- at25 = spi_get_drvdata(spi);
- nvmem_unregister(at25->nvmem);
-
- return 0;
-}
-
/*-------------------------------------------------------------------------*/
static const struct of_device_id at25_of_match[] = {
@@ -403,7 +389,6 @@
.of_match_table = at25_of_match,
},
.probe = at25_probe,
- .remove = at25_remove,
};
module_spi_driver(at25_driver);
diff --git a/drivers/misc/eeprom/digsy_mtc_eeprom.c b/drivers/misc/eeprom/digsy_mtc_eeprom.c
index fbde251..f1f766b 100644
--- a/drivers/misc/eeprom/digsy_mtc_eeprom.c
+++ b/drivers/misc/eeprom/digsy_mtc_eeprom.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* EEPROMs access control driver for display configuration EEPROMs
* on DigsyMTC board.
*
* (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* FIXME: this driver is used on a device-tree probed platform: it
* should be defined as a bit-banged SPI device and probed from the device
* tree and not like this with static grabbing of a few numbered GPIO
diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c
new file mode 100644
index 0000000..b081c67
--- /dev/null
+++ b/drivers/misc/eeprom/ee1004.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ee1004 - driver for DDR4 SPD EEPROMs
+ *
+ * Copyright (C) 2017-2019 Jean Delvare
+ *
+ * Based on the at24 driver:
+ * Copyright (C) 2005-2007 David Brownell
+ * Copyright (C) 2008 Wolfram Sang, Pengutronix
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+/*
+ * DDR4 memory modules use special EEPROMs following the Jedec EE1004
+ * specification. These are 512-byte EEPROMs using a single I2C address
+ * in the 0x50-0x57 range for data. One of two 256-byte page is selected
+ * by writing a command to I2C address 0x36 or 0x37 on the same I2C bus.
+ *
+ * Therefore we need to request these 2 additional addresses, and serialize
+ * access to all such EEPROMs with a single mutex.
+ *
+ * We assume it is safe to read up to 32 bytes at once from these EEPROMs.
+ * We use SMBus access even if I2C is available, these EEPROMs are small
+ * enough, and reading from them infrequent enough, that we favor simplicity
+ * over performance.
+ */
+
+#define EE1004_ADDR_SET_PAGE 0x36
+#define EE1004_EEPROM_SIZE 512
+#define EE1004_PAGE_SIZE 256
+#define EE1004_PAGE_SHIFT 8
+
+/*
+ * Mutex protects ee1004_set_page and ee1004_dev_count, and must be held
+ * from page selection to end of read.
+ */
+static DEFINE_MUTEX(ee1004_bus_lock);
+static struct i2c_client *ee1004_set_page[2];
+static unsigned int ee1004_dev_count;
+static int ee1004_current_page;
+
+static const struct i2c_device_id ee1004_ids[] = {
+ { "ee1004", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ee1004_ids);
+
+/*-------------------------------------------------------------------------*/
+
+static int ee1004_get_current_page(void)
+{
+ int err;
+
+ err = i2c_smbus_read_byte(ee1004_set_page[0]);
+ if (err == -ENXIO) {
+ /* Nack means page 1 is selected */
+ return 1;
+ }
+ if (err < 0) {
+ /* Anything else is a real error, bail out */
+ return err;
+ }
+
+ /* Ack means page 0 is selected, returned value meaningless */
+ return 0;
+}
+
+static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf,
+ unsigned int offset, size_t count)
+{
+ int status;
+
+ if (count > I2C_SMBUS_BLOCK_MAX)
+ count = I2C_SMBUS_BLOCK_MAX;
+ /* Can't cross page boundaries */
+ if (unlikely(offset + count > EE1004_PAGE_SIZE))
+ count = EE1004_PAGE_SIZE - offset;
+
+ status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset,
+ count, buf);
+ dev_dbg(&client->dev, "read %zu@%d --> %d\n", count, offset, status);
+
+ return status;
+}
+
+static ssize_t ee1004_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ size_t requested = count;
+ int page;
+
+ if (unlikely(!count))
+ return count;
+
+ page = off >> EE1004_PAGE_SHIFT;
+ if (unlikely(page > 1))
+ return 0;
+ off &= (1 << EE1004_PAGE_SHIFT) - 1;
+
+ /*
+ * Read data from chip, protecting against concurrent access to
+ * other EE1004 SPD EEPROMs on the same adapter.
+ */
+ mutex_lock(&ee1004_bus_lock);
+
+ while (count) {
+ int status;
+
+ /* Select page */
+ if (page != ee1004_current_page) {
+ /* Data is ignored */
+ status = i2c_smbus_write_byte(ee1004_set_page[page],
+ 0x00);
+ if (status == -ENXIO) {
+ /*
+ * Don't give up just yet. Some memory
+ * modules will select the page but not
+ * ack the command. Check which page is
+ * selected now.
+ */
+ if (ee1004_get_current_page() == page)
+ status = 0;
+ }
+ if (status < 0) {
+ dev_err(dev, "Failed to select page %d (%d)\n",
+ page, status);
+ mutex_unlock(&ee1004_bus_lock);
+ return status;
+ }
+ dev_dbg(dev, "Selected page %d\n", page);
+ ee1004_current_page = page;
+ }
+
+ status = ee1004_eeprom_read(client, buf, off, count);
+ if (status < 0) {
+ mutex_unlock(&ee1004_bus_lock);
+ return status;
+ }
+ buf += status;
+ off += status;
+ count -= status;
+
+ if (off == EE1004_PAGE_SIZE) {
+ page++;
+ off = 0;
+ }
+ }
+
+ mutex_unlock(&ee1004_bus_lock);
+
+ return requested;
+}
+
+static const struct bin_attribute eeprom_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = 0444,
+ },
+ .size = EE1004_EEPROM_SIZE,
+ .read = ee1004_read,
+};
+
+static int ee1004_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err, cnr = 0;
+ const char *slow = NULL;
+
+ /* Make sure we can operate on this adapter */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ slow = "word";
+ else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_READ_BYTE_DATA))
+ slow = "byte";
+ else
+ return -EPFNOSUPPORT;
+ }
+
+ /* Use 2 dummy devices for page select command */
+ mutex_lock(&ee1004_bus_lock);
+ if (++ee1004_dev_count == 1) {
+ for (cnr = 0; cnr < 2; cnr++) {
+ ee1004_set_page[cnr] = i2c_new_dummy_device(client->adapter,
+ EE1004_ADDR_SET_PAGE + cnr);
+ if (IS_ERR(ee1004_set_page[cnr])) {
+ dev_err(&client->dev,
+ "address 0x%02x unavailable\n",
+ EE1004_ADDR_SET_PAGE + cnr);
+ err = PTR_ERR(ee1004_set_page[cnr]);
+ goto err_clients;
+ }
+ }
+ } else if (i2c_adapter_id(client->adapter) !=
+ i2c_adapter_id(ee1004_set_page[0]->adapter)) {
+ dev_err(&client->dev,
+ "Driver only supports devices on a single I2C bus\n");
+ err = -EOPNOTSUPP;
+ goto err_clients;
+ }
+
+ /* Remember current page to avoid unneeded page select */
+ err = ee1004_get_current_page();
+ if (err < 0)
+ goto err_clients;
+ ee1004_current_page = err;
+ dev_dbg(&client->dev, "Currently selected page: %d\n",
+ ee1004_current_page);
+ mutex_unlock(&ee1004_bus_lock);
+
+ /* Create the sysfs eeprom file */
+ err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
+ if (err)
+ goto err_clients_lock;
+
+ dev_info(&client->dev,
+ "%u byte EE1004-compliant SPD EEPROM, read-only\n",
+ EE1004_EEPROM_SIZE);
+ if (slow)
+ dev_notice(&client->dev,
+ "Falling back to %s reads, performance will suffer\n",
+ slow);
+
+ return 0;
+
+ err_clients_lock:
+ mutex_lock(&ee1004_bus_lock);
+ err_clients:
+ if (--ee1004_dev_count == 0) {
+ for (cnr--; cnr >= 0; cnr--) {
+ i2c_unregister_device(ee1004_set_page[cnr]);
+ ee1004_set_page[cnr] = NULL;
+ }
+ }
+ mutex_unlock(&ee1004_bus_lock);
+
+ return err;
+}
+
+static int ee1004_remove(struct i2c_client *client)
+{
+ int i;
+
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
+
+ /* Remove page select clients if this is the last device */
+ mutex_lock(&ee1004_bus_lock);
+ if (--ee1004_dev_count == 0) {
+ for (i = 0; i < 2; i++) {
+ i2c_unregister_device(ee1004_set_page[i]);
+ ee1004_set_page[i] = NULL;
+ }
+ }
+ mutex_unlock(&ee1004_bus_lock);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver ee1004_driver = {
+ .driver = {
+ .name = "ee1004",
+ },
+ .probe = ee1004_probe,
+ .remove = ee1004_remove,
+ .id_table = ee1004_ids,
+};
+
+static int __init ee1004_init(void)
+{
+ return i2c_add_driver(&ee1004_driver);
+}
+module_init(ee1004_init);
+
+static void __exit ee1004_exit(void)
+{
+ i2c_del_driver(&ee1004_driver);
+}
+module_exit(ee1004_exit);
+
+MODULE_DESCRIPTION("Driver for EE1004-compliant DDR4 SPD EEPROMs");
+MODULE_AUTHOR("Jean Delvare");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c
index 60e3d91..2cfe3d4 100644
--- a/drivers/misc/eeprom/eeprom.c
+++ b/drivers/misc/eeprom/eeprom.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
* Philip Edelbrock <phil@netroedge.com>
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003 IBM Corp.
* Copyright (C) 2004 Jean Delvare <jdelvare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c
index 0cf2c9d..36a2eb8 100644
--- a/drivers/misc/eeprom/eeprom_93cx6.c
+++ b/drivers/misc/eeprom/eeprom_93cx6.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2004 - 2006 rt2x00 SourceForge Project
* <http://rt2x00.serialmonkey.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Module: eeprom_93cx6
* Abstract: EEPROM reader routines for 93cx6 chipsets.
* Supported chipsets: 93c46 & 93c66.
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
index 3876696..94cfb67 100644
--- a/drivers/misc/eeprom/eeprom_93xx46.c
+++ b/drivers/misc/eeprom/eeprom_93xx46.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for 93xx46 EEPROMs
*
* (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
@@ -439,7 +436,7 @@
return -ENODEV;
}
- edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+ edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL);
if (!edev)
return -ENOMEM;
@@ -449,8 +446,7 @@
edev->addrlen = 6;
else {
dev_err(&spi->dev, "unspecified address type\n");
- err = -EINVAL;
- goto fail;
+ return -EINVAL;
}
mutex_init(&edev->lock);
@@ -473,11 +469,9 @@
edev->nvmem_config.word_size = 1;
edev->nvmem_config.size = edev->size;
- edev->nvmem = nvmem_register(&edev->nvmem_config);
- if (IS_ERR(edev->nvmem)) {
- err = PTR_ERR(edev->nvmem);
- goto fail;
- }
+ edev->nvmem = devm_nvmem_register(&spi->dev, &edev->nvmem_config);
+ if (IS_ERR(edev->nvmem))
+ return PTR_ERR(edev->nvmem);
dev_info(&spi->dev, "%d-bit eeprom %s\n",
(pd->flags & EE_ADDR8) ? 8 : 16,
@@ -490,21 +484,15 @@
spi_set_drvdata(spi, edev);
return 0;
-fail:
- kfree(edev);
- return err;
}
static int eeprom_93xx46_remove(struct spi_device *spi)
{
struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi);
- nvmem_unregister(edev->nvmem);
-
if (!(edev->pdata->flags & EE_READONLY))
device_remove_file(&spi->dev, &dev_attr_erase);
- kfree(edev);
return 0;
}
diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c
index 8a46595..81c70e5 100644
--- a/drivers/misc/eeprom/idt_89hpesx.c
+++ b/drivers/misc/eeprom/idt_89hpesx.c
@@ -115,7 +115,6 @@
* @client: i2c client used to perform IO operations
*
* @ee_file: EEPROM read/write sysfs-file
- * @csr_file: CSR read/write debugfs-node
*/
struct idt_smb_seq;
struct idt_89hpesx_dev {
@@ -137,7 +136,6 @@
struct bin_attribute *ee_file;
struct dentry *csr_dir;
- struct dentry *csr_file;
};
/*
@@ -1378,8 +1376,8 @@
pdev->csr_dir = debugfs_create_dir(fname, csr_dbgdir);
/* Create Debugfs file for CSR read/write operations */
- pdev->csr_file = debugfs_create_file(cli->name, 0600,
- pdev->csr_dir, pdev, &csr_dbgfs_ops);
+ debugfs_create_file(cli->name, 0600, pdev->csr_dir, pdev,
+ &csr_dbgfs_ops);
}
/*
diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c
index fc0cf9a..9da81f6 100644
--- a/drivers/misc/eeprom/max6875.c
+++ b/drivers/misc/eeprom/max6875.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max6875.c - driver for MAX6874/MAX6875
*
@@ -20,10 +21,6 @@
*
* Note that the MAX6875 uses i2c_smbus_write_byte_data() to set the read
* address, so this driver is destructive if loaded for the wrong EEPROM chip.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
*/
#include <linux/kernel.h>
@@ -153,9 +150,9 @@
return -ENOMEM;
/* A fake client is created on the odd address */
- data->fake_client = i2c_new_dummy(client->adapter, client->addr + 1);
- if (!data->fake_client) {
- err = -ENOMEM;
+ data->fake_client = i2c_new_dummy_device(client->adapter, client->addr + 1);
+ if (IS_ERR(data->fake_client)) {
+ err = PTR_ERR(data->fake_client);
goto exit_kfree;
}