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_device.h> 13 #include <linux/platform_device.h> 14 #include <linux/slab.h> 15 16 enum fuse_type { 17 FUSE_FSB = 1, 18 FUSE_ELE = 2, 19 FUSE_INVALID = -1 20 }; 21 22 struct ocotp_map_entry { 23 u32 start; /* start word */ 24 u32 num; /* num words */ 25 enum fuse_type type; 26 }; 27 28 struct ocotp_devtype_data { 29 u32 reg_off; 30 char *name; 31 u32 size; 32 u32 num_entry; 33 u32 flag; 34 nvmem_reg_read_t reg_read; 35 struct ocotp_map_entry entry[]; 36 }; 37 38 struct imx_ocotp_priv { 39 struct device *dev; 40 void __iomem *base; 41 struct nvmem_config config; 42 struct mutex lock; 43 const struct ocotp_devtype_data *data; 44 }; 45 46 static enum fuse_type imx_ocotp_fuse_type(void *context, u32 index) 47 { 48 struct imx_ocotp_priv *priv = context; 49 const struct ocotp_devtype_data *data = priv->data; 50 u32 start, end; 51 int i; 52 53 for (i = 0; i < data->num_entry; i++) { 54 start = data->entry[i].start; 55 end = data->entry[i].start + data->entry[i].num; 56 57 if (index >= start && index < end) 58 return data->entry[i].type; 59 } 60 61 return FUSE_INVALID; 62 } 63 64 static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, size_t bytes) 65 { 66 struct imx_ocotp_priv *priv = context; 67 void __iomem *reg = priv->base + priv->data->reg_off; 68 u32 count, index, num_bytes; 69 enum fuse_type type; 70 u32 *buf; 71 void *p; 72 int i; 73 74 index = offset; 75 num_bytes = round_up(bytes, 4); 76 count = num_bytes >> 2; 77 78 if (count > ((priv->data->size >> 2) - index)) 79 count = (priv->data->size >> 2) - index; 80 81 p = kzalloc(num_bytes, GFP_KERNEL); 82 if (!p) 83 return -ENOMEM; 84 85 mutex_lock(&priv->lock); 86 87 buf = p; 88 89 for (i = index; i < (index + count); i++) { 90 type = imx_ocotp_fuse_type(context, i); 91 if (type == FUSE_INVALID || type == FUSE_ELE) { 92 *buf++ = 0; 93 continue; 94 } 95 96 *buf++ = readl_relaxed(reg + (i << 2)); 97 } 98 99 memcpy(val, (u8 *)p, bytes); 100 101 mutex_unlock(&priv->lock); 102 103 kfree(p); 104 105 return 0; 106 }; 107 108 static int imx_ele_ocotp_probe(struct platform_device *pdev) 109 { 110 struct device *dev = &pdev->dev; 111 struct imx_ocotp_priv *priv; 112 struct nvmem_device *nvmem; 113 114 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 115 if (!priv) 116 return -ENOMEM; 117 118 priv->data = of_device_get_match_data(dev); 119 120 priv->base = devm_platform_ioremap_resource(pdev, 0); 121 if (IS_ERR(priv->base)) 122 return PTR_ERR(priv->base); 123 124 priv->config.dev = dev; 125 priv->config.name = "ELE-OCOTP"; 126 priv->config.id = NVMEM_DEVID_AUTO; 127 priv->config.owner = THIS_MODULE; 128 priv->config.size = priv->data->size; 129 priv->config.reg_read = priv->data->reg_read; 130 priv->config.word_size = 4; 131 priv->config.stride = 1; 132 priv->config.priv = priv; 133 priv->config.read_only = true; 134 mutex_init(&priv->lock); 135 136 nvmem = devm_nvmem_register(dev, &priv->config); 137 if (IS_ERR(nvmem)) 138 return PTR_ERR(nvmem); 139 140 return 0; 141 } 142 143 static const struct ocotp_devtype_data imx93_ocotp_data = { 144 .reg_off = 0x8000, 145 .reg_read = imx_ocotp_reg_read, 146 .size = 2048, 147 .num_entry = 6, 148 .entry = { 149 { 0, 52, FUSE_FSB }, 150 { 63, 1, FUSE_ELE}, 151 { 128, 16, FUSE_ELE }, 152 { 182, 1, FUSE_ELE }, 153 { 188, 1, FUSE_ELE }, 154 { 312, 200, FUSE_FSB } 155 }, 156 }; 157 158 static const struct of_device_id imx_ele_ocotp_dt_ids[] = { 159 { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, }, 160 {}, 161 }; 162 MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids); 163 164 static struct platform_driver imx_ele_ocotp_driver = { 165 .driver = { 166 .name = "imx_ele_ocotp", 167 .of_match_table = imx_ele_ocotp_dt_ids, 168 }, 169 .probe = imx_ele_ocotp_probe, 170 }; 171 module_platform_driver(imx_ele_ocotp_driver); 172 173 MODULE_DESCRIPTION("i.MX OCOTP/ELE driver"); 174 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 175 MODULE_LICENSE("GPL"); 176