eeprom: at25: expose JEDEC ID via sysfs

Return the raw JEDEC ID bytes as returned by the RDID command, even for
variations that have the bytes in reverse order. This way we can avoid
ambiguity if the manufacturer ever releases a new chip that returns them
according to standard.

Signed-off-by: Patrick Wicki <patrick.wicki@siemens.com>
Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Tested-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Link: https://patch.msgid.link/20260120130603.1066559-2-patrick@subset.ch
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Patrick Wicki
2026-01-20 14:06:03 +01:00
committed by Greg Kroah-Hartman
parent 3fcd9a0fbb
commit 62bb2054f9
2 changed files with 41 additions and 8 deletions

View File

@@ -17,3 +17,14 @@ Description:
from the device.
This is a read-only attribute.
What: /sys/class/spi_master/spi<bus>/spi<bus>.<dev>/jedec_id
Date: January 2026
KernelVersion: 6.19
Contact: Patrick Wicki <patrick.wicki@siemens.com>
Description:
Contains the raw JEDEC ID bytes returned by the RDID (0x9f) command. The
bytes are exposed as a hex string in big-endian order as read from the
device.
This is a read-only attribute.

View File

@@ -34,6 +34,7 @@
*/
#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 {
@@ -44,6 +45,8 @@ struct at25_data {
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 */
@@ -64,8 +67,6 @@ struct at25_data {
#define AT25_INSTR_BIT3 0x08 /* additional address bit in instr */
#define FM25_ID_LEN 9 /* ID length */
/*
* Specs often allow 5ms for a page write, sometimes 20ms;
* it's important to recover from write timeouts.
@@ -180,11 +181,25 @@ static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, ch
}
static DEVICE_ATTR_RO(sernum);
static struct attribute *sernum_attrs[] = {
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(sernum);
ATTRIBUTE_GROUPS(at25);
/*
* Poll Read Status Register with timeout
@@ -378,7 +393,7 @@ 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_ID_LEN];
u8 id[FM25_MAX_ID_LEN];
u32 val;
int i;
@@ -388,7 +403,12 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
chip->byte_len = val;
} else {
/* Get ID of chip */
fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN);
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++) {
@@ -400,6 +420,7 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
}
if (id[6] == 0xc2) {
at25->id_len = 9;
switch (id[7]) {
case 0x21 ... 0x26:
chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024;
@@ -413,6 +434,7 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
return -ENODEV;
}
} else if (id[2] == 0x82 && id[3] == 0x06) {
at25->id_len = 8;
switch (id[1]) {
case 0x51 ... 0x54:
/* CY15B102QSN ... CY15B204QSN */
@@ -424,7 +446,7 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
}
} else {
dev_err(dev, "Error: unrecognized JEDEC ID format: %*ph\n",
FM25_ID_LEN, id);
FM25_MAX_ID_LEN, id);
return -ENODEV;
}
@@ -549,7 +571,7 @@ static struct spi_mem_driver at25_driver = {
.driver = {
.name = "at25",
.of_match_table = at25_of_match,
.dev_groups = sernum_groups,
.dev_groups = at25_groups,
},
.id_table = at25_spi_ids,
},