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/arm-smccc.h> 11 #include <linux/firmware/imx/sci.h> 12 #include <linux/module.h> 13 #include <linux/nvmem-provider.h> 14 #include <linux/of_device.h> 15 #include <linux/platform_device.h> 16 #include <linux/slab.h> 17 18 #define IMX_SIP_OTP 0xC200000A 19 #define IMX_SIP_OTP_WRITE 0x2 20 21 enum ocotp_devtype { 22 IMX8QXP, 23 IMX8QM, 24 }; 25 26 #define ECC_REGION BIT(0) 27 #define HOLE_REGION BIT(1) 28 29 struct ocotp_region { 30 u32 start; 31 u32 end; 32 u32 flag; 33 }; 34 35 struct ocotp_devtype_data { 36 int devtype; 37 int nregs; 38 u32 num_region; 39 struct ocotp_region region[]; 40 }; 41 42 struct ocotp_priv { 43 struct device *dev; 44 const struct ocotp_devtype_data *data; 45 struct imx_sc_ipc *nvmem_ipc; 46 }; 47 48 struct imx_sc_msg_misc_fuse_read { 49 struct imx_sc_rpc_msg hdr; 50 u32 word; 51 } __packed; 52 53 static DEFINE_MUTEX(scu_ocotp_mutex); 54 55 static struct ocotp_devtype_data imx8qxp_data = { 56 .devtype = IMX8QXP, 57 .nregs = 800, 58 .num_region = 3, 59 .region = { 60 {0x10, 0x10f, ECC_REGION}, 61 {0x110, 0x21F, HOLE_REGION}, 62 {0x220, 0x31F, ECC_REGION}, 63 }, 64 }; 65 66 static struct ocotp_devtype_data imx8qm_data = { 67 .devtype = IMX8QM, 68 .nregs = 800, 69 .num_region = 2, 70 .region = { 71 {0x10, 0x10f, ECC_REGION}, 72 {0x1a0, 0x1ff, ECC_REGION}, 73 }, 74 }; 75 76 static bool in_hole(void *context, u32 index) 77 { 78 struct ocotp_priv *priv = context; 79 const struct ocotp_devtype_data *data = priv->data; 80 int i; 81 82 for (i = 0; i < data->num_region; i++) { 83 if (data->region[i].flag & HOLE_REGION) { 84 if ((index >= data->region[i].start) && 85 (index <= data->region[i].end)) 86 return true; 87 } 88 } 89 90 return false; 91 } 92 93 static bool in_ecc(void *context, u32 index) 94 { 95 struct ocotp_priv *priv = context; 96 const struct ocotp_devtype_data *data = priv->data; 97 int i; 98 99 for (i = 0; i < data->num_region; i++) { 100 if (data->region[i].flag & ECC_REGION) { 101 if ((index >= data->region[i].start) && 102 (index <= data->region[i].end)) 103 return true; 104 } 105 } 106 107 return false; 108 } 109 110 static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc *ipc, u32 word, 111 u32 *val) 112 { 113 struct imx_sc_msg_misc_fuse_read msg; 114 struct imx_sc_rpc_msg *hdr = &msg.hdr; 115 int ret; 116 117 hdr->ver = IMX_SC_RPC_VERSION; 118 hdr->svc = IMX_SC_RPC_SVC_MISC; 119 hdr->func = IMX_SC_MISC_FUNC_OTP_FUSE_READ; 120 hdr->size = 2; 121 122 msg.word = word; 123 124 ret = imx_scu_call_rpc(ipc, &msg, true); 125 if (ret) 126 return ret; 127 128 *val = msg.word; 129 130 return 0; 131 } 132 133 static int imx_scu_ocotp_read(void *context, unsigned int offset, 134 void *val, size_t bytes) 135 { 136 struct ocotp_priv *priv = context; 137 u32 count, index, num_bytes; 138 u32 *buf; 139 void *p; 140 int i, ret; 141 142 index = offset >> 2; 143 num_bytes = round_up((offset % 4) + bytes, 4); 144 count = num_bytes >> 2; 145 146 if (count > (priv->data->nregs - index)) 147 count = priv->data->nregs - index; 148 149 p = kzalloc(num_bytes, GFP_KERNEL); 150 if (!p) 151 return -ENOMEM; 152 153 mutex_lock(&scu_ocotp_mutex); 154 155 buf = p; 156 157 for (i = index; i < (index + count); i++) { 158 if (in_hole(context, i)) { 159 *buf++ = 0; 160 continue; 161 } 162 163 ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, i, buf); 164 if (ret) { 165 mutex_unlock(&scu_ocotp_mutex); 166 kfree(p); 167 return ret; 168 } 169 buf++; 170 } 171 172 memcpy(val, (u8 *)p + offset % 4, bytes); 173 174 mutex_unlock(&scu_ocotp_mutex); 175 176 kfree(p); 177 178 return 0; 179 } 180 181 static int imx_scu_ocotp_write(void *context, unsigned int offset, 182 void *val, size_t bytes) 183 { 184 struct ocotp_priv *priv = context; 185 struct arm_smccc_res res; 186 u32 *buf = val; 187 u32 tmp; 188 u32 index; 189 int ret; 190 191 /* allow only writing one complete OTP word at a time */ 192 if ((bytes != 4) || (offset % 4)) 193 return -EINVAL; 194 195 index = offset >> 2; 196 197 if (in_hole(context, index)) 198 return -EINVAL; 199 200 if (in_ecc(context, index)) { 201 pr_warn("ECC region, only program once\n"); 202 mutex_lock(&scu_ocotp_mutex); 203 ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, index, &tmp); 204 mutex_unlock(&scu_ocotp_mutex); 205 if (ret) 206 return ret; 207 if (tmp) { 208 pr_warn("ECC region, already has value: %x\n", tmp); 209 return -EIO; 210 } 211 } 212 213 mutex_lock(&scu_ocotp_mutex); 214 215 arm_smccc_smc(IMX_SIP_OTP, IMX_SIP_OTP_WRITE, index, *buf, 216 0, 0, 0, 0, &res); 217 218 mutex_unlock(&scu_ocotp_mutex); 219 220 return res.a0; 221 } 222 223 static struct nvmem_config imx_scu_ocotp_nvmem_config = { 224 .name = "imx-scu-ocotp", 225 .read_only = false, 226 .word_size = 4, 227 .stride = 1, 228 .owner = THIS_MODULE, 229 .reg_read = imx_scu_ocotp_read, 230 .reg_write = imx_scu_ocotp_write, 231 }; 232 233 static const struct of_device_id imx_scu_ocotp_dt_ids[] = { 234 { .compatible = "fsl,imx8qxp-scu-ocotp", (void *)&imx8qxp_data }, 235 { .compatible = "fsl,imx8qm-scu-ocotp", (void *)&imx8qm_data }, 236 { }, 237 }; 238 MODULE_DEVICE_TABLE(of, imx_scu_ocotp_dt_ids); 239 240 static int imx_scu_ocotp_probe(struct platform_device *pdev) 241 { 242 struct device *dev = &pdev->dev; 243 struct ocotp_priv *priv; 244 struct nvmem_device *nvmem; 245 int ret; 246 247 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 248 if (!priv) 249 return -ENOMEM; 250 251 ret = imx_scu_get_handle(&priv->nvmem_ipc); 252 if (ret) 253 return ret; 254 255 priv->data = of_device_get_match_data(dev); 256 priv->dev = dev; 257 imx_scu_ocotp_nvmem_config.size = 4 * priv->data->nregs; 258 imx_scu_ocotp_nvmem_config.dev = dev; 259 imx_scu_ocotp_nvmem_config.priv = priv; 260 nvmem = devm_nvmem_register(dev, &imx_scu_ocotp_nvmem_config); 261 262 return PTR_ERR_OR_ZERO(nvmem); 263 } 264 265 static struct platform_driver imx_scu_ocotp_driver = { 266 .probe = imx_scu_ocotp_probe, 267 .driver = { 268 .name = "imx_scu_ocotp", 269 .of_match_table = imx_scu_ocotp_dt_ids, 270 }, 271 }; 272 module_platform_driver(imx_scu_ocotp_driver); 273 274 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 275 MODULE_DESCRIPTION("i.MX8 SCU OCOTP fuse box driver"); 276 MODULE_LICENSE("GPL v2"); 277