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