1 // SPDX-License-Identifier: GPL-2.0+
3 * i.MX8 OCOTP fusebox driver
7 * Peng Fan <peng.fan@nxp.com>
10 #include <linux/firmware/imx/sci.h>
11 #include <linux/module.h>
12 #include <linux/nvmem-provider.h>
13 #include <linux/of_device.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
22 struct ocotp_devtype_data
{
29 const struct ocotp_devtype_data
*data
;
30 struct imx_sc_ipc
*nvmem_ipc
;
33 struct imx_sc_msg_misc_fuse_read
{
34 struct imx_sc_rpc_msg hdr
;
38 static struct ocotp_devtype_data imx8qxp_data
= {
43 static struct ocotp_devtype_data imx8qm_data
= {
48 static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc
*ipc
, u32 word
,
51 struct imx_sc_msg_misc_fuse_read msg
;
52 struct imx_sc_rpc_msg
*hdr
= &msg
.hdr
;
55 hdr
->ver
= IMX_SC_RPC_VERSION
;
56 hdr
->svc
= IMX_SC_RPC_SVC_MISC
;
57 hdr
->func
= IMX_SC_MISC_FUNC_OTP_FUSE_READ
;
62 ret
= imx_scu_call_rpc(ipc
, &msg
, true);
71 static int imx_scu_ocotp_read(void *context
, unsigned int offset
,
72 void *val
, size_t bytes
)
74 struct ocotp_priv
*priv
= context
;
75 u32 count
, index
, num_bytes
;
81 num_bytes
= round_up((offset
% 4) + bytes
, 4);
82 count
= num_bytes
>> 2;
84 if (count
> (priv
->data
->nregs
- index
))
85 count
= priv
->data
->nregs
- index
;
87 p
= kzalloc(num_bytes
, GFP_KERNEL
);
93 for (i
= index
; i
< (index
+ count
); i
++) {
94 if (priv
->data
->devtype
== IMX8QXP
) {
95 if ((i
> 271) && (i
< 544)) {
101 ret
= imx_sc_misc_otp_fuse_read(priv
->nvmem_ipc
, i
, buf
);
109 memcpy(val
, (u8
*)p
+ offset
% 4, bytes
);
116 static struct nvmem_config imx_scu_ocotp_nvmem_config
= {
117 .name
= "imx-scu-ocotp",
121 .owner
= THIS_MODULE
,
122 .reg_read
= imx_scu_ocotp_read
,
125 static const struct of_device_id imx_scu_ocotp_dt_ids
[] = {
126 { .compatible
= "fsl,imx8qxp-scu-ocotp", (void *)&imx8qxp_data
},
127 { .compatible
= "fsl,imx8qm-scu-ocotp", (void *)&imx8qm_data
},
130 MODULE_DEVICE_TABLE(of
, imx_scu_ocotp_dt_ids
);
132 static int imx_scu_ocotp_probe(struct platform_device
*pdev
)
134 struct device
*dev
= &pdev
->dev
;
135 struct ocotp_priv
*priv
;
136 struct nvmem_device
*nvmem
;
139 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
143 ret
= imx_scu_get_handle(&priv
->nvmem_ipc
);
147 priv
->data
= of_device_get_match_data(dev
);
149 imx_scu_ocotp_nvmem_config
.size
= 4 * priv
->data
->nregs
;
150 imx_scu_ocotp_nvmem_config
.dev
= dev
;
151 imx_scu_ocotp_nvmem_config
.priv
= priv
;
152 nvmem
= devm_nvmem_register(dev
, &imx_scu_ocotp_nvmem_config
);
154 return PTR_ERR_OR_ZERO(nvmem
);
157 static struct platform_driver imx_scu_ocotp_driver
= {
158 .probe
= imx_scu_ocotp_probe
,
160 .name
= "imx_scu_ocotp",
161 .of_match_table
= imx_scu_ocotp_dt_ids
,
164 module_platform_driver(imx_scu_ocotp_driver
);
166 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
167 MODULE_DESCRIPTION("i.MX8 SCU OCOTP fuse box driver");
168 MODULE_LICENSE("GPL v2");