kernel: backport NVMEM patches queued for the v6.5
[openwrt/staging/hauke.git] / target / linux / generic / backport-5.15 / 813-v6.5-0010-nvmem-imx-support-i.MX93-OCOTP.patch
1 From 22e9e6fcfb5042cb6d6c7874c459b034800092f1 Mon Sep 17 00:00:00 2001
2 From: Peng Fan <peng.fan@nxp.com>
3 Date: Sun, 11 Jun 2023 15:03:25 +0100
4 Subject: [PATCH] nvmem: imx: support i.MX93 OCOTP
5
6 Add i.MX93 OCOTP support. i.MX93 OCOTP has two parts: Fuse shadow
7 block(fsb) and fuse managed by ELE. The FSB part could be directly
8 accessed with MMIO, the ELE could only be accessed with ELE API.
9
10 Currently the ELE API is not ready, so NULL function callback is used,
11 but it was tested with downstream ELE API.
12
13 Signed-off-by: Peng Fan <peng.fan@nxp.com>
14 Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
15 Message-ID: <20230611140330.154222-22-srinivas.kandagatla@linaro.org>
16 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
17 ---
18 drivers/nvmem/Kconfig | 9 ++
19 drivers/nvmem/Makefile | 2 +
20 drivers/nvmem/imx-ocotp-ele.c | 175 ++++++++++++++++++++++++++++++++++
21 3 files changed, 186 insertions(+)
22 create mode 100644 drivers/nvmem/imx-ocotp-ele.c
23
24 --- a/drivers/nvmem/Kconfig
25 +++ b/drivers/nvmem/Kconfig
26 @@ -83,6 +83,15 @@ config NVMEM_IMX_OCOTP
27 This driver can also be built as a module. If so, the module
28 will be called nvmem-imx-ocotp.
29
30 +config NVMEM_IMX_OCOTP_ELE
31 + tristate "i.MX On-Chip OTP Controller support"
32 + depends on ARCH_MXC || COMPILE_TEST
33 + depends on HAS_IOMEM
34 + depends on OF
35 + help
36 + This is a driver for the On-Chip OTP Controller (OCOTP)
37 + available on i.MX SoCs which has ELE.
38 +
39 config NVMEM_IMX_OCOTP_SCU
40 tristate "i.MX8 SCU On-Chip OTP Controller support"
41 depends on IMX_SCU
42 --- a/drivers/nvmem/Makefile
43 +++ b/drivers/nvmem/Makefile
44 @@ -18,6 +18,8 @@ obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-im
45 nvmem-imx-iim-y := imx-iim.o
46 obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
47 nvmem-imx-ocotp-y := imx-ocotp.o
48 +obj-$(CONFIG_NVMEM_IMX_OCOTP_ELE) += nvmem-imx-ocotp-ele.o
49 +nvmem-imx-ocotp-ele-y := imx-ocotp-ele.o
50 obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o
51 nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o
52 obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
53 --- /dev/null
54 +++ b/drivers/nvmem/imx-ocotp-ele.c
55 @@ -0,0 +1,175 @@
56 +// SPDX-License-Identifier: GPL-2.0-only
57 +/*
58 + * i.MX9 OCOTP fusebox driver
59 + *
60 + * Copyright 2023 NXP
61 + */
62 +
63 +#include <linux/device.h>
64 +#include <linux/io.h>
65 +#include <linux/module.h>
66 +#include <linux/nvmem-provider.h>
67 +#include <linux/of_device.h>
68 +#include <linux/platform_device.h>
69 +#include <linux/slab.h>
70 +
71 +enum fuse_type {
72 + FUSE_FSB = 1,
73 + FUSE_ELE = 2,
74 + FUSE_INVALID = -1
75 +};
76 +
77 +struct ocotp_map_entry {
78 + u32 start; /* start word */
79 + u32 num; /* num words */
80 + enum fuse_type type;
81 +};
82 +
83 +struct ocotp_devtype_data {
84 + u32 reg_off;
85 + char *name;
86 + u32 size;
87 + u32 num_entry;
88 + u32 flag;
89 + nvmem_reg_read_t reg_read;
90 + struct ocotp_map_entry entry[];
91 +};
92 +
93 +struct imx_ocotp_priv {
94 + struct device *dev;
95 + void __iomem *base;
96 + struct nvmem_config config;
97 + struct mutex lock;
98 + const struct ocotp_devtype_data *data;
99 +};
100 +
101 +static enum fuse_type imx_ocotp_fuse_type(void *context, u32 index)
102 +{
103 + struct imx_ocotp_priv *priv = context;
104 + const struct ocotp_devtype_data *data = priv->data;
105 + u32 start, end;
106 + int i;
107 +
108 + for (i = 0; i < data->num_entry; i++) {
109 + start = data->entry[i].start;
110 + end = data->entry[i].start + data->entry[i].num;
111 +
112 + if (index >= start && index < end)
113 + return data->entry[i].type;
114 + }
115 +
116 + return FUSE_INVALID;
117 +}
118 +
119 +static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, size_t bytes)
120 +{
121 + struct imx_ocotp_priv *priv = context;
122 + void __iomem *reg = priv->base + priv->data->reg_off;
123 + u32 count, index, num_bytes;
124 + enum fuse_type type;
125 + u32 *buf;
126 + void *p;
127 + int i;
128 +
129 + index = offset;
130 + num_bytes = round_up(bytes, 4);
131 + count = num_bytes >> 2;
132 +
133 + if (count > ((priv->data->size >> 2) - index))
134 + count = (priv->data->size >> 2) - index;
135 +
136 + p = kzalloc(num_bytes, GFP_KERNEL);
137 + if (!p)
138 + return -ENOMEM;
139 +
140 + mutex_lock(&priv->lock);
141 +
142 + buf = p;
143 +
144 + for (i = index; i < (index + count); i++) {
145 + type = imx_ocotp_fuse_type(context, i);
146 + if (type == FUSE_INVALID || type == FUSE_ELE) {
147 + *buf++ = 0;
148 + continue;
149 + }
150 +
151 + *buf++ = readl_relaxed(reg + (i << 2));
152 + }
153 +
154 + memcpy(val, (u8 *)p, bytes);
155 +
156 + mutex_unlock(&priv->lock);
157 +
158 + kfree(p);
159 +
160 + return 0;
161 +};
162 +
163 +static int imx_ele_ocotp_probe(struct platform_device *pdev)
164 +{
165 + struct device *dev = &pdev->dev;
166 + struct imx_ocotp_priv *priv;
167 + struct nvmem_device *nvmem;
168 +
169 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
170 + if (!priv)
171 + return -ENOMEM;
172 +
173 + priv->data = of_device_get_match_data(dev);
174 +
175 + priv->base = devm_platform_ioremap_resource(pdev, 0);
176 + if (IS_ERR(priv->base))
177 + return PTR_ERR(priv->base);
178 +
179 + priv->config.dev = dev;
180 + priv->config.name = "ELE-OCOTP";
181 + priv->config.id = NVMEM_DEVID_AUTO;
182 + priv->config.owner = THIS_MODULE;
183 + priv->config.size = priv->data->size;
184 + priv->config.reg_read = priv->data->reg_read;
185 + priv->config.word_size = 4;
186 + priv->config.stride = 1;
187 + priv->config.priv = priv;
188 + priv->config.read_only = true;
189 + mutex_init(&priv->lock);
190 +
191 + nvmem = devm_nvmem_register(dev, &priv->config);
192 + if (IS_ERR(nvmem))
193 + return PTR_ERR(nvmem);
194 +
195 + return 0;
196 +}
197 +
198 +static const struct ocotp_devtype_data imx93_ocotp_data = {
199 + .reg_off = 0x8000,
200 + .reg_read = imx_ocotp_reg_read,
201 + .size = 2048,
202 + .num_entry = 6,
203 + .entry = {
204 + { 0, 52, FUSE_FSB },
205 + { 63, 1, FUSE_ELE},
206 + { 128, 16, FUSE_ELE },
207 + { 182, 1, FUSE_ELE },
208 + { 188, 1, FUSE_ELE },
209 + { 312, 200, FUSE_FSB }
210 + },
211 +};
212 +
213 +static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
214 + { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
215 + {},
216 +};
217 +MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
218 +
219 +static struct platform_driver imx_ele_ocotp_driver = {
220 + .driver = {
221 + .name = "imx_ele_ocotp",
222 + .of_match_table = imx_ele_ocotp_dt_ids,
223 + },
224 + .probe = imx_ele_ocotp_probe,
225 +};
226 +module_platform_driver(imx_ele_ocotp_driver);
227 +
228 +MODULE_DESCRIPTION("i.MX OCOTP/ELE driver");
229 +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
230 +MODULE_LICENSE("GPL");