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> 1549cd8e85SPhilipp Tomsich #include <linux/bitops.h> 1649cd8e85SPhilipp Tomsich #include <linux/delay.h> 1749cd8e85SPhilipp Tomsich #include <misc.h> 1849cd8e85SPhilipp Tomsich 1949cd8e85SPhilipp Tomsich #define RK3399_A_SHIFT 16 2049cd8e85SPhilipp Tomsich #define RK3399_A_MASK 0x3ff 2149cd8e85SPhilipp Tomsich #define RK3399_NFUSES 32 2249cd8e85SPhilipp Tomsich #define RK3399_BYTES_PER_FUSE 4 2349cd8e85SPhilipp Tomsich #define RK3399_STROBSFTSEL BIT(9) 2449cd8e85SPhilipp Tomsich #define RK3399_RSB BIT(7) 2549cd8e85SPhilipp Tomsich #define RK3399_PD BIT(5) 2649cd8e85SPhilipp Tomsich #define RK3399_PGENB BIT(3) 2749cd8e85SPhilipp Tomsich #define RK3399_LOAD BIT(2) 2849cd8e85SPhilipp Tomsich #define RK3399_STROBE BIT(1) 2949cd8e85SPhilipp Tomsich #define RK3399_CSB BIT(0) 3049cd8e85SPhilipp Tomsich 31b4fa32f0SFrancis Fan #define RK3288_A_SHIFT 6 32b4fa32f0SFrancis Fan #define RK3288_A_MASK 0x3ff 33b4fa32f0SFrancis Fan #define RK3288_NFUSES 32 34b4fa32f0SFrancis Fan #define RK3288_BYTES_PER_FUSE 1 35b4fa32f0SFrancis Fan #define RK3288_PGENB BIT(3) 36b4fa32f0SFrancis Fan #define RK3288_LOAD BIT(2) 37b4fa32f0SFrancis Fan #define RK3288_STROBE BIT(1) 38b4fa32f0SFrancis Fan #define RK3288_CSB BIT(0) 39b4fa32f0SFrancis Fan 40*59a83996SJoseph Chen #define RK3328_INT_STATUS 0x0018 41*59a83996SJoseph Chen #define RK3328_DOUT 0x0020 42*59a83996SJoseph Chen #define RK3328_AUTO_CTRL 0x0024 43*59a83996SJoseph Chen #define RK3328_INT_FINISH BIT(0) 44*59a83996SJoseph Chen #define RK3328_AUTO_ENB BIT(0) 45*59a83996SJoseph Chen #define RK3328_AUTO_RD BIT(1) 46*59a83996SJoseph Chen 47b4fa32f0SFrancis Fan typedef int (*EFUSE_READ)(struct udevice *dev, int offset, void *buf, int size); 48b4fa32f0SFrancis Fan 4949cd8e85SPhilipp Tomsich struct rockchip_efuse_regs { 5049cd8e85SPhilipp Tomsich u32 ctrl; /* 0x00 efuse control register */ 5149cd8e85SPhilipp Tomsich u32 dout; /* 0x04 efuse data out register */ 5249cd8e85SPhilipp Tomsich u32 rf; /* 0x08 efuse redundancy bit used register */ 5349cd8e85SPhilipp Tomsich u32 _rsvd0; 5449cd8e85SPhilipp Tomsich u32 jtag_pass; /* 0x10 JTAG password */ 5549cd8e85SPhilipp Tomsich u32 strobe_finish_ctrl; 5649cd8e85SPhilipp Tomsich /* 0x14 efuse strobe finish control register */ 57*59a83996SJoseph Chen u32 int_status;/* 0x18 */ 58*59a83996SJoseph Chen u32 reserved; /* 0x1c */ 59*59a83996SJoseph Chen u32 dout2; /* 0x20 */ 60*59a83996SJoseph Chen u32 auto_ctrl; /* 0x24 */ 6149cd8e85SPhilipp Tomsich }; 6249cd8e85SPhilipp Tomsich 6349cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata { 6449cd8e85SPhilipp Tomsich void __iomem *base; 6549cd8e85SPhilipp Tomsich struct clk *clk; 6649cd8e85SPhilipp Tomsich }; 6749cd8e85SPhilipp Tomsich 6849cd8e85SPhilipp Tomsich #if defined(DEBUG) 6949cd8e85SPhilipp Tomsich static int dump_efuses(cmd_tbl_t *cmdtp, int flag, 7049cd8e85SPhilipp Tomsich int argc, char * const argv[]) 7149cd8e85SPhilipp Tomsich { 7249cd8e85SPhilipp Tomsich /* 7349cd8e85SPhilipp Tomsich * N.B.: This function is tailored towards the RK3399 and assumes that 7449cd8e85SPhilipp Tomsich * there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to 7549cd8e85SPhilipp Tomsich * be read. 7649cd8e85SPhilipp Tomsich */ 7749cd8e85SPhilipp Tomsich 7849cd8e85SPhilipp Tomsich struct udevice *dev; 79b4fa32f0SFrancis Fan u8 fuses[128] = {0}; 8049cd8e85SPhilipp Tomsich int ret; 8149cd8e85SPhilipp Tomsich 8249cd8e85SPhilipp Tomsich /* retrieve the device */ 8349cd8e85SPhilipp Tomsich ret = uclass_get_device_by_driver(UCLASS_MISC, 8449cd8e85SPhilipp Tomsich DM_GET_DRIVER(rockchip_efuse), &dev); 8549cd8e85SPhilipp Tomsich if (ret) { 8649cd8e85SPhilipp Tomsich printf("%s: no misc-device found\n", __func__); 8749cd8e85SPhilipp Tomsich return 0; 8849cd8e85SPhilipp Tomsich } 8949cd8e85SPhilipp Tomsich 9049cd8e85SPhilipp Tomsich ret = misc_read(dev, 0, &fuses, sizeof(fuses)); 9149cd8e85SPhilipp Tomsich if (ret) { 9249cd8e85SPhilipp Tomsich printf("%s: misc_read failed\n", __func__); 9349cd8e85SPhilipp Tomsich return 0; 9449cd8e85SPhilipp Tomsich } 9549cd8e85SPhilipp Tomsich 9649cd8e85SPhilipp Tomsich printf("efuse-contents:\n"); 9749cd8e85SPhilipp Tomsich print_buffer(0, fuses, 1, 128, 16); 9849cd8e85SPhilipp Tomsich 9949cd8e85SPhilipp Tomsich return 0; 10049cd8e85SPhilipp Tomsich } 10149cd8e85SPhilipp Tomsich 10249cd8e85SPhilipp Tomsich U_BOOT_CMD( 103b4fa32f0SFrancis Fan rockchip_dump_efuses, 1, 1, dump_efuses, 10449cd8e85SPhilipp Tomsich "Dump the content of the efuses", 10549cd8e85SPhilipp Tomsich "" 10649cd8e85SPhilipp Tomsich ); 10749cd8e85SPhilipp Tomsich #endif 10849cd8e85SPhilipp Tomsich 10949cd8e85SPhilipp Tomsich static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, 11049cd8e85SPhilipp Tomsich void *buf, int size) 11149cd8e85SPhilipp Tomsich { 11249cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 11349cd8e85SPhilipp Tomsich struct rockchip_efuse_regs *efuse = 11449cd8e85SPhilipp Tomsich (struct rockchip_efuse_regs *)plat->base; 11549cd8e85SPhilipp Tomsich 11649cd8e85SPhilipp Tomsich unsigned int addr_start, addr_end, addr_offset; 11749cd8e85SPhilipp Tomsich u32 out_value; 11849cd8e85SPhilipp Tomsich u8 bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE]; 11949cd8e85SPhilipp Tomsich int i = 0; 12049cd8e85SPhilipp Tomsich u32 addr; 12149cd8e85SPhilipp Tomsich 12249cd8e85SPhilipp Tomsich addr_start = offset / RK3399_BYTES_PER_FUSE; 12349cd8e85SPhilipp Tomsich addr_offset = offset % RK3399_BYTES_PER_FUSE; 12449cd8e85SPhilipp Tomsich addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE); 12549cd8e85SPhilipp Tomsich 12649cd8e85SPhilipp Tomsich /* cap to the size of the efuse block */ 12749cd8e85SPhilipp Tomsich if (addr_end > RK3399_NFUSES) 12849cd8e85SPhilipp Tomsich addr_end = RK3399_NFUSES; 12949cd8e85SPhilipp Tomsich 13049cd8e85SPhilipp Tomsich writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 13149cd8e85SPhilipp Tomsich &efuse->ctrl); 13249cd8e85SPhilipp Tomsich udelay(1); 13349cd8e85SPhilipp Tomsich for (addr = addr_start; addr < addr_end; addr++) { 13449cd8e85SPhilipp Tomsich setbits_le32(&efuse->ctrl, 13549cd8e85SPhilipp Tomsich RK3399_STROBE | (addr << RK3399_A_SHIFT)); 13649cd8e85SPhilipp Tomsich udelay(1); 13749cd8e85SPhilipp Tomsich out_value = readl(&efuse->dout); 13849cd8e85SPhilipp Tomsich clrbits_le32(&efuse->ctrl, RK3399_STROBE); 13949cd8e85SPhilipp Tomsich udelay(1); 14049cd8e85SPhilipp Tomsich 14149cd8e85SPhilipp Tomsich memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE); 14249cd8e85SPhilipp Tomsich i += RK3399_BYTES_PER_FUSE; 14349cd8e85SPhilipp Tomsich } 14449cd8e85SPhilipp Tomsich 14549cd8e85SPhilipp Tomsich /* Switch to standby mode */ 14649cd8e85SPhilipp Tomsich writel(RK3399_PD | RK3399_CSB, &efuse->ctrl); 14749cd8e85SPhilipp Tomsich 14849cd8e85SPhilipp Tomsich memcpy(buf, bytes + addr_offset, size); 14949cd8e85SPhilipp Tomsich 15049cd8e85SPhilipp Tomsich return 0; 15149cd8e85SPhilipp Tomsich } 15249cd8e85SPhilipp Tomsich 153b4fa32f0SFrancis Fan static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, 154b4fa32f0SFrancis Fan void *buf, int size) 155b4fa32f0SFrancis Fan { 156b4fa32f0SFrancis Fan struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 157b4fa32f0SFrancis Fan struct rockchip_efuse_regs *efuse = 158b4fa32f0SFrancis Fan (struct rockchip_efuse_regs *)plat->base; 159b4fa32f0SFrancis Fan u8 *buffer = buf; 160b4fa32f0SFrancis Fan int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE; 161b4fa32f0SFrancis Fan 162b4fa32f0SFrancis Fan if (size > (max_size - offset)) 163b4fa32f0SFrancis Fan size = max_size - offset; 164b4fa32f0SFrancis Fan 165b4fa32f0SFrancis Fan /* Switch to read mode */ 166b4fa32f0SFrancis Fan writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl); 167b4fa32f0SFrancis Fan udelay(1); 168b4fa32f0SFrancis Fan 169b4fa32f0SFrancis Fan while (size--) { 170b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) & 171b4fa32f0SFrancis Fan (~(RK3288_A_MASK << RK3288_A_SHIFT)), 172b4fa32f0SFrancis Fan &efuse->ctrl); 173b4fa32f0SFrancis Fan /* set addr */ 174b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) | 175b4fa32f0SFrancis Fan ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 176b4fa32f0SFrancis Fan &efuse->ctrl); 177b4fa32f0SFrancis Fan udelay(1); 178b4fa32f0SFrancis Fan /* strobe low to high */ 179b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) | 180b4fa32f0SFrancis Fan RK3288_STROBE, &efuse->ctrl); 181b4fa32f0SFrancis Fan ndelay(60); 182b4fa32f0SFrancis Fan /* read data */ 183b4fa32f0SFrancis Fan *buffer++ = readl(&efuse->dout); 184b4fa32f0SFrancis Fan /* reset strobe to low */ 185b4fa32f0SFrancis Fan writel(readl(&efuse->ctrl) & 186b4fa32f0SFrancis Fan (~RK3288_STROBE), &efuse->ctrl); 187b4fa32f0SFrancis Fan udelay(1); 188b4fa32f0SFrancis Fan } 189b4fa32f0SFrancis Fan 190b4fa32f0SFrancis Fan /* Switch to standby mode */ 191b4fa32f0SFrancis Fan writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl); 192b4fa32f0SFrancis Fan 193b4fa32f0SFrancis Fan return 0; 194b4fa32f0SFrancis Fan } 195b4fa32f0SFrancis Fan 196*59a83996SJoseph Chen static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset, 197*59a83996SJoseph Chen void *buf, int size) 198*59a83996SJoseph Chen { 199*59a83996SJoseph Chen struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 200*59a83996SJoseph Chen struct rockchip_efuse_regs *efuse = 201*59a83996SJoseph Chen (struct rockchip_efuse_regs *)plat->base; 202*59a83996SJoseph Chen unsigned int addr_start, addr_end, addr_offset, addr_len; 203*59a83996SJoseph Chen u32 out_value, status; 204*59a83996SJoseph Chen u8 *buffer; 205*59a83996SJoseph Chen int ret = 0, i = 0, j = 0; 206*59a83996SJoseph Chen 207*59a83996SJoseph Chen /* Max non-secure Byte */ 208*59a83996SJoseph Chen if (size > 32) 209*59a83996SJoseph Chen size = 32; 210*59a83996SJoseph Chen 211*59a83996SJoseph Chen /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ 212*59a83996SJoseph Chen offset += 96; 213*59a83996SJoseph Chen addr_start = rounddown(offset, RK3399_BYTES_PER_FUSE) / 214*59a83996SJoseph Chen RK3399_BYTES_PER_FUSE; 215*59a83996SJoseph Chen addr_end = roundup(offset + size, RK3399_BYTES_PER_FUSE) / 216*59a83996SJoseph Chen RK3399_BYTES_PER_FUSE; 217*59a83996SJoseph Chen addr_offset = offset % RK3399_BYTES_PER_FUSE; 218*59a83996SJoseph Chen addr_len = addr_end - addr_start; 219*59a83996SJoseph Chen 220*59a83996SJoseph Chen buffer = calloc(1, sizeof(*buffer) * addr_len * RK3399_BYTES_PER_FUSE); 221*59a83996SJoseph Chen if (!buffer) 222*59a83996SJoseph Chen return -ENOMEM; 223*59a83996SJoseph Chen 224*59a83996SJoseph Chen for (j = 0; j < addr_len; j++) { 225*59a83996SJoseph Chen writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | 226*59a83996SJoseph Chen ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 227*59a83996SJoseph Chen &efuse->auto_ctrl); 228*59a83996SJoseph Chen udelay(5); 229*59a83996SJoseph Chen status = readl(&efuse->int_status); 230*59a83996SJoseph Chen if (!(status & RK3328_INT_FINISH)) { 231*59a83996SJoseph Chen ret = -EIO; 232*59a83996SJoseph Chen goto err; 233*59a83996SJoseph Chen } 234*59a83996SJoseph Chen out_value = readl(&efuse->dout2); 235*59a83996SJoseph Chen writel(RK3328_INT_FINISH, &efuse->int_status); 236*59a83996SJoseph Chen 237*59a83996SJoseph Chen memcpy(&buffer[i], &out_value, RK3399_BYTES_PER_FUSE); 238*59a83996SJoseph Chen i += RK3399_BYTES_PER_FUSE; 239*59a83996SJoseph Chen } 240*59a83996SJoseph Chen memcpy(buf, buffer + addr_offset, size); 241*59a83996SJoseph Chen err: 242*59a83996SJoseph Chen free(buffer); 243*59a83996SJoseph Chen 244*59a83996SJoseph Chen return ret; 245*59a83996SJoseph Chen } 246*59a83996SJoseph Chen 24749cd8e85SPhilipp Tomsich static int rockchip_efuse_read(struct udevice *dev, int offset, 24849cd8e85SPhilipp Tomsich void *buf, int size) 24949cd8e85SPhilipp Tomsich { 250b4fa32f0SFrancis Fan EFUSE_READ efuse_read = NULL; 251b4fa32f0SFrancis Fan 252b4fa32f0SFrancis Fan efuse_read = (EFUSE_READ)dev_get_driver_data(dev); 253b4fa32f0SFrancis Fan if (!efuse_read) 254b4fa32f0SFrancis Fan return -ENOSYS; 255b4fa32f0SFrancis Fan 256b4fa32f0SFrancis Fan return (*efuse_read)(dev, offset, buf, size); 25749cd8e85SPhilipp Tomsich } 25849cd8e85SPhilipp Tomsich 25949cd8e85SPhilipp Tomsich static const struct misc_ops rockchip_efuse_ops = { 26049cd8e85SPhilipp Tomsich .read = rockchip_efuse_read, 26149cd8e85SPhilipp Tomsich }; 26249cd8e85SPhilipp Tomsich 26349cd8e85SPhilipp Tomsich static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev) 26449cd8e85SPhilipp Tomsich { 26549cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 26649cd8e85SPhilipp Tomsich 26763a453e6SPhilipp Tomsich plat->base = dev_read_addr_ptr(dev); 26849cd8e85SPhilipp Tomsich return 0; 26949cd8e85SPhilipp Tomsich } 27049cd8e85SPhilipp Tomsich 27149cd8e85SPhilipp Tomsich static const struct udevice_id rockchip_efuse_ids[] = { 272b4fa32f0SFrancis Fan { 273b4fa32f0SFrancis Fan .compatible = "rockchip,rockchip-efuse", 274e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 275b4fa32f0SFrancis Fan }, 276b4fa32f0SFrancis Fan { 277b4fa32f0SFrancis Fan .compatible = "rockchip,rk3066a-efuse", 278e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 279b4fa32f0SFrancis Fan }, 280b4fa32f0SFrancis Fan { 281b4fa32f0SFrancis Fan .compatible = "rockchip,rk3188-efuse", 282e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 283b4fa32f0SFrancis Fan }, 284b4fa32f0SFrancis Fan { 285b4fa32f0SFrancis Fan .compatible = "rockchip,rk322x-efuse", 286e9cfb932SCody Xie .data = (ulong)&rockchip_rk3288_efuse_read, 287b4fa32f0SFrancis Fan }, 288b4fa32f0SFrancis Fan { 289*59a83996SJoseph Chen .compatible = "rockchip,rk3328-efuse", 290*59a83996SJoseph Chen .data = (ulong)&rockchip_rk3328_efuse_read, 291*59a83996SJoseph Chen }, 292*59a83996SJoseph Chen { 293b4fa32f0SFrancis Fan .compatible = "rockchip,rk3399-efuse", 294b4fa32f0SFrancis Fan .data = (ulong)&rockchip_rk3399_efuse_read, 295b4fa32f0SFrancis Fan }, 29649cd8e85SPhilipp Tomsich {} 29749cd8e85SPhilipp Tomsich }; 29849cd8e85SPhilipp Tomsich 29949cd8e85SPhilipp Tomsich U_BOOT_DRIVER(rockchip_efuse) = { 30049cd8e85SPhilipp Tomsich .name = "rockchip_efuse", 30149cd8e85SPhilipp Tomsich .id = UCLASS_MISC, 30249cd8e85SPhilipp Tomsich .of_match = rockchip_efuse_ids, 30349cd8e85SPhilipp Tomsich .ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata, 30449cd8e85SPhilipp Tomsich .platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata), 30549cd8e85SPhilipp Tomsich .ops = &rockchip_efuse_ops, 30649cd8e85SPhilipp Tomsich }; 307