File Index
Files and directories
Sign in
FILE

imx-ocotp-ele.c

File preview with syntax highlighting.

linux-6.19.14/drivers/nvmem/imx-ocotp-ele.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * i.MX9 OCOTP fusebox driver
  4 *
  5 * Copyright 2023 NXP
  6 */
  7
  8#include <linux/device.h>
  9#include <linux/io.h>
 10#include <linux/module.h>
 11#include <linux/nvmem-provider.h>
 12#include <linux/of.h>
 13#include <linux/platform_device.h>
 14#include <linux/slab.h>
 15#include <linux/if_ether.h>	/* ETH_ALEN */
 16
 17enum fuse_type {
 18	FUSE_FSB = BIT(0),
 19	FUSE_ELE = BIT(1),
 20	FUSE_ECC = BIT(2),
 21	FUSE_INVALID = -1
 22};
 23
 24struct ocotp_map_entry {
 25	u32 start; /* start word */
 26	u32 num; /* num words */
 27	enum fuse_type type;
 28};
 29
 30struct ocotp_devtype_data {
 31	u32 reg_off;
 32	char *name;
 33	u32 size;
 34	u32 num_entry;
 35	u32 flag;
 36	nvmem_reg_read_t reg_read;
 37	struct ocotp_map_entry entry[];
 38};
 39
 40struct imx_ocotp_priv {
 41	struct device *dev;
 42	void __iomem *base;
 43	struct nvmem_config config;
 44	struct mutex lock;
 45	const struct ocotp_devtype_data *data;
 46};
 47
 48static enum fuse_type imx_ocotp_fuse_type(void *context, u32 index)
 49{
 50	struct imx_ocotp_priv *priv = context;
 51	const struct ocotp_devtype_data *data = priv->data;
 52	u32 start, end;
 53	int i;
 54
 55	for (i = 0; i < data->num_entry; i++) {
 56		start = data->entry[i].start;
 57		end = data->entry[i].start + data->entry[i].num;
 58
 59		if (index >= start && index < end)
 60			return data->entry[i].type;
 61	}
 62
 63	return FUSE_INVALID;
 64}
 65
 66static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, size_t bytes)
 67{
 68	struct imx_ocotp_priv *priv = context;
 69	void __iomem *reg = priv->base + priv->data->reg_off;
 70	u32 count, index, num_bytes;
 71	enum fuse_type type;
 72	u32 *buf;
 73	void *p;
 74	int i;
 75	u8 skipbytes;
 76
 77	if (offset + bytes > priv->data->size)
 78		bytes = priv->data->size - offset;
 79
 80	index = offset >> 2;
 81	skipbytes = offset - (index << 2);
 82	num_bytes = round_up(bytes + skipbytes, 4);
 83	count = num_bytes >> 2;
 84
 85	p = kzalloc(num_bytes, GFP_KERNEL);
 86	if (!p)
 87		return -ENOMEM;
 88
 89	mutex_lock(&priv->lock);
 90
 91	buf = p;
 92
 93	for (i = index; i < (index + count); i++) {
 94		type = imx_ocotp_fuse_type(context, i);
 95		if (type == FUSE_INVALID || type == FUSE_ELE) {
 96			*buf++ = 0;
 97			continue;
 98		}
 99
100		if (type & FUSE_ECC)
101			*buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0);
102		else
103			*buf++ = readl_relaxed(reg + (i << 2));
104	}
105
106	memcpy(val, ((u8 *)p) + skipbytes, bytes);
107
108	mutex_unlock(&priv->lock);
109
110	kfree(p);
111
112	return 0;
113};
114
115static int imx_ocotp_cell_pp(void *context, const char *id, int index,
116			     unsigned int offset, void *data, size_t bytes)
117{
118	u8 *buf = data;
119	int i;
120
121	/* Deal with some post processing of nvmem cell data */
122	if (id && !strcmp(id, "mac-address")) {
123		bytes = min(bytes, ETH_ALEN);
124		for (i = 0; i < bytes / 2; i++)
125			swap(buf[i], buf[bytes - i - 1]);
126	}
127
128	return 0;
129}
130
131static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
132					 struct nvmem_cell_info *cell)
133{
134	cell->raw_len = round_up(cell->bytes, 4);
135	cell->read_post_process = imx_ocotp_cell_pp;
136}
137
138static int imx_ele_ocotp_probe(struct platform_device *pdev)
139{
140	struct device *dev = &pdev->dev;
141	struct imx_ocotp_priv *priv;
142	struct nvmem_device *nvmem;
143
144	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
145	if (!priv)
146		return -ENOMEM;
147
148	priv->data = of_device_get_match_data(dev);
149
150	priv->base = devm_platform_ioremap_resource(pdev, 0);
151	if (IS_ERR(priv->base))
152		return PTR_ERR(priv->base);
153
154	priv->config.dev = dev;
155	priv->config.name = "ELE-OCOTP";
156	priv->config.id = NVMEM_DEVID_AUTO;
157	priv->config.owner = THIS_MODULE;
158	priv->config.size = priv->data->size;
159	priv->config.reg_read = priv->data->reg_read;
160	priv->config.word_size = 1;
161	priv->config.stride = 1;
162	priv->config.priv = priv;
163	priv->config.read_only = true;
164	priv->config.add_legacy_fixed_of_cells = true;
165	priv->config.fixup_dt_cell_info = imx_ocotp_fixup_dt_cell_info;
166	mutex_init(&priv->lock);
167
168	nvmem = devm_nvmem_register(dev, &priv->config);
169	if (IS_ERR(nvmem))
170		return PTR_ERR(nvmem);
171
172	return 0;
173}
174
175static const struct ocotp_devtype_data imx93_ocotp_data = {
176	.reg_off = 0x8000,
177	.reg_read = imx_ocotp_reg_read,
178	.size = 2048,
179	.num_entry = 6,
180	.entry = {
181		{ 0, 52, FUSE_FSB },
182		{ 63, 1, FUSE_ELE},
183		{ 128, 16, FUSE_ELE },
184		{ 182, 1, FUSE_ELE },
185		{ 188, 1, FUSE_ELE },
186		{ 312, 200, FUSE_FSB }
187	},
188};
189
190static const struct ocotp_devtype_data imx94_ocotp_data = {
191	.reg_off = 0x8000,
192	.reg_read = imx_ocotp_reg_read,
193	.size = 3296, /* 103 Banks */
194	.num_entry = 10,
195	.entry = {
196		{ 0, 1, FUSE_FSB | FUSE_ECC },
197		{ 7, 1, FUSE_FSB | FUSE_ECC },
198		{ 9, 3, FUSE_FSB | FUSE_ECC },
199		{ 12, 24, FUSE_FSB },
200		{ 36, 2, FUSE_FSB  | FUSE_ECC },
201		{ 38, 14, FUSE_FSB },
202		{ 59, 1, FUSE_ELE },
203		{ 525, 2, FUSE_FSB | FUSE_ECC },
204		{ 528, 7, FUSE_FSB },
205		{ 536, 280, FUSE_FSB },
206	},
207};
208
209static const struct ocotp_devtype_data imx95_ocotp_data = {
210	.reg_off = 0x8000,
211	.reg_read = imx_ocotp_reg_read,
212	.size = 2048,
213	.num_entry = 12,
214	.entry = {
215		{ 0, 1, FUSE_FSB | FUSE_ECC },
216		{ 7, 1, FUSE_FSB | FUSE_ECC },
217		{ 9, 3, FUSE_FSB | FUSE_ECC },
218		{ 12, 24, FUSE_FSB },
219		{ 36, 2, FUSE_FSB  | FUSE_ECC },
220		{ 38, 14, FUSE_FSB },
221		{ 63, 1, FUSE_ELE },
222		{ 128, 16, FUSE_ELE },
223		{ 188, 1, FUSE_ELE },
224		{ 317, 2, FUSE_FSB | FUSE_ECC },
225		{ 320, 7, FUSE_FSB },
226		{ 328, 184, FUSE_FSB }
227	},
228};
229
230static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
231	{ .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
232	{ .compatible = "fsl,imx94-ocotp", .data = &imx94_ocotp_data, },
233	{ .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, },
234	{},
235};
236MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
237
238static struct platform_driver imx_ele_ocotp_driver = {
239	.driver = {
240		.name = "imx_ele_ocotp",
241		.of_match_table = imx_ele_ocotp_dt_ids,
242	},
243	.probe = imx_ele_ocotp_probe,
244};
245module_platform_driver(imx_ele_ocotp_driver);
246
247MODULE_DESCRIPTION("i.MX OCOTP/ELE driver");
248MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
249MODULE_LICENSE("GPL");
~/ Read-only file preview.