diff options
| author | Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com> | 2026-07-29 10:46:43 +0100 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-31 14:15:53 +0200 |
| commit | cf09b7a05618bea99468b67fe80202efba1b48ad (patch) | |
| tree | 808f953f4bbc4bcd54b1475a3ca38ccf49c458fd /drivers/misc | |
| parent | b594cbb7e8cfc8e2faeebf6b7b11b4a6990fc732 (diff) | |
eeprom: move nvmem EEPROM drivers to drivers/nvmem/
For historical reasons EEPROM drivers have lived under
drivers/misc/eeprom/. Also for historical reasons changes to most of
them would go through the char-misc tree while some would be queued
through the I2C tree. Over the years some of them have also been
converted to using nvmem - the dedicated subsystem for non-volatile
memory - while get_maintainer.pl does not Cc the maintainer of nvmem on
patches changing them.
Move the EEPROM drivers using nvmem under drivers/nvmem/ for
consistency of the review process and path upstream.
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
Link: https://patch.msgid.link/20260729094647.111468-11-srini@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc')
| -rw-r--r-- | drivers/misc/eeprom/Kconfig | 93 | ||||
| -rw-r--r-- | drivers/misc/eeprom/Makefile | 5 | ||||
| -rw-r--r-- | drivers/misc/eeprom/at24.c | 871 | ||||
| -rw-r--r-- | drivers/misc/eeprom/at25.c | 585 | ||||
| -rw-r--r-- | drivers/misc/eeprom/ee1004.c | 351 | ||||
| -rw-r--r-- | drivers/misc/eeprom/eeprom_93xx46.c | 560 | ||||
| -rw-r--r-- | drivers/misc/eeprom/m24lr.c | 606 |
7 files changed, 0 insertions, 3071 deletions
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 4d0ce47aa282..d6af02588c66 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -1,52 +1,6 @@ # 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 - 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 - configure the driver to know about each chip on your target - board. Use these generic chip names, instead of vendor-specific - ones like at24c64, 24lc02 or fm24c04: - - 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, - 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 - 24c16 (16 kbit) or smaller, and vice versa. Marking the chip - as read-only won't help recover from this. Also, if your chip - has any software write-protect mechanism you may want to review the - code to make sure this driver won't turn it on by accident. - - If you use this with an SMBus adapter instead of an I2C adapter, - full functionality is not available. Only smaller devices are - supported (24c16 and below, max 4 kByte). - - This driver can also be built as a module. If so, the module - will be called at24. - -config EEPROM_AT25 - tristate "SPI EEPROMs (FRAMs) from most vendors" - depends on SPI && SYSFS - select NVMEM - select NVMEM_SYSFS - select SPI_MEM - help - Enable this driver to get read/write support to most SPI EEPROMs - and Cypress FRAMs, - after you configure the board init code to know about each eeprom - on your target board. - - This driver can also be built as a module. If so, the module - will be called at25. - config EEPROM_MAX6875 tristate "Maxim MAX6874/5 power supply supervisor" depends on I2C @@ -69,22 +23,6 @@ config EEPROM_93CX6 If unsure, say N. -config EEPROM_93XX46 - tristate "Microwire EEPROM 93XX46 support" - 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 - erase the whole EEPROM. - - This driver can also be built as a module. If so, the module - will be called eeprom_93xx46. - - If unsure, say N. - config EEPROM_DIGSY_MTC_CFG bool "DigsyMTC display configuration EEPROMs device" depends on GPIO_MPC5200 && SPI_GPIO @@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX 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 - select NVMEM - select NVMEM_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. - -config EEPROM_M24LR - tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" - depends on I2C && SYSFS - select REGMAP_I2C - select NVMEM - select NVMEM_SYSFS - help - This enables support for STMicroelectronics M24LR RFID/NFC EEPROM - chips. These dual-interface devices expose two I2C addresses: - one for EEPROM memory access and another for control and system - configuration (e.g. UID, password handling). - - This driver provides a sysfs interface for control functions and - integrates with the nvmem subsystem for EEPROM access. - - To compile this driver as a module, choose M here: the - module will be called m24lr. - endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 8f311fd6a4ce..44a20cf025a1 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_EEPROM_AT24) += at24.o -obj-$(CONFIG_EEPROM_AT25) += at25.o obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -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 -obj-$(CONFIG_EEPROM_M24LR) += m24lr.o diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c deleted file mode 100644 index 772c4d9fa651..000000000000 --- a/drivers/misc/eeprom/at24.c +++ /dev/null @@ -1,871 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * at24.c - handle most I2C EEPROMs - * - * Copyright (C) 2005-2007 David Brownell - * Copyright (C) 2008 Wolfram Sang, Pengutronix - */ - -#include <linux/acpi.h> -#include <linux/bitops.h> -#include <linux/capability.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/jiffies.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/nvmem-provider.h> -#include <linux/pm_runtime.h> -#include <linux/property.h> -#include <linux/regmap.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.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 - * MicroChip 24LC, etc) won't much matter for typical read/write access. - * There are also I2C RAM chips, likewise interchangeable. One example - * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). - * - * However, misconfiguration can lose data. "Set 16-bit memory address" - * to a part with 8-bit addressing will overwrite data. Writing with too - * big a page size also loses data. And it's not safe to assume that the - * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC - * uses 0x51, for just one example. - * - * Accordingly, explicit board-specific configuration data should be used - * in almost all cases. (One partial exception is an SMBus used to access - * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) - * - * So this driver uses "new style" I2C driver binding, expecting to be - * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or - * similar kernel-resident tables; or, configuration data coming from - * a bootloader. - * - * Other than binding model, current differences from "eeprom" driver are - * that this one handles write access and isn't restricted to 24c02 devices. - * It also handles larger devices (32 kbit and up) with two-byte addresses, - * which won't work on pure SMBus systems. - */ - -struct at24_data { - /* - * Lock protects against activities from other Linux tasks, - * but not from changes by other I2C masters. - */ - struct mutex lock; - - unsigned int write_max; - unsigned int num_addresses; - unsigned int offset_adj; - - u32 byte_len; - u16 page_size; - u8 flags; - - struct nvmem_device *nvmem; - struct regulator *vcc_reg; - void (*read_post)(unsigned int off, char *buf, size_t count); - - /* - * Some chips tie up multiple I2C addresses; dummy devices reserve - * them for us. - */ - u8 bank_addr_shift; - struct regmap *client_regmaps[] __counted_by(num_addresses); -}; - -/* - * This parameter is to help this driver avoid blocking other drivers out - * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C - * clock, one 256 byte read takes about 1/43 second which is excessive; - * but the 1/170 second it takes at 400 kHz may be quite reasonable; and - * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. - * - * This value is forced to be a power of two so that writes align on pages. - */ -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 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)"); - -struct at24_chip_data { - u32 byte_len; - u8 flags; - u8 bank_addr_shift; - void (*read_post)(unsigned int off, char *buf, size_t count); -}; - -#define AT24_CHIP_DATA(_name, _len, _flags) \ - static const struct at24_chip_data _name = { \ - .byte_len = _len, .flags = _flags, \ - } - -#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \ - static const struct at24_chip_data _name = { \ - .byte_len = _len, .flags = _flags, \ - .read_post = _read_post, \ - } - -#define AT24_CHIP_DATA_BS(_name, _len, _flags, _bank_addr_shift) \ - static const struct at24_chip_data _name = { \ - .byte_len = _len, .flags = _flags, \ - .bank_addr_shift = _bank_addr_shift \ - } - -static void at24_read_post_vaio(unsigned int off, char *buf, size_t count) -{ - int i; - - if (capable(CAP_SYS_ADMIN)) - return; - - /* - * Hide VAIO private settings to regular users: - * - BIOS passwords: bytes 0x00 to 0x0f - * - UUID: bytes 0x10 to 0x1f - * - Serial number: 0xc0 to 0xdf - */ - for (i = 0; i < count; i++) { - if ((off + i <= 0x1f) || - (off + i >= 0xc0 && off + i <= 0xdf)) - buf[i] = 0; - } -} - -/* 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); -AT24_CHIP_DATA(at24_data_24aa025e48, 48 / 8, - AT24_FLAG_READONLY); -AT24_CHIP_DATA(at24_data_24aa025e64, 64 / 8, - AT24_FLAG_READONLY); -/* spd is a 24c02 in memory DIMMs */ -AT24_CHIP_DATA(at24_data_spd, 2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO); -/* 24c02_vaio is a 24c02 on some Sony laptops */ -AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO, - at24_read_post_vaio); -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); -/* M24C32-D Additional Write lockable page (M24C32-D order codes) */ -AT24_CHIP_DATA(at24_data_24c32d_wlp, 32, 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); -/* M24C64-D Additional Write lockable page (M24C64-D order codes) */ -AT24_CHIP_DATA(at24_data_24c64d_wlp, 32, 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); -/* M24256E Additional Write lockable page (M24256E-F order codes) */ -AT24_CHIP_DATA(at24_data_24256e_wlp, 64, 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_BS(at24_data_24c1025, 1048576 / 8, AT24_FLAG_ADDR16, 2); -AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); -/* identical to 24c08 ? */ -AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); - -static const struct i2c_device_id at24_ids[] = { - { .name = "24c00", .driver_data = (kernel_ulong_t)&at24_data_24c00 }, - { .name = "24c01", .driver_data = (kernel_ulong_t)&at24_data_24c01 }, - { .name = "24cs01", .driver_data = (kernel_ulong_t)&at24_data_24cs01 }, - { .name = "24c02", .driver_data = (kernel_ulong_t)&at24_data_24c02 }, - { .name = "24cs02", .driver_data = (kernel_ulong_t)&at24_data_24cs02 }, - { .name = "24mac402", .driver_data = (kernel_ulong_t)&at24_data_24mac402 }, - { .name = "24mac602", .driver_data = (kernel_ulong_t)&at24_data_24mac602 }, - { .name = "24aa025e48", .driver_data = (kernel_ulong_t)&at24_data_24aa025e48 }, - { .name = "24aa025e64", .driver_data = (kernel_ulong_t)&at24_data_24aa025e64 }, - { .name = "spd", .driver_data = (kernel_ulong_t)&at24_data_spd }, - { .name = "24c02-vaio", .driver_data = (kernel_ulong_t)&at24_data_24c02_vaio }, - { .name = "24c04", .driver_data = (kernel_ulong_t)&at24_data_24c04 }, - { .name = "24cs04", .driver_data = (kernel_ulong_t)&at24_data_24cs04 }, - { .name = "24c08", .driver_data = (kernel_ulong_t)&at24_data_24c08 }, - { .name = "24cs08", .driver_data = (kernel_ulong_t)&at24_data_24cs08 }, - { .name = "24c16", .driver_data = (kernel_ulong_t)&at24_data_24c16 }, - { .name = "24cs16", .driver_data = (kernel_ulong_t)&at24_data_24cs16 }, - { .name = "24c32", .driver_data = (kernel_ulong_t)&at24_data_24c32 }, - { .name = "24c32d-wl", .driver_data = (kernel_ulong_t)&at24_data_24c32d_wlp }, - { .name = "24cs32", .driver_data = (kernel_ulong_t)&at24_data_24cs32 }, - { .name = "24c64", .driver_data = (kernel_ulong_t)&at24_data_24c64 }, - { .name = "24c64-wl", .driver_data = (kernel_ulong_t)&at24_data_24c64d_wlp }, - { .name = "24cs64", .driver_data = (kernel_ulong_t)&at24_data_24cs64 }, - { .name = "24c128", .driver_data = (kernel_ulong_t)&at24_data_24c128 }, - { .name = "24c256", .driver_data = (kernel_ulong_t)&at24_data_24c256 }, - { .name = "24256e-wl", .driver_data = (kernel_ulong_t)&at24_data_24256e_wlp }, - { .name = "24c512", .driver_data = (kernel_ulong_t)&at24_data_24c512 }, - { .name = "24c1024", .driver_data = (kernel_ulong_t)&at24_data_24c1024 }, - { .name = "24c1025", .driver_data = (kernel_ulong_t)&at24_data_24c1025 }, - { .name = "24c2048", .driver_data = (kernel_ulong_t)&at24_data_24c2048 }, - { .name = "at24", .driver_data = 0 }, - { /* END OF LIST */ } -}; -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,24c32d-wl", .data = &at24_data_24c32d_wlp }, - { .compatible = "atmel,24cs32", .data = &at24_data_24cs32 }, - { .compatible = "atmel,24c64", .data = &at24_data_24c64 }, - { .compatible = "atmel,24c64d-wl", .data = &at24_data_24c64d_wlp }, - { .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 }, - { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 }, - { .compatible = "atmel,24c1025", .data = &at24_data_24c1025 }, - { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 }, - { .compatible = "microchip,24aa025e48", .data = &at24_data_24aa025e48 }, - { .compatible = "microchip,24aa025e64", .data = &at24_data_24aa025e64 }, - { .compatible = "st,24256e-wl", .data = &at24_data_24256e_wlp }, - { /* END OF LIST */ }, -}; -MODULE_DEVICE_TABLE(of, at24_of_match); - -static const struct acpi_device_id at24_acpi_ids[] = { - { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, - { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 }, - { /* END OF LIST */ } -}; -MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); - -/* - * This routine supports chips which consume multiple I2C addresses. It - * computes the addressing information to be used for a given r/w request. - * Assumes that sanity checks for offset happened at sysfs-layer. - * - * 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. - */ -static struct regmap *at24_translate_offset(struct at24_data *at24, - unsigned int *offset) -{ - unsigned int i; - - if (at24->flags & AT24_FLAG_ADDR16) { - i = *offset >> 16; - *offset &= 0xffff; - } else { - i = *offset >> 8; - *offset &= 0xff; - } - - return at24->client_regmaps[i]; -} - -static struct device *at24_base_client_dev(struct at24_data *at24) -{ - return regmap_get_device(at24->client_regmaps[0]); -} - -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->flags & AT24_FLAG_NO_RDROL) { - bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; - remainder = BIT(bits) - offset; - if (count > remainder) - count = remainder; - } - - if (count > at24_io_limit) - count = at24_io_limit; - - return count; -} - -static ssize_t at24_regmap_read(struct at24_data *at24, char *buf, - unsigned int offset, size_t count) -{ - unsigned long timeout, read_time; - struct regmap *regmap; - int ret; - - regmap = at24_translate_offset(at24, &offset); - count = at24_adjust_read_count(at24, offset, count); - - /* adjust offset for mac and serial read ops */ - offset += at24->offset_adj; - - 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(regmap_get_device(regmap), "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; -} - -/* - * Note that if the hardware write-protect pin is pulled high, the whole - * chip is normally write protected. But there are plenty of product - * variants here, including OTP fuses and partial chip protect. - * - * We only use page mode writes; the alternative is sloooow. These routines - * write at most one page. - */ - -static size_t at24_adjust_write_count(struct at24_data *at24, - unsigned int offset, size_t count) -{ - unsigned int next_page; - - /* write_max is at most a page */ - if (count > at24->write_max) - count = at24->write_max; - - /* Never roll over backwards, to the start of this page */ - next_page = roundup(offset + 1, at24->page_size); - if (offset + count > next_page) - count = next_page - offset; - - return count; -} - -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 regmap *regmap; - int ret; - - regmap = at24_translate_offset(at24, &offset); - count = at24_adjust_write_count(at24, offset, count); - 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. - */ - write_time = jiffies; - - ret = regmap_bulk_write(regmap, offset, buf, count); - dev_dbg(regmap_get_device(regmap), "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; -} - -static int at24_read(void *priv, unsigned int off, void *val, size_t count) -{ - struct at24_data *at24; - struct device *dev; - char *buf = val; - int i, ret; - - at24 = priv; - dev = at24_base_client_dev(at24); - - if (unlikely(!count)) - return count; - - if (off + count > at24->byte_len) - return -EINVAL; - - ret = pm_runtime_resume_and_get(dev); - if (ret) - return ret; - /* - * Read data from chip, protecting against concurrent updates - * from this host, but not from other I2C masters. - */ - mutex_lock(&at24->lock); - - for (i = 0; count; i += ret, count -= ret) { - ret = at24_regmap_read(at24, buf + i, off + i, count); - if (ret < 0) { - mutex_unlock(&at24->lock); - pm_runtime_put(dev); - return ret; - } - } - - mutex_unlock(&at24->lock); - - pm_runtime_put(dev); - - if (unlikely(at24->read_post)) - at24->read_post(off, buf, i); - - return 0; -} - -static int at24_write(void *priv, unsigned int off, void *val, size_t count) -{ - struct at24_data *at24; - struct device *dev; - char *buf = val; - int ret; - - at24 = priv; - dev = at24_base_client_dev(at24); - - if (unlikely(!count)) - return -EINVAL; - - if (off + count > at24->byte_len) - return -EINVAL; - - ret = pm_runtime_resume_and_get(dev); - if (ret) - return ret; - /* - * Write data to chip, protecting against concurrent updates - * from this host, but not from other I2C masters. - */ - mutex_lock(&at24->lock); - - while (count) { - ret = at24_regmap_write(at24, buf, off, count); - if (ret < 0) { - mutex_unlock(&at24->lock); - pm_runtime_put(dev); - return ret; - } - buf += ret; - off += ret; - count -= ret; - } - - mutex_unlock(&at24->lock); - - pm_runtime_put(dev); - - return 0; -} - -static int at24_make_dummy_client(struct at24_data *at24, unsigned int index, - struct i2c_client *base_client, - struct regmap_config *regmap_config) -{ - struct i2c_client *dummy_client; - struct regmap *regmap; - - dummy_client = devm_i2c_new_dummy_device(&base_client->dev, - base_client->adapter, - base_client->addr + - (index << at24->bank_addr_shift)); - if (IS_ERR(dummy_client)) - return PTR_ERR(dummy_client); - - regmap = devm_regmap_init_i2c(dummy_client, regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - at24->client_regmaps[index] = regmap; - - return 0; -} - -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 void at24_probe_temp_sensor(struct i2c_client *client) -{ - struct at24_data *at24 = i2c_get_clientdata(client); - struct i2c_board_info info = { .type = "jc42" }; - int ret; - u8 val; - - /* - * Byte 2 has value 11 for DDR3, earlier versions don't - * support the thermal sensor present flag - */ - ret = at24_read(at24, 2, &val, 1); - if (ret || val != 11) - return; - - /* Byte 32, bit 7 is set if temp sensor is present */ - ret = at24_read(at24, 32, &val, 1); - if (ret || !(val & BIT(7))) - return; - - info.addr = 0x18 | (client->addr & 7); - - i2c_new_client_device(client->adapter, &info); -} - -static int at24_probe(struct i2c_client *client) -{ - struct regmap_config regmap_config = { }; - struct nvmem_config nvmem_config = { }; - 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; - bool full_power; - struct regmap *regmap; - bool writable; - u8 test_byte; - int err; - - i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); - i2c_fn_block = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); - - cdata = i2c_get_match_data(client); - if (!cdata) - return -ENODEV; - - err = device_property_read_u32(dev, "pagesize", &page_size); - if (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) - page_size = 1; - - if (!page_size) - return dev_err_probe(dev, -EINVAL, "page_size must not be 0!\n"); - - if (!is_power_of_2(page_size)) - dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); - - 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 ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) - return dev_err_probe(dev, -EINVAL, - "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); - - regmap_config.val_bits = 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 = devm_kzalloc(dev, struct_size(at24, client_regmaps, num_addresses), - GFP_KERNEL); - if (!at24) - return -ENOMEM; - - mutex_init(&at24->lock); - at24->byte_len = byte_len; - at24->page_size = page_size; - at24->flags = flags; - at24->read_post = cdata->read_post; - at24->bank_addr_shift = cdata->bank_addr_shift; - at24->num_addresses = num_addresses; - at24->offset_adj = at24_get_offset_adj(flags, byte_len); - at24->client_regmaps[0] = regmap; - - at24->vcc_reg = devm_regulator_get(dev, "vcc"); - if (IS_ERR(at24->vcc_reg)) - return PTR_ERR(at24->vcc_reg); - - writable = !(flags & AT24_FLAG_READONLY); - if (writable) { - at24->write_max = min_t(unsigned int, - page_size, at24_io_limit); - if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) - at24->write_max = I2C_SMBUS_BLOCK_MAX; - } - - /* use dummy devices for multiple-address chips */ - for (i = 1; i < num_addresses; i++) { - err = at24_make_dummy_client(at24, i, client, ®map_config); - if (err) - return err; - } - - /* - * We initialize nvmem_config.id to NVMEM_DEVID_AUTO even if the - * label property is set as some platform can have multiple eeproms - * with same label and we can not register each of those with same - * label. Failing to register those eeproms trigger cascade failure - * on such platform. - */ - nvmem_config.id = NVMEM_DEVID_AUTO; - - if (device_property_present(dev, "label")) { - err = device_property_read_string(dev, "label", - &nvmem_config.name); - if (err) - return err; - } else { - nvmem_config.name = dev_name(dev); - } - - nvmem_config.type = NVMEM_TYPE_EEPROM; - 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; - - i2c_set_clientdata(client, at24); - - full_power = acpi_dev_state_d0(&client->dev); - if (full_power) { - err = regulator_enable(at24->vcc_reg); - if (err) - return dev_err_probe(dev, err, "Failed to enable vcc regulator\n"); - - pm_runtime_set_active(dev); - } - pm_runtime_enable(dev); - - /* - * Perform a one-byte test read to verify that the chip is functional, - * unless powering on the device is to be avoided during probe (i.e. - * it's powered off right now). - */ - if (full_power) { - err = at24_read(at24, 0, &test_byte, 1); - if (err) { - pm_runtime_disable(dev); - if (!pm_runtime_status_suspended(dev)) - regulator_disable(at24->vcc_reg); - return -ENODEV; - } - } - - at24->nvmem = devm_nvmem_register(dev, &nvmem_config); - if (IS_ERR(at24->nvmem)) { - pm_runtime_disable(dev); - if (!pm_runtime_status_suspended(dev)) - regulator_disable(at24->vcc_reg); - return dev_err_probe(dev, PTR_ERR(at24->nvmem), - "failed to register nvmem\n"); - } - - /* If this a SPD EEPROM, probe for DDR3 thermal sensor */ - if (cdata == &at24_data_spd) - at24_probe_temp_sensor(client); - - pm_runtime_idle(dev); - - if (writable) - dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n", - byte_len, client->name, at24->write_max); - else - dev_info(dev, "%u byte %s EEPROM, read-only\n", - byte_len, client->name); - - return 0; -} - -static void at24_remove(struct i2c_client *client) -{ - struct at24_data *at24 = i2c_get_clientdata(client); - - pm_runtime_disable(&client->dev); - if (acpi_dev_state_d0(&client->dev)) { - if (!pm_runtime_status_suspended(&client->dev)) - regulator_disable(at24->vcc_reg); - pm_runtime_set_suspended(&client->dev); - } -} - -static int __maybe_unused at24_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct at24_data *at24 = i2c_get_clientdata(client); - - return regulator_disable(at24->vcc_reg); -} - -static int __maybe_unused at24_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct at24_data *at24 = i2c_get_clientdata(client); - - return regulator_enable(at24->vcc_reg); -} - -static const struct dev_pm_ops at24_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(at24_suspend, at24_resume, NULL) -}; - -static struct i2c_driver at24_driver = { - .driver = { - .name = "at24", - .pm = &at24_pm_ops, - .of_match_table = at24_of_match, - .acpi_match_table = at24_acpi_ids, - }, - .probe = at24_probe, - .remove = at24_remove, - .id_table = at24_ids, - .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, -}; - -static int __init at24_init(void) -{ - if (!at24_io_limit) { - pr_err("at24: at24_io_limit must not be 0!\n"); - return -EINVAL; - } - - at24_io_limit = rounddown_pow_of_two(at24_io_limit); - return i2c_add_driver(&at24_driver); -} -module_init(at24_init); - -static void __exit at24_exit(void) -{ - i2c_del_driver(&at24_driver); -} -module_exit(at24_exit); - -MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); -MODULE_AUTHOR("David Brownell and Wolfram Sang"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c deleted file mode 100644 index bc2cfb75d9bb..000000000000 --- a/drivers/misc/eeprom/at25.c +++ /dev/null @@ -1,585 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for most of the SPI EEPROMs, such as Atmel AT25 models - * and Cypress FRAMs FM25 models. - * - * Copyright (C) 2006 David Brownell - */ - -#include <linux/bits.h> -#include <linux/cleanup.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/property.h> -#include <linux/sched.h> -#include <linux/slab.h> - -#include <linux/spi/eeprom.h> -#include <linux/spi/spi.h> -#include <linux/spi/spi-mem.h> - -#include <linux/nvmem-provider.h> - -/* - * NOTE: this is an *EEPROM* driver. The vagaries of product naming - * mean that some AT25 products are EEPROMs, and others are FLASH. - * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver, - * not this one! - * - * EEPROMs that can be used with this driver include, for example: - * AT25M02, AT25128B - */ - -#define FM25_SN_LEN 8 /* serial number length */ -#define FM25_MAX_ID_LEN 9 /* ID length */ -#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ - -struct at25_data { - struct spi_eeprom chip; - struct spi_mem *spimem; - struct mutex lock; - unsigned addrlen; - struct nvmem_config nvmem_config; - struct nvmem_device *nvmem; - u8 sernum[FM25_SN_LEN]; - u8 id[FM25_MAX_ID_LEN]; - u8 id_len; -}; - -#define AT25_WREN 0x06 /* latch the write enable */ -#define AT25_WRDI 0x04 /* reset the write enable */ -#define AT25_RDSR 0x05 /* read status register */ -#define AT25_WRSR 0x01 /* write status register */ -#define AT25_READ 0x03 /* read byte(s) */ -#define AT25_WRITE 0x02 /* write byte(s)/sector */ -#define FM25_SLEEP 0xb9 /* enter sleep mode */ -#define FM25_RDID 0x9f /* read device ID */ -#define FM25_RDSN 0xc3 /* read S/N */ - -#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ -#define AT25_SR_WEN 0x02 /* write enable (latched) */ -#define AT25_SR_BP0 0x04 /* BP for software writeprotect */ -#define AT25_SR_BP1 0x08 -#define AT25_SR_WPEN 0x80 /* writeprotect enable */ - -#define AT25_INSTR_BIT3 0x08 /* additional address bit in instr */ - -/* - * Specs often allow 5ms for a page write, sometimes 20ms; - * it's important to recover from write timeouts. - */ -#define EE_TIMEOUT 25 - -/*-------------------------------------------------------------------------*/ - -#define io_limit PAGE_SIZE /* bytes */ - -/* Handle the address MSB as part of instruction byte */ -static u8 at25_instr(struct at25_data *at25, u8 instr, unsigned int off) -{ - if (!(at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)) - return instr; - if (off < BIT(at25->addrlen * 8)) - return instr; - return instr | AT25_INSTR_BIT3; -} - -static int at25_ee_read(void *priv, unsigned int offset, - void *val, size_t count) -{ - u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL); - struct at25_data *at25 = priv; - char *buf = val; - unsigned int msg_offset = offset; - size_t bytes_left = count; - size_t segment; - int status; - - if (!bounce) - return -ENOMEM; - - if (unlikely(offset >= at25->chip.byte_len)) - return -EINVAL; - if ((offset + count) > at25->chip.byte_len) - count = at25->chip.byte_len - offset; - if (unlikely(!count)) - return -EINVAL; - - do { - struct spi_mem_op op; - - segment = min(bytes_left, io_limit); - - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_READ, - msg_offset), 1), - SPI_MEM_OP_ADDR(at25->addrlen, msg_offset, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(segment, bounce, 1)); - - status = spi_mem_adjust_op_size(at25->spimem, &op); - if (status) - return status; - segment = op.data.nbytes; - - mutex_lock(&at25->lock); - status = spi_mem_exec_op(at25->spimem, &op); - mutex_unlock(&at25->lock); - if (status) - return status; - memcpy(buf, bounce, segment); - - msg_offset += segment; - buf += segment; - bytes_left -= segment; - } while (bytes_left > 0); - - dev_dbg(&at25->spimem->spi->dev, "read %zu bytes at %d\n", - count, offset); - return 0; -} - -/* - * Read extra registers as ID or serial number - * - * Allow for the callers to provide @buf on stack (not necessary DMA-capable) - * by allocating a bounce buffer internally. - */ -static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, - int len) -{ - u8 *bounce __free(kfree) = kmalloc(len, GFP_KERNEL); - struct spi_mem_op op; - int status; - - if (!bounce) - return -ENOMEM; - - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(command, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(len, bounce, 1)); - - status = spi_mem_exec_op(at25->spimem, &op); - dev_dbg(&at25->spimem->spi->dev, "read %d aux bytes --> %d\n", len, status); - if (status) - return status; - - memcpy(buf, bounce, len); - - return 0; -} - -static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct at25_data *at25; - - at25 = dev_get_drvdata(dev); - return sysfs_emit(buf, "%*ph\n", (int)sizeof(at25->sernum), at25->sernum); -} -static DEVICE_ATTR_RO(sernum); - -static ssize_t jedec_id_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct at25_data *at25; - - at25 = dev_get_drvdata(dev); - - if (!at25->id_len) - return -EOPNOTSUPP; - - return sysfs_emit(buf, "%*phN\n", at25->id_len, at25->id); -} -static DEVICE_ATTR_RO(jedec_id); - -static struct attribute *at25_attrs[] = { - &dev_attr_sernum.attr, - &dev_attr_jedec_id.attr, - NULL, -}; -ATTRIBUTE_GROUPS(at25); - -/* - * Poll Read Status Register with timeout - * - * Return: - * 0, if the chip is ready - * [positive] Status Register value as-is, if the chip is busy - * [negative] error code in case of read failure - */ -static int at25_wait_ready(struct at25_data *at25) -{ - u8 *bounce __free(kfree) = kmalloc(1, GFP_KERNEL); - struct spi_mem_op op; - int status; - - if (!bounce) - return -ENOMEM; - - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_RDSR, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, bounce, 1)); - - read_poll_timeout(spi_mem_exec_op, status, - status || !(bounce[0] & AT25_SR_nRDY), false, - USEC_PER_MSEC, USEC_PER_MSEC * EE_TIMEOUT, - at25->spimem, &op); - if (status < 0) - return status; - if (!(bounce[0] & AT25_SR_nRDY)) - return 0; - - return bounce[0]; -} - -static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) -{ - u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL); - struct at25_data *at25 = priv; - const char *buf = val; - unsigned int buf_size; - int status; - - if (unlikely(off >= at25->chip.byte_len)) - return -EFBIG; - if ((off + count) > at25->chip.byte_len) - count = at25->chip.byte_len - off; - if (unlikely(!count)) - return -EINVAL; - - buf_size = at25->chip.page_size; - - if (!bounce) - return -ENOMEM; - - /* - * For write, rollover is within the page ... so we write at - * most one page, then manually roll over to the next page. - */ - guard(mutex)(&at25->lock); - do { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_WREN, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); - unsigned int segment; - - status = spi_mem_exec_op(at25->spimem, &op); - if (status < 0) { - dev_dbg(&at25->spimem->spi->dev, "WREN --> %d\n", status); - return status; - } - - /* Write as much of a page as we can */ - segment = buf_size - (off % buf_size); - if (segment > count) - segment = count; - if (segment > io_limit) - segment = io_limit; - - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_WRITE, off), - 1), - SPI_MEM_OP_ADDR(at25->addrlen, off, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(segment, bounce, 1)); - - status = spi_mem_adjust_op_size(at25->spimem, &op); - if (status) - return status; - segment = op.data.nbytes; - - memcpy(bounce, buf, segment); - - status = spi_mem_exec_op(at25->spimem, &op); - dev_dbg(&at25->spimem->spi->dev, "write %u bytes at %u --> %d\n", - segment, off, status); - if (status) - return status; - - /* - * REVISIT this should detect (or prevent) failed writes - * to read-only sections of the EEPROM... - */ - - status = at25_wait_ready(at25); - if (status < 0) { - dev_err_probe(&at25->spimem->spi->dev, status, - "Read Status Redister command failed\n"); - return status; - } - if (status) { - dev_dbg(&at25->spimem->spi->dev, - "Status %02x\n", status); - dev_err(&at25->spimem->spi->dev, - "write %u bytes offset %u, timeout after %u msecs\n", - segment, off, EE_TIMEOUT); - return -ETIMEDOUT; - } - - off += segment; - buf += segment; - count -= segment; - - } while (count > 0); - - return status; -} - -/*-------------------------------------------------------------------------*/ - -static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) -{ - u32 val; - int err; - - strscpy(chip->name, "at25", sizeof(chip->name)); - - err = device_property_read_u32(dev, "size", &val); - if (err) - err = device_property_read_u32(dev, "at25,byte-len", &val); - if (err) { - dev_err(dev, "Error: missing \"size\" property\n"); - return err; - } - chip->byte_len = val; - - err = device_property_read_u32(dev, "pagesize", &val); - if (err) - err = device_property_read_u32(dev, "at25,page-size", &val); - if (err) { - dev_err(dev, "Error: missing \"pagesize\" property\n"); - return err; - } - chip->page_size = val; - - err = device_property_read_u32(dev, "address-width", &val); - if (err) { - err = device_property_read_u32(dev, "at25,addr-mode", &val); - if (err) { - dev_err(dev, "Error: missing \"address-width\" property\n"); - return err; - } - chip->flags = (u16)val; - } else { - switch (val) { - case 9: - chip->flags |= EE_INSTR_BIT3_IS_ADDR; - fallthrough; - case 8: - chip->flags |= EE_ADDR1; - break; - case 16: - chip->flags |= EE_ADDR2; - break; - case 24: - chip->flags |= EE_ADDR3; - break; - default: - dev_err(dev, - "Error: bad \"address-width\" property: %u\n", - val); - return -ENODEV; - } - if (device_property_present(dev, "read-only")) - chip->flags |= EE_READONLY; - } - return 0; -} - -static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) -{ - struct at25_data *at25 = container_of(chip, struct at25_data, chip); - u8 sernum[FM25_SN_LEN]; - u8 id[FM25_MAX_ID_LEN]; - u32 val; - int i; - - strscpy(chip->name, "fm25", sizeof(chip->name)); - - if (!device_property_read_u32(dev, "size", &val)) { - chip->byte_len = val; - } else { - /* Get ID of chip */ - fm25_aux_read(at25, id, FM25_RDID, FM25_MAX_ID_LEN); - - /* Store the unprocessed ID for exposing via sysfs */ - memcpy(at25->id, id, FM25_MAX_ID_LEN); - at25->id_len = FM25_MAX_ID_LEN; - - /* There are inside-out FRAM variations, detect them and reverse the ID bytes */ - if (id[6] == 0x7f && id[2] == 0xc2) - for (i = 0; i < ARRAY_SIZE(id) / 2; i++) { - u8 tmp = id[i]; - int j = ARRAY_SIZE(id) - i - 1; - - id[i] = id[j]; - id[j] = tmp; - } - - if (id[6] == 0xc2) { - at25->id_len = 9; - switch (id[7]) { - case 0x21 ... 0x26: - chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024; - break; - case 0x2a ... 0x30: - /* CY15B102QN ... CY15B116QN */ - chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13); - break; - default: - dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]); - return -ENODEV; - } - } else if (id[2] == 0x82 && id[3] == 0x06) { - at25->id_len = 8; - switch (id[1]) { - case 0x51 ... 0x54: - /* CY15B102QSN ... CY15B204QSN */ - chip->byte_len = BIT(((id[0] >> 3) & 0x1F) + 9); - break; - default: - dev_err(dev, "Error: unsupported product id %02x\n", id[1]); - return -ENODEV; - } - } else { - dev_err(dev, "Error: unrecognized JEDEC ID format: %*ph\n", - FM25_MAX_ID_LEN, id); - return -ENODEV; - } - - fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN); - /* Swap byte order */ - for (i = 0; i < FM25_SN_LEN; i++) - at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i]; - } - - if (chip->byte_len > 64 * 1024) - chip->flags |= EE_ADDR3; - else - chip->flags |= EE_ADDR2; - - chip->page_size = PAGE_SIZE; - return 0; -} - -static const struct of_device_id at25_of_match[] = { - { .compatible = "atmel,at25" }, - { .compatible = "cypress,fm25" }, - { } -}; -MODULE_DEVICE_TABLE(of, at25_of_match); - -static const struct spi_device_id at25_spi_ids[] = { - { .name = "at25" }, - { .name = "fm25" }, - { } -}; -MODULE_DEVICE_TABLE(spi, at25_spi_ids); - -static int at25_probe(struct spi_mem *mem) -{ - struct spi_device *spi = mem->spi; - struct spi_eeprom *pdata; - struct at25_data *at25; - bool is_fram; - int err; - - at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL); - if (!at25) - return -ENOMEM; - - at25->spimem = mem; - - /* - * Ping the chip ... the status register is pretty portable, - * unlike probing manufacturer IDs. - */ - err = at25_wait_ready(at25); - if (err < 0) - return dev_err_probe(&spi->dev, err, "Read Status Register command failed\n"); - if (err) { - dev_err(&spi->dev, "Not ready (%02x)\n", err); - return -ENXIO; - } - - mutex_init(&at25->lock); - spi_set_drvdata(spi, at25); - - is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25"); - - /* Chip description */ - pdata = dev_get_platdata(&spi->dev); - if (pdata) { - at25->chip = *pdata; - } else { - if (is_fram) - err = at25_fram_to_chip(&spi->dev, &at25->chip); - else - err = at25_fw_to_chip(&spi->dev, &at25->chip); - if (err) - return err; - } - - /* For now we only support 8/16/24 bit addressing */ - if (at25->chip.flags & EE_ADDR1) - at25->addrlen = 1; - else if (at25->chip.flags & EE_ADDR2) - at25->addrlen = 2; - else if (at25->chip.flags & EE_ADDR3) - at25->addrlen = 3; - else { - dev_dbg(&spi->dev, "unsupported address type\n"); - return -EINVAL; - } - - at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM; - at25->nvmem_config.name = dev_name(&spi->dev); - at25->nvmem_config.dev = &spi->dev; - at25->nvmem_config.read_only = at25->chip.flags & EE_READONLY; - at25->nvmem_config.root_only = true; - at25->nvmem_config.owner = THIS_MODULE; - at25->nvmem_config.compat = true; - at25->nvmem_config.base_dev = &spi->dev; - at25->nvmem_config.reg_read = at25_ee_read; - at25->nvmem_config.reg_write = at25_ee_write; - at25->nvmem_config.priv = at25; - at25->nvmem_config.stride = 1; - at25->nvmem_config.word_size = 1; - at25->nvmem_config.size = at25->chip.byte_len; - - at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config); - if (IS_ERR(at25->nvmem)) - return PTR_ERR(at25->nvmem); - - dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n", - (at25->chip.byte_len < 1024) ? - at25->chip.byte_len : (at25->chip.byte_len / 1024), - (at25->chip.byte_len < 1024) ? "Byte" : "KByte", - at25->chip.name, is_fram ? "fram" : "eeprom", - (at25->chip.flags & EE_READONLY) ? " (readonly)" : "", - at25->chip.page_size); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct spi_mem_driver at25_driver = { - .spidrv = { - .driver = { - .name = "at25", - .of_match_table = at25_of_match, - .dev_groups = at25_groups, - }, - .id_table = at25_spi_ids, - }, - .probe = at25_probe, -}; - -module_spi_mem_driver(at25_driver); - -MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); -MODULE_AUTHOR("David Brownell"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c deleted file mode 100644 index 923f404a44c0..000000000000 --- a/drivers/misc/eeprom/ee1004.c +++ /dev/null @@ -1,351 +0,0 @@ -// 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/device.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/nvmem-provider.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_MAX_BUSSES 8 -#define EE1004_ADDR_SET_PAGE 0x36 -#define EE1004_NUM_PAGES 2 -#define EE1004_PAGE_SIZE 256 -#define EE1004_PAGE_SHIFT 8 -#define EE1004_EEPROM_SIZE (EE1004_PAGE_SIZE * EE1004_NUM_PAGES) - -/* - * 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 ee1004_bus_data { - struct i2c_adapter *adap; - struct i2c_client *set_page[EE1004_NUM_PAGES]; - unsigned int dev_count; - int current_page; -} ee1004_bus_data[EE1004_MAX_BUSSES]; - -static const struct i2c_device_id ee1004_ids[] = { - { "ee1004" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ee1004_ids); - -/*-------------------------------------------------------------------------*/ - -static struct ee1004_bus_data *ee1004_get_bus_data(struct i2c_adapter *adap) -{ - int i; - - for (i = 0; i < EE1004_MAX_BUSSES; i++) - if (ee1004_bus_data[i].adap == adap) - return ee1004_bus_data + i; - - /* If not existent yet, create new entry */ - for (i = 0; i < EE1004_MAX_BUSSES; i++) - if (!ee1004_bus_data[i].adap) { - ee1004_bus_data[i].adap = adap; - return ee1004_bus_data + i; - } - - return NULL; -} - -static int ee1004_get_current_page(struct ee1004_bus_data *bd) -{ - int err; - - err = i2c_smbus_read_byte(bd->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 int ee1004_set_current_page(struct i2c_client *client, int page) -{ - struct ee1004_bus_data *bd = i2c_get_clientdata(client); - int ret; - - if (page == bd->current_page) - return 0; - - /* Data is ignored */ - ret = i2c_smbus_write_byte(bd->set_page[page], 0x00); - /* - * 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 (ret == -ENXIO && ee1004_get_current_page(bd) == page) - ret = 0; - if (ret < 0) { - dev_err(&client->dev, "Failed to select page %d (%d)\n", page, ret); - return ret; - } - - dev_dbg(&client->dev, "Selected page %d\n", page); - bd->current_page = page; - - return 0; -} - -static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, - unsigned int offset, size_t count) -{ - int status, page; - - page = offset >> EE1004_PAGE_SHIFT; - offset &= (1 << EE1004_PAGE_SHIFT) - 1; - - status = ee1004_set_current_page(client, page); - if (status) - return status; - - /* Can't cross page boundaries */ - if (offset + count > EE1004_PAGE_SIZE) - count = EE1004_PAGE_SIZE - offset; - - if (count > I2C_SMBUS_BLOCK_MAX) - count = I2C_SMBUS_BLOCK_MAX; - - return i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf); -} - -static int ee1004_read(void *priv, unsigned int off, void *val, size_t count) -{ - struct i2c_client *client = priv; - char *buf = val; - int ret; - - if (unlikely(!count)) - return count; - - if (off + count > EE1004_EEPROM_SIZE) - return -EINVAL; - - /* - * Read data from chip, protecting against concurrent access to - * other EE1004 SPD EEPROMs on the same adapter. - */ - mutex_lock(&ee1004_bus_lock); - - while (count) { - ret = ee1004_eeprom_read(client, buf, off, count); - if (ret < 0) { - mutex_unlock(&ee1004_bus_lock); - return ret; - } - - buf += ret; - off += ret; - count -= ret; - } - - mutex_unlock(&ee1004_bus_lock); - - return 0; -} - -static void ee1004_probe_temp_sensor(struct i2c_client *client) -{ - struct i2c_board_info info = { .type = "jc42" }; - unsigned short addr = 0x18 | (client->addr & 7); - unsigned short addr_list[] = { addr, I2C_CLIENT_END }; - u8 data[2]; - int ret; - - /* byte 14, bit 7 is set if temp sensor is present */ - ret = ee1004_eeprom_read(client, data, 14, 1); - if (ret != 1) - return; - - if (!(data[0] & BIT(7))) { - /* - * If the SPD data suggests that there is no temperature - * sensor, it may still be there for SPD revision 1.0. - * See SPD Annex L, Revision 1 and 2, for details. - * Check DIMM type and SPD revision; if it is a DDR4 - * with SPD revision 1.0, check the thermal sensor address - * and instantiate the jc42 driver if a chip is found at - * that address. - * It is not necessary to check if there is a chip at the - * temperature sensor address since i2c_new_scanned_device() - * will do that and return silently if no chip is found. - */ - ret = ee1004_eeprom_read(client, data, 1, 2); - if (ret != 2 || data[0] != 0x10 || data[1] != 0x0c) - return; - } - i2c_new_scanned_device(client->adapter, &info, addr_list, NULL); -} - -static void ee1004_cleanup(int idx, struct ee1004_bus_data *bd) -{ - if (--bd->dev_count == 0) { - while (--idx >= 0) - i2c_unregister_device(bd->set_page[idx]); - memset(bd, 0, sizeof(struct ee1004_bus_data)); - } -} - -static void ee1004_cleanup_bus_data(void *data) -{ - struct ee1004_bus_data *bd = data; - - /* Remove page select clients if this is the last device */ - mutex_lock(&ee1004_bus_lock); - ee1004_cleanup(EE1004_NUM_PAGES, bd); - mutex_unlock(&ee1004_bus_lock); -} - -static int ee1004_init_bus_data(struct i2c_client *client) -{ - struct ee1004_bus_data *bd; - int err, cnr = 0; - - bd = ee1004_get_bus_data(client->adapter); - if (!bd) - return dev_err_probe(&client->dev, -ENOSPC, "Only %d busses supported", - EE1004_MAX_BUSSES); - - i2c_set_clientdata(client, bd); - - if (++bd->dev_count == 1) { - /* Use 2 dummy devices for page select command */ - for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) { - struct i2c_client *cl; - - cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr); - if (IS_ERR(cl)) { - err = PTR_ERR(cl); - goto err_out; - } - - bd->set_page[cnr] = cl; - } - - /* Remember current page to avoid unneeded page select */ - err = ee1004_get_current_page(bd); - if (err < 0) - goto err_out; - - dev_dbg(&client->dev, "Currently selected page: %d\n", err); - bd->current_page = err; - } - - return 0; - -err_out: - ee1004_cleanup(cnr, bd); - - return err; -} - -static int ee1004_probe(struct i2c_client *client) -{ - struct nvmem_config config = { - .dev = &client->dev, - .name = dev_name(&client->dev), - .id = NVMEM_DEVID_NONE, - .owner = THIS_MODULE, - .type = NVMEM_TYPE_EEPROM, - .read_only = true, - .root_only = false, - .reg_read = ee1004_read, - .size = EE1004_EEPROM_SIZE, - .word_size = 1, - .stride = 1, - .priv = client, - .compat = true, - .base_dev = &client->dev, - }; - struct nvmem_device *ndev; - int err; - - /* Make sure we can operate on this adapter */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_I2C_BLOCK) && - !i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) - return -EPFNOSUPPORT; - - err = i2c_smbus_read_byte(client); - if (err < 0) - return -ENODEV; - - mutex_lock(&ee1004_bus_lock); - - err = ee1004_init_bus_data(client); - if (err < 0) { - mutex_unlock(&ee1004_bus_lock); - return err; - } - - ee1004_probe_temp_sensor(client); - - mutex_unlock(&ee1004_bus_lock); - - err = devm_add_action_or_reset(&client->dev, ee1004_cleanup_bus_data, - i2c_get_clientdata(client)); - if (err < 0) - return err; - - ndev = devm_nvmem_register(&client->dev, &config); - if (IS_ERR(ndev)) - return PTR_ERR(ndev); - - dev_info(&client->dev, - "%u byte EE1004-compliant SPD EEPROM, read-only\n", - EE1004_EEPROM_SIZE); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct i2c_driver ee1004_driver = { - .driver = { - .name = "ee1004", - }, - .probe = ee1004_probe, - .id_table = ee1004_ids, -}; -module_i2c_driver(ee1004_driver); - -MODULE_DESCRIPTION("Driver for EE1004-compliant DDR4 SPD EEPROMs"); -MODULE_AUTHOR("Jean Delvare"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c deleted file mode 100644 index f9c3ab52c2f9..000000000000 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ /dev/null @@ -1,560 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for 93xx46 EEPROMs - * - * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de> - */ - -#include <linux/array_size.h> -#include <linux/bits.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/gpio/consumer.h> -#include <linux/kstrtox.h> -#include <linux/log2.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/property.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> -#include <linux/string_choices.h> - -#include <linux/nvmem-provider.h> - -struct eeprom_93xx46_platform_data { - unsigned char flags; -#define EE_ADDR8 0x01 /* 8 bit addr. cfg */ -#define EE_ADDR16 0x02 /* 16 bit addr. cfg */ -#define EE_READONLY 0x08 /* forbid writing */ -#define EE_SIZE1K 0x10 /* 1 kb of data, that is a 93xx46 */ -#define EE_SIZE2K 0x20 /* 2 kb of data, that is a 93xx56 */ -#define EE_SIZE4K 0x40 /* 4 kb of data, that is a 93xx66 */ - - unsigned int quirks; -/* Single word read transfers only; no sequential read. */ -#define EEPROM_93XX46_QUIRK_SINGLE_WORD_READ (1 << 0) -/* Instructions such as EWEN are (addrlen + 2) in length. */ -#define EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH (1 << 1) -/* Add extra cycle after address during a read */ -#define EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE BIT(2) - - struct gpio_desc *select; -}; - -#define OP_START 0x4 -#define OP_WRITE (OP_START | 0x1) -#define OP_READ (OP_START | 0x2) -/* The following addresses are offset for the 1K EEPROM variant in 16-bit mode */ -#define ADDR_EWDS 0x00 -#define ADDR_ERAL 0x20 -#define ADDR_EWEN 0x30 - -struct eeprom_93xx46_devtype_data { - unsigned int quirks; - unsigned char flags; -}; - -static const struct eeprom_93xx46_devtype_data at93c46_data = { - .flags = EE_SIZE1K, -}; - -static const struct eeprom_93xx46_devtype_data at93c56_data = { - .flags = EE_SIZE2K, -}; - -static const struct eeprom_93xx46_devtype_data at93c66_data = { - .flags = EE_SIZE4K, -}; - -static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = { - .flags = EE_SIZE1K, - .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ | - EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH, -}; - -static const struct eeprom_93xx46_devtype_data microchip_93lc46b_data = { - .flags = EE_SIZE1K, - .quirks = EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE, -}; - -struct eeprom_93xx46_dev { - struct spi_device *spi; - struct eeprom_93xx46_platform_data *pdata; - struct mutex lock; - struct nvmem_config nvmem_config; - struct nvmem_device *nvmem; - int addrlen; - int size; -}; - -static inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev) -{ - return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ; -} - -static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev) -{ - return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH; -} - -static inline bool has_quirk_extra_read_cycle(struct eeprom_93xx46_dev *edev) -{ - return edev->pdata->quirks & EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE; -} - -static int eeprom_93xx46_read(void *priv, unsigned int off, - void *val, size_t count) -{ - struct eeprom_93xx46_dev *edev = priv; - char *buf = val; - int err = 0; - int bits; - - if (unlikely(off >= edev->size)) - return 0; - if ((off + count) > edev->size) - count = edev->size - off; - if (unlikely(!count)) - return count; - - mutex_lock(&edev->lock); - - gpiod_set_value_cansleep(edev->pdata->select, 1); - - /* The opcode in front of the address is three bits. */ - bits = edev->addrlen + 3; - - while (count) { - struct spi_message m; - struct spi_transfer t[2] = {}; - u16 cmd_addr = OP_READ << edev->addrlen; - size_t nbytes = count; - - if (edev->pdata->flags & EE_ADDR8) { - cmd_addr |= off; - if (has_quirk_single_word_read(edev)) - nbytes = 1; - } else { - cmd_addr |= (off >> 1); - if (has_quirk_single_word_read(edev)) - nbytes = 2; - } - - dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", - cmd_addr, edev->spi->max_speed_hz); - - if (has_quirk_extra_read_cycle(edev)) { - cmd_addr <<= 1; - bits += 1; - } - - t[0].tx_buf = (char *)&cmd_addr; - t[0].len = 2; - t[0].bits_per_word = bits; - - t[1].rx_buf = buf; - t[1].len = count; - t[1].bits_per_word = 8; - - spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); - - err = spi_sync(edev->spi, &m); - /* have to wait at least Tcsl ns */ - ndelay(250); - - if (err) { - dev_err(&edev->spi->dev, "read %zu bytes at %u: err. %d\n", - nbytes, off, err); - break; - } - - buf += nbytes; - off += nbytes; - count -= nbytes; - } - - gpiod_set_value_cansleep(edev->pdata->select, 0); - - mutex_unlock(&edev->lock); - - return err; -} - -static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) -{ - struct spi_message m; - struct spi_transfer t = {}; - int bits, ret; - u16 cmd_addr; - - /* The opcode in front of the address is three bits. */ - bits = edev->addrlen + 3; - - cmd_addr = OP_START << edev->addrlen; - cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << (edev->addrlen - 6); - - if (has_quirk_instruction_length(edev)) { - cmd_addr <<= 2; - bits += 2; - } - - dev_dbg(&edev->spi->dev, "ew %s cmd 0x%04x, %d bits\n", - str_enable_disable(is_on), cmd_addr, bits); - - t.tx_buf = &cmd_addr; - t.len = 2; - t.bits_per_word = bits; - - spi_message_init_with_transfers(&m, &t, 1); - - mutex_lock(&edev->lock); - - gpiod_set_value_cansleep(edev->pdata->select, 1); - - ret = spi_sync(edev->spi, &m); - /* have to wait at least Tcsl ns */ - ndelay(250); - if (ret) - dev_err(&edev->spi->dev, "erase/write %s error %d\n", - str_enable_disable(is_on), ret); - - gpiod_set_value_cansleep(edev->pdata->select, 0); - - mutex_unlock(&edev->lock); - return ret; -} - -static ssize_t -eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, - const char *buf, unsigned int off) -{ - struct spi_message m; - struct spi_transfer t[2] = {}; - int bits, data_len, ret; - u16 cmd_addr; - - if (unlikely(off >= edev->size)) - return -EINVAL; - - /* The opcode in front of the address is three bits. */ - bits = edev->addrlen + 3; - - cmd_addr = OP_WRITE << edev->addrlen; - - if (edev->pdata->flags & EE_ADDR8) { - cmd_addr |= off; - data_len = 1; - } else { - cmd_addr |= (off >> 1); - data_len = 2; - } - - dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr); - - t[0].tx_buf = (char *)&cmd_addr; - t[0].len = 2; - t[0].bits_per_word = bits; - - t[1].tx_buf = buf; - t[1].len = data_len; - t[1].bits_per_word = 8; - - spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); - - ret = spi_sync(edev->spi, &m); - /* have to wait program cycle time Twc ms */ - mdelay(6); - return ret; -} - -static int eeprom_93xx46_write(void *priv, unsigned int off, - void *val, size_t count) -{ - struct eeprom_93xx46_dev *edev = priv; - char *buf = val; - int ret, step = 1; - unsigned int i; - - if (unlikely(off >= edev->size)) - return -EFBIG; - if ((off + count) > edev->size) - count = edev->size - off; - if (unlikely(!count)) - return count; - - /* only write even number of bytes on 16-bit devices */ - if (edev->pdata->flags & EE_ADDR16) { - step = 2; - count &= ~1; - } - - /* erase/write enable */ - ret = eeprom_93xx46_ew(edev, 1); - if (ret) - return ret; - - mutex_lock(&edev->lock); - - gpiod_set_value_cansleep(edev->pdata->select, 1); - - for (i = 0; i < count; i += step) { - ret = eeprom_93xx46_write_word(edev, &buf[i], off + i); - if (ret) { - dev_err(&edev->spi->dev, "write failed at %u: %d\n", off + i, ret); - break; - } - } - - gpiod_set_value_cansleep(edev->pdata->select, 0); - - mutex_unlock(&edev->lock); - - /* erase/write disable */ - eeprom_93xx46_ew(edev, 0); - return ret; -} - -static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) -{ - struct spi_message m; - struct spi_transfer t = {}; - int bits, ret; - u16 cmd_addr; - - /* The opcode in front of the address is three bits. */ - bits = edev->addrlen + 3; - - cmd_addr = OP_START << edev->addrlen; - cmd_addr |= ADDR_ERAL << (edev->addrlen - 6); - - if (has_quirk_instruction_length(edev)) { - cmd_addr <<= 2; - bits += 2; - } - - dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits); - - t.tx_buf = &cmd_addr; - t.len = 2; - t.bits_per_word = bits; - - spi_message_init_with_transfers(&m, &t, 1); - - mutex_lock(&edev->lock); - - gpiod_set_value_cansleep(edev->pdata->select, 1); - - ret = spi_sync(edev->spi, &m); - if (ret) - dev_err(&edev->spi->dev, "erase error %d\n", ret); - /* have to wait erase cycle time Tec ms */ - mdelay(6); - - gpiod_set_value_cansleep(edev->pdata->select, 0); - - mutex_unlock(&edev->lock); - return ret; -} - -static ssize_t erase_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev); - bool erase; - int ret; - - ret = kstrtobool(buf, &erase); - if (ret) - return ret; - - if (erase) { - ret = eeprom_93xx46_ew(edev, 1); - if (ret) - return ret; - ret = eeprom_93xx46_eral(edev); - if (ret) - return ret; - ret = eeprom_93xx46_ew(edev, 0); - if (ret) - return ret; - } - return count; -} -static DEVICE_ATTR_WO(erase); - -static const struct of_device_id eeprom_93xx46_of_table[] = { - { .compatible = "eeprom-93xx46", .data = &at93c46_data, }, - { .compatible = "atmel,at93c46", .data = &at93c46_data, }, - { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, - { .compatible = "atmel,at93c56", .data = &at93c56_data, }, - { .compatible = "atmel,at93c66", .data = &at93c66_data, }, - { .compatible = "microchip,93lc46b", .data = µchip_93lc46b_data, }, - {} -}; -MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); - -static const struct spi_device_id eeprom_93xx46_spi_ids[] = { - { .name = "eeprom-93xx46", - .driver_data = (kernel_ulong_t)&at93c46_data, }, - { .name = "at93c46", - .driver_data = (kernel_ulong_t)&at93c46_data, }, - { .name = "at93c46d", - .driver_data = (kernel_ulong_t)&atmel_at93c46d_data, }, - { .name = "at93c56", - .driver_data = (kernel_ulong_t)&at93c56_data, }, - { .name = "at93c66", - .driver_data = (kernel_ulong_t)&at93c66_data, }, - { .name = "93lc46b", - .driver_data = (kernel_ulong_t)µchip_93lc46b_data, }, - {} -}; -MODULE_DEVICE_TABLE(spi, eeprom_93xx46_spi_ids); - -static int eeprom_93xx46_probe_fw(struct device *dev) -{ - const struct eeprom_93xx46_devtype_data *data; - struct eeprom_93xx46_platform_data *pd; - u32 tmp; - int ret; - - pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - ret = device_property_read_u32(dev, "data-size", &tmp); - if (ret < 0) { - dev_err(dev, "data-size property not found\n"); - return ret; - } - - if (tmp == 8) { - pd->flags |= EE_ADDR8; - } else if (tmp == 16) { - pd->flags |= EE_ADDR16; - } else { - dev_err(dev, "invalid data-size (%d)\n", tmp); - return -EINVAL; - } - - if (device_property_read_bool(dev, "read-only")) - pd->flags |= EE_READONLY; - - pd->select = devm_gpiod_get_optional(dev, "select", GPIOD_OUT_LOW); - if (IS_ERR(pd->select)) - return PTR_ERR(pd->select); - gpiod_set_consumer_name(pd->select, "93xx46 EEPROMs OE"); - - data = spi_get_device_match_data(to_spi_device(dev)); - if (data) { - pd->quirks = data->quirks; - pd->flags |= data->flags; - } - - dev->platform_data = pd; - - return 0; -} - -static int eeprom_93xx46_probe(struct spi_device *spi) -{ - struct eeprom_93xx46_platform_data *pd; - struct eeprom_93xx46_dev *edev; - struct device *dev = &spi->dev; - int err; - - err = eeprom_93xx46_probe_fw(dev); - if (err < 0) - return err; - - pd = spi->dev.platform_data; - if (!pd) { - dev_err(&spi->dev, "missing platform data\n"); - return -ENODEV; - } - - edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL); - if (!edev) - return -ENOMEM; - - if (pd->flags & EE_SIZE1K) - edev->size = 128; - else if (pd->flags & EE_SIZE2K) - edev->size = 256; - else if (pd->flags & EE_SIZE4K) - edev->size = 512; - else { - dev_err(&spi->dev, "unspecified size\n"); - return -EINVAL; - } - - if (pd->flags & EE_ADDR8) - edev->addrlen = ilog2(edev->size); - else if (pd->flags & EE_ADDR16) - edev->addrlen = ilog2(edev->size) - 1; - else { - dev_err(&spi->dev, "unspecified address type\n"); - return -EINVAL; - } - - mutex_init(&edev->lock); - - edev->spi = spi; - edev->pdata = pd; - - edev->nvmem_config.type = NVMEM_TYPE_EEPROM; - edev->nvmem_config.name = dev_name(&spi->dev); - edev->nvmem_config.dev = &spi->dev; - edev->nvmem_config.read_only = pd->flags & EE_READONLY; - edev->nvmem_config.root_only = true; - edev->nvmem_config.owner = THIS_MODULE; - edev->nvmem_config.compat = true; - edev->nvmem_config.base_dev = &spi->dev; - edev->nvmem_config.reg_read = eeprom_93xx46_read; - edev->nvmem_config.reg_write = eeprom_93xx46_write; - edev->nvmem_config.priv = edev; - edev->nvmem_config.stride = 4; - edev->nvmem_config.word_size = 1; - edev->nvmem_config.size = edev->size; - - 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 containing %d bytes %s\n", - (pd->flags & EE_ADDR8) ? 8 : 16, - edev->size, - (pd->flags & EE_READONLY) ? "(readonly)" : ""); - - if (!(pd->flags & EE_READONLY)) { - if (device_create_file(&spi->dev, &dev_attr_erase)) - dev_err(&spi->dev, "can't create erase interface\n"); - } - - spi_set_drvdata(spi, edev); - return 0; -} - -static void eeprom_93xx46_remove(struct spi_device *spi) -{ - struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi); - - if (!(edev->pdata->flags & EE_READONLY)) - device_remove_file(&spi->dev, &dev_attr_erase); -} - -static struct spi_driver eeprom_93xx46_driver = { - .driver = { - .name = "93xx46", - .of_match_table = eeprom_93xx46_of_table, - }, - .probe = eeprom_93xx46_probe, - .remove = eeprom_93xx46_remove, - .id_table = eeprom_93xx46_spi_ids, -}; - -module_spi_driver(eeprom_93xx46_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs"); -MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); -MODULE_ALIAS("spi:93xx46"); diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/misc/eeprom/m24lr.c deleted file mode 100644 index 7a9fd45a8e46..000000000000 --- a/drivers/misc/eeprom/m24lr.c +++ /dev/null @@ -1,606 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * m24lr.c - Sysfs control interface for ST M24LR series RFID/NFC chips - * - * Copyright (c) 2025 Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com> - * - * This driver implements both the sysfs-based control interface and EEPROM - * access for STMicroelectronics M24LR series chips (e.g., M24LR04E-R). - * It provides access to control registers for features such as password - * authentication, memory protection, and device configuration. In addition, - * it manages read and write operations to the EEPROM region of the chip. - */ - -#include <linux/device.h> -#include <linux/i2c.h> -#include <linux/module.h> -#include <linux/nvmem-provider.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/regmap.h> - -#define M24LR_WRITE_TIMEOUT 25u -#define M24LR_READ_TIMEOUT (M24LR_WRITE_TIMEOUT) - -/** - * struct m24lr_chip - describes chip-specific sysfs layout - * @sss_len: the length of the sss region - * @page_size: chip-specific limit on the maximum number of bytes allowed - * in a single write operation. - * @eeprom_size: size of the EEPROM in byte - * - * Supports multiple M24LR chip variants (e.g., M24LRxx) by allowing each - * to define its own set of sysfs attributes, depending on its available - * registers and features. - */ -struct m24lr_chip { - unsigned int sss_len; - unsigned int page_size; - unsigned int eeprom_size; -}; - -/** - * struct m24lr - core driver data for M24LR chip control - * @uid: 64 bits unique identifier stored in the device - * @sss_len: the length of the sss region - * @page_size: chip-specific limit on the maximum number of bytes allowed - * in a single write operation. - * @eeprom_size: size of the EEPROM in byte - * @ctl_regmap: regmap interface for accessing the system parameter sector - * @eeprom_regmap: regmap interface for accessing the EEPROM - * @lock: mutex to synchronize operations to the device - * - * Central data structure holding the state and resources used by the - * M24LR device driver. - */ -struct m24lr { - u64 uid; - unsigned int sss_len; - unsigned int page_size; - unsigned int eeprom_size; - struct regmap *ctl_regmap; - struct regmap *eeprom_regmap; - struct mutex lock; /* synchronize operations to the device */ -}; - -static const struct regmap_range m24lr_ctl_vo_ranges[] = { - regmap_reg_range(0, 63), -}; - -static const struct regmap_access_table m24lr_ctl_vo_table = { - .yes_ranges = m24lr_ctl_vo_ranges, - .n_yes_ranges = ARRAY_SIZE(m24lr_ctl_vo_ranges), -}; - -static const struct regmap_config m24lr_ctl_regmap_conf = { - .name = "m24lr_ctl", - .reg_stride = 1, - .reg_bits = 16, - .val_bits = 8, - .disable_locking = false, - .cache_type = REGCACHE_RBTREE,/* Flat can't be used, there's huge gap */ - .volatile_table = &m24lr_ctl_vo_table, -}; - -/* Chip descriptor for M24LR04E-R variant */ -static const struct m24lr_chip m24lr04e_r_chip = { - .page_size = 4, - .eeprom_size = 512, - .sss_len = 4, -}; - -/* Chip descriptor for M24LR16E-R variant */ -static const struct m24lr_chip m24lr16e_r_chip = { - .page_size = 4, - .eeprom_size = 2048, - .sss_len = 16, -}; - -/* Chip descriptor for M24LR64E-R variant */ -static const struct m24lr_chip m24lr64e_r_chip = { - .page_size = 4, - .eeprom_size = 8192, - .sss_len = 64, -}; - -static const struct i2c_device_id m24lr_ids[] = { - { "m24lr04e-r", (kernel_ulong_t)&m24lr04e_r_chip}, - { "m24lr16e-r", (kernel_ulong_t)&m24lr16e_r_chip}, - { "m24lr64e-r", (kernel_ulong_t)&m24lr64e_r_chip}, - { } -}; -MODULE_DEVICE_TABLE(i2c, m24lr_ids); - -static const struct of_device_id m24lr_of_match[] = { - { .compatible = "st,m24lr04e-r", .data = &m24lr04e_r_chip}, - { .compatible = "st,m24lr16e-r", .data = &m24lr16e_r_chip}, - { .compatible = "st,m24lr64e-r", .data = &m24lr64e_r_chip}, - { } -}; -MODULE_DEVICE_TABLE(of, m24lr_of_match); - -/** - * m24lr_regmap_read - read data using regmap with retry on failure - * @regmap: regmap instance for the device - * @buf: buffer to store the read data - * @size: number of bytes to read - * @offset: starting register address - * - * Attempts to read a block of data from the device with retries and timeout. - * Some M24LR chips may transiently NACK reads (e.g., during internal write - * cycles), so this function retries with a short sleep until the timeout - * expires. - * - * Returns: - * Number of bytes read on success, - * -ETIMEDOUT if the read fails within the timeout window. - */ -static ssize_t m24lr_regmap_read(struct regmap *regmap, u8 *buf, - size_t size, unsigned int offset) -{ - int err; - unsigned long timeout, read_time; - ssize_t ret = -ETIMEDOUT; - - timeout = jiffies + msecs_to_jiffies(M24LR_READ_TIMEOUT); - do { - read_time = jiffies; - - err = regmap_bulk_read(regmap, offset, buf, size); - if (!err) { - ret = size; - break; - } - - usleep_range(1000, 2000); - } while (time_before(read_time, timeout)); - - return ret; -} - -/** - * m24lr_regmap_write - write data using regmap with retry on failure - * @regmap: regmap instance for the device - * @buf: buffer containing the data to write - * @size: number of bytes to write - * @offset: starting register address - * - * Attempts to write a block of data to the device with retries and a timeout. - * Some M24LR devices may NACK I2C writes while an internal write operation - * is in progress. This function retries the write operation with a short delay - * until it succeeds or the timeout is reached. - * - * Returns: - * Number of bytes written on success, - * -ETIMEDOUT if the write fails within the timeout window. - */ -static ssize_t m24lr_regmap_write(struct regmap *regmap, const u8 *buf, - size_t size, unsigned int offset) -{ - int err; - unsigned long timeout, write_time; - ssize_t ret = -ETIMEDOUT; - - timeout = jiffies + msecs_to_jiffies(M24LR_WRITE_TIMEOUT); - - do { - write_time = jiffies; - - err = regmap_bulk_write(regmap, offset, buf, size); - if (!err) { - ret = size; - break; - } - - usleep_range(1000, 2000); - } while (time_before(write_time, timeout)); - - return ret; -} - -static ssize_t m24lr_read(struct m24lr *m24lr, u8 *buf, size_t size, - unsigned int offset, bool is_eeprom) -{ - struct regmap *regmap; - ssize_t ret; - - if (is_eeprom) - regmap = m24lr->eeprom_regmap; - else - regmap = m24lr->ctl_regmap; - - mutex_lock(&m24lr->lock); - ret = m24lr_regmap_read(regmap, buf, size, offset); - mutex_unlock(&m24lr->lock); - - return ret; -} - -/** - * m24lr_write - write buffer to M24LR device with page alignment handling - * @m24lr: pointer to driver context - * @buf: data buffer to write - * @size: number of bytes to write - * @offset: target register address in the device - * @is_eeprom: true if the write should target the EEPROM, - * false if it should target the system parameters sector. - * - * Writes data to the M24LR device using regmap, split into chunks no larger - * than page_size to respect device-specific write limitations (e.g., page - * size or I2C hold-time concerns). Each chunk is aligned to the page boundary - * defined by page_size. - * - * Returns: - * Total number of bytes written on success, - * A negative error code if any write fails. - */ -static ssize_t m24lr_write(struct m24lr *m24lr, const u8 *buf, size_t size, - unsigned int offset, bool is_eeprom) -{ - unsigned int n, next_sector; - struct regmap *regmap; - ssize_t ret = 0; - ssize_t err; - - if (is_eeprom) - regmap = m24lr->eeprom_regmap; - else - regmap = m24lr->ctl_regmap; - - n = min_t(unsigned int, size, m24lr->page_size); - next_sector = roundup(offset + 1, m24lr->page_size); - if (offset + n > next_sector) - n = next_sector - offset; - - mutex_lock(&m24lr->lock); - while (n) { - err = m24lr_regmap_write(regmap, buf + offset, n, offset); - if (IS_ERR_VALUE(err)) { - if (!ret) - ret = err; - - break; - } - - offset += n; - size -= n; - ret += n; - n = min_t(unsigned int, size, m24lr->page_size); - } - mutex_unlock(&m24lr->lock); - - return ret; -} - -/** - * m24lr_write_pass - Write password to M24LR043-R using secure format - * @m24lr: Pointer to device control structure - * @buf: Input buffer containing hex-encoded password - * @count: Number of bytes in @buf - * @code: Operation code to embed between password copies - * - * This function parses a 4-byte password, encodes it in big-endian format, - * and constructs a 9-byte sequence of the form: - * - * [BE(password), code, BE(password)] - * - * The result is written to register 0x0900 (2304), which is the password - * register in M24LR04E-R chip. - * - * Return: Number of bytes written on success, or negative error code on failure - */ -static ssize_t m24lr_write_pass(struct m24lr *m24lr, const char *buf, - size_t count, u8 code) -{ - __be32 be_pass; - u8 output[9]; - ssize_t ret; - u32 pass; - int err; - - if (!count) - return -EINVAL; - - if (count > 8) - return -EINVAL; - - err = kstrtou32(buf, 16, &pass); - if (err) - return err; - - be_pass = cpu_to_be32(pass); - - memcpy(output, &be_pass, sizeof(be_pass)); - output[4] = code; - memcpy(output + 5, &be_pass, sizeof(be_pass)); - - mutex_lock(&m24lr->lock); - ret = m24lr_regmap_write(m24lr->ctl_regmap, output, 9, 2304); - mutex_unlock(&m24lr->lock); - - return ret; -} - -static ssize_t m24lr_read_reg_le(struct m24lr *m24lr, u64 *val, - unsigned int reg_addr, - unsigned int reg_size) -{ - ssize_t ret; - __le64 input = 0; - - ret = m24lr_read(m24lr, (u8 *)&input, reg_size, reg_addr, false); - if (IS_ERR_VALUE(ret)) - return ret; - - if (ret != reg_size) - return -EINVAL; - - switch (reg_size) { - case 1: - *val = *(u8 *)&input; - break; - case 2: - *val = le16_to_cpu((__le16)input); - break; - case 4: - *val = le32_to_cpu((__le32)input); - break; - case 8: - *val = le64_to_cpu((__le64)input); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int m24lr_nvmem_read(void *priv, unsigned int offset, void *val, - size_t bytes) -{ - ssize_t err; - struct m24lr *m24lr = priv; - - if (!bytes) - return bytes; - - if (offset + bytes > m24lr->eeprom_size) - return -EINVAL; - - err = m24lr_read(m24lr, val, bytes, offset, true); - if (IS_ERR_VALUE(err)) - return err; - - return 0; -} - -static int m24lr_nvmem_write(void *priv, unsigned int offset, void *val, - size_t bytes) -{ - ssize_t err; - struct m24lr *m24lr = priv; - - if (!bytes) - return -EINVAL; - - if (offset + bytes > m24lr->eeprom_size) - return -EINVAL; - - err = m24lr_write(m24lr, val, bytes, offset, true); - if (IS_ERR_VALUE(err)) - return err; - - return 0; -} - -static ssize_t m24lr_ctl_sss_read(struct file *filep, struct kobject *kobj, - const struct bin_attribute *attr, char *buf, - loff_t offset, size_t count) -{ - struct m24lr *m24lr = attr->private; - - if (!count) - return count; - - if (size_add(offset, count) > m24lr->sss_len) - return -EINVAL; - - return m24lr_read(m24lr, buf, count, offset, false); -} - -static ssize_t m24lr_ctl_sss_write(struct file *filep, struct kobject *kobj, - const struct bin_attribute *attr, char *buf, - loff_t offset, size_t count) -{ - struct m24lr *m24lr = attr->private; - - if (!count) - return -EINVAL; - - if (size_add(offset, count) > m24lr->sss_len) - return -EINVAL; - - return m24lr_write(m24lr, buf, count, offset, false); -} -static BIN_ATTR(sss, 0600, m24lr_ctl_sss_read, m24lr_ctl_sss_write, 0); - -static ssize_t new_pass_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); - - return m24lr_write_pass(m24lr, buf, count, 7); -} -static DEVICE_ATTR_WO(new_pass); - -static ssize_t unlock_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); - - return m24lr_write_pass(m24lr, buf, count, 9); -} -static DEVICE_ATTR_WO(unlock); - -static ssize_t uid_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); - - return sysfs_emit(buf, "%llx\n", m24lr->uid); -} -static DEVICE_ATTR_RO(uid); - -static ssize_t total_sectors_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev)); - - return sysfs_emit(buf, "%x\n", m24lr->sss_len); -} -static DEVICE_ATTR_RO(total_sectors); - -static struct attribute *m24lr_ctl_dev_attrs[] = { - &dev_attr_unlock.attr, - &dev_attr_new_pass.attr, - &dev_attr_uid.attr, - &dev_attr_total_sectors.attr, - NULL, -}; - -static const struct m24lr_chip *m24lr_get_chip(struct device *dev) -{ - const struct m24lr_chip *ret; - const struct i2c_device_id *id; - - id = i2c_match_id(m24lr_ids, to_i2c_client(dev)); - - if (dev->of_node && of_match_device(m24lr_of_match, dev)) - ret = of_device_get_match_data(dev); - else if (id) - ret = (void *)id->driver_data; - else - ret = acpi_device_get_match_data(dev); - - return ret; -} - -static int m24lr_probe(struct i2c_client *client) -{ - struct regmap_config eeprom_regmap_conf = {0}; - struct nvmem_config nvmem_conf = {0}; - struct device *dev = &client->dev; - struct i2c_client *eeprom_client; - const struct m24lr_chip *chip; - struct regmap *eeprom_regmap; - struct nvmem_device *nvmem; - struct regmap *ctl_regmap; - struct m24lr *m24lr; - u32 regs[2]; - long err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -EOPNOTSUPP; - - chip = m24lr_get_chip(dev); - if (!chip) - return -ENODEV; - - m24lr = devm_kzalloc(dev, sizeof(struct m24lr), GFP_KERNEL); - if (!m24lr) - return -ENOMEM; - - err = device_property_read_u32_array(dev, "reg", regs, ARRAY_SIZE(regs)); - if (err) - return dev_err_probe(dev, err, "Failed to read 'reg' property\n"); - - /* Create a second I2C client for the eeprom interface */ - eeprom_client = devm_i2c_new_dummy_device(dev, client->adapter, regs[1]); - if (IS_ERR(eeprom_client)) - return dev_err_probe(dev, PTR_ERR(eeprom_client), - "Failed to create dummy I2C client for the EEPROM\n"); - - ctl_regmap = devm_regmap_init_i2c(client, &m24lr_ctl_regmap_conf); - if (IS_ERR(ctl_regmap)) - return dev_err_probe(dev, PTR_ERR(ctl_regmap), - "Failed to init regmap\n"); - - eeprom_regmap_conf.name = "m24lr_eeprom"; - eeprom_regmap_conf.reg_bits = 16; - eeprom_regmap_conf.val_bits = 8; - eeprom_regmap_conf.disable_locking = true; - eeprom_regmap_conf.max_register = chip->eeprom_size - 1; - - eeprom_regmap = devm_regmap_init_i2c(eeprom_client, - &eeprom_regmap_conf); - if (IS_ERR(eeprom_regmap)) - return dev_err_probe(dev, PTR_ERR(eeprom_regmap), - "Failed to init regmap\n"); - - mutex_init(&m24lr->lock); - m24lr->sss_len = chip->sss_len; - m24lr->page_size = chip->page_size; - m24lr->eeprom_size = chip->eeprom_size; - m24lr->eeprom_regmap = eeprom_regmap; - m24lr->ctl_regmap = ctl_regmap; - - nvmem_conf.dev = &eeprom_client->dev; - nvmem_conf.owner = THIS_MODULE; - nvmem_conf.type = NVMEM_TYPE_EEPROM; - nvmem_conf.reg_read = m24lr_nvmem_read; - nvmem_conf.reg_write = m24lr_nvmem_write; - nvmem_conf.size = chip->eeprom_size; - nvmem_conf.word_size = 1; - nvmem_conf.stride = 1; - nvmem_conf.priv = m24lr; - - nvmem = devm_nvmem_register(dev, &nvmem_conf); - if (IS_ERR(nvmem)) - return dev_err_probe(dev, PTR_ERR(nvmem), - "Failed to register nvmem\n"); - - i2c_set_clientdata(client, m24lr); - i2c_set_clientdata(eeprom_client, m24lr); - - bin_attr_sss.size = chip->sss_len; - bin_attr_sss.private = m24lr; - err = sysfs_create_bin_file(&dev->kobj, &bin_attr_sss); - if (err) - return dev_err_probe(dev, err, - "Failed to create sss bin file\n"); - - /* test by reading the uid, if success store it */ - err = m24lr_read_reg_le(m24lr, &m24lr->uid, 2324, sizeof(m24lr->uid)); - if (IS_ERR_VALUE(err)) - goto remove_bin_file; - - return 0; - -remove_bin_file: - sysfs_remove_bin_file(&dev->kobj, &bin_attr_sss); - - return err; -} - -static void m24lr_remove(struct i2c_client *client) -{ - sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_sss); -} - -ATTRIBUTE_GROUPS(m24lr_ctl_dev); - -static struct i2c_driver m24lr_driver = { - .driver = { - .name = "m24lr", - .of_match_table = m24lr_of_match, - .dev_groups = m24lr_ctl_dev_groups, - }, - .probe = m24lr_probe, - .remove = m24lr_remove, - .id_table = m24lr_ids, -}; -module_i2c_driver(m24lr_driver); - -MODULE_AUTHOR("Abd-Alrhman Masalkhi"); -MODULE_DESCRIPTION("st m24lr control driver"); -MODULE_LICENSE("GPL"); |
