290 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * OTP Memory controller4 *5 * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries6 *7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>8 */9 10#include <linux/bitfield.h>11#include <linux/iopoll.h>12#include <linux/module.h>13#include <linux/nvmem-provider.h>14#include <linux/of.h>15#include <linux/platform_device.h>16 17#define MCHP_OTPC_CR (0x0)18#define MCHP_OTPC_CR_READ BIT(6)19#define MCHP_OTPC_MR (0x4)20#define MCHP_OTPC_MR_ADDR GENMASK(31, 16)21#define MCHP_OTPC_AR (0x8)22#define MCHP_OTPC_SR (0xc)23#define MCHP_OTPC_SR_READ BIT(6)24#define MCHP_OTPC_HR (0x20)25#define MCHP_OTPC_HR_SIZE GENMASK(15, 8)26#define MCHP_OTPC_DR (0x24)27 28#define MCHP_OTPC_NAME "mchp-otpc"29#define MCHP_OTPC_SIZE (11 * 1024)30 31/**32 * struct mchp_otpc - OTPC private data structure33 * @base: base address34 * @dev: struct device pointer35 * @packets: list of packets in OTP memory36 * @npackets: number of packets in OTP memory37 */38struct mchp_otpc {39 void __iomem *base;40 struct device *dev;41 struct list_head packets;42 u32 npackets;43};44 45/**46 * struct mchp_otpc_packet - OTPC packet data structure47 * @list: list head48 * @id: packet ID49 * @offset: packet offset (in words) in OTP memory50 */51struct mchp_otpc_packet {52 struct list_head list;53 u32 id;54 u32 offset;55};56 57static struct mchp_otpc_packet *mchp_otpc_id_to_packet(struct mchp_otpc *otpc,58 u32 id)59{60 struct mchp_otpc_packet *packet;61 62 if (id >= otpc->npackets)63 return NULL;64 65 list_for_each_entry(packet, &otpc->packets, list) {66 if (packet->id == id)67 return packet;68 }69 70 return NULL;71}72 73static int mchp_otpc_prepare_read(struct mchp_otpc *otpc,74 unsigned int offset)75{76 u32 tmp;77 78 /* Set address. */79 tmp = readl_relaxed(otpc->base + MCHP_OTPC_MR);80 tmp &= ~MCHP_OTPC_MR_ADDR;81 tmp |= FIELD_PREP(MCHP_OTPC_MR_ADDR, offset);82 writel_relaxed(tmp, otpc->base + MCHP_OTPC_MR);83 84 /* Set read. */85 tmp = readl_relaxed(otpc->base + MCHP_OTPC_CR);86 tmp |= MCHP_OTPC_CR_READ;87 writel_relaxed(tmp, otpc->base + MCHP_OTPC_CR);88 89 /* Wait for packet to be transferred into temporary buffers. */90 return read_poll_timeout(readl_relaxed, tmp, !(tmp & MCHP_OTPC_SR_READ),91 10000, 2000, false, otpc->base + MCHP_OTPC_SR);92}93 94/*95 * OTPC memory is organized into packets. Each packets contains a header and96 * a payload. Header is 4 bytes long and contains the size of the payload.97 * Payload size varies. The memory footprint is something as follows:98 *99 * Memory offset Memory footprint Packet ID100 * ------------- ---------------- ---------101 *102 * 0x0 +------------+ <-- packet 0103 * | header 0 |104 * 0x4 +------------+105 * | payload 0 |106 * . .107 * . ... .108 * . .109 * offset1 +------------+ <-- packet 1110 * | header 1 |111 * offset1 + 0x4 +------------+112 * | payload 1 |113 * . .114 * . ... .115 * . .116 * offset2 +------------+ <-- packet 2117 * . .118 * . ... .119 * . .120 * offsetN +------------+ <-- packet N121 * | header N |122 * offsetN + 0x4 +------------+123 * | payload N |124 * . .125 * . ... .126 * . .127 * +------------+128 *129 * where offset1, offset2, offsetN depends on the size of payload 0, payload 1,130 * payload N-1.131 *132 * The access to memory is done on a per packet basis: the control registers133 * need to be updated with an offset address (within a packet range) and the134 * data registers will be update by controller with information contained by135 * that packet. E.g. if control registers are updated with any address within136 * the range [offset1, offset2) the data registers are updated by controller137 * with packet 1. Header data is accessible though MCHP_OTPC_HR register.138 * Payload data is accessible though MCHP_OTPC_DR and MCHP_OTPC_AR registers.139 * There is no direct mapping b/w the offset requested by software and the140 * offset returned by hardware.141 *142 * For this, the read function will return the first requested bytes in the143 * packet. The user will have to be aware of the memory footprint before doing144 * the read request.145 */146static int mchp_otpc_read(void *priv, unsigned int off, void *val,147 size_t bytes)148{149 struct mchp_otpc *otpc = priv;150 struct mchp_otpc_packet *packet;151 u32 *buf = val;152 u32 offset;153 size_t len = 0;154 int ret, payload_size;155 156 /*157 * We reach this point with off being multiple of stride = 4 to158 * be able to cross the subsystem. Inside the driver we use continuous159 * unsigned integer numbers for packet id, thus devide off by 4160 * before passing it to mchp_otpc_id_to_packet().161 */162 packet = mchp_otpc_id_to_packet(otpc, off / 4);163 if (!packet)164 return -EINVAL;165 offset = packet->offset;166 167 while (len < bytes) {168 ret = mchp_otpc_prepare_read(otpc, offset);169 if (ret)170 return ret;171 172 /* Read and save header content. */173 *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_HR);174 len += sizeof(*buf);175 offset++;176 if (len >= bytes)177 break;178 179 /* Read and save payload content. */180 payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, *(buf - 1));181 writel_relaxed(0UL, otpc->base + MCHP_OTPC_AR);182 do {183 *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_DR);184 len += sizeof(*buf);185 offset++;186 payload_size--;187 } while (payload_size >= 0 && len < bytes);188 }189 190 return 0;191}192 193static int mchp_otpc_init_packets_list(struct mchp_otpc *otpc, u32 *size)194{195 struct mchp_otpc_packet *packet;196 u32 word, word_pos = 0, id = 0, npackets = 0, payload_size;197 int ret;198 199 INIT_LIST_HEAD(&otpc->packets);200 *size = 0;201 202 while (*size < MCHP_OTPC_SIZE) {203 ret = mchp_otpc_prepare_read(otpc, word_pos);204 if (ret)205 return ret;206 207 word = readl_relaxed(otpc->base + MCHP_OTPC_HR);208 payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, word);209 if (!payload_size)210 break;211 212 packet = devm_kzalloc(otpc->dev, sizeof(*packet), GFP_KERNEL);213 if (!packet)214 return -ENOMEM;215 216 packet->id = id++;217 packet->offset = word_pos;218 INIT_LIST_HEAD(&packet->list);219 list_add_tail(&packet->list, &otpc->packets);220 221 /* Count size by adding header and paload sizes. */222 *size += 4 * (payload_size + 1);223 /* Next word: this packet (header, payload) position + 1. */224 word_pos += payload_size + 2;225 226 npackets++;227 }228 229 otpc->npackets = npackets;230 231 return 0;232}233 234static struct nvmem_config mchp_nvmem_config = {235 .name = MCHP_OTPC_NAME,236 .type = NVMEM_TYPE_OTP,237 .read_only = true,238 .word_size = 4,239 .stride = 4,240 .reg_read = mchp_otpc_read,241};242 243static int mchp_otpc_probe(struct platform_device *pdev)244{245 struct nvmem_device *nvmem;246 struct mchp_otpc *otpc;247 u32 size;248 int ret;249 250 otpc = devm_kzalloc(&pdev->dev, sizeof(*otpc), GFP_KERNEL);251 if (!otpc)252 return -ENOMEM;253 254 otpc->base = devm_platform_ioremap_resource(pdev, 0);255 if (IS_ERR(otpc->base))256 return PTR_ERR(otpc->base);257 258 otpc->dev = &pdev->dev;259 ret = mchp_otpc_init_packets_list(otpc, &size);260 if (ret)261 return ret;262 263 mchp_nvmem_config.dev = otpc->dev;264 mchp_nvmem_config.add_legacy_fixed_of_cells = true;265 mchp_nvmem_config.size = size;266 mchp_nvmem_config.priv = otpc;267 nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config);268 269 return PTR_ERR_OR_ZERO(nvmem);270}271 272static const struct of_device_id __maybe_unused mchp_otpc_ids[] = {273 { .compatible = "microchip,sama7g5-otpc", },274 { },275};276MODULE_DEVICE_TABLE(of, mchp_otpc_ids);277 278static struct platform_driver mchp_otpc_driver = {279 .probe = mchp_otpc_probe,280 .driver = {281 .name = MCHP_OTPC_NAME,282 .of_match_table = of_match_ptr(mchp_otpc_ids),283 },284};285module_platform_driver(mchp_otpc_driver);286 287MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");288MODULE_DESCRIPTION("Microchip SAMA7G5 OTPC driver");289MODULE_LICENSE("GPL");290