149cd8e85SPhilipp Tomsich /* 249cd8e85SPhilipp Tomsich * eFuse driver for Rockchip devices 349cd8e85SPhilipp Tomsich * 449cd8e85SPhilipp Tomsich * Copyright 2017, Theobroma Systems Design und Consulting GmbH 549cd8e85SPhilipp Tomsich * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com> 649cd8e85SPhilipp Tomsich * 749cd8e85SPhilipp Tomsich * SPDX-License-Identifier: GPL-2.0+ 849cd8e85SPhilipp Tomsich */ 949cd8e85SPhilipp Tomsich 1049cd8e85SPhilipp Tomsich #include <common.h> 1149cd8e85SPhilipp Tomsich #include <asm/io.h> 1249cd8e85SPhilipp Tomsich #include <command.h> 1349cd8e85SPhilipp Tomsich #include <display_options.h> 1449cd8e85SPhilipp Tomsich #include <dm.h> 15492844a3SFinley Xiao #include <linux/arm-smccc.h> 1649cd8e85SPhilipp Tomsich #include <linux/bitops.h> 1749cd8e85SPhilipp Tomsich #include <linux/delay.h> 1849cd8e85SPhilipp Tomsich #include <misc.h> 19492844a3SFinley Xiao #include <asm/arch/rockchip_smccc.h> 2049cd8e85SPhilipp Tomsich 21b13cc84cSFinley Xiao #define T_CSB_P_S 0 22b13cc84cSFinley Xiao #define T_PGENB_P_S 0 23b13cc84cSFinley Xiao #define T_LOAD_P_S 0 24b13cc84cSFinley Xiao #define T_ADDR_P_S 0 25b13cc84cSFinley Xiao #define T_STROBE_P_S (0 + 110) /* 1.1us */ 26b13cc84cSFinley Xiao #define T_CSB_P_L (0 + 110 + 1000 + 20) /* 200ns */ 27b13cc84cSFinley Xiao #define T_PGENB_P_L (0 + 110 + 1000 + 20) 28b13cc84cSFinley Xiao #define T_LOAD_P_L (0 + 110 + 1000 + 20) 29b13cc84cSFinley Xiao #define T_ADDR_P_L (0 + 110 + 1000 + 20) 30b13cc84cSFinley Xiao #define T_STROBE_P_L (0 + 110 + 1000) /* 10us */ 31b13cc84cSFinley Xiao #define T_CSB_R_S 0 32b13cc84cSFinley Xiao #define T_PGENB_R_S 0 33b13cc84cSFinley Xiao #define T_LOAD_R_S 0 34b13cc84cSFinley Xiao #define T_ADDR_R_S 2 35b13cc84cSFinley Xiao #define T_STROBE_R_S (2 + 3) 36b13cc84cSFinley Xiao #define T_CSB_R_L (2 + 3 + 3 + 3) 37b13cc84cSFinley Xiao #define T_PGENB_R_L (2 + 3 + 3 + 3) 38b13cc84cSFinley Xiao #define T_LOAD_R_L (2 + 3 + 3 + 3) 39b13cc84cSFinley Xiao #define T_ADDR_R_L (2 + 3 + 3 + 2) 40b13cc84cSFinley Xiao #define T_STROBE_R_L (2 + 3 + 3) 41b13cc84cSFinley Xiao 42b13cc84cSFinley Xiao #define T_CSB_P 0x28 43b13cc84cSFinley Xiao #define T_PGENB_P 0x2c 44b13cc84cSFinley Xiao #define T_LOAD_P 0x30 45b13cc84cSFinley Xiao #define T_ADDR_P 0x34 46b13cc84cSFinley Xiao #define T_STROBE_P 0x38 47b13cc84cSFinley Xiao #define T_CSB_R 0x3c 48b13cc84cSFinley Xiao #define T_PGENB_R 0x40 49b13cc84cSFinley Xiao #define T_LOAD_R 0x44 50b13cc84cSFinley Xiao #define T_ADDR_R 0x48 51b13cc84cSFinley Xiao #define T_STROBE_R 0x4c 52b13cc84cSFinley Xiao 53b13cc84cSFinley Xiao #define RK1808_USER_MODE BIT(0) 54b13cc84cSFinley Xiao #define RK1808_INT_FINISH BIT(0) 55b13cc84cSFinley Xiao #define RK1808_AUTO_ENB BIT(0) 56b13cc84cSFinley Xiao #define RK1808_AUTO_RD BIT(1) 57b13cc84cSFinley Xiao #define RK1808_A_SHIFT 16 58b13cc84cSFinley Xiao #define RK1808_A_MASK 0x3ff 59b13cc84cSFinley Xiao #define RK1808_NBYTES 4 60b13cc84cSFinley Xiao 6149cd8e85SPhilipp Tomsich #define RK3399_A_SHIFT 16 6249cd8e85SPhilipp Tomsich #define RK3399_A_MASK 0x3ff 6349cd8e85SPhilipp Tomsich #define RK3399_NFUSES 32 6449cd8e85SPhilipp Tomsich #define RK3399_BYTES_PER_FUSE 4 6549cd8e85SPhilipp Tomsich #define RK3399_STROBSFTSEL BIT(9) 6649cd8e85SPhilipp Tomsich #define RK3399_RSB BIT(7) 6749cd8e85SPhilipp Tomsich #define RK3399_PD BIT(5) 6849cd8e85SPhilipp Tomsich #define RK3399_PGENB BIT(3) 6949cd8e85SPhilipp Tomsich #define RK3399_LOAD BIT(2) 7049cd8e85SPhilipp Tomsich #define RK3399_STROBE BIT(1) 7149cd8e85SPhilipp Tomsich #define RK3399_CSB BIT(0) 7249cd8e85SPhilipp Tomsich 73b4fa32f0SFrancis Fan #define RK3288_A_SHIFT 6 74b4fa32f0SFrancis Fan #define RK3288_A_MASK 0x3ff 75b4fa32f0SFrancis Fan #define RK3288_NFUSES 32 76b4fa32f0SFrancis Fan #define RK3288_BYTES_PER_FUSE 1 77b4fa32f0SFrancis Fan #define RK3288_PGENB BIT(3) 78b4fa32f0SFrancis Fan #define RK3288_LOAD BIT(2) 79b4fa32f0SFrancis Fan #define RK3288_STROBE BIT(1) 80b4fa32f0SFrancis Fan #define RK3288_CSB BIT(0) 81b4fa32f0SFrancis Fan 8259a83996SJoseph Chen #define RK3328_INT_STATUS 0x0018 8359a83996SJoseph Chen #define RK3328_DOUT 0x0020 8459a83996SJoseph Chen #define RK3328_AUTO_CTRL 0x0024 8559a83996SJoseph Chen #define RK3328_INT_FINISH BIT(0) 8659a83996SJoseph Chen #define RK3328_AUTO_ENB BIT(0) 8759a83996SJoseph Chen #define RK3328_AUTO_RD BIT(1) 8859a83996SJoseph Chen 89b4fa32f0SFrancis Fan typedef int (*EFUSE_READ)(struct udevice *dev, int offset, void *buf, int size); 90b4fa32f0SFrancis Fan 9149cd8e85SPhilipp Tomsich struct rockchip_efuse_regs { 9249cd8e85SPhilipp Tomsich u32 ctrl; /* 0x00 efuse control register */ 9349cd8e85SPhilipp Tomsich u32 dout; /* 0x04 efuse data out register */ 9449cd8e85SPhilipp Tomsich u32 rf; /* 0x08 efuse redundancy bit used register */ 9549cd8e85SPhilipp Tomsich u32 _rsvd0; 9649cd8e85SPhilipp Tomsich u32 jtag_pass; /* 0x10 JTAG password */ 9749cd8e85SPhilipp Tomsich u32 strobe_finish_ctrl; 9849cd8e85SPhilipp Tomsich /* 0x14 efuse strobe finish control register */ 9959a83996SJoseph Chen u32 int_status;/* 0x18 */ 10059a83996SJoseph Chen u32 reserved; /* 0x1c */ 10159a83996SJoseph Chen u32 dout2; /* 0x20 */ 10259a83996SJoseph Chen u32 auto_ctrl; /* 0x24 */ 10349cd8e85SPhilipp Tomsich }; 10449cd8e85SPhilipp Tomsich 10549cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata { 10649cd8e85SPhilipp Tomsich void __iomem *base; 10749cd8e85SPhilipp Tomsich struct clk *clk; 10849cd8e85SPhilipp Tomsich }; 10949cd8e85SPhilipp Tomsich 110b13cc84cSFinley Xiao static void rk1808_efuse_timing_init(void __iomem *base) 111b13cc84cSFinley Xiao { 112b13cc84cSFinley Xiao static bool init; 113b13cc84cSFinley Xiao 114b13cc84cSFinley Xiao if (init) 115b13cc84cSFinley Xiao return; 116b13cc84cSFinley Xiao 117b13cc84cSFinley Xiao /* enable auto mode */ 118b13cc84cSFinley Xiao writel(readl(base) & (~RK1808_USER_MODE), base); 119b13cc84cSFinley Xiao 120b13cc84cSFinley Xiao /* setup efuse timing */ 121b13cc84cSFinley Xiao writel((T_CSB_P_S << 16) | T_CSB_P_L, base + T_CSB_P); 122b13cc84cSFinley Xiao writel((T_PGENB_P_S << 16) | T_PGENB_P_L, base + T_PGENB_P); 123b13cc84cSFinley Xiao writel((T_LOAD_P_S << 16) | T_LOAD_P_L, base + T_LOAD_P); 124b13cc84cSFinley Xiao writel((T_ADDR_P_S << 16) | T_ADDR_P_L, base + T_ADDR_P); 125b13cc84cSFinley Xiao writel((T_STROBE_P_S << 16) | T_STROBE_P_L, base + T_STROBE_P); 126b13cc84cSFinley Xiao writel((T_CSB_R_S << 16) | T_CSB_R_L, base + T_CSB_R); 127b13cc84cSFinley Xiao writel((T_PGENB_R_S << 16) | T_PGENB_R_L, base + T_PGENB_R); 128b13cc84cSFinley Xiao writel((T_LOAD_R_S << 16) | T_LOAD_R_L, base + T_LOAD_R); 129b13cc84cSFinley Xiao writel((T_ADDR_R_S << 16) | T_ADDR_R_L, base + T_ADDR_R); 130b13cc84cSFinley Xiao writel((T_STROBE_R_S << 16) | T_STROBE_R_L, base + T_STROBE_R); 131b13cc84cSFinley Xiao 132b13cc84cSFinley Xiao init = true; 133b13cc84cSFinley Xiao } 134b13cc84cSFinley Xiao 135b13cc84cSFinley Xiao static int rockchip_rk1808_efuse_read(struct udevice *dev, int offset, 136b13cc84cSFinley Xiao void *buf, int size) 137b13cc84cSFinley Xiao { 138b13cc84cSFinley Xiao struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 139b13cc84cSFinley Xiao struct rockchip_efuse_regs *efuse = 140b13cc84cSFinley Xiao (struct rockchip_efuse_regs *)plat->base; 141b13cc84cSFinley Xiao unsigned int addr_start, addr_end, addr_offset, addr_len; 142b13cc84cSFinley Xiao u32 out_value, status; 143b13cc84cSFinley Xiao u8 *buffer; 144b13cc84cSFinley Xiao int ret = 0, i = 0; 145b13cc84cSFinley Xiao 146b13cc84cSFinley Xiao rk1808_efuse_timing_init(plat->base); 147b13cc84cSFinley Xiao 148b13cc84cSFinley Xiao addr_start = rounddown(offset, RK1808_NBYTES) / RK1808_NBYTES; 149b13cc84cSFinley Xiao addr_end = roundup(offset + size, RK1808_NBYTES) / RK1808_NBYTES; 150b13cc84cSFinley Xiao addr_offset = offset % RK1808_NBYTES; 151b13cc84cSFinley Xiao addr_len = addr_end - addr_start; 152b13cc84cSFinley Xiao 153b13cc84cSFinley Xiao buffer = calloc(1, sizeof(*buffer) * addr_len * RK1808_NBYTES); 154b13cc84cSFinley Xiao if (!buffer) 155b13cc84cSFinley Xiao return -ENOMEM; 156b13cc84cSFinley Xiao 157b13cc84cSFinley Xiao while (addr_len--) { 158b13cc84cSFinley Xiao writel(RK1808_AUTO_RD | RK1808_AUTO_ENB | 159b13cc84cSFinley Xiao ((addr_start++ & RK1808_A_MASK) << RK1808_A_SHIFT), 160b13cc84cSFinley Xiao &efuse->auto_ctrl); 161b13cc84cSFinley Xiao udelay(2); 162b13cc84cSFinley Xiao status = readl(&efuse->int_status); 163b13cc84cSFinley Xiao if (!(status & RK1808_INT_FINISH)) { 164b13cc84cSFinley Xiao ret = -EIO; 165b13cc84cSFinley Xiao goto err; 166b13cc84cSFinley Xiao } 167b13cc84cSFinley Xiao out_value = readl(&efuse->dout2); 168b13cc84cSFinley Xiao writel(RK1808_INT_FINISH, &efuse->int_status); 169b13cc84cSFinley Xiao 170b13cc84cSFinley Xiao memcpy(&buffer[i], &out_value, RK1808_NBYTES); 171b13cc84cSFinley Xiao i += RK1808_NBYTES; 172b13cc84cSFinley Xiao } 173b13cc84cSFinley Xiao memcpy(buf, buffer + addr_offset, size); 174b13cc84cSFinley Xiao err: 175b13cc84cSFinley Xiao kfree(buffer); 176b13cc84cSFinley Xiao 177b13cc84cSFinley Xiao return ret; 178b13cc84cSFinley Xiao } 179b13cc84cSFinley Xiao 18049cd8e85SPhilipp Tomsich static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, 18149cd8e85SPhilipp Tomsich void *buf, int size) 18249cd8e85SPhilipp Tomsich { 18349cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 18449cd8e85SPhilipp Tomsich struct rockchip_efuse_regs *efuse = 18549cd8e85SPhilipp Tomsich (struct rockchip_efuse_regs *)plat->base; 18649cd8e85SPhilipp Tomsich 18749cd8e85SPhilipp Tomsich unsigned int addr_start, addr_end, addr_offset; 18849cd8e85SPhilipp Tomsich u32 out_value; 18949cd8e85SPhilipp Tomsich u8 bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE]; 19049cd8e85SPhilipp Tomsich int i = 0; 19149cd8e85SPhilipp Tomsich u32 addr; 19249cd8e85SPhilipp Tomsich 19349cd8e85SPhilipp Tomsich addr_start = offset / RK3399_BYTES_PER_FUSE; 19449cd8e85SPhilipp Tomsich addr_offset = offset % RK3399_BYTES_PER_FUSE; 19549cd8e85SPhilipp Tomsich addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE); 19649cd8e85SPhilipp Tomsich 19749cd8e85SPhilipp Tomsich /* cap to the size of the efuse block */ 19849cd8e85SPhilipp Tomsich if (addr_end > RK3399_NFUSES) 19949cd8e85SPhilipp Tomsich addr_end = RK3399_NFUSES; 20049cd8e85SPhilipp Tomsich 20149cd8e85SPhilipp Tomsich writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 20249cd8e85SPhilipp Tomsich &efuse->ctrl); 20349cd8e85SPhilipp Tomsich udelay(1); 20449cd8e85SPhilipp Tomsich for (addr = addr_start; addr < addr_end; addr++) { 20549cd8e85SPhilipp Tomsich setbits_le32(&efuse->ctrl, 20649cd8e85SPhilipp Tomsich RK3399_STROBE | (addr << RK3399_A_SHIFT)); 20749cd8e85SPhilipp Tomsich udelay(1); 20849cd8e85SPhilipp Tomsich out_value = readl(&efuse->dout); 20949cd8e85SPhilipp Tomsich clrbits_le32(&efuse->ctrl, RK3399_STROBE); 21049cd8e85SPhilipp Tomsich udelay(1); 21149cd8e85SPhilipp Tomsich 21249cd8e85SPhilipp Tomsich memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE); 21349cd8e85SPhilipp Tomsich i += RK3399_BYTES_PER_FUSE; 21449cd8e85SPhilipp Tomsich } 21549cd8e85SPhilipp Tomsich 21649cd8e85SPhilipp Tomsich /* Switch to standby mode */ 21749cd8e85SPhilipp Tomsich writel(RK3399_PD | RK3399_CSB, &efuse->ctrl); 21849cd8e85SPhilipp Tomsich 21949cd8e85SPhilipp Tomsich memcpy(buf, bytes + addr_offset, size); 22049cd8e85SPhilipp Tomsich 22149cd8e85SPhilipp Tomsich return 0; 22249cd8e85SPhilipp Tomsich } 22349cd8e85SPhilipp Tomsich 224b4fa32f0SFrancis Fan static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, 225b4fa32f0SFrancis Fan void *buf, int size) 226b4fa32f0SFrancis Fan { 227b4fa32f0SFrancis Fan struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 228b4fa32f0SFrancis Fan struct rockchip_efuse_regs *efuse = 229b4fa32f0SFrancis Fan (struct rockchip_efuse_regs *)plat->base; 230b4fa32f0SFrancis Fan u8 *buffer = buf; 231b4fa32f0SFrancis Fan int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE; 232b4fa32f0SFrancis Fan 233b4fa32f0SFrancis Fan if (size > (max_size - offset)) 234b4fa32f0SFrancis Fan size = max_size - offset; 235b4fa32f0SFrancis Fan 236b4fa32f0SFrancis Fan /* Switch to read mode */ 237b4fa32f0SFrancis Fan writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl); 238b4fa32f0SFrancis Fan udelay(1); 239b4fa32f0SFrancis Fan 240b4fa32f0SFrancis Fan while (size--) { 241b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) & 242b4fa32f0SFrancis Fan (~(RK3288_A_MASK << RK3288_A_SHIFT)), 243b4fa32f0SFrancis Fan &efuse->ctrl); 244b4fa32f0SFrancis Fan /* set addr */ 245b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) | 246b4fa32f0SFrancis Fan ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 247b4fa32f0SFrancis Fan &efuse->ctrl); 248b4fa32f0SFrancis Fan udelay(1); 249b4fa32f0SFrancis Fan /* strobe low to high */ 250b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) | 251b4fa32f0SFrancis Fan RK3288_STROBE, &efuse->ctrl); 252b4fa32f0SFrancis Fan ndelay(60); 253b4fa32f0SFrancis Fan /* read data */ 254b4fa32f0SFrancis Fan *buffer++ = readl(&efuse->dout); 255b4fa32f0SFrancis Fan /* reset strobe to low */ 256b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) & 257b4fa32f0SFrancis Fan (~RK3288_STROBE), &efuse->ctrl); 258b4fa32f0SFrancis Fan udelay(1); 259b4fa32f0SFrancis Fan } 260b4fa32f0SFrancis Fan 261b4fa32f0SFrancis Fan /* Switch to standby mode */ 262b4fa32f0SFrancis Fan writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl); 263b4fa32f0SFrancis Fan 264b4fa32f0SFrancis Fan return 0; 265b4fa32f0SFrancis Fan } 266b4fa32f0SFrancis Fan 267492844a3SFinley Xiao #ifndef CONFIG_SPL_BUILD 268492844a3SFinley Xiao static int rockchip_rk3288_efuse_secure_read(struct udevice *dev, int offset, 269492844a3SFinley Xiao void *buf, int size) 270492844a3SFinley Xiao { 271492844a3SFinley Xiao struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 272492844a3SFinley Xiao struct rockchip_efuse_regs *efuse = 273492844a3SFinley Xiao (struct rockchip_efuse_regs *)plat->base; 274492844a3SFinley Xiao u8 *buffer = buf; 275492844a3SFinley Xiao int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE; 276492844a3SFinley Xiao struct arm_smccc_res res; 277492844a3SFinley Xiao 278492844a3SFinley Xiao if (size > (max_size - offset)) 279492844a3SFinley Xiao size = max_size - offset; 280492844a3SFinley Xiao 281492844a3SFinley Xiao /* Switch to read mode */ 282492844a3SFinley Xiao sip_smc_secure_reg_write((ulong)&efuse->ctrl, 283492844a3SFinley Xiao RK3288_LOAD | RK3288_PGENB); 284492844a3SFinley Xiao udelay(1); 285492844a3SFinley Xiao while (size--) { 286492844a3SFinley Xiao res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 287492844a3SFinley Xiao sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 & 288492844a3SFinley Xiao (~(RK3288_A_MASK << RK3288_A_SHIFT))); 289492844a3SFinley Xiao /* set addr */ 290492844a3SFinley Xiao res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 291492844a3SFinley Xiao sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 | 292492844a3SFinley Xiao ((offset++ & RK3288_A_MASK) << 293492844a3SFinley Xiao RK3288_A_SHIFT)); 294492844a3SFinley Xiao udelay(1); 295492844a3SFinley Xiao /* strobe low to high */ 296492844a3SFinley Xiao res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 297492844a3SFinley Xiao sip_smc_secure_reg_write((ulong)&efuse->ctrl, 298492844a3SFinley Xiao res.a1 | RK3288_STROBE); 299492844a3SFinley Xiao ndelay(60); 300492844a3SFinley Xiao /* read data */ 301492844a3SFinley Xiao res = sip_smc_secure_reg_read((ulong)&efuse->dout); 302492844a3SFinley Xiao *buffer++ = res.a1; 303492844a3SFinley Xiao /* reset strobe to low */ 304492844a3SFinley Xiao res = sip_smc_secure_reg_read((ulong)&efuse->ctrl); 305492844a3SFinley Xiao sip_smc_secure_reg_write((ulong)&efuse->ctrl, 306492844a3SFinley Xiao res.a1 & (~RK3288_STROBE)); 307492844a3SFinley Xiao udelay(1); 308492844a3SFinley Xiao } 309492844a3SFinley Xiao 310492844a3SFinley Xiao /* Switch to standby mode */ 311492844a3SFinley Xiao sip_smc_secure_reg_write((ulong)&efuse->ctrl, 312492844a3SFinley Xiao RK3288_PGENB | RK3288_CSB); 313492844a3SFinley Xiao 314492844a3SFinley Xiao return 0; 315492844a3SFinley Xiao } 316492844a3SFinley Xiao #endif 317492844a3SFinley Xiao 31859a83996SJoseph Chen static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset, 31959a83996SJoseph Chen void *buf, int size) 32059a83996SJoseph Chen { 32159a83996SJoseph Chen struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 32259a83996SJoseph Chen struct rockchip_efuse_regs *efuse = 32359a83996SJoseph Chen (struct rockchip_efuse_regs *)plat->base; 32459a83996SJoseph Chen unsigned int addr_start, addr_end, addr_offset, addr_len; 32559a83996SJoseph Chen u32 out_value, status; 32659a83996SJoseph Chen u8 *buffer; 32759a83996SJoseph Chen int ret = 0, i = 0, j = 0; 32859a83996SJoseph Chen 32959a83996SJoseph Chen /* Max non-secure Byte */ 33059a83996SJoseph Chen if (size > 32) 33159a83996SJoseph Chen size = 32; 33259a83996SJoseph Chen 33359a83996SJoseph Chen /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ 33459a83996SJoseph Chen offset += 96; 33559a83996SJoseph Chen addr_start = rounddown(offset, RK3399_BYTES_PER_FUSE) / 33659a83996SJoseph Chen RK3399_BYTES_PER_FUSE; 33759a83996SJoseph Chen addr_end = roundup(offset + size, RK3399_BYTES_PER_FUSE) / 33859a83996SJoseph Chen RK3399_BYTES_PER_FUSE; 33959a83996SJoseph Chen addr_offset = offset % RK3399_BYTES_PER_FUSE; 34059a83996SJoseph Chen addr_len = addr_end - addr_start; 34159a83996SJoseph Chen 34259a83996SJoseph Chen buffer = calloc(1, sizeof(*buffer) * addr_len * RK3399_BYTES_PER_FUSE); 34359a83996SJoseph Chen if (!buffer) 34459a83996SJoseph Chen return -ENOMEM; 34559a83996SJoseph Chen 34659a83996SJoseph Chen for (j = 0; j < addr_len; j++) { 34759a83996SJoseph Chen writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | 34859a83996SJoseph Chen ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 34959a83996SJoseph Chen &efuse->auto_ctrl); 35059a83996SJoseph Chen udelay(5); 35159a83996SJoseph Chen status = readl(&efuse->int_status); 35259a83996SJoseph Chen if (!(status & RK3328_INT_FINISH)) { 35359a83996SJoseph Chen ret = -EIO; 35459a83996SJoseph Chen goto err; 35559a83996SJoseph Chen } 35659a83996SJoseph Chen out_value = readl(&efuse->dout2); 35759a83996SJoseph Chen writel(RK3328_INT_FINISH, &efuse->int_status); 35859a83996SJoseph Chen 35959a83996SJoseph Chen memcpy(&buffer[i], &out_value, RK3399_BYTES_PER_FUSE); 36059a83996SJoseph Chen i += RK3399_BYTES_PER_FUSE; 36159a83996SJoseph Chen } 36259a83996SJoseph Chen memcpy(buf, buffer + addr_offset, size); 36359a83996SJoseph Chen err: 36459a83996SJoseph Chen free(buffer); 36559a83996SJoseph Chen 36659a83996SJoseph Chen return ret; 36759a83996SJoseph Chen } 36859a83996SJoseph Chen 36949cd8e85SPhilipp Tomsich static int rockchip_efuse_read(struct udevice *dev, int offset, 37049cd8e85SPhilipp Tomsich void *buf, int size) 37149cd8e85SPhilipp Tomsich { 372b4fa32f0SFrancis Fan EFUSE_READ efuse_read = NULL; 373b4fa32f0SFrancis Fan 374b4fa32f0SFrancis Fan efuse_read = (EFUSE_READ)dev_get_driver_data(dev); 375b4fa32f0SFrancis Fan if (!efuse_read) 376b4fa32f0SFrancis Fan return -ENOSYS; 377b4fa32f0SFrancis Fan 378b4fa32f0SFrancis Fan return (*efuse_read)(dev, offset, buf, size); 37949cd8e85SPhilipp Tomsich } 38049cd8e85SPhilipp Tomsich 381*40f8ee7aSJoseph Chen static int rockchip_efuse_capatiblity(struct udevice *dev, u32 *buf) 382*40f8ee7aSJoseph Chen { 383*40f8ee7aSJoseph Chen *buf = device_is_compatible(dev, "rockchip,rk3288-secure-efuse") ? 384*40f8ee7aSJoseph Chen OTP_S : OTP_NS; 385*40f8ee7aSJoseph Chen 386*40f8ee7aSJoseph Chen return 0; 387*40f8ee7aSJoseph Chen } 388*40f8ee7aSJoseph Chen 389*40f8ee7aSJoseph Chen static int rockchip_efuse_ioctl(struct udevice *dev, unsigned long request, 390*40f8ee7aSJoseph Chen void *buf) 391*40f8ee7aSJoseph Chen { 392*40f8ee7aSJoseph Chen int ret = -EINVAL; 393*40f8ee7aSJoseph Chen 394*40f8ee7aSJoseph Chen switch (request) { 395*40f8ee7aSJoseph Chen case IOCTL_REQ_CAPABILITY: 396*40f8ee7aSJoseph Chen ret = rockchip_efuse_capatiblity(dev, buf); 397*40f8ee7aSJoseph Chen break; 398*40f8ee7aSJoseph Chen } 399*40f8ee7aSJoseph Chen 400*40f8ee7aSJoseph Chen return ret; 401*40f8ee7aSJoseph Chen } 402*40f8ee7aSJoseph Chen 40349cd8e85SPhilipp Tomsich static const struct misc_ops rockchip_efuse_ops = { 40449cd8e85SPhilipp Tomsich .read = rockchip_efuse_read, 405*40f8ee7aSJoseph Chen .ioctl = rockchip_efuse_ioctl, 40649cd8e85SPhilipp Tomsich }; 40749cd8e85SPhilipp Tomsich 40849cd8e85SPhilipp Tomsich static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev) 40949cd8e85SPhilipp Tomsich { 41049cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 41149cd8e85SPhilipp Tomsich 41263a453e6SPhilipp Tomsich plat->base = dev_read_addr_ptr(dev); 41349cd8e85SPhilipp Tomsich return 0; 41449cd8e85SPhilipp Tomsich } 41549cd8e85SPhilipp Tomsich 41649cd8e85SPhilipp Tomsich static const struct udevice_id rockchip_efuse_ids[] = { 417b4fa32f0SFrancis Fan { 418b13cc84cSFinley Xiao .compatible = "rockchip,rk1808-efuse", 419b13cc84cSFinley Xiao .data = (ulong)&rockchip_rk1808_efuse_read, 420b13cc84cSFinley Xiao }, 421492844a3SFinley Xiao #ifndef CONFIG_SPL_BUILD 422b13cc84cSFinley Xiao { 423492844a3SFinley Xiao .compatible = "rockchip,rk3288-secure-efuse", 424492844a3SFinley Xiao .data = (ulong)&rockchip_rk3288_efuse_secure_read, 425b4fa32f0SFrancis Fan }, 426492844a3SFinley Xiao #endif 427b4fa32f0SFrancis Fan { 428b4fa32f0SFrancis Fan .compatible = "rockchip,rk3066a-efuse", 429e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 430b4fa32f0SFrancis Fan }, 431b4fa32f0SFrancis Fan { 432b4fa32f0SFrancis Fan .compatible = "rockchip,rk3188-efuse", 433e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 434b4fa32f0SFrancis Fan }, 435b4fa32f0SFrancis Fan { 436b4fa32f0SFrancis Fan .compatible = "rockchip,rk322x-efuse", 437e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 438b4fa32f0SFrancis Fan }, 439b4fa32f0SFrancis Fan { 44059a83996SJoseph Chen .compatible = "rockchip,rk3328-efuse", 44159a83996SJoseph Chen .data = (ulong)&rockchip_rk3328_efuse_read, 44259a83996SJoseph Chen }, 44359a83996SJoseph Chen { 444b4fa32f0SFrancis Fan .compatible = "rockchip,rk3399-efuse", 445b4fa32f0SFrancis Fan .data = (ulong)&rockchip_rk3399_efuse_read, 446b4fa32f0SFrancis Fan }, 44749cd8e85SPhilipp Tomsich {} 44849cd8e85SPhilipp Tomsich }; 44949cd8e85SPhilipp Tomsich 45049cd8e85SPhilipp Tomsich U_BOOT_DRIVER(rockchip_efuse) = { 45149cd8e85SPhilipp Tomsich .name = "rockchip_efuse", 45249cd8e85SPhilipp Tomsich .id = UCLASS_MISC, 45349cd8e85SPhilipp Tomsich .of_match = rockchip_efuse_ids, 45449cd8e85SPhilipp Tomsich .ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata, 45549cd8e85SPhilipp Tomsich .platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata), 45649cd8e85SPhilipp Tomsich .ops = &rockchip_efuse_ops, 45749cd8e85SPhilipp Tomsich }; 458