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

nvmem: Add R-Car E-FUSE driver

R-Car Gen4 SoCs contain fuses indicating hardware support or hardware
(e.g. tuning) parameters. Add a driver to access the state of the
fuses. This supports two types of hardware fuse providers:
1. E-FUSE non-volatile memory accessible through the Pin Function
Controller on R-Car V3U and S4-8,
2. E-FUSE non-volatile memory accessible through OTP_MEM on R-Car V4H
and V4M.

The state of the cells can be read using the NVMEM framework, either
from kernel space (e.g. by the Renesas UFSHCD driver), or from
userspace.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20241030140315.40562-3-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Geert Uytterhoeven and committed by
Greg Kroah-Hartman
1530b923 2aea0d17

+156
+1
MAINTAINERS
··· 2930 2930 F: arch/arm/mach-shmobile/ 2931 2931 F: arch/arm64/boot/dts/renesas/ 2932 2932 F: arch/riscv/boot/dts/renesas/ 2933 + F: drivers/nvmem/rcar-efuse.c 2933 2934 F: drivers/pmdomain/renesas/ 2934 2935 F: drivers/soc/renesas/ 2935 2936 F: include/linux/soc/renesas/
+11
drivers/nvmem/Kconfig
··· 246 246 help 247 247 Say y here to enable Rave SP EEPROM support. 248 248 249 + config NVMEM_RCAR_EFUSE 250 + tristate "Renesas R-Car Gen4 E-FUSE support" 251 + depends on (ARCH_RENESAS && ARM64) || COMPILE_TEST 252 + depends on NVMEM 253 + help 254 + Enable support for reading the fuses in the E-FUSE or OTP 255 + non-volatile memory block on Renesas R-Car Gen4 SoCs. 256 + 257 + This driver can also be built as a module. If so, the module 258 + will be called nvmem-rcar-efuse. 259 + 249 260 config NVMEM_RMEM 250 261 tristate "Reserved Memory Based Driver Support" 251 262 depends on HAS_IOMEM
+2
drivers/nvmem/Makefile
··· 52 52 nvmem_sec_qfprom-y := sec-qfprom.o 53 53 obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o 54 54 nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o 55 + obj-$(CONFIG_NVMEM_RCAR_EFUSE) += nvmem-rcar-efuse.o 56 + nvmem-rcar-efuse-y := rcar-efuse.o 55 57 obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o 56 58 nvmem-rmem-y := rmem.o 57 59 obj-$(CONFIG_NVMEM_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
+142
drivers/nvmem/rcar-efuse.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Renesas R-Car E-FUSE/OTP Driver 4 + * 5 + * Copyright (C) 2024 Glider bv 6 + */ 7 + 8 + #include <linux/device.h> 9 + #include <linux/export.h> 10 + #include <linux/io.h> 11 + #include <linux/mod_devicetable.h> 12 + #include <linux/nvmem-provider.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/pm_runtime.h> 15 + #include <linux/property.h> 16 + 17 + struct rcar_fuse { 18 + struct nvmem_keepout keepouts[2]; 19 + struct nvmem_device *nvmem; 20 + struct device *dev; 21 + void __iomem *base; 22 + }; 23 + 24 + struct rcar_fuse_data { 25 + unsigned int bank; /* 0: PFC + E-FUSE, 1: OPT_MEM + E-FUSE */ 26 + unsigned int start; /* inclusive */ 27 + unsigned int end; /* exclusive */ 28 + }; 29 + 30 + static int rcar_fuse_reg_read(void *priv, unsigned int offset, void *val, 31 + size_t bytes) 32 + { 33 + struct rcar_fuse *fuse = priv; 34 + int ret; 35 + 36 + ret = pm_runtime_resume_and_get(fuse->dev); 37 + if (ret < 0) 38 + return ret; 39 + 40 + __ioread32_copy(val, fuse->base + offset, bytes / 4); 41 + 42 + pm_runtime_put(fuse->dev); 43 + 44 + return 0; 45 + } 46 + 47 + static int rcar_fuse_probe(struct platform_device *pdev) 48 + { 49 + struct device *dev = &pdev->dev; 50 + const struct rcar_fuse_data *data = device_get_match_data(dev); 51 + struct nvmem_config config = { 52 + .dev = dev, 53 + .name = "rcar-fuse", 54 + .id = NVMEM_DEVID_NONE, 55 + .owner = THIS_MODULE, 56 + .type = NVMEM_TYPE_OTP, 57 + .read_only = true, 58 + .root_only = true, 59 + .reg_read = rcar_fuse_reg_read, 60 + .word_size = 4, 61 + .stride = 4, 62 + }; 63 + struct rcar_fuse *fuse; 64 + struct resource *res; 65 + int ret; 66 + 67 + ret = devm_pm_runtime_enable(dev); 68 + if (ret < 0) 69 + return ret; 70 + 71 + fuse = devm_kzalloc(dev, sizeof(*fuse), GFP_KERNEL); 72 + if (!fuse) 73 + return -ENOMEM; 74 + 75 + fuse->base = devm_platform_get_and_ioremap_resource(pdev, data->bank, 76 + &res); 77 + if (IS_ERR(fuse->base)) 78 + return PTR_ERR(fuse->base); 79 + 80 + fuse->dev = dev; 81 + fuse->keepouts[0].start = 0; 82 + fuse->keepouts[0].end = data->start; 83 + fuse->keepouts[1].start = data->end; 84 + fuse->keepouts[1].end = resource_size(res); 85 + 86 + config.keepout = fuse->keepouts; 87 + config.nkeepout = ARRAY_SIZE(fuse->keepouts); 88 + config.size = resource_size(res); 89 + config.priv = fuse; 90 + 91 + fuse->nvmem = devm_nvmem_register(dev, &config); 92 + if (IS_ERR(fuse->nvmem)) 93 + return dev_err_probe(dev, PTR_ERR(fuse->nvmem), 94 + "Failed to register NVMEM device\n"); 95 + 96 + return 0; 97 + } 98 + 99 + static const struct rcar_fuse_data rcar_fuse_v3u = { 100 + .bank = 0, 101 + .start = 0x0c0, 102 + .end = 0x0e8, 103 + }; 104 + 105 + static const struct rcar_fuse_data rcar_fuse_s4 = { 106 + .bank = 0, 107 + .start = 0x0c0, 108 + .end = 0x14c, 109 + }; 110 + 111 + static const struct rcar_fuse_data rcar_fuse_v4h = { 112 + .bank = 1, 113 + .start = 0x100, 114 + .end = 0x1a0, 115 + }; 116 + 117 + static const struct rcar_fuse_data rcar_fuse_v4m = { 118 + .bank = 1, 119 + .start = 0x100, 120 + .end = 0x110, 121 + }; 122 + 123 + static const struct of_device_id rcar_fuse_match[] = { 124 + { .compatible = "renesas,r8a779a0-efuse", .data = &rcar_fuse_v3u }, 125 + { .compatible = "renesas,r8a779f0-efuse", .data = &rcar_fuse_s4 }, 126 + { .compatible = "renesas,r8a779g0-otp", .data = &rcar_fuse_v4h }, 127 + { .compatible = "renesas,r8a779h0-otp", .data = &rcar_fuse_v4m }, 128 + { /* sentinel */ } 129 + }; 130 + 131 + static struct platform_driver rcar_fuse_driver = { 132 + .probe = rcar_fuse_probe, 133 + .driver = { 134 + .name = "rcar_fuse", 135 + .of_match_table = rcar_fuse_match, 136 + }, 137 + }; 138 + module_platform_driver(rcar_fuse_driver); 139 + 140 + MODULE_DESCRIPTION("Renesas R-Car E-FUSE/OTP driver"); 141 + MODULE_LICENSE("GPL"); 142 + MODULE_AUTHOR("Geert Uytterhoeven");