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

nvmem: lan9662-otp: add support

Add support for OTP controller available on LAN9662. The OTPC controls
the access to a non-volatile memory. The size of the memory is 8KB.
The OTPC can access the memory based on an offset.
Implement both the read and the write functionality.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20220916122100.170016-13-srinivas.kandagatla@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Horatiu Vultur and committed by
Greg Kroah-Hartman
9e8f208a d1b274c4

+232
+8
drivers/nvmem/Kconfig
··· 98 98 To compile this driver as a module, choose M here: the module 99 99 will be called nvmem_jz4780_efuse. 100 100 101 + config NVMEM_LAN9662_OTPC 102 + tristate "Microchip LAN9662 OTP controller support" 103 + depends on SOC_LAN966 || COMPILE_TEST 104 + depends on HAS_IOMEM 105 + help 106 + This driver enables the OTP controller available on Microchip LAN9662 107 + SoCs. It controls the access to the OTP memory connected to it. 108 + 101 109 config NVMEM_LAYERSCAPE_SFP 102 110 tristate "Layerscape SFP (Security Fuse Processor) support" 103 111 depends on ARCH_LAYERSCAPE || COMPILE_TEST
+2
drivers/nvmem/Makefile
··· 21 21 nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o 22 22 obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o 23 23 nvmem_jz4780_efuse-y := jz4780-efuse.o 24 + obj-$(CONFIG_NVMEM_LAN9662_OTPC) += nvmem-lan9662-otpc.o 25 + nvmem-lan9662-otpc-y := lan9662-otpc.o 24 26 obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o 25 27 nvmem-layerscape-sfp-y := layerscape-sfp.o 26 28 obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o
+222
drivers/nvmem/lan9662-otpc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/iopoll.h> 4 + #include <linux/module.h> 5 + #include <linux/nvmem-provider.h> 6 + #include <linux/of.h> 7 + #include <linux/platform_device.h> 8 + 9 + #define OTP_OTP_PWR_DN(t) (t + 0x00) 10 + #define OTP_OTP_PWR_DN_OTP_PWRDN_N BIT(0) 11 + #define OTP_OTP_ADDR_HI(t) (t + 0x04) 12 + #define OTP_OTP_ADDR_LO(t) (t + 0x08) 13 + #define OTP_OTP_PRGM_DATA(t) (t + 0x10) 14 + #define OTP_OTP_PRGM_MODE(t) (t + 0x14) 15 + #define OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE BIT(0) 16 + #define OTP_OTP_RD_DATA(t) (t + 0x18) 17 + #define OTP_OTP_FUNC_CMD(t) (t + 0x20) 18 + #define OTP_OTP_FUNC_CMD_OTP_PROGRAM BIT(1) 19 + #define OTP_OTP_FUNC_CMD_OTP_READ BIT(0) 20 + #define OTP_OTP_CMD_GO(t) (t + 0x28) 21 + #define OTP_OTP_CMD_GO_OTP_GO BIT(0) 22 + #define OTP_OTP_PASS_FAIL(t) (t + 0x2c) 23 + #define OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED BIT(3) 24 + #define OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED BIT(2) 25 + #define OTP_OTP_PASS_FAIL_OTP_FAIL BIT(0) 26 + #define OTP_OTP_STATUS(t) (t + 0x30) 27 + #define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1) 28 + #define OTP_OTP_STATUS_OTP_BUSY BIT(0) 29 + 30 + #define OTP_MEM_SIZE 8192 31 + #define OTP_SLEEP_US 10 32 + #define OTP_TIMEOUT_US 500000 33 + 34 + struct lan9662_otp { 35 + struct device *dev; 36 + void __iomem *base; 37 + }; 38 + 39 + static bool lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag) 40 + { 41 + u32 val; 42 + 43 + return readl_poll_timeout(reg, val, !(val & flag), 44 + OTP_SLEEP_US, OTP_TIMEOUT_US); 45 + } 46 + 47 + static int lan9662_otp_power(struct lan9662_otp *otp, bool up) 48 + { 49 + void __iomem *pwrdn = OTP_OTP_PWR_DN(otp->base); 50 + 51 + if (up) { 52 + writel(readl(pwrdn) & ~OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn); 53 + if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base), 54 + OTP_OTP_STATUS_OTP_CPUMPEN)) 55 + return -ETIMEDOUT; 56 + } else { 57 + writel(readl(pwrdn) | OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn); 58 + } 59 + 60 + return 0; 61 + } 62 + 63 + static int lan9662_otp_execute(struct lan9662_otp *otp) 64 + { 65 + if (lan9662_otp_wait_flag_clear(OTP_OTP_CMD_GO(otp->base), 66 + OTP_OTP_CMD_GO_OTP_GO)) 67 + return -ETIMEDOUT; 68 + 69 + if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base), 70 + OTP_OTP_STATUS_OTP_BUSY)) 71 + return -ETIMEDOUT; 72 + 73 + return 0; 74 + } 75 + 76 + static void lan9662_otp_set_address(struct lan9662_otp *otp, u32 offset) 77 + { 78 + writel(0xff & (offset >> 8), OTP_OTP_ADDR_HI(otp->base)); 79 + writel(0xff & offset, OTP_OTP_ADDR_LO(otp->base)); 80 + } 81 + 82 + static int lan9662_otp_read_byte(struct lan9662_otp *otp, u32 offset, u8 *dst) 83 + { 84 + u32 pass; 85 + int rc; 86 + 87 + lan9662_otp_set_address(otp, offset); 88 + writel(OTP_OTP_FUNC_CMD_OTP_READ, OTP_OTP_FUNC_CMD(otp->base)); 89 + writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base)); 90 + rc = lan9662_otp_execute(otp); 91 + if (!rc) { 92 + pass = readl(OTP_OTP_PASS_FAIL(otp->base)); 93 + if (pass & OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED) 94 + return -EACCES; 95 + *dst = (u8) readl(OTP_OTP_RD_DATA(otp->base)); 96 + } 97 + return rc; 98 + } 99 + 100 + static int lan9662_otp_write_byte(struct lan9662_otp *otp, u32 offset, u8 data) 101 + { 102 + u32 pass; 103 + int rc; 104 + 105 + lan9662_otp_set_address(otp, offset); 106 + writel(OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE, OTP_OTP_PRGM_MODE(otp->base)); 107 + writel(data, OTP_OTP_PRGM_DATA(otp->base)); 108 + writel(OTP_OTP_FUNC_CMD_OTP_PROGRAM, OTP_OTP_FUNC_CMD(otp->base)); 109 + writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base)); 110 + 111 + rc = lan9662_otp_execute(otp); 112 + if (!rc) { 113 + pass = readl(OTP_OTP_PASS_FAIL(otp->base)); 114 + if (pass & OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED) 115 + return -EACCES; 116 + if (pass & OTP_OTP_PASS_FAIL_OTP_FAIL) 117 + return -EIO; 118 + } 119 + return rc; 120 + } 121 + 122 + static int lan9662_otp_read(void *context, unsigned int offset, 123 + void *_val, size_t bytes) 124 + { 125 + struct lan9662_otp *otp = context; 126 + u8 *val = _val; 127 + uint8_t data; 128 + int i, rc = 0; 129 + 130 + lan9662_otp_power(otp, true); 131 + for (i = 0; i < bytes; i++) { 132 + rc = lan9662_otp_read_byte(otp, offset + i, &data); 133 + if (rc < 0) 134 + break; 135 + *val++ = data; 136 + } 137 + lan9662_otp_power(otp, false); 138 + 139 + return rc; 140 + } 141 + 142 + static int lan9662_otp_write(void *context, unsigned int offset, 143 + void *_val, size_t bytes) 144 + { 145 + struct lan9662_otp *otp = context; 146 + u8 *val = _val; 147 + u8 data, newdata; 148 + int i, rc = 0; 149 + 150 + lan9662_otp_power(otp, true); 151 + for (i = 0; i < bytes; i++) { 152 + /* Skip zero bytes */ 153 + if (val[i]) { 154 + rc = lan9662_otp_read_byte(otp, offset + i, &data); 155 + if (rc < 0) 156 + break; 157 + 158 + newdata = data | val[i]; 159 + if (newdata == data) 160 + continue; 161 + 162 + rc = lan9662_otp_write_byte(otp, offset + i, 163 + newdata); 164 + if (rc < 0) 165 + break; 166 + } 167 + } 168 + lan9662_otp_power(otp, false); 169 + 170 + return rc; 171 + } 172 + 173 + static struct nvmem_config otp_config = { 174 + .name = "lan9662-otp", 175 + .stride = 1, 176 + .word_size = 1, 177 + .reg_read = lan9662_otp_read, 178 + .reg_write = lan9662_otp_write, 179 + .size = OTP_MEM_SIZE, 180 + }; 181 + 182 + static int lan9662_otp_probe(struct platform_device *pdev) 183 + { 184 + struct device *dev = &pdev->dev; 185 + struct nvmem_device *nvmem; 186 + struct lan9662_otp *otp; 187 + 188 + otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL); 189 + if (!otp) 190 + return -ENOMEM; 191 + 192 + otp->dev = dev; 193 + otp->base = devm_platform_ioremap_resource(pdev, 0); 194 + if (IS_ERR(otp->base)) 195 + return PTR_ERR(otp->base); 196 + 197 + otp_config.priv = otp; 198 + otp_config.dev = dev; 199 + 200 + nvmem = devm_nvmem_register(dev, &otp_config); 201 + 202 + return PTR_ERR_OR_ZERO(nvmem); 203 + } 204 + 205 + static const struct of_device_id lan9662_otp_match[] = { 206 + { .compatible = "microchip,lan9662-otp", }, 207 + { }, 208 + }; 209 + MODULE_DEVICE_TABLE(of, lan9662_otp_match); 210 + 211 + static struct platform_driver lan9662_otp_driver = { 212 + .probe = lan9662_otp_probe, 213 + .driver = { 214 + .name = "lan9662-otp", 215 + .of_match_table = lan9662_otp_match, 216 + }, 217 + }; 218 + module_platform_driver(lan9662_otp_driver); 219 + 220 + MODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>"); 221 + MODULE_DESCRIPTION("lan9662 OTP driver"); 222 + MODULE_LICENSE("GPL");