13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Rockchip eFuse Driver 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (c) 2015 Rockchip Electronics Co. Ltd. 63d0407baSopenharmony_ci * Author: Caesar Wang <wxt@rock-chips.com> 73d0407baSopenharmony_ci */ 83d0407baSopenharmony_ci 93d0407baSopenharmony_ci#include <linux/clk.h> 103d0407baSopenharmony_ci#include <linux/clk-provider.h> 113d0407baSopenharmony_ci#include <linux/delay.h> 123d0407baSopenharmony_ci#include <linux/device.h> 133d0407baSopenharmony_ci#include <linux/io.h> 143d0407baSopenharmony_ci#include <linux/module.h> 153d0407baSopenharmony_ci#include <linux/nvmem-provider.h> 163d0407baSopenharmony_ci#include <linux/slab.h> 173d0407baSopenharmony_ci#include <linux/of.h> 183d0407baSopenharmony_ci#include <linux/of_platform.h> 193d0407baSopenharmony_ci#include <linux/platform_device.h> 203d0407baSopenharmony_ci#include <linux/rockchip/rockchip_sip.h> 213d0407baSopenharmony_ci 223d0407baSopenharmony_ci#define T_CSB_P_S 0 233d0407baSopenharmony_ci#define T_PGENB_P_S 0 243d0407baSopenharmony_ci#define T_LOAD_P_S 0 253d0407baSopenharmony_ci#define T_ADDR_P_S 0 263d0407baSopenharmony_ci#define T_STROBE_P_S (0 + 110) /* 1.1us */ 273d0407baSopenharmony_ci#define T_CSB_P_L (0 + 110 + 1000 + 20) /* 200ns */ 283d0407baSopenharmony_ci#define T_PGENB_P_L (0 + 110 + 1000 + 20) 293d0407baSopenharmony_ci#define T_LOAD_P_L (0 + 110 + 1000 + 20) 303d0407baSopenharmony_ci#define T_ADDR_P_L (0 + 110 + 1000 + 20) 313d0407baSopenharmony_ci#define T_STROBE_P_L (0 + 110 + 1000) /* 10us */ 323d0407baSopenharmony_ci#define T_CSB_R_S 0 333d0407baSopenharmony_ci#define T_PGENB_R_S 0 343d0407baSopenharmony_ci#define T_LOAD_R_S 0 353d0407baSopenharmony_ci#define T_ADDR_R_S 2 363d0407baSopenharmony_ci#define T_STROBE_R_S (2 + 3) 373d0407baSopenharmony_ci#define T_CSB_R_L (2 + 3 + 3 + 3) 383d0407baSopenharmony_ci#define T_PGENB_R_L (2 + 3 + 3 + 3) 393d0407baSopenharmony_ci#define T_LOAD_R_L (2 + 3 + 3 + 3) 403d0407baSopenharmony_ci#define T_ADDR_R_L (2 + 3 + 3 + 2) 413d0407baSopenharmony_ci#define T_STROBE_R_L (2 + 3 + 3) 423d0407baSopenharmony_ci 433d0407baSopenharmony_ci#define T_CSB_P 0x28 443d0407baSopenharmony_ci#define T_PGENB_P 0x2c 453d0407baSopenharmony_ci#define T_LOAD_P 0x30 463d0407baSopenharmony_ci#define T_ADDR_P 0x34 473d0407baSopenharmony_ci#define T_STROBE_P 0x38 483d0407baSopenharmony_ci#define T_CSB_R 0x3c 493d0407baSopenharmony_ci#define T_PGENB_R 0x40 503d0407baSopenharmony_ci#define T_LOAD_R 0x44 513d0407baSopenharmony_ci#define T_ADDR_R 0x48 523d0407baSopenharmony_ci#define T_STROBE_R 0x4c 533d0407baSopenharmony_ci 543d0407baSopenharmony_ci#define RK1808_MOD 0x00 553d0407baSopenharmony_ci#define RK1808_INT_STATUS RK3328_INT_STATUS 563d0407baSopenharmony_ci#define RK1808_DOUT RK3328_DOUT 573d0407baSopenharmony_ci#define RK1808_AUTO_CTRL RK3328_AUTO_CTRL 583d0407baSopenharmony_ci#define RK1808_USER_MODE BIT(0) 593d0407baSopenharmony_ci#define RK1808_INT_FINISH RK3328_INT_FINISH 603d0407baSopenharmony_ci#define RK1808_AUTO_ENB RK3328_AUTO_ENB 613d0407baSopenharmony_ci#define RK1808_AUTO_RD RK3328_AUTO_RD 623d0407baSopenharmony_ci#define RK1808_A_SHIFT RK3399_A_SHIFT 633d0407baSopenharmony_ci#define RK1808_A_MASK RK3399_A_MASK 643d0407baSopenharmony_ci#define RK1808_NBYTES RK3399_NBYTES 653d0407baSopenharmony_ci 663d0407baSopenharmony_ci#define RK3128_A_SHIFT 7 673d0407baSopenharmony_ci#define RK3288_A_SHIFT 6 683d0407baSopenharmony_ci#define RK3288_A_MASK 0x3ff 693d0407baSopenharmony_ci#define RK3288_PGENB BIT(3) 703d0407baSopenharmony_ci#define RK3288_LOAD BIT(2) 713d0407baSopenharmony_ci#define RK3288_STROBE BIT(1) 723d0407baSopenharmony_ci#define RK3288_CSB BIT(0) 733d0407baSopenharmony_ci 743d0407baSopenharmony_ci#define RK3328_SECURE_SIZES 96 753d0407baSopenharmony_ci#define RK3328_INT_STATUS 0x0018 763d0407baSopenharmony_ci#define RK3328_DOUT 0x0020 773d0407baSopenharmony_ci#define RK3328_AUTO_CTRL 0x0024 783d0407baSopenharmony_ci#define RK3328_INT_FINISH BIT(0) 793d0407baSopenharmony_ci#define RK3328_AUTO_ENB BIT(0) 803d0407baSopenharmony_ci#define RK3328_AUTO_RD BIT(1) 813d0407baSopenharmony_ci 823d0407baSopenharmony_ci#define RK3399_A_SHIFT 16 833d0407baSopenharmony_ci#define RK3399_A_MASK 0x3ff 843d0407baSopenharmony_ci#define RK3399_NBYTES 4 853d0407baSopenharmony_ci#define RK3399_STROBSFTSEL BIT(9) 863d0407baSopenharmony_ci#define RK3399_RSB BIT(7) 873d0407baSopenharmony_ci#define RK3399_PD BIT(5) 883d0407baSopenharmony_ci#define RK3399_PGENB BIT(3) 893d0407baSopenharmony_ci#define RK3399_LOAD BIT(2) 903d0407baSopenharmony_ci#define RK3399_STROBE BIT(1) 913d0407baSopenharmony_ci#define RK3399_CSB BIT(0) 923d0407baSopenharmony_ci 933d0407baSopenharmony_ci#define REG_EFUSE_CTRL 0x0000 943d0407baSopenharmony_ci#define REG_EFUSE_DOUT 0x0004 953d0407baSopenharmony_ci 963d0407baSopenharmony_cistruct rockchip_efuse_chip { 973d0407baSopenharmony_ci struct device *dev; 983d0407baSopenharmony_ci void __iomem *base; 993d0407baSopenharmony_ci struct clk_bulk_data *clks; 1003d0407baSopenharmony_ci int num_clks; 1013d0407baSopenharmony_ci phys_addr_t phys; 1023d0407baSopenharmony_ci struct mutex mutex; 1033d0407baSopenharmony_ci}; 1043d0407baSopenharmony_ci 1053d0407baSopenharmony_cistatic void rk1808_efuse_timing_init(void __iomem *base) 1063d0407baSopenharmony_ci{ 1073d0407baSopenharmony_ci /* enable auto mode */ 1083d0407baSopenharmony_ci writel(readl(base + RK1808_MOD) & (~RK1808_USER_MODE), base + RK1808_MOD); 1093d0407baSopenharmony_ci 1103d0407baSopenharmony_ci /* setup efuse timing */ 1113d0407baSopenharmony_ci writel((T_CSB_P_S << 16) | T_CSB_P_L, base + T_CSB_P); 1123d0407baSopenharmony_ci writel((T_PGENB_P_S << 16) | T_PGENB_P_L, base + T_PGENB_P); 1133d0407baSopenharmony_ci writel((T_LOAD_P_S << 16) | T_LOAD_P_L, base + T_LOAD_P); 1143d0407baSopenharmony_ci writel((T_ADDR_P_S << 16) | T_ADDR_P_L, base + T_ADDR_P); 1153d0407baSopenharmony_ci writel((T_STROBE_P_S << 16) | T_STROBE_P_L, base + T_STROBE_P); 1163d0407baSopenharmony_ci writel((T_CSB_R_S << 16) | T_CSB_R_L, base + T_CSB_R); 1173d0407baSopenharmony_ci writel((T_PGENB_R_S << 16) | T_PGENB_R_L, base + T_PGENB_R); 1183d0407baSopenharmony_ci writel((T_LOAD_R_S << 16) | T_LOAD_R_L, base + T_LOAD_R); 1193d0407baSopenharmony_ci writel((T_ADDR_R_S << 16) | T_ADDR_R_L, base + T_ADDR_R); 1203d0407baSopenharmony_ci writel((T_STROBE_R_S << 16) | T_STROBE_R_L, base + T_STROBE_R); 1213d0407baSopenharmony_ci} 1223d0407baSopenharmony_ci 1233d0407baSopenharmony_cistatic void rk1808_efuse_timing_deinit(void __iomem *base) 1243d0407baSopenharmony_ci{ 1253d0407baSopenharmony_ci /* disable auto mode */ 1263d0407baSopenharmony_ci writel(readl(base + RK1808_MOD) | RK1808_USER_MODE, base + RK1808_MOD); 1273d0407baSopenharmony_ci 1283d0407baSopenharmony_ci /* clear efuse timing */ 1293d0407baSopenharmony_ci writel(0, base + T_CSB_P); 1303d0407baSopenharmony_ci writel(0, base + T_PGENB_P); 1313d0407baSopenharmony_ci writel(0, base + T_LOAD_P); 1323d0407baSopenharmony_ci writel(0, base + T_ADDR_P); 1333d0407baSopenharmony_ci writel(0, base + T_STROBE_P); 1343d0407baSopenharmony_ci writel(0, base + T_CSB_R); 1353d0407baSopenharmony_ci writel(0, base + T_PGENB_R); 1363d0407baSopenharmony_ci writel(0, base + T_LOAD_R); 1373d0407baSopenharmony_ci writel(0, base + T_ADDR_R); 1383d0407baSopenharmony_ci writel(0, base + T_STROBE_R); 1393d0407baSopenharmony_ci} 1403d0407baSopenharmony_ci 1413d0407baSopenharmony_cistatic int rockchip_rk1808_efuse_read(void *context, unsigned int offset, void *val, size_t bytes) 1423d0407baSopenharmony_ci{ 1433d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 1443d0407baSopenharmony_ci unsigned int addr_start, addr_end, addr_offset, addr_len; 1453d0407baSopenharmony_ci u32 out_value, status; 1463d0407baSopenharmony_ci u8 *buf; 1473d0407baSopenharmony_ci int ret, i = 0; 1483d0407baSopenharmony_ci 1493d0407baSopenharmony_ci mutex_lock(&efuse->mutex); 1503d0407baSopenharmony_ci 1513d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 1523d0407baSopenharmony_ci if (ret < 0) { 1533d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 1543d0407baSopenharmony_ci goto out; 1553d0407baSopenharmony_ci } 1563d0407baSopenharmony_ci 1573d0407baSopenharmony_ci addr_start = rounddown(offset, RK1808_NBYTES) / RK1808_NBYTES; 1583d0407baSopenharmony_ci addr_end = roundup(offset + bytes, RK1808_NBYTES) / RK1808_NBYTES; 1593d0407baSopenharmony_ci addr_offset = offset % RK1808_NBYTES; 1603d0407baSopenharmony_ci addr_len = addr_end - addr_start; 1613d0407baSopenharmony_ci 1623d0407baSopenharmony_ci buf = kzalloc(sizeof(*buf) * addr_len * RK1808_NBYTES, GFP_KERNEL); 1633d0407baSopenharmony_ci if (!buf) { 1643d0407baSopenharmony_ci ret = -ENOMEM; 1653d0407baSopenharmony_ci goto nomem; 1663d0407baSopenharmony_ci } 1673d0407baSopenharmony_ci 1683d0407baSopenharmony_ci rk1808_efuse_timing_init(efuse->base); 1693d0407baSopenharmony_ci 1703d0407baSopenharmony_ci while (addr_len--) { 1713d0407baSopenharmony_ci writel(RK1808_AUTO_RD | RK1808_AUTO_ENB | ((addr_start++ & RK1808_A_MASK) << RK1808_A_SHIFT), 1723d0407baSopenharmony_ci efuse->base + RK1808_AUTO_CTRL); 1733d0407baSopenharmony_ci udelay(0x2); 1743d0407baSopenharmony_ci status = readl(efuse->base + RK1808_INT_STATUS); 1753d0407baSopenharmony_ci if (!(status & RK1808_INT_FINISH)) { 1763d0407baSopenharmony_ci ret = -EIO; 1773d0407baSopenharmony_ci goto err; 1783d0407baSopenharmony_ci } 1793d0407baSopenharmony_ci out_value = readl(efuse->base + RK1808_DOUT); 1803d0407baSopenharmony_ci writel(RK1808_INT_FINISH, efuse->base + RK1808_INT_STATUS); 1813d0407baSopenharmony_ci 1823d0407baSopenharmony_ci memcpy(&buf[i], &out_value, RK1808_NBYTES); 1833d0407baSopenharmony_ci i += RK1808_NBYTES; 1843d0407baSopenharmony_ci } 1853d0407baSopenharmony_ci memcpy(val, buf + addr_offset, bytes); 1863d0407baSopenharmony_cierr: 1873d0407baSopenharmony_ci rk1808_efuse_timing_deinit(efuse->base); 1883d0407baSopenharmony_ci kfree(buf); 1893d0407baSopenharmony_cinomem: 1903d0407baSopenharmony_ci rk1808_efuse_timing_deinit(efuse->base); 1913d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 1923d0407baSopenharmony_ciout: 1933d0407baSopenharmony_ci mutex_unlock(&efuse->mutex); 1943d0407baSopenharmony_ci 1953d0407baSopenharmony_ci return ret; 1963d0407baSopenharmony_ci} 1973d0407baSopenharmony_ci 1983d0407baSopenharmony_cistatic int rockchip_rk3128_efuse_read(void *context, unsigned int offset, void *val, size_t bytes) 1993d0407baSopenharmony_ci{ 2003d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 2013d0407baSopenharmony_ci u8 *buf = val; 2023d0407baSopenharmony_ci int ret; 2033d0407baSopenharmony_ci 2043d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 2053d0407baSopenharmony_ci if (ret < 0) { 2063d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 2073d0407baSopenharmony_ci return ret; 2083d0407baSopenharmony_ci } 2093d0407baSopenharmony_ci 2103d0407baSopenharmony_ci writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); 2113d0407baSopenharmony_ci udelay(1); 2123d0407baSopenharmony_ci while (bytes--) { 2133d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & (~(RK3288_A_MASK << RK3128_A_SHIFT)), 2143d0407baSopenharmony_ci efuse->base + REG_EFUSE_CTRL); 2153d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | ((offset++ & RK3288_A_MASK) << RK3128_A_SHIFT), 2163d0407baSopenharmony_ci efuse->base + REG_EFUSE_CTRL); 2173d0407baSopenharmony_ci udelay(1); 2183d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); 2193d0407baSopenharmony_ci udelay(1); 2203d0407baSopenharmony_ci *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 2213d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); 2223d0407baSopenharmony_ci udelay(1); 2233d0407baSopenharmony_ci } 2243d0407baSopenharmony_ci 2253d0407baSopenharmony_ci /* Switch to standby mode */ 2263d0407baSopenharmony_ci writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); 2273d0407baSopenharmony_ci 2283d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_ci return 0; 2313d0407baSopenharmony_ci} 2323d0407baSopenharmony_ci 2333d0407baSopenharmony_cistatic int rockchip_rk3288_efuse_read(void *context, unsigned int offset, void *val, size_t bytes) 2343d0407baSopenharmony_ci{ 2353d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 2363d0407baSopenharmony_ci u8 *buf = val; 2373d0407baSopenharmony_ci int ret; 2383d0407baSopenharmony_ci 2393d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 2403d0407baSopenharmony_ci if (ret < 0) { 2413d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 2423d0407baSopenharmony_ci return ret; 2433d0407baSopenharmony_ci } 2443d0407baSopenharmony_ci 2453d0407baSopenharmony_ci writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); 2463d0407baSopenharmony_ci udelay(1); 2473d0407baSopenharmony_ci while (bytes--) { 2483d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & (~(RK3288_A_MASK << RK3288_A_SHIFT)), 2493d0407baSopenharmony_ci efuse->base + REG_EFUSE_CTRL); 2503d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 2513d0407baSopenharmony_ci efuse->base + REG_EFUSE_CTRL); 2523d0407baSopenharmony_ci udelay(1); 2533d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); 2543d0407baSopenharmony_ci udelay(1); 2553d0407baSopenharmony_ci *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 2563d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); 2573d0407baSopenharmony_ci udelay(1); 2583d0407baSopenharmony_ci } 2593d0407baSopenharmony_ci 2603d0407baSopenharmony_ci /* Switch to standby mode */ 2613d0407baSopenharmony_ci writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); 2623d0407baSopenharmony_ci 2633d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 2643d0407baSopenharmony_ci 2653d0407baSopenharmony_ci return 0; 2663d0407baSopenharmony_ci} 2673d0407baSopenharmony_ci 2683d0407baSopenharmony_cistatic int rockchip_rk3288_efuse_secure_read(void *context, unsigned int offset, void *val, size_t bytes) 2693d0407baSopenharmony_ci{ 2703d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 2713d0407baSopenharmony_ci u8 *buf = val; 2723d0407baSopenharmony_ci u32 wr_val; 2733d0407baSopenharmony_ci int ret; 2743d0407baSopenharmony_ci 2753d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 2763d0407baSopenharmony_ci if (ret < 0) { 2773d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 2783d0407baSopenharmony_ci return ret; 2793d0407baSopenharmony_ci } 2803d0407baSopenharmony_ci 2813d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, RK3288_LOAD | RK3288_PGENB); 2823d0407baSopenharmony_ci udelay(1); 2833d0407baSopenharmony_ci while (bytes--) { 2843d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & (~(RK3288_A_MASK << RK3288_A_SHIFT)); 2853d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 2863d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT); 2873d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 2883d0407baSopenharmony_ci udelay(1); 2893d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | RK3288_STROBE; 2903d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 2913d0407baSopenharmony_ci udelay(1); 2923d0407baSopenharmony_ci *buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT); 2933d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & (~RK3288_STROBE); 2943d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 2953d0407baSopenharmony_ci udelay(1); 2963d0407baSopenharmony_ci } 2973d0407baSopenharmony_ci 2983d0407baSopenharmony_ci /* Switch to standby mode */ 2993d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, RK3288_PGENB | RK3288_CSB); 3003d0407baSopenharmony_ci 3013d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 3023d0407baSopenharmony_ci 3033d0407baSopenharmony_ci return 0; 3043d0407baSopenharmony_ci} 3053d0407baSopenharmony_ci 3063d0407baSopenharmony_cistatic int rockchip_rk3328_efuse_read(void *context, unsigned int offset, void *val, size_t bytes) 3073d0407baSopenharmony_ci{ 3083d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 3093d0407baSopenharmony_ci unsigned int addr_start, addr_end, addr_offset, addr_len; 3103d0407baSopenharmony_ci u32 out_value, status; 3113d0407baSopenharmony_ci u8 *buf; 3123d0407baSopenharmony_ci int ret, i = 0; 3133d0407baSopenharmony_ci 3143d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 3153d0407baSopenharmony_ci if (ret < 0) { 3163d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 3173d0407baSopenharmony_ci return ret; 3183d0407baSopenharmony_ci } 3193d0407baSopenharmony_ci 3203d0407baSopenharmony_ci /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ 3213d0407baSopenharmony_ci offset += RK3328_SECURE_SIZES; 3223d0407baSopenharmony_ci addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 3233d0407baSopenharmony_ci addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 3243d0407baSopenharmony_ci addr_offset = offset % RK3399_NBYTES; 3253d0407baSopenharmony_ci addr_len = addr_end - addr_start; 3263d0407baSopenharmony_ci 3273d0407baSopenharmony_ci buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), GFP_KERNEL); 3283d0407baSopenharmony_ci if (!buf) { 3293d0407baSopenharmony_ci ret = -ENOMEM; 3303d0407baSopenharmony_ci goto nomem; 3313d0407baSopenharmony_ci } 3323d0407baSopenharmony_ci 3333d0407baSopenharmony_ci while (addr_len--) { 3343d0407baSopenharmony_ci writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 3353d0407baSopenharmony_ci efuse->base + RK3328_AUTO_CTRL); 3363d0407baSopenharmony_ci udelay(0x4); 3373d0407baSopenharmony_ci status = readl(efuse->base + RK3328_INT_STATUS); 3383d0407baSopenharmony_ci if (!(status & RK3328_INT_FINISH)) { 3393d0407baSopenharmony_ci ret = -EIO; 3403d0407baSopenharmony_ci goto err; 3413d0407baSopenharmony_ci } 3423d0407baSopenharmony_ci out_value = readl(efuse->base + RK3328_DOUT); 3433d0407baSopenharmony_ci writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS); 3443d0407baSopenharmony_ci 3453d0407baSopenharmony_ci memcpy(&buf[i], &out_value, RK3399_NBYTES); 3463d0407baSopenharmony_ci i += RK3399_NBYTES; 3473d0407baSopenharmony_ci } 3483d0407baSopenharmony_ci 3493d0407baSopenharmony_ci memcpy(val, buf + addr_offset, bytes); 3503d0407baSopenharmony_cierr: 3513d0407baSopenharmony_ci kfree(buf); 3523d0407baSopenharmony_cinomem: 3533d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 3543d0407baSopenharmony_ci 3553d0407baSopenharmony_ci return ret; 3563d0407baSopenharmony_ci} 3573d0407baSopenharmony_ci 3583d0407baSopenharmony_cistatic int rockchip_rk3368_efuse_read(void *context, unsigned int offset, void *val, size_t bytes) 3593d0407baSopenharmony_ci{ 3603d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 3613d0407baSopenharmony_ci u8 *buf = val; 3623d0407baSopenharmony_ci u32 wr_val; 3633d0407baSopenharmony_ci int ret; 3643d0407baSopenharmony_ci 3653d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 3663d0407baSopenharmony_ci if (ret < 0) { 3673d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 3683d0407baSopenharmony_ci return ret; 3693d0407baSopenharmony_ci } 3703d0407baSopenharmony_ci 3713d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, RK3288_LOAD | RK3288_PGENB); 3723d0407baSopenharmony_ci udelay(1); 3733d0407baSopenharmony_ci while (bytes--) { 3743d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & (~(RK3288_A_MASK << RK3288_A_SHIFT)); 3753d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 3763d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT); 3773d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 3783d0407baSopenharmony_ci udelay(1); 3793d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) | RK3288_STROBE; 3803d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 3813d0407baSopenharmony_ci udelay(1); 3823d0407baSopenharmony_ci *buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT); 3833d0407baSopenharmony_ci wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) & (~RK3288_STROBE); 3843d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val); 3853d0407baSopenharmony_ci udelay(1); 3863d0407baSopenharmony_ci } 3873d0407baSopenharmony_ci 3883d0407baSopenharmony_ci /* Switch to standby mode */ 3893d0407baSopenharmony_ci sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, RK3288_PGENB | RK3288_CSB); 3903d0407baSopenharmony_ci 3913d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 3923d0407baSopenharmony_ci 3933d0407baSopenharmony_ci return 0; 3943d0407baSopenharmony_ci} 3953d0407baSopenharmony_ci 3963d0407baSopenharmony_cistatic int rockchip_rk3399_efuse_read(void *context, unsigned int offset, void *val, size_t bytes) 3973d0407baSopenharmony_ci{ 3983d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse = context; 3993d0407baSopenharmony_ci unsigned int addr_start, addr_end, addr_offset, addr_len; 4003d0407baSopenharmony_ci u32 out_value; 4013d0407baSopenharmony_ci u8 *buf; 4023d0407baSopenharmony_ci int ret, i = 0; 4033d0407baSopenharmony_ci 4043d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks); 4053d0407baSopenharmony_ci if (ret < 0) { 4063d0407baSopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 4073d0407baSopenharmony_ci return ret; 4083d0407baSopenharmony_ci } 4093d0407baSopenharmony_ci 4103d0407baSopenharmony_ci addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 4113d0407baSopenharmony_ci addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 4123d0407baSopenharmony_ci addr_offset = offset % RK3399_NBYTES; 4133d0407baSopenharmony_ci addr_len = addr_end - addr_start; 4143d0407baSopenharmony_ci 4153d0407baSopenharmony_ci buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), GFP_KERNEL); 4163d0407baSopenharmony_ci if (!buf) { 4173d0407baSopenharmony_ci ret = -ENOMEM; 4183d0407baSopenharmony_ci goto disable_clks; 4193d0407baSopenharmony_ci } 4203d0407baSopenharmony_ci 4213d0407baSopenharmony_ci writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, efuse->base + REG_EFUSE_CTRL); 4223d0407baSopenharmony_ci udelay(1); 4233d0407baSopenharmony_ci while (addr_len--) { 4243d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE | ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 4253d0407baSopenharmony_ci efuse->base + REG_EFUSE_CTRL); 4263d0407baSopenharmony_ci udelay(1); 4273d0407baSopenharmony_ci out_value = readl(efuse->base + REG_EFUSE_DOUT); 4283d0407baSopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE), efuse->base + REG_EFUSE_CTRL); 4293d0407baSopenharmony_ci udelay(1); 4303d0407baSopenharmony_ci 4313d0407baSopenharmony_ci memcpy(&buf[i], &out_value, RK3399_NBYTES); 4323d0407baSopenharmony_ci i += RK3399_NBYTES; 4333d0407baSopenharmony_ci } 4343d0407baSopenharmony_ci 4353d0407baSopenharmony_ci /* Switch to standby mode */ 4363d0407baSopenharmony_ci writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); 4373d0407baSopenharmony_ci 4383d0407baSopenharmony_ci memcpy(val, buf + addr_offset, bytes); 4393d0407baSopenharmony_ci 4403d0407baSopenharmony_ci kfree(buf); 4413d0407baSopenharmony_ci 4423d0407baSopenharmony_cidisable_clks: 4433d0407baSopenharmony_ci clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks); 4443d0407baSopenharmony_ci 4453d0407baSopenharmony_ci return ret; 4463d0407baSopenharmony_ci} 4473d0407baSopenharmony_ci 4483d0407baSopenharmony_cistatic struct nvmem_config econfig = { 4493d0407baSopenharmony_ci .name = "rockchip-efuse", 4503d0407baSopenharmony_ci .stride = 1, 4513d0407baSopenharmony_ci .word_size = 1, 4523d0407baSopenharmony_ci .read_only = true, 4533d0407baSopenharmony_ci}; 4543d0407baSopenharmony_ci 4553d0407baSopenharmony_cistatic const struct of_device_id rockchip_efuse_match[] = { 4563d0407baSopenharmony_ci /* deprecated but kept around for dts binding compatibility */ 4573d0407baSopenharmony_ci { 4583d0407baSopenharmony_ci .compatible = "rockchip,rk1808-efuse", 4593d0407baSopenharmony_ci .data = (void *)&rockchip_rk1808_efuse_read, 4603d0407baSopenharmony_ci }, 4613d0407baSopenharmony_ci { 4623d0407baSopenharmony_ci .compatible = "rockchip,rockchip-efuse", 4633d0407baSopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 4643d0407baSopenharmony_ci }, 4653d0407baSopenharmony_ci { 4663d0407baSopenharmony_ci .compatible = "rockchip,rk3066a-efuse", 4673d0407baSopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 4683d0407baSopenharmony_ci }, 4693d0407baSopenharmony_ci { 4703d0407baSopenharmony_ci .compatible = "rockchip,rk3128-efuse", 4713d0407baSopenharmony_ci .data = (void *)&rockchip_rk3128_efuse_read, 4723d0407baSopenharmony_ci }, 4733d0407baSopenharmony_ci { 4743d0407baSopenharmony_ci .compatible = "rockchip,rk3188-efuse", 4753d0407baSopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 4763d0407baSopenharmony_ci }, 4773d0407baSopenharmony_ci { 4783d0407baSopenharmony_ci .compatible = "rockchip,rk3228-efuse", 4793d0407baSopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 4803d0407baSopenharmony_ci }, 4813d0407baSopenharmony_ci { 4823d0407baSopenharmony_ci .compatible = "rockchip,rk3288-efuse", 4833d0407baSopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 4843d0407baSopenharmony_ci }, 4853d0407baSopenharmony_ci { 4863d0407baSopenharmony_ci .compatible = "rockchip,rk3288-secure-efuse", 4873d0407baSopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_secure_read, 4883d0407baSopenharmony_ci }, 4893d0407baSopenharmony_ci { 4903d0407baSopenharmony_ci .compatible = "rockchip,rk3328-efuse", 4913d0407baSopenharmony_ci .data = (void *)&rockchip_rk3328_efuse_read, 4923d0407baSopenharmony_ci }, 4933d0407baSopenharmony_ci { 4943d0407baSopenharmony_ci .compatible = "rockchip,rk3368-efuse", 4953d0407baSopenharmony_ci .data = (void *)&rockchip_rk3368_efuse_read, 4963d0407baSopenharmony_ci }, 4973d0407baSopenharmony_ci { 4983d0407baSopenharmony_ci .compatible = "rockchip,rk3399-efuse", 4993d0407baSopenharmony_ci .data = (void *)&rockchip_rk3399_efuse_read, 5003d0407baSopenharmony_ci }, 5013d0407baSopenharmony_ci {}, 5023d0407baSopenharmony_ci}; 5033d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_efuse_match); 5043d0407baSopenharmony_ci 5053d0407baSopenharmony_cistatic int rockchip_efuse_probe(struct platform_device *pdev) 5063d0407baSopenharmony_ci{ 5073d0407baSopenharmony_ci struct resource *res; 5083d0407baSopenharmony_ci struct nvmem_device *nvmem; 5093d0407baSopenharmony_ci struct rockchip_efuse_chip *efuse; 5103d0407baSopenharmony_ci const void *data; 5113d0407baSopenharmony_ci struct device *dev = &pdev->dev; 5123d0407baSopenharmony_ci 5133d0407baSopenharmony_ci data = of_device_get_match_data(dev); 5143d0407baSopenharmony_ci if (!data) { 5153d0407baSopenharmony_ci dev_err(dev, "failed to get match data\n"); 5163d0407baSopenharmony_ci return -EINVAL; 5173d0407baSopenharmony_ci } 5183d0407baSopenharmony_ci 5193d0407baSopenharmony_ci efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip), GFP_KERNEL); 5203d0407baSopenharmony_ci if (!efuse) { 5213d0407baSopenharmony_ci return -ENOMEM; 5223d0407baSopenharmony_ci } 5233d0407baSopenharmony_ci 5243d0407baSopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5253d0407baSopenharmony_ci efuse->phys = res->start; 5263d0407baSopenharmony_ci efuse->base = devm_ioremap_resource(dev, res); 5273d0407baSopenharmony_ci if (IS_ERR(efuse->base)) { 5283d0407baSopenharmony_ci return PTR_ERR(efuse->base); 5293d0407baSopenharmony_ci } 5303d0407baSopenharmony_ci 5313d0407baSopenharmony_ci efuse->num_clks = devm_clk_bulk_get_all(dev, &efuse->clks); 5323d0407baSopenharmony_ci if (efuse->num_clks < 1) { 5333d0407baSopenharmony_ci return -ENODEV; 5343d0407baSopenharmony_ci } 5353d0407baSopenharmony_ci 5363d0407baSopenharmony_ci mutex_init(&efuse->mutex); 5373d0407baSopenharmony_ci 5383d0407baSopenharmony_ci efuse->dev = dev; 5393d0407baSopenharmony_ci if (of_property_read_u32(dev->of_node, "rockchip,efuse-size", &econfig.size)) { 5403d0407baSopenharmony_ci econfig.size = resource_size(res); 5413d0407baSopenharmony_ci } 5423d0407baSopenharmony_ci econfig.reg_read = data; 5433d0407baSopenharmony_ci econfig.priv = efuse; 5443d0407baSopenharmony_ci econfig.dev = efuse->dev; 5453d0407baSopenharmony_ci nvmem = devm_nvmem_register(dev, &econfig); 5463d0407baSopenharmony_ci 5473d0407baSopenharmony_ci return PTR_ERR_OR_ZERO(nvmem); 5483d0407baSopenharmony_ci} 5493d0407baSopenharmony_ci 5503d0407baSopenharmony_cistatic struct platform_driver rockchip_efuse_driver = { 5513d0407baSopenharmony_ci .probe = rockchip_efuse_probe, 5523d0407baSopenharmony_ci .driver = 5533d0407baSopenharmony_ci { 5543d0407baSopenharmony_ci .name = "rockchip-efuse", 5553d0407baSopenharmony_ci .of_match_table = rockchip_efuse_match, 5563d0407baSopenharmony_ci }, 5573d0407baSopenharmony_ci}; 5583d0407baSopenharmony_ci 5593d0407baSopenharmony_cistatic int __init rockchip_efuse_init(void) 5603d0407baSopenharmony_ci{ 5613d0407baSopenharmony_ci int ret; 5623d0407baSopenharmony_ci 5633d0407baSopenharmony_ci ret = platform_driver_register(&rockchip_efuse_driver); 5643d0407baSopenharmony_ci if (ret) { 5653d0407baSopenharmony_ci pr_err("failed to register efuse driver\n"); 5663d0407baSopenharmony_ci return ret; 5673d0407baSopenharmony_ci } 5683d0407baSopenharmony_ci 5693d0407baSopenharmony_ci return 0; 5703d0407baSopenharmony_ci} 5713d0407baSopenharmony_ci 5723d0407baSopenharmony_cistatic void __exit rockchip_efuse_exit(void) 5733d0407baSopenharmony_ci{ 5743d0407baSopenharmony_ci return platform_driver_unregister(&rockchip_efuse_driver); 5753d0407baSopenharmony_ci} 5763d0407baSopenharmony_ci 5773d0407baSopenharmony_cisubsys_initcall(rockchip_efuse_init); 5783d0407baSopenharmony_cimodule_exit(rockchip_efuse_exit); 5793d0407baSopenharmony_ci 5803d0407baSopenharmony_ciMODULE_DESCRIPTION("rockchip_efuse driver"); 5813d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 582