0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/bitfield.h>
0011 #include <linux/iopoll.h>
0012 #include <linux/module.h>
0013 #include <linux/nvmem-provider.h>
0014 #include <linux/of.h>
0015 #include <linux/platform_device.h>
0016
0017 #define MCHP_OTPC_CR (0x0)
0018 #define MCHP_OTPC_CR_READ BIT(6)
0019 #define MCHP_OTPC_MR (0x4)
0020 #define MCHP_OTPC_MR_ADDR GENMASK(31, 16)
0021 #define MCHP_OTPC_AR (0x8)
0022 #define MCHP_OTPC_SR (0xc)
0023 #define MCHP_OTPC_SR_READ BIT(6)
0024 #define MCHP_OTPC_HR (0x20)
0025 #define MCHP_OTPC_HR_SIZE GENMASK(15, 8)
0026 #define MCHP_OTPC_DR (0x24)
0027
0028 #define MCHP_OTPC_NAME "mchp-otpc"
0029 #define MCHP_OTPC_SIZE (11 * 1024)
0030
0031
0032
0033
0034
0035
0036
0037
0038 struct mchp_otpc {
0039 void __iomem *base;
0040 struct device *dev;
0041 struct list_head packets;
0042 u32 npackets;
0043 };
0044
0045
0046
0047
0048
0049
0050
0051 struct mchp_otpc_packet {
0052 struct list_head list;
0053 u32 id;
0054 u32 offset;
0055 };
0056
0057 static struct mchp_otpc_packet *mchp_otpc_id_to_packet(struct mchp_otpc *otpc,
0058 u32 id)
0059 {
0060 struct mchp_otpc_packet *packet;
0061
0062 if (id >= otpc->npackets)
0063 return NULL;
0064
0065 list_for_each_entry(packet, &otpc->packets, list) {
0066 if (packet->id == id)
0067 return packet;
0068 }
0069
0070 return NULL;
0071 }
0072
0073 static int mchp_otpc_prepare_read(struct mchp_otpc *otpc,
0074 unsigned int offset)
0075 {
0076 u32 tmp;
0077
0078
0079 tmp = readl_relaxed(otpc->base + MCHP_OTPC_MR);
0080 tmp &= ~MCHP_OTPC_MR_ADDR;
0081 tmp |= FIELD_PREP(MCHP_OTPC_MR_ADDR, offset);
0082 writel_relaxed(tmp, otpc->base + MCHP_OTPC_MR);
0083
0084
0085 tmp = readl_relaxed(otpc->base + MCHP_OTPC_CR);
0086 tmp |= MCHP_OTPC_CR_READ;
0087 writel_relaxed(tmp, otpc->base + MCHP_OTPC_CR);
0088
0089
0090 return read_poll_timeout(readl_relaxed, tmp, !(tmp & MCHP_OTPC_SR_READ),
0091 10000, 2000, false, otpc->base + MCHP_OTPC_SR);
0092 }
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146 static int mchp_otpc_read(void *priv, unsigned int off, void *val,
0147 size_t bytes)
0148 {
0149 struct mchp_otpc *otpc = priv;
0150 struct mchp_otpc_packet *packet;
0151 u32 *buf = val;
0152 u32 offset;
0153 size_t len = 0;
0154 int ret, payload_size;
0155
0156
0157
0158
0159
0160
0161
0162 packet = mchp_otpc_id_to_packet(otpc, off / 4);
0163 if (!packet)
0164 return -EINVAL;
0165 offset = packet->offset;
0166
0167 while (len < bytes) {
0168 ret = mchp_otpc_prepare_read(otpc, offset);
0169 if (ret)
0170 return ret;
0171
0172
0173 *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_HR);
0174 len += sizeof(*buf);
0175 offset++;
0176 if (len >= bytes)
0177 break;
0178
0179
0180 payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, *(buf - 1));
0181 writel_relaxed(0UL, otpc->base + MCHP_OTPC_AR);
0182 do {
0183 *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_DR);
0184 len += sizeof(*buf);
0185 offset++;
0186 payload_size--;
0187 } while (payload_size >= 0 && len < bytes);
0188 }
0189
0190 return 0;
0191 }
0192
0193 static int mchp_otpc_init_packets_list(struct mchp_otpc *otpc, u32 *size)
0194 {
0195 struct mchp_otpc_packet *packet;
0196 u32 word, word_pos = 0, id = 0, npackets = 0, payload_size;
0197 int ret;
0198
0199 INIT_LIST_HEAD(&otpc->packets);
0200 *size = 0;
0201
0202 while (*size < MCHP_OTPC_SIZE) {
0203 ret = mchp_otpc_prepare_read(otpc, word_pos);
0204 if (ret)
0205 return ret;
0206
0207 word = readl_relaxed(otpc->base + MCHP_OTPC_HR);
0208 payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, word);
0209 if (!payload_size)
0210 break;
0211
0212 packet = devm_kzalloc(otpc->dev, sizeof(*packet), GFP_KERNEL);
0213 if (!packet)
0214 return -ENOMEM;
0215
0216 packet->id = id++;
0217 packet->offset = word_pos;
0218 INIT_LIST_HEAD(&packet->list);
0219 list_add_tail(&packet->list, &otpc->packets);
0220
0221
0222 *size += 4 * (payload_size + 1);
0223
0224 word_pos += payload_size + 2;
0225
0226 npackets++;
0227 }
0228
0229 otpc->npackets = npackets;
0230
0231 return 0;
0232 }
0233
0234 static struct nvmem_config mchp_nvmem_config = {
0235 .name = MCHP_OTPC_NAME,
0236 .type = NVMEM_TYPE_OTP,
0237 .read_only = true,
0238 .word_size = 4,
0239 .stride = 4,
0240 .reg_read = mchp_otpc_read,
0241 };
0242
0243 static int mchp_otpc_probe(struct platform_device *pdev)
0244 {
0245 struct nvmem_device *nvmem;
0246 struct mchp_otpc *otpc;
0247 u32 size;
0248 int ret;
0249
0250 otpc = devm_kzalloc(&pdev->dev, sizeof(*otpc), GFP_KERNEL);
0251 if (!otpc)
0252 return -ENOMEM;
0253
0254 otpc->base = devm_platform_ioremap_resource(pdev, 0);
0255 if (IS_ERR(otpc->base))
0256 return PTR_ERR(otpc->base);
0257
0258 otpc->dev = &pdev->dev;
0259 ret = mchp_otpc_init_packets_list(otpc, &size);
0260 if (ret)
0261 return ret;
0262
0263 mchp_nvmem_config.dev = otpc->dev;
0264 mchp_nvmem_config.size = size;
0265 mchp_nvmem_config.priv = otpc;
0266 nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config);
0267
0268 return PTR_ERR_OR_ZERO(nvmem);
0269 }
0270
0271 static const struct of_device_id __maybe_unused mchp_otpc_ids[] = {
0272 { .compatible = "microchip,sama7g5-otpc", },
0273 { },
0274 };
0275 MODULE_DEVICE_TABLE(of, mchp_otpc_ids);
0276
0277 static struct platform_driver mchp_otpc_driver = {
0278 .probe = mchp_otpc_probe,
0279 .driver = {
0280 .name = MCHP_OTPC_NAME,
0281 .of_match_table = of_match_ptr(mchp_otpc_ids),
0282 },
0283 };
0284 module_platform_driver(mchp_otpc_driver);
0285
0286 MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");
0287 MODULE_DESCRIPTION("Microchip SAMA7G5 OTPC driver");
0288 MODULE_LICENSE("GPL");