nvmem: rockchip-efuse: add support for rk3288 secure efuse
[firefly-linux-kernel-4.4.55.git] / drivers / nvmem / rockchip-efuse.c
index 92417aec0df61477959036155de2c1ff68d55fcd..c117d071d96772ac6838184867dd4f6b8749fff2 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/rockchip/rockchip_sip.h>
 
 #define RK3288_A_SHIFT         6
 #define RK3288_A_MASK          0x3ff
 #define RK3288_STROBE          BIT(1)
 #define RK3288_CSB             BIT(0)
 
+#define RK3366_A_SHIFT         6
+#define RK3366_A_MASK          0x3ff
+#define RK3366_RDEN            BIT(2)
+#define RK3366_AEN             BIT(1)
+
 #define RK3399_A_SHIFT         16
 #define RK3399_A_MASK          0x3ff
 #define RK3399_NBYTES          4
@@ -50,6 +56,7 @@ struct rockchip_efuse_chip {
        struct device *dev;
        void __iomem *base;
        struct clk *clk;
+       phys_addr_t phys;
 };
 
 static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
@@ -92,6 +99,137 @@ static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
        return 0;
 }
 
+static int rockchip_rk3288_efuse_secure_read(void *context,
+                                            unsigned int offset,
+                                            void *val, size_t bytes)
+{
+       struct rockchip_efuse_chip *efuse = context;
+       u8 *buf = val;
+       u32 wr_val;
+       int ret;
+
+       ret = clk_prepare_enable(efuse->clk);
+       if (ret < 0) {
+               dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+               return ret;
+       }
+
+       sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+                                RK3288_LOAD | RK3288_PGENB);
+       udelay(1);
+       while (bytes--) {
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+                        (~(RK3288_A_MASK << RK3288_A_SHIFT));
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+                        ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT);
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               udelay(1);
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+                        RK3288_STROBE;
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               udelay(1);
+               *buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT);
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+                        (~RK3288_STROBE);
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               udelay(1);
+       }
+
+       /* Switch to standby mode */
+       sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+                                RK3288_PGENB | RK3288_CSB);
+
+       clk_disable_unprepare(efuse->clk);
+
+       return 0;
+}
+
+static int rockchip_rk3366_efuse_read(void *context, unsigned int offset,
+                                     void *val, size_t bytes)
+{
+       struct rockchip_efuse_chip *efuse = context;
+       u8 *buf = val;
+       int ret;
+
+       ret = clk_prepare_enable(efuse->clk);
+       if (ret < 0) {
+               dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+               return ret;
+       }
+
+       writel(RK3366_RDEN, efuse->base + REG_EFUSE_CTRL);
+       udelay(1);
+       while (bytes--) {
+               writel(readl(efuse->base + REG_EFUSE_CTRL) &
+                      (~(RK3366_A_MASK << RK3366_A_SHIFT)),
+                      efuse->base + REG_EFUSE_CTRL);
+               writel(readl(efuse->base + REG_EFUSE_CTRL) |
+                      ((offset++ & RK3366_A_MASK) << RK3366_A_SHIFT),
+                      efuse->base + REG_EFUSE_CTRL);
+               udelay(1);
+               writel(readl(efuse->base + REG_EFUSE_CTRL) |
+                      RK3366_AEN, efuse->base + REG_EFUSE_CTRL);
+               udelay(1);
+               *buf++ = readb(efuse->base + REG_EFUSE_DOUT);
+               writel(readl(efuse->base + REG_EFUSE_CTRL) &
+                      (~RK3366_AEN), efuse->base + REG_EFUSE_CTRL);
+               udelay(1);
+       }
+
+       writel(readl(efuse->base + REG_EFUSE_CTRL) &
+              (~RK3366_RDEN), efuse->base + REG_EFUSE_CTRL);
+
+       clk_disable_unprepare(efuse->clk);
+
+       return 0;
+}
+
+static int rockchip_rk3368_efuse_read(void *context, unsigned int offset,
+                                     void *val, size_t bytes)
+{
+       struct rockchip_efuse_chip *efuse = context;
+       u8 *buf = val;
+       u32 wr_val;
+       int ret;
+
+       ret = clk_prepare_enable(efuse->clk);
+       if (ret < 0) {
+               dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+               return ret;
+       }
+
+       sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+                                RK3288_LOAD | RK3288_PGENB);
+       udelay(1);
+       while (bytes--) {
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+                        (~(RK3288_A_MASK << RK3288_A_SHIFT));
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+                        ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT);
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               udelay(1);
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+                        RK3288_STROBE;
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               udelay(1);
+               *buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT);
+               wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+                        (~RK3288_STROBE);
+               sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+               udelay(1);
+       }
+
+       /* Switch to standby mode */
+       sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+                                RK3288_PGENB | RK3288_CSB);
+
+       clk_disable_unprepare(efuse->clk);
+
+       return 0;
+}
+
 static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
                                      void *val, size_t bytes)
 {
@@ -173,6 +311,18 @@ static const struct of_device_id rockchip_efuse_match[] = {
                .compatible = "rockchip,rk3288-efuse",
                .data = (void *)&rockchip_rk3288_efuse_read,
        },
+       {
+               .compatible = "rockchip,rk3288-secure-efuse",
+               .data = (void *)&rockchip_rk3288_efuse_secure_read,
+       },
+       {
+               .compatible = "rockchip,rk3366-efuse",
+               .data = (void *)&rockchip_rk3366_efuse_read,
+       },
+       {
+               .compatible = "rockchip,rk3368-efuse",
+               .data = (void *)&rockchip_rk3368_efuse_read,
+       },
        {
                .compatible = "rockchip,rk3399-efuse",
                .data = (void *)&rockchip_rk3399_efuse_read,
@@ -201,6 +351,7 @@ static int __init rockchip_efuse_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       efuse->phys = res->start;
        efuse->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(efuse->base))
                return PTR_ERR(efuse->base);