Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2015 Toradex AG.
0004  *
0005  * Author: Sanchayan Maity <sanchayan.maity@toradex.com>
0006  *
0007  * Based on the barebox ocotp driver,
0008  * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>
0009  *  Orex Computed Radiography
0010  */
0011 
0012 #include <linux/clk.h>
0013 #include <linux/delay.h>
0014 #include <linux/device.h>
0015 #include <linux/io.h>
0016 #include <linux/module.h>
0017 #include <linux/nvmem-provider.h>
0018 #include <linux/of.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/slab.h>
0021 
0022 /* OCOTP Register Offsets */
0023 #define OCOTP_CTRL_REG              0x00
0024 #define OCOTP_CTRL_SET              0x04
0025 #define OCOTP_CTRL_CLR              0x08
0026 #define OCOTP_TIMING                0x10
0027 #define OCOTP_DATA              0x20
0028 #define OCOTP_READ_CTRL_REG         0x30
0029 #define OCOTP_READ_FUSE_DATA            0x40
0030 
0031 /* OCOTP Register bits and masks */
0032 #define OCOTP_CTRL_WR_UNLOCK            16
0033 #define OCOTP_CTRL_WR_UNLOCK_KEY        0x3E77
0034 #define OCOTP_CTRL_WR_UNLOCK_MASK       GENMASK(31, 16)
0035 #define OCOTP_CTRL_ADDR             0
0036 #define OCOTP_CTRL_ADDR_MASK            GENMASK(6, 0)
0037 #define OCOTP_CTRL_RELOAD_SHADOWS       BIT(10)
0038 #define OCOTP_CTRL_ERR              BIT(9)
0039 #define OCOTP_CTRL_BUSY             BIT(8)
0040 
0041 #define OCOTP_TIMING_STROBE_READ        16
0042 #define OCOTP_TIMING_STROBE_READ_MASK       GENMASK(21, 16)
0043 #define OCOTP_TIMING_RELAX          12
0044 #define OCOTP_TIMING_RELAX_MASK         GENMASK(15, 12)
0045 #define OCOTP_TIMING_STROBE_PROG        0
0046 #define OCOTP_TIMING_STROBE_PROG_MASK       GENMASK(11, 0)
0047 
0048 #define OCOTP_READ_CTRL_READ_FUSE       0x1
0049 
0050 #define VF610_OCOTP_TIMEOUT         100000
0051 
0052 #define BF(value, field)        (((value) << field) & field##_MASK)
0053 
0054 #define DEF_RELAX               20
0055 
0056 static const int base_to_fuse_addr_mappings[][2] = {
0057     {0x400, 0x00},
0058     {0x410, 0x01},
0059     {0x420, 0x02},
0060     {0x450, 0x05},
0061     {0x4F0, 0x0F},
0062     {0x600, 0x20},
0063     {0x610, 0x21},
0064     {0x620, 0x22},
0065     {0x630, 0x23},
0066     {0x640, 0x24},
0067     {0x650, 0x25},
0068     {0x660, 0x26},
0069     {0x670, 0x27},
0070     {0x6F0, 0x2F},
0071     {0x880, 0x38},
0072     {0x890, 0x39},
0073     {0x8A0, 0x3A},
0074     {0x8B0, 0x3B},
0075     {0x8C0, 0x3C},
0076     {0x8D0, 0x3D},
0077     {0x8E0, 0x3E},
0078     {0x8F0, 0x3F},
0079     {0xC80, 0x78},
0080     {0xC90, 0x79},
0081     {0xCA0, 0x7A},
0082     {0xCB0, 0x7B},
0083     {0xCC0, 0x7C},
0084     {0xCD0, 0x7D},
0085     {0xCE0, 0x7E},
0086     {0xCF0, 0x7F},
0087 };
0088 
0089 struct vf610_ocotp {
0090     void __iomem *base;
0091     struct clk *clk;
0092     struct device *dev;
0093     struct nvmem_device *nvmem;
0094     int timing;
0095 };
0096 
0097 static int vf610_ocotp_wait_busy(void __iomem *base)
0098 {
0099     int timeout = VF610_OCOTP_TIMEOUT;
0100 
0101     while ((readl(base) & OCOTP_CTRL_BUSY) && --timeout)
0102         udelay(10);
0103 
0104     if (!timeout) {
0105         writel(OCOTP_CTRL_ERR, base + OCOTP_CTRL_CLR);
0106         return -ETIMEDOUT;
0107     }
0108 
0109     udelay(10);
0110 
0111     return 0;
0112 }
0113 
0114 static int vf610_ocotp_calculate_timing(struct vf610_ocotp *ocotp_dev)
0115 {
0116     u32 clk_rate;
0117     u32 relax, strobe_read, strobe_prog;
0118     u32 timing;
0119 
0120     clk_rate = clk_get_rate(ocotp_dev->clk);
0121 
0122     /* Refer section OTP read/write timing parameters in TRM */
0123     relax = clk_rate / (1000000000 / DEF_RELAX) - 1;
0124     strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1;
0125     strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1;
0126 
0127     timing = BF(relax, OCOTP_TIMING_RELAX);
0128     timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
0129     timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
0130 
0131     return timing;
0132 }
0133 
0134 static int vf610_get_fuse_address(int base_addr_offset)
0135 {
0136     int i;
0137 
0138     for (i = 0; i < ARRAY_SIZE(base_to_fuse_addr_mappings); i++) {
0139         if (base_to_fuse_addr_mappings[i][0] == base_addr_offset)
0140             return base_to_fuse_addr_mappings[i][1];
0141     }
0142 
0143     return -EINVAL;
0144 }
0145 
0146 static int vf610_ocotp_read(void *context, unsigned int offset,
0147             void *val, size_t bytes)
0148 {
0149     struct vf610_ocotp *ocotp = context;
0150     void __iomem *base = ocotp->base;
0151     u32 reg, *buf = val;
0152     int fuse_addr;
0153     int ret;
0154 
0155     while (bytes > 0) {
0156         fuse_addr = vf610_get_fuse_address(offset);
0157         if (fuse_addr > 0) {
0158             writel(ocotp->timing, base + OCOTP_TIMING);
0159             ret = vf610_ocotp_wait_busy(base + OCOTP_CTRL_REG);
0160             if (ret)
0161                 return ret;
0162 
0163             reg = readl(base + OCOTP_CTRL_REG);
0164             reg &= ~OCOTP_CTRL_ADDR_MASK;
0165             reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
0166             reg |= BF(fuse_addr, OCOTP_CTRL_ADDR);
0167             writel(reg, base + OCOTP_CTRL_REG);
0168 
0169             writel(OCOTP_READ_CTRL_READ_FUSE,
0170                 base + OCOTP_READ_CTRL_REG);
0171             ret = vf610_ocotp_wait_busy(base + OCOTP_CTRL_REG);
0172             if (ret)
0173                 return ret;
0174 
0175             if (readl(base) & OCOTP_CTRL_ERR) {
0176                 dev_dbg(ocotp->dev, "Error reading from fuse address %x\n",
0177                     fuse_addr);
0178                 writel(OCOTP_CTRL_ERR, base + OCOTP_CTRL_CLR);
0179             }
0180 
0181             /*
0182              * In case of error, we do not abort and expect to read
0183              * 0xBADABADA as mentioned by the TRM. We just read this
0184              * value and return.
0185              */
0186             *buf = readl(base + OCOTP_READ_FUSE_DATA);
0187         } else {
0188             *buf = 0;
0189         }
0190 
0191         buf++;
0192         bytes -= 4;
0193         offset += 4;
0194     }
0195 
0196     return 0;
0197 }
0198 
0199 static struct nvmem_config ocotp_config = {
0200     .name = "ocotp",
0201     .stride = 4,
0202     .word_size = 4,
0203     .reg_read = vf610_ocotp_read,
0204 };
0205 
0206 static const struct of_device_id ocotp_of_match[] = {
0207     { .compatible = "fsl,vf610-ocotp", },
0208     {/* sentinel */},
0209 };
0210 MODULE_DEVICE_TABLE(of, ocotp_of_match);
0211 
0212 static int vf610_ocotp_probe(struct platform_device *pdev)
0213 {
0214     struct device *dev = &pdev->dev;
0215     struct resource *res;
0216     struct vf610_ocotp *ocotp_dev;
0217 
0218     ocotp_dev = devm_kzalloc(dev, sizeof(struct vf610_ocotp), GFP_KERNEL);
0219     if (!ocotp_dev)
0220         return -ENOMEM;
0221 
0222     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0223     ocotp_dev->base = devm_ioremap_resource(dev, res);
0224     if (IS_ERR(ocotp_dev->base))
0225         return PTR_ERR(ocotp_dev->base);
0226 
0227     ocotp_dev->clk = devm_clk_get(dev, NULL);
0228     if (IS_ERR(ocotp_dev->clk)) {
0229         dev_err(dev, "failed getting clock, err = %ld\n",
0230             PTR_ERR(ocotp_dev->clk));
0231         return PTR_ERR(ocotp_dev->clk);
0232     }
0233     ocotp_dev->dev = dev;
0234     ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
0235 
0236     ocotp_config.size = resource_size(res);
0237     ocotp_config.priv = ocotp_dev;
0238     ocotp_config.dev = dev;
0239 
0240     ocotp_dev->nvmem = devm_nvmem_register(dev, &ocotp_config);
0241 
0242     return PTR_ERR_OR_ZERO(ocotp_dev->nvmem);
0243 }
0244 
0245 static struct platform_driver vf610_ocotp_driver = {
0246     .probe = vf610_ocotp_probe,
0247     .driver = {
0248         .name = "vf610-ocotp",
0249         .of_match_table = ocotp_of_match,
0250     },
0251 };
0252 module_platform_driver(vf610_ocotp_driver);
0253 MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
0254 MODULE_DESCRIPTION("Vybrid OCOTP driver");
0255 MODULE_LICENSE("GPL v2");