/* 
 | 
 * eFuse driver for Rockchip devices 
 | 
 * 
 | 
 * Copyright 2017, Theobroma Systems Design und Consulting GmbH 
 | 
 * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com> 
 | 
 * 
 | 
 * SPDX-License-Identifier:    GPL-2.0+ 
 | 
 */ 
 | 
  
 | 
#include <common.h> 
 | 
#include <asm/io.h> 
 | 
#include <command.h> 
 | 
#include <display_options.h> 
 | 
#include <dm.h> 
 | 
#include <linux/arm-smccc.h> 
 | 
#include <linux/bitops.h> 
 | 
#include <linux/delay.h> 
 | 
#include <misc.h> 
 | 
#include <asm/arch/rockchip_smccc.h> 
 | 
  
 | 
#define T_CSB_P_S        0 
 | 
#define T_PGENB_P_S        0 
 | 
#define T_LOAD_P_S        0 
 | 
#define T_ADDR_P_S        0 
 | 
#define T_STROBE_P_S        (0 + 110) /* 1.1us */ 
 | 
#define T_CSB_P_L        (0 + 110 + 1000 + 20) /* 200ns */ 
 | 
#define T_PGENB_P_L        (0 + 110 + 1000 + 20) 
 | 
#define T_LOAD_P_L        (0 + 110 + 1000 + 20) 
 | 
#define T_ADDR_P_L        (0 + 110 + 1000 + 20) 
 | 
#define T_STROBE_P_L        (0 + 110 + 1000) /* 10us */ 
 | 
#define T_CSB_R_S        0 
 | 
#define T_PGENB_R_S        0 
 | 
#define T_LOAD_R_S        0 
 | 
#define T_ADDR_R_S        2 
 | 
#define T_STROBE_R_S        (2 + 3) 
 | 
#define T_CSB_R_L        (2 + 3 + 3 + 3) 
 | 
#define T_PGENB_R_L        (2 + 3 + 3 + 3) 
 | 
#define T_LOAD_R_L        (2 + 3 + 3 + 3) 
 | 
#define T_ADDR_R_L        (2 + 3 + 3 + 2) 
 | 
#define T_STROBE_R_L        (2 + 3 + 3) 
 | 
  
 | 
#define T_CSB_P            0x28 
 | 
#define T_PGENB_P        0x2c 
 | 
#define T_LOAD_P        0x30 
 | 
#define T_ADDR_P        0x34 
 | 
#define T_STROBE_P        0x38 
 | 
#define T_CSB_R            0x3c 
 | 
#define T_PGENB_R        0x40 
 | 
#define T_LOAD_R        0x44 
 | 
#define T_ADDR_R        0x48 
 | 
#define T_STROBE_R        0x4c 
 | 
  
 | 
#define RK1808_USER_MODE    BIT(0) 
 | 
#define RK1808_INT_FINISH    BIT(0) 
 | 
#define RK1808_AUTO_ENB        BIT(0) 
 | 
#define RK1808_AUTO_RD        BIT(1) 
 | 
#define RK1808_A_SHIFT        16 
 | 
#define RK1808_A_MASK        0x3ff 
 | 
#define RK1808_NBYTES        4 
 | 
  
 | 
#define RK3399_A_SHIFT          16 
 | 
#define RK3399_A_MASK           0x3ff 
 | 
#define RK3399_NFUSES           32 
 | 
#define RK3399_BYTES_PER_FUSE   4 
 | 
#define RK3399_STROBSFTSEL      BIT(9) 
 | 
#define RK3399_RSB              BIT(7) 
 | 
#define RK3399_PD               BIT(5) 
 | 
#define RK3399_PGENB            BIT(3) 
 | 
#define RK3399_LOAD             BIT(2) 
 | 
#define RK3399_STROBE           BIT(1) 
 | 
#define RK3399_CSB              BIT(0) 
 | 
  
 | 
#define RK3288_A_SHIFT          6 
 | 
#define RK3288_A_MASK           0x3ff 
 | 
#define RK3288_NFUSES           32 
 | 
#define RK3288_BYTES_PER_FUSE   1 
 | 
#define RK3288_PGENB            BIT(3) 
 | 
#define RK3288_LOAD             BIT(2) 
 | 
#define RK3288_STROBE           BIT(1) 
 | 
#define RK3288_CSB              BIT(0) 
 | 
  
 | 
#define RK3328_INT_STATUS    0x0018 
 | 
#define RK3328_DOUT        0x0020 
 | 
#define RK3328_AUTO_CTRL    0x0024 
 | 
#define RK3328_INT_FINISH    BIT(0) 
 | 
#define RK3328_AUTO_ENB        BIT(0) 
 | 
#define RK3328_AUTO_RD        BIT(1) 
 | 
  
 | 
typedef int (*EFUSE_READ)(struct udevice *dev, int offset, void *buf, int size); 
 | 
  
 | 
struct rockchip_efuse_regs { 
 | 
    u32 ctrl;      /* 0x00  efuse control register */ 
 | 
    u32 dout;      /* 0x04  efuse data out register */ 
 | 
    u32 rf;        /* 0x08  efuse redundancy bit used register */ 
 | 
    u32 _rsvd0; 
 | 
    u32 jtag_pass; /* 0x10  JTAG password */ 
 | 
    u32 strobe_finish_ctrl; 
 | 
               /* 0x14    efuse strobe finish control register */ 
 | 
    u32 int_status;/* 0x18 */ 
 | 
    u32 reserved;  /* 0x1c */ 
 | 
    u32 dout2;     /* 0x20 */ 
 | 
    u32 auto_ctrl; /* 0x24 */ 
 | 
}; 
 | 
  
 | 
struct rockchip_efuse_platdata { 
 | 
    void __iomem *base; 
 | 
    struct clk *clk; 
 | 
}; 
 | 
  
 | 
static void rk1808_efuse_timing_init(void __iomem *base) 
 | 
{ 
 | 
    static bool init; 
 | 
  
 | 
    if (init) 
 | 
        return; 
 | 
  
 | 
    /* enable auto mode */ 
 | 
    writel(readl(base) & (~RK1808_USER_MODE), base); 
 | 
  
 | 
    /* setup efuse timing */ 
 | 
    writel((T_CSB_P_S << 16) | T_CSB_P_L, base + T_CSB_P); 
 | 
    writel((T_PGENB_P_S << 16) | T_PGENB_P_L, base + T_PGENB_P); 
 | 
    writel((T_LOAD_P_S << 16) | T_LOAD_P_L, base + T_LOAD_P); 
 | 
    writel((T_ADDR_P_S << 16) | T_ADDR_P_L, base + T_ADDR_P); 
 | 
    writel((T_STROBE_P_S << 16) | T_STROBE_P_L, base + T_STROBE_P); 
 | 
    writel((T_CSB_R_S << 16) | T_CSB_R_L, base + T_CSB_R); 
 | 
    writel((T_PGENB_R_S << 16) | T_PGENB_R_L, base + T_PGENB_R); 
 | 
    writel((T_LOAD_R_S << 16) | T_LOAD_R_L, base + T_LOAD_R); 
 | 
    writel((T_ADDR_R_S << 16) | T_ADDR_R_L, base + T_ADDR_R); 
 | 
    writel((T_STROBE_R_S << 16) | T_STROBE_R_L, base + T_STROBE_R); 
 | 
  
 | 
    init = true; 
 | 
} 
 | 
  
 | 
static int rockchip_rk1808_efuse_read(struct udevice *dev, int offset, 
 | 
                      void *buf, int size) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
    struct rockchip_efuse_regs *efuse = 
 | 
        (struct rockchip_efuse_regs *)plat->base; 
 | 
    unsigned int addr_start, addr_end, addr_offset, addr_len; 
 | 
    u32 out_value, status; 
 | 
    u8 *buffer; 
 | 
    int ret = 0, i = 0; 
 | 
  
 | 
    rk1808_efuse_timing_init(plat->base); 
 | 
  
 | 
    addr_start = rounddown(offset, RK1808_NBYTES) / RK1808_NBYTES; 
 | 
    addr_end = roundup(offset + size, RK1808_NBYTES) / RK1808_NBYTES; 
 | 
    addr_offset = offset % RK1808_NBYTES; 
 | 
    addr_len = addr_end - addr_start; 
 | 
  
 | 
    buffer = calloc(1, sizeof(*buffer) * addr_len * RK1808_NBYTES); 
 | 
    if (!buffer) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    while (addr_len--) { 
 | 
        writel(RK1808_AUTO_RD | RK1808_AUTO_ENB | 
 | 
               ((addr_start++ & RK1808_A_MASK) << RK1808_A_SHIFT), 
 | 
               &efuse->auto_ctrl); 
 | 
        udelay(2); 
 | 
        status = readl(&efuse->int_status); 
 | 
        if (!(status & RK1808_INT_FINISH)) { 
 | 
            ret = -EIO; 
 | 
            goto err; 
 | 
        } 
 | 
        out_value = readl(&efuse->dout2); 
 | 
        writel(RK1808_INT_FINISH, &efuse->int_status); 
 | 
  
 | 
        memcpy(&buffer[i], &out_value, RK1808_NBYTES); 
 | 
        i += RK1808_NBYTES; 
 | 
    } 
 | 
    memcpy(buf, buffer + addr_offset, size); 
 | 
err: 
 | 
    kfree(buffer); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_SPL_BUILD 
 | 
static int rockchip_rk3368_efuse_read(struct udevice *dev, int offset, 
 | 
                      void *buf, int size) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
    struct rockchip_efuse_regs *efuse = 
 | 
        (struct rockchip_efuse_regs *)plat->base; 
 | 
    u8 *buffer = buf; 
 | 
    struct arm_smccc_res res; 
 | 
  
 | 
    /* Switch to read mode */ 
 | 
    sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                 RK3288_LOAD | RK3288_PGENB); 
 | 
    udelay(1); 
 | 
    while (size--) { 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 & 
 | 
                     (~(RK3288_A_MASK << RK3288_A_SHIFT))); 
 | 
        /* set addr */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 | 
 | 
                     ((offset++ & RK3288_A_MASK) << 
 | 
                      RK3288_A_SHIFT)); 
 | 
        udelay(1); 
 | 
        /* strobe low to high */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                     res.a1 | RK3288_STROBE); 
 | 
        ndelay(60); 
 | 
        /* read data */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->dout); 
 | 
        *buffer++ = res.a1; 
 | 
        /* reset strobe to low */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                     res.a1 & (~RK3288_STROBE)); 
 | 
        udelay(1); 
 | 
    } 
 | 
  
 | 
    /* Switch to standby mode */ 
 | 
    sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                 RK3288_PGENB | RK3288_CSB); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
  
 | 
static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, 
 | 
                      void *buf, int size) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
    struct rockchip_efuse_regs *efuse = 
 | 
        (struct rockchip_efuse_regs *)plat->base; 
 | 
  
 | 
    unsigned int addr_start, addr_end, addr_offset; 
 | 
    u32 out_value; 
 | 
    u8  bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE]; 
 | 
    int i = 0; 
 | 
    u32 addr; 
 | 
  
 | 
    addr_start = offset / RK3399_BYTES_PER_FUSE; 
 | 
    addr_offset = offset % RK3399_BYTES_PER_FUSE; 
 | 
    addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE); 
 | 
  
 | 
    /* cap to the size of the efuse block */ 
 | 
    if (addr_end > RK3399_NFUSES) 
 | 
        addr_end = RK3399_NFUSES; 
 | 
  
 | 
    writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 
 | 
           &efuse->ctrl); 
 | 
    udelay(1); 
 | 
    for (addr = addr_start; addr < addr_end; addr++) { 
 | 
        setbits_le32(&efuse->ctrl, 
 | 
                 RK3399_STROBE | (addr << RK3399_A_SHIFT)); 
 | 
        udelay(1); 
 | 
        out_value = readl(&efuse->dout); 
 | 
        clrbits_le32(&efuse->ctrl, RK3399_STROBE); 
 | 
        udelay(1); 
 | 
  
 | 
        memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE); 
 | 
        i += RK3399_BYTES_PER_FUSE; 
 | 
    } 
 | 
  
 | 
    /* Switch to standby mode */ 
 | 
    writel(RK3399_PD | RK3399_CSB, &efuse->ctrl); 
 | 
  
 | 
    memcpy(buf, bytes + addr_offset, size); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, 
 | 
                      void *buf, int size) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
    struct rockchip_efuse_regs *efuse = 
 | 
        (struct rockchip_efuse_regs *)plat->base; 
 | 
    u8 *buffer = buf; 
 | 
    int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE; 
 | 
  
 | 
    if (size > (max_size - offset)) 
 | 
        size = max_size - offset; 
 | 
  
 | 
    /* Switch to read mode */ 
 | 
    writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl); 
 | 
    udelay(1); 
 | 
  
 | 
    while (size--) { 
 | 
        writel(readl(&efuse->ctrl) & 
 | 
                (~(RK3288_A_MASK << RK3288_A_SHIFT)), 
 | 
                &efuse->ctrl); 
 | 
        /* set addr */ 
 | 
        writel(readl(&efuse->ctrl) | 
 | 
                ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 
 | 
                &efuse->ctrl); 
 | 
        udelay(1); 
 | 
        /* strobe low to high */ 
 | 
        writel(readl(&efuse->ctrl) | 
 | 
                RK3288_STROBE, &efuse->ctrl); 
 | 
        ndelay(60); 
 | 
        /* read data */ 
 | 
        *buffer++ = readl(&efuse->dout); 
 | 
        /* reset strobe to low */ 
 | 
        writel(readl(&efuse->ctrl) & 
 | 
                (~RK3288_STROBE), &efuse->ctrl); 
 | 
        udelay(1); 
 | 
    } 
 | 
  
 | 
    /* Switch to standby mode */ 
 | 
    writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#ifndef CONFIG_SPL_BUILD 
 | 
static int rockchip_rk3288_efuse_secure_read(struct udevice *dev, int offset, 
 | 
                         void *buf, int size) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
    struct rockchip_efuse_regs *efuse = 
 | 
        (struct rockchip_efuse_regs *)plat->base; 
 | 
    u8 *buffer = buf; 
 | 
    int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE; 
 | 
    struct arm_smccc_res res; 
 | 
  
 | 
    if (size > (max_size - offset)) 
 | 
        size = max_size - offset; 
 | 
  
 | 
    /* Switch to read mode */ 
 | 
    sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                 RK3288_LOAD | RK3288_PGENB); 
 | 
    udelay(1); 
 | 
    while (size--) { 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 & 
 | 
                     (~(RK3288_A_MASK << RK3288_A_SHIFT))); 
 | 
        /* set addr */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 | 
 | 
                     ((offset++ & RK3288_A_MASK) << 
 | 
                      RK3288_A_SHIFT)); 
 | 
        udelay(1); 
 | 
        /* strobe low to high */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                     res.a1 | RK3288_STROBE); 
 | 
        ndelay(60); 
 | 
        /* read data */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->dout); 
 | 
        *buffer++ = res.a1; 
 | 
        /* reset strobe to low */ 
 | 
        res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 
 | 
        sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                     res.a1 & (~RK3288_STROBE)); 
 | 
        udelay(1); 
 | 
    } 
 | 
  
 | 
    /* Switch to standby mode */ 
 | 
    sip_smc_secure_reg_write((ulong)&efuse->ctrl, 
 | 
                 RK3288_PGENB | RK3288_CSB); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
#endif 
 | 
  
 | 
static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset, 
 | 
                      void *buf, int size) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
    struct rockchip_efuse_regs *efuse = 
 | 
        (struct rockchip_efuse_regs *)plat->base; 
 | 
    unsigned int addr_start, addr_end, addr_offset, addr_len; 
 | 
    u32 out_value, status; 
 | 
    u8 *buffer; 
 | 
    int ret = 0, i = 0, j = 0; 
 | 
  
 | 
    /* Max non-secure Byte */ 
 | 
    if (size > 32) 
 | 
        size = 32; 
 | 
  
 | 
    /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ 
 | 
    offset += 96; 
 | 
    addr_start = rounddown(offset, RK3399_BYTES_PER_FUSE) / 
 | 
                        RK3399_BYTES_PER_FUSE; 
 | 
    addr_end = roundup(offset + size, RK3399_BYTES_PER_FUSE) / 
 | 
                        RK3399_BYTES_PER_FUSE; 
 | 
    addr_offset = offset % RK3399_BYTES_PER_FUSE; 
 | 
    addr_len = addr_end - addr_start; 
 | 
  
 | 
    buffer = calloc(1, sizeof(*buffer) * addr_len * RK3399_BYTES_PER_FUSE); 
 | 
    if (!buffer) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    for (j = 0; j < addr_len; j++) { 
 | 
        writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | 
 | 
               ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 
 | 
                 &efuse->auto_ctrl); 
 | 
        udelay(5); 
 | 
        status = readl(&efuse->int_status); 
 | 
        if (!(status & RK3328_INT_FINISH)) { 
 | 
            ret = -EIO; 
 | 
            goto err; 
 | 
        } 
 | 
        out_value = readl(&efuse->dout2); 
 | 
        writel(RK3328_INT_FINISH, &efuse->int_status); 
 | 
  
 | 
        memcpy(&buffer[i], &out_value, RK3399_BYTES_PER_FUSE); 
 | 
        i += RK3399_BYTES_PER_FUSE; 
 | 
    } 
 | 
    memcpy(buf, buffer + addr_offset, size); 
 | 
err: 
 | 
    free(buffer); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static int rockchip_efuse_read(struct udevice *dev, int offset, 
 | 
                   void *buf, int size) 
 | 
{ 
 | 
    EFUSE_READ efuse_read = NULL; 
 | 
  
 | 
    efuse_read = (EFUSE_READ)dev_get_driver_data(dev); 
 | 
    if (!efuse_read) 
 | 
        return -ENOSYS; 
 | 
  
 | 
    return (*efuse_read)(dev, offset, buf, size); 
 | 
} 
 | 
  
 | 
static int rockchip_efuse_capatiblity(struct udevice *dev, u32 *buf) 
 | 
{ 
 | 
    *buf = device_is_compatible(dev, "rockchip,rk3288-secure-efuse") ? 
 | 
           OTP_S : OTP_NS; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int rockchip_efuse_ioctl(struct udevice *dev, unsigned long request, 
 | 
                void *buf) 
 | 
{ 
 | 
    int ret = -EINVAL; 
 | 
  
 | 
    switch (request) { 
 | 
    case IOCTL_REQ_CAPABILITY: 
 | 
        ret = rockchip_efuse_capatiblity(dev, buf); 
 | 
        break; 
 | 
    } 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static const struct misc_ops rockchip_efuse_ops = { 
 | 
    .read = rockchip_efuse_read, 
 | 
    .ioctl = rockchip_efuse_ioctl, 
 | 
}; 
 | 
  
 | 
static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev) 
 | 
{ 
 | 
    struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 
 | 
  
 | 
    plat->base = dev_read_addr_ptr(dev); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static const struct udevice_id rockchip_efuse_ids[] = { 
 | 
    { 
 | 
        .compatible = "rockchip,rk1808-efuse", 
 | 
        .data = (ulong)&rockchip_rk1808_efuse_read, 
 | 
    }, 
 | 
#ifndef CONFIG_SPL_BUILD 
 | 
    { 
 | 
        .compatible = "rockchip,rk3288-secure-efuse", 
 | 
        .data = (ulong)&rockchip_rk3288_efuse_secure_read, 
 | 
    }, 
 | 
#endif 
 | 
    { 
 | 
        .compatible = "rockchip,rk3066a-efuse", 
 | 
        .data = (ulong)&rockchip_rk3288_efuse_read, 
 | 
    }, 
 | 
    { 
 | 
        .compatible = "rockchip,rk3188-efuse", 
 | 
        .data = (ulong)&rockchip_rk3288_efuse_read, 
 | 
    }, 
 | 
    { 
 | 
        .compatible = "rockchip,rk322x-efuse", 
 | 
        .data = (ulong)&rockchip_rk3288_efuse_read, 
 | 
    }, 
 | 
    { 
 | 
        .compatible = "rockchip,rk3328-efuse", 
 | 
        .data = (ulong)&rockchip_rk3328_efuse_read, 
 | 
    }, 
 | 
#ifndef CONFIG_SPL_BUILD 
 | 
    { 
 | 
        .compatible = "rockchip,rk3368-efuse", 
 | 
        .data = (ulong)&rockchip_rk3368_efuse_read, 
 | 
    }, 
 | 
#endif 
 | 
    { 
 | 
        .compatible = "rockchip,rk3399-efuse", 
 | 
        .data = (ulong)&rockchip_rk3399_efuse_read, 
 | 
    }, 
 | 
    {} 
 | 
}; 
 | 
  
 | 
U_BOOT_DRIVER(rockchip_efuse) = { 
 | 
    .name = "rockchip_efuse", 
 | 
    .id = UCLASS_MISC, 
 | 
    .of_match = rockchip_efuse_ids, 
 | 
    .ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata, 
 | 
    .platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata), 
 | 
    .ops = &rockchip_efuse_ops, 
 | 
}; 
 |