* [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs @ 2026-01-20 13:06 patrick 2026-01-20 13:06 ` [PATCH 2/2] eeprom: at25: expose JEDEC ID via sysfs patrick 2026-01-21 8:58 ` [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs Sverdlin, Alexander 0 siblings, 2 replies; 4+ messages in thread From: patrick @ 2026-01-20 13:06 UTC (permalink / raw) To: linux-kernel; +Cc: alexander.sverdlin, gregkh, arnd, Patrick Wicki From: Patrick Wicki <patrick.wicki@siemens.com> Add support for Infineon Cypress CY15****QSN FRAM chips. Unlike the QN variants these chips have an 8 byte JEDEC ID. The layout of the serial number matches that of already supported chips like the CY15B204QN, so make the read-out unconditional. Tested with the CY15B204QSN. According to Infineon datasheets, all QSN variants appear to share a consistent pattern so the size should be correctly detected based on the density bits in the ID: CY15B201QSN: 0x00 00 00 00 06 82 54 40, density_id: 8: 1Mb CY15B102QSN: 0x00 00 00 00 06 82 51 48, density_id: 9: 2Mb CY15B204QSN: 0x00 00 00 00 06 82 54 50, density_id: 10: 4Mb CY15V108QSN: 0x00 00 00 00 06 82 52 58, density_id: 11: 8Mb Signed-off-by: Patrick Wicki <patrick.wicki@siemens.com> --- drivers/misc/eeprom/at25.c | 50 +++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 883dfd0ed6583..5b00921d07d2e 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -398,30 +398,40 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) id[i] = id[j]; id[j] = tmp; } - if (id[6] != 0xc2) { - dev_err(dev, "Error: no Cypress FRAM with device ID (manufacturer ID bank 7: %02x)\n", id[6]); - return -ENODEV; - } - 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]); + if (id[6] == 0xc2) { + 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) { + 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_ID_LEN, id); return -ENODEV; } - if (id[8]) { - 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]; - } + 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) -- 2.52.0 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 2/2] eeprom: at25: expose JEDEC ID via sysfs 2026-01-20 13:06 [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs patrick @ 2026-01-20 13:06 ` patrick 2026-01-21 8:58 ` Sverdlin, Alexander 2026-01-21 8:58 ` [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs Sverdlin, Alexander 1 sibling, 1 reply; 4+ messages in thread From: patrick @ 2026-01-20 13:06 UTC (permalink / raw) To: linux-kernel; +Cc: alexander.sverdlin, gregkh, arnd, Patrick Wicki From: Patrick Wicki <patrick.wicki@siemens.com> 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> --- .../ABI/testing/sysfs-class-spi-eeprom | 11 ++++++ drivers/misc/eeprom/at25.c | 38 +++++++++++++++---- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-spi-eeprom b/Documentation/ABI/testing/sysfs-class-spi-eeprom index 1ff7579820798..f4bc7d9454cfb 100644 --- a/Documentation/ABI/testing/sysfs-class-spi-eeprom +++ b/Documentation/ABI/testing/sysfs-class-spi-eeprom @@ -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. diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 5b00921d07d2e..bc2cfb75d9bb4 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -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, }, -- 2.52.0 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] eeprom: at25: expose JEDEC ID via sysfs 2026-01-20 13:06 ` [PATCH 2/2] eeprom: at25: expose JEDEC ID via sysfs patrick @ 2026-01-21 8:58 ` Sverdlin, Alexander 0 siblings, 0 replies; 4+ messages in thread From: Sverdlin, Alexander @ 2026-01-21 8:58 UTC (permalink / raw) To: patrick, linux-kernel; +Cc: Wicki, Patrick On Tue, 2026-01-20 at 14:06 +0100, patrick@subset.ch wrote: > From: Patrick Wicki <patrick.wicki@siemens.com> > > 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> Tested with CY15B204QSN: Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com> Tested-by: Alexander Sverdlin <alexander.sverdlin@siemens.com> > --- > .../ABI/testing/sysfs-class-spi-eeprom | 11 ++++++ > drivers/misc/eeprom/at25.c | 38 +++++++++++++++---- > 2 files changed, 41 insertions(+), 8 deletions(-) > > diff --git a/Documentation/ABI/testing/sysfs-class-spi-eeprom b/Documentation/ABI/testing/sysfs-class-spi-eeprom > index 1ff7579820798..f4bc7d9454cfb 100644 > --- a/Documentation/ABI/testing/sysfs-class-spi-eeprom > +++ b/Documentation/ABI/testing/sysfs-class-spi-eeprom > @@ -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. > diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c > index 5b00921d07d2e..bc2cfb75d9bb4 100644 > --- a/drivers/misc/eeprom/at25.c > +++ b/drivers/misc/eeprom/at25.c > @@ -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, > }, -- Alexander Sverdlin Siemens AG www.siemens.com ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs 2026-01-20 13:06 [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs patrick 2026-01-20 13:06 ` [PATCH 2/2] eeprom: at25: expose JEDEC ID via sysfs patrick @ 2026-01-21 8:58 ` Sverdlin, Alexander 1 sibling, 0 replies; 4+ messages in thread From: Sverdlin, Alexander @ 2026-01-21 8:58 UTC (permalink / raw) To: patrick, linux-kernel; +Cc: Wicki, Patrick On Tue, 2026-01-20 at 14:06 +0100, patrick@subset.ch wrote: > From: Patrick Wicki <patrick.wicki@siemens.com> > > Add support for Infineon Cypress CY15****QSN FRAM chips. > Unlike the QN variants these chips have an 8 byte JEDEC ID. > > The layout of the serial number matches that of already supported chips > like the CY15B204QN, so make the read-out unconditional. > > Tested with the CY15B204QSN. According to Infineon datasheets, all QSN > variants appear to share a consistent pattern so the size should be > correctly detected based on the density bits in the ID: > > CY15B201QSN: 0x00 00 00 00 06 82 54 40, density_id: 8: 1Mb > CY15B102QSN: 0x00 00 00 00 06 82 51 48, density_id: 9: 2Mb > CY15B204QSN: 0x00 00 00 00 06 82 54 50, density_id: 10: 4Mb > CY15V108QSN: 0x00 00 00 00 06 82 52 58, density_id: 11: 8Mb > > Signed-off-by: Patrick Wicki <patrick.wicki@siemens.com> Tested with CY15B204QSN: Reviewed-by: Alexander Sverdlin <alexander.sverdlin@siemens.com> Tested-by: Alexander Sverdlin <alexander.sverdlin@siemens.com> > --- > drivers/misc/eeprom/at25.c | 50 +++++++++++++++++++++++--------------- > 1 file changed, 30 insertions(+), 20 deletions(-) > > diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c > index 883dfd0ed6583..5b00921d07d2e 100644 > --- a/drivers/misc/eeprom/at25.c > +++ b/drivers/misc/eeprom/at25.c > @@ -398,30 +398,40 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) > id[i] = id[j]; > id[j] = tmp; > } > - if (id[6] != 0xc2) { > - dev_err(dev, "Error: no Cypress FRAM with device ID (manufacturer ID bank 7: %02x)\n", id[6]); > - return -ENODEV; > - } > > - 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]); > + if (id[6] == 0xc2) { > + 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) { > + 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_ID_LEN, id); > return -ENODEV; > } > > - if (id[8]) { > - 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]; > - } > + 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) -- Alexander Sverdlin Siemens AG www.siemens.com ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-01-21 8:58 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-01-20 13:06 [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs patrick 2026-01-20 13:06 ` [PATCH 2/2] eeprom: at25: expose JEDEC ID via sysfs patrick 2026-01-21 8:58 ` Sverdlin, Alexander 2026-01-21 8:58 ` [PATCH 1/2] eeprom: at25: add support for Infineon Cypress QSN FRAMs Sverdlin, Alexander
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®