Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Rockchip eFuse Driver
0004  *
0005  * Copyright (c) 2015 Rockchip Electronics Co. Ltd.
0006  * Author: Caesar Wang <wxt@rock-chips.com>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/delay.h>
0011 #include <linux/device.h>
0012 #include <linux/io.h>
0013 #include <linux/module.h>
0014 #include <linux/nvmem-provider.h>
0015 #include <linux/slab.h>
0016 #include <linux/of.h>
0017 #include <linux/of_platform.h>
0018 #include <linux/platform_device.h>
0019 
0020 #define RK3288_A_SHIFT      6
0021 #define RK3288_A_MASK       0x3ff
0022 #define RK3288_PGENB        BIT(3)
0023 #define RK3288_LOAD     BIT(2)
0024 #define RK3288_STROBE       BIT(1)
0025 #define RK3288_CSB      BIT(0)
0026 
0027 #define RK3328_SECURE_SIZES 96
0028 #define RK3328_INT_STATUS   0x0018
0029 #define RK3328_DOUT     0x0020
0030 #define RK3328_AUTO_CTRL    0x0024
0031 #define RK3328_INT_FINISH   BIT(0)
0032 #define RK3328_AUTO_ENB     BIT(0)
0033 #define RK3328_AUTO_RD      BIT(1)
0034 
0035 #define RK3399_A_SHIFT      16
0036 #define RK3399_A_MASK       0x3ff
0037 #define RK3399_NBYTES       4
0038 #define RK3399_STROBSFTSEL  BIT(9)
0039 #define RK3399_RSB      BIT(7)
0040 #define RK3399_PD       BIT(5)
0041 #define RK3399_PGENB        BIT(3)
0042 #define RK3399_LOAD     BIT(2)
0043 #define RK3399_STROBE       BIT(1)
0044 #define RK3399_CSB      BIT(0)
0045 
0046 #define REG_EFUSE_CTRL      0x0000
0047 #define REG_EFUSE_DOUT      0x0004
0048 
0049 struct rockchip_efuse_chip {
0050     struct device *dev;
0051     void __iomem *base;
0052     struct clk *clk;
0053 };
0054 
0055 static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
0056                       void *val, size_t bytes)
0057 {
0058     struct rockchip_efuse_chip *efuse = context;
0059     u8 *buf = val;
0060     int ret;
0061 
0062     ret = clk_prepare_enable(efuse->clk);
0063     if (ret < 0) {
0064         dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
0065         return ret;
0066     }
0067 
0068     writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL);
0069     udelay(1);
0070     while (bytes--) {
0071         writel(readl(efuse->base + REG_EFUSE_CTRL) &
0072                  (~(RK3288_A_MASK << RK3288_A_SHIFT)),
0073                  efuse->base + REG_EFUSE_CTRL);
0074         writel(readl(efuse->base + REG_EFUSE_CTRL) |
0075                  ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT),
0076                  efuse->base + REG_EFUSE_CTRL);
0077         udelay(1);
0078         writel(readl(efuse->base + REG_EFUSE_CTRL) |
0079                  RK3288_STROBE, efuse->base + REG_EFUSE_CTRL);
0080         udelay(1);
0081         *buf++ = readb(efuse->base + REG_EFUSE_DOUT);
0082         writel(readl(efuse->base + REG_EFUSE_CTRL) &
0083                (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL);
0084         udelay(1);
0085     }
0086 
0087     /* Switch to standby mode */
0088     writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL);
0089 
0090     clk_disable_unprepare(efuse->clk);
0091 
0092     return 0;
0093 }
0094 
0095 static int rockchip_rk3328_efuse_read(void *context, unsigned int offset,
0096                       void *val, size_t bytes)
0097 {
0098     struct rockchip_efuse_chip *efuse = context;
0099     unsigned int addr_start, addr_end, addr_offset, addr_len;
0100     u32 out_value, status;
0101     u8 *buf;
0102     int ret, i = 0;
0103 
0104     ret = clk_prepare_enable(efuse->clk);
0105     if (ret < 0) {
0106         dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
0107         return ret;
0108     }
0109 
0110     /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */
0111     offset += RK3328_SECURE_SIZES;
0112     addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES;
0113     addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES;
0114     addr_offset = offset % RK3399_NBYTES;
0115     addr_len = addr_end - addr_start;
0116 
0117     buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)),
0118               GFP_KERNEL);
0119     if (!buf) {
0120         ret = -ENOMEM;
0121         goto nomem;
0122     }
0123 
0124     while (addr_len--) {
0125         writel(RK3328_AUTO_RD | RK3328_AUTO_ENB |
0126                ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),
0127                efuse->base + RK3328_AUTO_CTRL);
0128         udelay(4);
0129         status = readl(efuse->base + RK3328_INT_STATUS);
0130         if (!(status & RK3328_INT_FINISH)) {
0131             ret = -EIO;
0132             goto err;
0133         }
0134         out_value = readl(efuse->base + RK3328_DOUT);
0135         writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS);
0136 
0137         memcpy(&buf[i], &out_value, RK3399_NBYTES);
0138         i += RK3399_NBYTES;
0139     }
0140 
0141     memcpy(val, buf + addr_offset, bytes);
0142 err:
0143     kfree(buf);
0144 nomem:
0145     clk_disable_unprepare(efuse->clk);
0146 
0147     return ret;
0148 }
0149 
0150 static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
0151                       void *val, size_t bytes)
0152 {
0153     struct rockchip_efuse_chip *efuse = context;
0154     unsigned int addr_start, addr_end, addr_offset, addr_len;
0155     u32 out_value;
0156     u8 *buf;
0157     int ret, i = 0;
0158 
0159     ret = clk_prepare_enable(efuse->clk);
0160     if (ret < 0) {
0161         dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
0162         return ret;
0163     }
0164 
0165     addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES;
0166     addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES;
0167     addr_offset = offset % RK3399_NBYTES;
0168     addr_len = addr_end - addr_start;
0169 
0170     buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)),
0171               GFP_KERNEL);
0172     if (!buf) {
0173         clk_disable_unprepare(efuse->clk);
0174         return -ENOMEM;
0175     }
0176 
0177     writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
0178            efuse->base + REG_EFUSE_CTRL);
0179     udelay(1);
0180     while (addr_len--) {
0181         writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE |
0182                ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),
0183                efuse->base + REG_EFUSE_CTRL);
0184         udelay(1);
0185         out_value = readl(efuse->base + REG_EFUSE_DOUT);
0186         writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE),
0187                efuse->base + REG_EFUSE_CTRL);
0188         udelay(1);
0189 
0190         memcpy(&buf[i], &out_value, RK3399_NBYTES);
0191         i += RK3399_NBYTES;
0192     }
0193 
0194     /* Switch to standby mode */
0195     writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL);
0196 
0197     memcpy(val, buf + addr_offset, bytes);
0198 
0199     kfree(buf);
0200 
0201     clk_disable_unprepare(efuse->clk);
0202 
0203     return 0;
0204 }
0205 
0206 static struct nvmem_config econfig = {
0207     .name = "rockchip-efuse",
0208     .stride = 1,
0209     .word_size = 1,
0210     .read_only = true,
0211 };
0212 
0213 static const struct of_device_id rockchip_efuse_match[] = {
0214     /* deprecated but kept around for dts binding compatibility */
0215     {
0216         .compatible = "rockchip,rockchip-efuse",
0217         .data = (void *)&rockchip_rk3288_efuse_read,
0218     },
0219     {
0220         .compatible = "rockchip,rk3066a-efuse",
0221         .data = (void *)&rockchip_rk3288_efuse_read,
0222     },
0223     {
0224         .compatible = "rockchip,rk3188-efuse",
0225         .data = (void *)&rockchip_rk3288_efuse_read,
0226     },
0227     {
0228         .compatible = "rockchip,rk3228-efuse",
0229         .data = (void *)&rockchip_rk3288_efuse_read,
0230     },
0231     {
0232         .compatible = "rockchip,rk3288-efuse",
0233         .data = (void *)&rockchip_rk3288_efuse_read,
0234     },
0235     {
0236         .compatible = "rockchip,rk3368-efuse",
0237         .data = (void *)&rockchip_rk3288_efuse_read,
0238     },
0239     {
0240         .compatible = "rockchip,rk3328-efuse",
0241         .data = (void *)&rockchip_rk3328_efuse_read,
0242     },
0243     {
0244         .compatible = "rockchip,rk3399-efuse",
0245         .data = (void *)&rockchip_rk3399_efuse_read,
0246     },
0247     { /* sentinel */},
0248 };
0249 MODULE_DEVICE_TABLE(of, rockchip_efuse_match);
0250 
0251 static int rockchip_efuse_probe(struct platform_device *pdev)
0252 {
0253     struct resource *res;
0254     struct nvmem_device *nvmem;
0255     struct rockchip_efuse_chip *efuse;
0256     const void *data;
0257     struct device *dev = &pdev->dev;
0258 
0259     data = of_device_get_match_data(dev);
0260     if (!data) {
0261         dev_err(dev, "failed to get match data\n");
0262         return -EINVAL;
0263     }
0264 
0265     efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip),
0266                  GFP_KERNEL);
0267     if (!efuse)
0268         return -ENOMEM;
0269 
0270     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0271     efuse->base = devm_ioremap_resource(dev, res);
0272     if (IS_ERR(efuse->base))
0273         return PTR_ERR(efuse->base);
0274 
0275     efuse->clk = devm_clk_get(dev, "pclk_efuse");
0276     if (IS_ERR(efuse->clk))
0277         return PTR_ERR(efuse->clk);
0278 
0279     efuse->dev = dev;
0280     if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",
0281                  &econfig.size))
0282         econfig.size = resource_size(res);
0283     econfig.reg_read = data;
0284     econfig.priv = efuse;
0285     econfig.dev = efuse->dev;
0286     nvmem = devm_nvmem_register(dev, &econfig);
0287 
0288     return PTR_ERR_OR_ZERO(nvmem);
0289 }
0290 
0291 static struct platform_driver rockchip_efuse_driver = {
0292     .probe = rockchip_efuse_probe,
0293     .driver = {
0294         .name = "rockchip-efuse",
0295         .of_match_table = rockchip_efuse_match,
0296     },
0297 };
0298 
0299 module_platform_driver(rockchip_efuse_driver);
0300 MODULE_DESCRIPTION("rockchip_efuse driver");
0301 MODULE_LICENSE("GPL v2");