1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * i.MX8 OCOTP fusebox driver 4 * 5 * Copyright 2019 NXP 6 * 7 * Peng Fan <peng.fan@nxp.com> 8 */ 9 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> 16 17 enum ocotp_devtype { 18 IMX8QXP, 19 }; 20 21 struct ocotp_devtype_data { 22 int devtype; 23 int nregs; 24 }; 25 26 struct ocotp_priv { 27 struct device *dev; 28 const struct ocotp_devtype_data *data; 29 struct imx_sc_ipc *nvmem_ipc; 30 }; 31 32 struct imx_sc_msg_misc_fuse_read { 33 struct imx_sc_rpc_msg hdr; 34 u32 word; 35 } __packed; 36 37 static struct ocotp_devtype_data imx8qxp_data = { 38 .devtype = IMX8QXP, 39 .nregs = 800, 40 }; 41 42 static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc *ipc, u32 word, 43 u32 *val) 44 { 45 struct imx_sc_msg_misc_fuse_read msg; 46 struct imx_sc_rpc_msg *hdr = &msg.hdr; 47 int ret; 48 49 hdr->ver = IMX_SC_RPC_VERSION; 50 hdr->svc = IMX_SC_RPC_SVC_MISC; 51 hdr->func = IMX_SC_MISC_FUNC_OTP_FUSE_READ; 52 hdr->size = 2; 53 54 msg.word = word; 55 56 ret = imx_scu_call_rpc(ipc, &msg, true); 57 if (ret) 58 return ret; 59 60 *val = msg.word; 61 62 return 0; 63 } 64 65 static int imx_scu_ocotp_read(void *context, unsigned int offset, 66 void *val, size_t bytes) 67 { 68 struct ocotp_priv *priv = context; 69 u32 count, index, num_bytes; 70 u32 *buf; 71 void *p; 72 int i, ret; 73 74 index = offset >> 2; 75 num_bytes = round_up((offset % 4) + bytes, 4); 76 count = num_bytes >> 2; 77 78 if (count > (priv->data->nregs - index)) 79 count = priv->data->nregs - index; 80 81 p = kzalloc(num_bytes, GFP_KERNEL); 82 if (!p) 83 return -ENOMEM; 84 85 buf = p; 86 87 for (i = index; i < (index + count); i++) { 88 if (priv->data->devtype == IMX8QXP) { 89 if ((i > 271) && (i < 544)) { 90 *buf++ = 0; 91 continue; 92 } 93 } 94 95 ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, i, buf); 96 if (ret) { 97 kfree(p); 98 return ret; 99 } 100 buf++; 101 } 102 103 memcpy(val, (u8 *)p + offset % 4, bytes); 104 105 kfree(p); 106 107 return 0; 108 } 109 110 static struct nvmem_config imx_scu_ocotp_nvmem_config = { 111 .name = "imx-scu-ocotp", 112 .read_only = true, 113 .word_size = 4, 114 .stride = 1, 115 .owner = THIS_MODULE, 116 .reg_read = imx_scu_ocotp_read, 117 }; 118 119 static const struct of_device_id imx_scu_ocotp_dt_ids[] = { 120 { .compatible = "fsl,imx8qxp-scu-ocotp", (void *)&imx8qxp_data }, 121 { }, 122 }; 123 MODULE_DEVICE_TABLE(of, imx_scu_ocotp_dt_ids); 124 125 static int imx_scu_ocotp_probe(struct platform_device *pdev) 126 { 127 struct device *dev = &pdev->dev; 128 struct ocotp_priv *priv; 129 struct nvmem_device *nvmem; 130 int ret; 131 132 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 133 if (!priv) 134 return -ENOMEM; 135 136 ret = imx_scu_get_handle(&priv->nvmem_ipc); 137 if (ret) 138 return ret; 139 140 priv->data = of_device_get_match_data(dev); 141 priv->dev = dev; 142 imx_scu_ocotp_nvmem_config.size = 4 * priv->data->nregs; 143 imx_scu_ocotp_nvmem_config.dev = dev; 144 imx_scu_ocotp_nvmem_config.priv = priv; 145 nvmem = devm_nvmem_register(dev, &imx_scu_ocotp_nvmem_config); 146 147 return PTR_ERR_OR_ZERO(nvmem); 148 } 149 150 static struct platform_driver imx_scu_ocotp_driver = { 151 .probe = imx_scu_ocotp_probe, 152 .driver = { 153 .name = "imx_scu_ocotp", 154 .of_match_table = imx_scu_ocotp_dt_ids, 155 }, 156 }; 157 module_platform_driver(imx_scu_ocotp_driver); 158 159 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 160 MODULE_DESCRIPTION("i.MX8 SCU OCOTP fuse box driver"); 161 MODULE_LICENSE("GPL v2"); 162