1 /* 2 * eFuse driver for Rockchip devices 3 * 4 * Copyright 2017, Theobroma Systems Design und Consulting GmbH 5 * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com> 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <asm/io.h> 12 #include <command.h> 13 #include <display_options.h> 14 #include <dm.h> 15 #include <linux/bitops.h> 16 #include <linux/delay.h> 17 #include <misc.h> 18 19 #define RK3399_A_SHIFT 16 20 #define RK3399_A_MASK 0x3ff 21 #define RK3399_NFUSES 32 22 #define RK3399_BYTES_PER_FUSE 4 23 #define RK3399_STROBSFTSEL BIT(9) 24 #define RK3399_RSB BIT(7) 25 #define RK3399_PD BIT(5) 26 #define RK3399_PGENB BIT(3) 27 #define RK3399_LOAD BIT(2) 28 #define RK3399_STROBE BIT(1) 29 #define RK3399_CSB BIT(0) 30 31 #define RK3288_A_SHIFT 6 32 #define RK3288_A_MASK 0x3ff 33 #define RK3288_NFUSES 32 34 #define RK3288_BYTES_PER_FUSE 1 35 #define RK3288_PGENB BIT(3) 36 #define RK3288_LOAD BIT(2) 37 #define RK3288_STROBE BIT(1) 38 #define RK3288_CSB BIT(0) 39 40 typedef int (*EFUSE_READ)(struct udevice *dev, int offset, void *buf, int size); 41 42 struct rockchip_efuse_regs { 43 u32 ctrl; /* 0x00 efuse control register */ 44 u32 dout; /* 0x04 efuse data out register */ 45 u32 rf; /* 0x08 efuse redundancy bit used register */ 46 u32 _rsvd0; 47 u32 jtag_pass; /* 0x10 JTAG password */ 48 u32 strobe_finish_ctrl; 49 /* 0x14 efuse strobe finish control register */ 50 }; 51 52 struct rockchip_efuse_platdata { 53 void __iomem *base; 54 struct clk *clk; 55 }; 56 57 #if defined(DEBUG) 58 static int dump_efuses(cmd_tbl_t *cmdtp, int flag, 59 int argc, char * const argv[]) 60 { 61 /* 62 * N.B.: This function is tailored towards the RK3399 and assumes that 63 * there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to 64 * be read. 65 */ 66 67 struct udevice *dev; 68 u8 fuses[128] = {0}; 69 int ret; 70 71 /* retrieve the device */ 72 ret = uclass_get_device_by_driver(UCLASS_MISC, 73 DM_GET_DRIVER(rockchip_efuse), &dev); 74 if (ret) { 75 printf("%s: no misc-device found\n", __func__); 76 return 0; 77 } 78 79 ret = misc_read(dev, 0, &fuses, sizeof(fuses)); 80 if (ret) { 81 printf("%s: misc_read failed\n", __func__); 82 return 0; 83 } 84 85 printf("efuse-contents:\n"); 86 print_buffer(0, fuses, 1, 128, 16); 87 88 return 0; 89 } 90 91 U_BOOT_CMD( 92 rockchip_dump_efuses, 1, 1, dump_efuses, 93 "Dump the content of the efuses", 94 "" 95 ); 96 #endif 97 98 static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, 99 void *buf, int size) 100 { 101 struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 102 struct rockchip_efuse_regs *efuse = 103 (struct rockchip_efuse_regs *)plat->base; 104 105 unsigned int addr_start, addr_end, addr_offset; 106 u32 out_value; 107 u8 bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE]; 108 int i = 0; 109 u32 addr; 110 111 addr_start = offset / RK3399_BYTES_PER_FUSE; 112 addr_offset = offset % RK3399_BYTES_PER_FUSE; 113 addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE); 114 115 /* cap to the size of the efuse block */ 116 if (addr_end > RK3399_NFUSES) 117 addr_end = RK3399_NFUSES; 118 119 writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 120 &efuse->ctrl); 121 udelay(1); 122 for (addr = addr_start; addr < addr_end; addr++) { 123 setbits_le32(&efuse->ctrl, 124 RK3399_STROBE | (addr << RK3399_A_SHIFT)); 125 udelay(1); 126 out_value = readl(&efuse->dout); 127 clrbits_le32(&efuse->ctrl, RK3399_STROBE); 128 udelay(1); 129 130 memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE); 131 i += RK3399_BYTES_PER_FUSE; 132 } 133 134 /* Switch to standby mode */ 135 writel(RK3399_PD | RK3399_CSB, &efuse->ctrl); 136 137 memcpy(buf, bytes + addr_offset, size); 138 139 return 0; 140 } 141 142 static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, 143 void *buf, int size) 144 { 145 struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 146 struct rockchip_efuse_regs *efuse = 147 (struct rockchip_efuse_regs *)plat->base; 148 u8 *buffer = buf; 149 int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE; 150 151 if (size > (max_size - offset)) 152 size = max_size - offset; 153 154 /* Switch to read mode */ 155 writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl); 156 udelay(1); 157 158 while (size--) { 159 writel(readl(&efuse->ctrl) & 160 (~(RK3288_A_MASK << RK3288_A_SHIFT)), 161 &efuse->ctrl); 162 /* set addr */ 163 writel(readl(&efuse->ctrl) | 164 ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 165 &efuse->ctrl); 166 udelay(1); 167 /* strobe low to high */ 168 writel(readl(&efuse->ctrl) | 169 RK3288_STROBE, &efuse->ctrl); 170 ndelay(60); 171 /* read data */ 172 *buffer++ = readl(&efuse->dout); 173 /* reset strobe to low */ 174 writel(readl(&efuse->ctrl) & 175 (~RK3288_STROBE), &efuse->ctrl); 176 udelay(1); 177 } 178 179 /* Switch to standby mode */ 180 writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl); 181 182 return 0; 183 } 184 185 static int rockchip_efuse_read(struct udevice *dev, int offset, 186 void *buf, int size) 187 { 188 EFUSE_READ efuse_read = NULL; 189 190 efuse_read = (EFUSE_READ)dev_get_driver_data(dev); 191 if (!efuse_read) 192 return -ENOSYS; 193 194 return (*efuse_read)(dev, offset, buf, size); 195 } 196 197 static const struct misc_ops rockchip_efuse_ops = { 198 .read = rockchip_efuse_read, 199 }; 200 201 static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev) 202 { 203 struct rockchip_efuse_platdata *plat = dev_get_platdata(dev); 204 205 plat->base = dev_read_addr_ptr(dev); 206 return 0; 207 } 208 209 static const struct udevice_id rockchip_efuse_ids[] = { 210 { 211 .compatible = "rockchip,rockchip-efuse", 212 .data = (ulong)&rockchip_rk3288_efuse_read, 213 }, 214 { 215 .compatible = "rockchip,rk3066a-efuse", 216 .data = (ulong)&rockchip_rk3288_efuse_read, 217 }, 218 { 219 .compatible = "rockchip,rk3188-efuse", 220 .data = (ulong)&rockchip_rk3288_efuse_read, 221 }, 222 { 223 .compatible = "rockchip,rk322x-efuse", 224 .data = (ulong)&rockchip_rk3288_efuse_read, 225 }, 226 { 227 .compatible = "rockchip,rk3399-efuse", 228 .data = (ulong)&rockchip_rk3399_efuse_read, 229 }, 230 {} 231 }; 232 233 U_BOOT_DRIVER(rockchip_efuse) = { 234 .name = "rockchip_efuse", 235 .id = UCLASS_MISC, 236 .of_match = rockchip_efuse_ids, 237 .ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata, 238 .platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata), 239 .ops = &rockchip_efuse_ops, 240 }; 241