Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * OTP Memory controller
0004  *
0005  * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
0006  *
0007  * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
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  * struct mchp_otpc - OTPC private data structure
0033  * @base: base address
0034  * @dev: struct device pointer
0035  * @packets: list of packets in OTP memory
0036  * @npackets: number of packets in OTP memory
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  * struct mchp_otpc_packet - OTPC packet data structure
0047  * @list: list head
0048  * @id: packet ID
0049  * @offset: packet offset (in words) in OTP memory
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     /* Set address. */
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     /* Set read. */
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     /* Wait for packet to be transferred into temporary buffers. */
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  * OTPC memory is organized into packets. Each packets contains a header and
0096  * a payload. Header is 4 bytes long and contains the size of the payload.
0097  * Payload size varies. The memory footprint is something as follows:
0098  *
0099  * Memory offset  Memory footprint     Packet ID
0100  * -------------  ----------------     ---------
0101  *
0102  * 0x0            +------------+   <-- packet 0
0103  *                | header  0  |
0104  * 0x4            +------------+
0105  *                | payload 0  |
0106  *                .            .
0107  *                .    ...     .
0108  *                .            .
0109  * offset1        +------------+   <-- packet 1
0110  *                | header  1  |
0111  * offset1 + 0x4  +------------+
0112  *                | payload 1  |
0113  *                .            .
0114  *                .    ...     .
0115  *                .            .
0116  * offset2        +------------+   <-- packet 2
0117  *                .            .
0118  *                .    ...     .
0119  *                .            .
0120  * offsetN        +------------+   <-- packet N
0121  *                | header  N  |
0122  * offsetN + 0x4  +------------+
0123  *                | payload N  |
0124  *                .            .
0125  *                .    ...     .
0126  *                .            .
0127  *                +------------+
0128  *
0129  * where offset1, offset2, offsetN depends on the size of payload 0, payload 1,
0130  * payload N-1.
0131  *
0132  * The access to memory is done on a per packet basis: the control registers
0133  * need to be updated with an offset address (within a packet range) and the
0134  * data registers will be update by controller with information contained by
0135  * that packet. E.g. if control registers are updated with any address within
0136  * the range [offset1, offset2) the data registers are updated by controller
0137  * with packet 1. Header data is accessible though MCHP_OTPC_HR register.
0138  * Payload data is accessible though MCHP_OTPC_DR and MCHP_OTPC_AR registers.
0139  * There is no direct mapping b/w the offset requested by software and the
0140  * offset returned by hardware.
0141  *
0142  * For this, the read function will return the first requested bytes in the
0143  * packet. The user will have to be aware of the memory footprint before doing
0144  * the read request.
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      * We reach this point with off being multiple of stride = 4 to
0158      * be able to cross the subsystem. Inside the driver we use continuous
0159      * unsigned integer numbers for packet id, thus devide off by 4
0160      * before passing it to mchp_otpc_id_to_packet().
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         /* Read and save header content. */
0173         *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_HR);
0174         len += sizeof(*buf);
0175         offset++;
0176         if (len >= bytes)
0177             break;
0178 
0179         /* Read and save payload content. */
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         /* Count size by adding header and paload sizes. */
0222         *size += 4 * (payload_size + 1);
0223         /* Next word: this packet (header, payload) position + 1. */
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");