Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nvmem: prepare basics for FRAM support

Added enum and string for FRAM (ferroelectric RAM) to expose it as file
named "fram".
Added documentation of sysfs file.

Signed-off-by: Jiri Prchal <jiri.prchal@aksignal.cz>
Link: https://lore.kernel.org/r/20210611094601.95131-2-jiri.prchal@aksignal.cz
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Jiri Prchal and committed by
Greg Kroah-Hartman
fd307a4a 989f77e3

+183 -38
+19
Documentation/ABI/testing/sysfs-class-spi-eeprom
··· 1 + What: /sys/class/spi_master/spi<bus>/spi<bus>.<dev>/fram 2 + Date: June 2021 3 + KernelVersion: 5.14 4 + Contact: Jiri Prchal <jiri.prchal@aksignal.cz> 5 + Description: 6 + Contains the FRAM binary data. Same as EEPROM, just another file 7 + name to indicate that it employs ferroelectric process. 8 + It performs write operations at bus speed - no write delays. 9 + 10 + What: /sys/class/spi_master/spi<bus>/spi<bus>.<dev>/sernum 11 + Date: May 2021 12 + KernelVersion: 5.14 13 + Contact: Jiri Prchal <jiri.prchal@aksignal.cz> 14 + Description: 15 + Contains the serial number of the Cypress FRAM (FM25VN) if it is 16 + present. It will be displayed as a 8 byte hex string, as read 17 + from the device. 18 + 19 + This is a read-only attribute.
+25 -6
Documentation/devicetree/bindings/eeprom/at25.yaml
··· 4 4 $id: "http://devicetree.org/schemas/eeprom/at25.yaml#" 5 5 $schema: "http://devicetree.org/meta-schemas/core.yaml#" 6 6 7 - title: SPI EEPROMs compatible with Atmel's AT25 7 + title: SPI EEPROMs or FRAMs compatible with Atmel's AT25 8 8 9 9 maintainers: 10 10 - Christian Eggers <ceggers@arri.de> 11 11 12 12 properties: 13 13 $nodename: 14 - pattern: "^eeprom@[0-9a-f]{1,2}$" 14 + anyOf: 15 + - pattern: "^eeprom@[0-9a-f]{1,2}$" 16 + - pattern: "^fram@[0-9a-f]{1,2}$" 15 17 16 18 # There are multiple known vendors who manufacture EEPROM chips compatible 17 19 # with Atmel's AT25. The compatible string requires two items where the ··· 33 31 - microchip,25lc040 34 32 - st,m95m02 35 33 - st,m95256 34 + - cypress,fm25 36 35 37 36 - const: atmel,at25 38 37 ··· 50 47 $ref: /schemas/types.yaml#/definitions/uint32 51 48 enum: [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072] 52 49 description: 53 - Size of the eeprom page. 50 + Size of the eeprom page. FRAMs don't have pages. 54 51 55 52 size: 56 53 $ref: /schemas/types.yaml#/definitions/uint32 ··· 103 100 - compatible 104 101 - reg 105 102 - spi-max-frequency 106 - - pagesize 107 - - size 108 - - address-width 103 + 104 + allOf: 105 + - if: 106 + properties: 107 + compatible: 108 + not: 109 + contains: 110 + const: cypress,fm25 111 + then: 112 + required: 113 + - pagesize 114 + - size 115 + - address-width 109 116 110 117 additionalProperties: false 111 118 ··· 137 124 pagesize = <64>; 138 125 size = <32768>; 139 126 address-width = <16>; 127 + }; 128 + 129 + fram@1 { 130 + compatible = "cypress,fm25", "atmel,at25"; 131 + reg = <1>; 132 + spi-max-frequency = <40000000>; 140 133 }; 141 134 };
+3 -2
drivers/misc/eeprom/Kconfig
··· 32 32 will be called at24. 33 33 34 34 config EEPROM_AT25 35 - tristate "SPI EEPROMs from most vendors" 35 + tristate "SPI EEPROMs (FRAMs) from most vendors" 36 36 depends on SPI && SYSFS 37 37 select NVMEM 38 38 select NVMEM_SYSFS 39 39 help 40 - Enable this driver to get read/write support to most SPI EEPROMs, 40 + Enable this driver to get read/write support to most SPI EEPROMs 41 + and Cypress FRAMs, 41 42 after you configure the board init code to know about each eeprom 42 43 on your target board. 43 44
+131 -30
drivers/misc/eeprom/at25.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 3 * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models 4 + * and Cypress FRAMs FM25 models 4 5 * 5 6 * Copyright (C) 2006 David Brownell 6 7 */ ··· 17 16 #include <linux/spi/spi.h> 18 17 #include <linux/spi/eeprom.h> 19 18 #include <linux/property.h> 19 + #include <linux/of.h> 20 + #include <linux/of_device.h> 21 + #include <linux/math.h> 20 22 21 23 /* 22 24 * NOTE: this is an *EEPROM* driver. The vagaries of product naming ··· 31 27 * AT25M02, AT25128B 32 28 */ 33 29 30 + #define FM25_SN_LEN 8 /* serial number length */ 34 31 struct at25_data { 35 32 struct spi_device *spi; 36 33 struct mutex lock; ··· 39 34 unsigned addrlen; 40 35 struct nvmem_config nvmem_config; 41 36 struct nvmem_device *nvmem; 37 + u8 sernum[FM25_SN_LEN]; 42 38 }; 43 39 44 40 #define AT25_WREN 0x06 /* latch the write enable */ ··· 48 42 #define AT25_WRSR 0x01 /* write status register */ 49 43 #define AT25_READ 0x03 /* read byte(s) */ 50 44 #define AT25_WRITE 0x02 /* write byte(s)/sector */ 45 + #define FM25_SLEEP 0xb9 /* enter sleep mode */ 46 + #define FM25_RDID 0x9f /* read device ID */ 47 + #define FM25_RDSN 0xc3 /* read S/N */ 51 48 52 49 #define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ 53 50 #define AT25_SR_WEN 0x02 /* write enable (latched) */ ··· 60 51 61 52 #define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */ 62 53 54 + #define FM25_ID_LEN 9 /* ID length */ 55 + 63 56 #define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ 64 57 65 58 /* Specs often allow 5 msec for a page write, sometimes 20 msec; 66 59 * it's important to recover from write timeouts. 67 60 */ 68 61 #define EE_TIMEOUT 25 62 + 63 + #define IS_EEPROM 0 64 + #define IS_FRAM 1 69 65 70 66 /*-------------------------------------------------------------------------*/ 71 67 ··· 142 128 mutex_unlock(&at25->lock); 143 129 return status; 144 130 } 131 + 132 + /* 133 + * read extra registers as ID or serial number 134 + */ 135 + static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, 136 + int len) 137 + { 138 + int status; 139 + struct spi_transfer t[2]; 140 + struct spi_message m; 141 + 142 + spi_message_init(&m); 143 + memset(t, 0, sizeof(t)); 144 + 145 + t[0].tx_buf = &command; 146 + t[0].len = 1; 147 + spi_message_add_tail(&t[0], &m); 148 + 149 + t[1].rx_buf = buf; 150 + t[1].len = len; 151 + spi_message_add_tail(&t[1], &m); 152 + 153 + mutex_lock(&at25->lock); 154 + 155 + status = spi_sync(at25->spi, &m); 156 + dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status); 157 + 158 + mutex_unlock(&at25->lock); 159 + return status; 160 + } 161 + 162 + static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf) 163 + { 164 + struct at25_data *at25; 165 + 166 + at25 = dev_get_drvdata(dev); 167 + return sysfs_emit(buf, "%*ph\n", sizeof(at25->sernum), at25->sernum); 168 + } 169 + static DEVICE_ATTR_RO(sernum); 170 + 171 + static struct attribute *sernum_attrs[] = { 172 + &dev_attr_sernum.attr, 173 + NULL, 174 + }; 175 + ATTRIBUTE_GROUPS(sernum); 145 176 146 177 static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) 147 178 { ··· 362 303 return 0; 363 304 } 364 305 306 + static const struct of_device_id at25_of_match[] = { 307 + { .compatible = "atmel,at25", .data = (const void *)IS_EEPROM }, 308 + { .compatible = "cypress,fm25", .data = (const void *)IS_FRAM }, 309 + { } 310 + }; 311 + MODULE_DEVICE_TABLE(of, at25_of_match); 312 + 365 313 static int at25_probe(struct spi_device *spi) 366 314 { 367 315 struct at25_data *at25 = NULL; 368 316 struct spi_eeprom chip; 369 317 int err; 370 318 int sr; 371 - int addrlen; 319 + u8 id[FM25_ID_LEN]; 320 + u8 sernum[FM25_SN_LEN]; 321 + int i; 322 + const struct of_device_id *match; 323 + int is_fram = 0; 324 + 325 + match = of_match_device(of_match_ptr(at25_of_match), &spi->dev); 326 + if (match) 327 + is_fram = (int)match->data; 372 328 373 329 /* Chip description */ 374 330 if (!spi->dev.platform_data) { 375 - err = at25_fw_to_chip(&spi->dev, &chip); 376 - if (err) 377 - return err; 331 + if (!is_fram) { 332 + err = at25_fw_to_chip(&spi->dev, &chip); 333 + if (err) 334 + return err; 335 + } 378 336 } else 379 337 chip = *(struct spi_eeprom *)spi->dev.platform_data; 380 - 381 - /* For now we only support 8/16/24 bit addressing */ 382 - if (chip.flags & EE_ADDR1) 383 - addrlen = 1; 384 - else if (chip.flags & EE_ADDR2) 385 - addrlen = 2; 386 - else if (chip.flags & EE_ADDR3) 387 - addrlen = 3; 388 - else { 389 - dev_dbg(&spi->dev, "unsupported address type\n"); 390 - return -EINVAL; 391 - } 392 338 393 339 /* Ping the chip ... the status register is pretty portable, 394 340 * unlike probing manufacturer IDs. We do expect that system ··· 413 349 at25->chip = chip; 414 350 at25->spi = spi; 415 351 spi_set_drvdata(spi, at25); 416 - at25->addrlen = addrlen; 417 352 418 - at25->nvmem_config.type = NVMEM_TYPE_EEPROM; 353 + if (is_fram) { 354 + /* Get ID of chip */ 355 + fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); 356 + if (id[6] != 0xc2) { 357 + dev_err(&spi->dev, 358 + "Error: no Cypress FRAM (id %02x)\n", id[6]); 359 + return -ENODEV; 360 + } 361 + /* set size found in ID */ 362 + if (id[7] < 0x21 || id[7] > 0x26) { 363 + dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]); 364 + return -ENODEV; 365 + } 366 + chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; 367 + 368 + if (at25->chip.byte_len > 64 * 1024) 369 + at25->chip.flags |= EE_ADDR3; 370 + else 371 + at25->chip.flags |= EE_ADDR2; 372 + 373 + if (id[8]) { 374 + fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN); 375 + /* swap byte order */ 376 + for (i = 0; i < FM25_SN_LEN; i++) 377 + at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i]; 378 + } 379 + 380 + at25->chip.page_size = PAGE_SIZE; 381 + strncpy(at25->chip.name, "fm25", sizeof(at25->chip.name)); 382 + } 383 + 384 + /* For now we only support 8/16/24 bit addressing */ 385 + if (at25->chip.flags & EE_ADDR1) 386 + at25->addrlen = 1; 387 + else if (at25->chip.flags & EE_ADDR2) 388 + at25->addrlen = 2; 389 + else if (at25->chip.flags & EE_ADDR3) 390 + at25->addrlen = 3; 391 + else { 392 + dev_dbg(&spi->dev, "unsupported address type\n"); 393 + return -EINVAL; 394 + } 395 + 396 + at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM; 419 397 at25->nvmem_config.name = dev_name(&spi->dev); 420 398 at25->nvmem_config.dev = &spi->dev; 421 399 at25->nvmem_config.read_only = chip.flags & EE_READONLY; ··· 476 370 if (IS_ERR(at25->nvmem)) 477 371 return PTR_ERR(at25->nvmem); 478 372 479 - dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n", 480 - (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), 481 - (chip.byte_len < 1024) ? "Byte" : "KByte", 482 - at25->chip.name, 483 - (chip.flags & EE_READONLY) ? " (readonly)" : "", 484 - at25->chip.page_size); 373 + dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n", 374 + (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), 375 + (chip.byte_len < 1024) ? "Byte" : "KByte", 376 + at25->chip.name, is_fram ? "fram" : "eeprom", 377 + (chip.flags & EE_READONLY) ? " (readonly)" : "", 378 + at25->chip.page_size); 485 379 return 0; 486 380 } 487 381 488 382 /*-------------------------------------------------------------------------*/ 489 383 490 - static const struct of_device_id at25_of_match[] = { 491 - { .compatible = "atmel,at25", }, 492 - { } 493 - }; 494 - MODULE_DEVICE_TABLE(of, at25_of_match); 495 - 496 384 static struct spi_driver at25_driver = { 497 385 .driver = { 498 386 .name = "at25", 499 387 .of_match_table = at25_of_match, 388 + .dev_groups = sernum_groups, 500 389 }, 501 390 .probe = at25_probe, 502 391 };
+4
drivers/nvmem/core.c
··· 180 180 [NVMEM_TYPE_EEPROM] = "EEPROM", 181 181 [NVMEM_TYPE_OTP] = "OTP", 182 182 [NVMEM_TYPE_BATTERY_BACKED] = "Battery backed", 183 + [NVMEM_TYPE_FRAM] = "FRAM", 183 184 }; 184 185 185 186 #ifdef CONFIG_DEBUG_LOCK_ALLOC ··· 359 358 360 359 if (!config->base_dev) 361 360 return -EINVAL; 361 + 362 + if (config->type == NVMEM_TYPE_FRAM) 363 + bin_attr_nvmem_eeprom_compat.attr.name = "fram"; 362 364 363 365 nvmem->eeprom = bin_attr_nvmem_eeprom_compat; 364 366 nvmem->eeprom.attr.mode = nvmem_bin_attr_get_umode(nvmem);
+1
include/linux/nvmem-provider.h
··· 25 25 NVMEM_TYPE_EEPROM, 26 26 NVMEM_TYPE_OTP, 27 27 NVMEM_TYPE_BATTERY_BACKED, 28 + NVMEM_TYPE_FRAM, 28 29 }; 29 30 30 31 #define NVMEM_DEVID_NONE (-1)