1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * eFuse driver for Rockchip devices 4 * 5 * Copyright 2017, Theobroma Systems Design und Consulting GmbH 6 * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com> 7 */ 8 9#include <common.h> 10#include <asm/io.h> 11#include <command.h> 12#include <display_options.h> 13#include <dm.h> 14#include <linux/bitops.h> 15#include <linux/delay.h> 16#include <linux/iopoll.h> 17#include <malloc.h> 18#include <misc.h> 19 20#define EFUSE_CTRL 0x0000 21#define RK3036_A_SHIFT 8 22#define RK3036_A_MASK GENMASK(15, 8) 23#define RK3036_ADDR(n) ((n) << RK3036_A_SHIFT) 24#define RK3128_A_SHIFT 7 25#define RK3128_A_MASK GENMASK(15, 7) 26#define RK3128_ADDR(n) ((n) << RK3128_A_SHIFT) 27#define RK3288_A_SHIFT 6 28#define RK3288_A_MASK GENMASK(15, 6) 29#define RK3288_ADDR(n) ((n) << RK3288_A_SHIFT) 30#define RK3399_A_SHIFT 16 31#define RK3399_A_MASK GENMASK(25, 16) 32#define RK3399_ADDR(n) ((n) << RK3399_A_SHIFT) 33#define RK3399_STROBSFTSEL BIT(9) 34#define RK3399_RSB BIT(7) 35#define RK3399_PD BIT(5) 36#define EFUSE_PGENB BIT(3) 37#define EFUSE_LOAD BIT(2) 38#define EFUSE_STROBE BIT(1) 39#define EFUSE_CSB BIT(0) 40#define EFUSE_DOUT 0x0004 41#define RK3328_INT_STATUS 0x0018 42#define RK3328_INT_FINISH BIT(0) 43#define RK3328_DOUT 0x0020 44#define RK3328_AUTO_CTRL 0x0024 45#define RK3328_AUTO_RD BIT(1) 46#define RK3328_AUTO_ENB BIT(0) 47 48struct rockchip_efuse_plat { 49 void __iomem *base; 50}; 51 52struct rockchip_efuse_data { 53 int (*read)(struct udevice *dev, int offset, void *buf, int size); 54 int offset; 55 int size; 56 int block_size; 57}; 58 59#if defined(DEBUG) 60static int dump_efuse(struct cmd_tbl *cmdtp, int flag, 61 int argc, char *const argv[]) 62{ 63 struct udevice *dev; 64 u8 data[4]; 65 int ret, i; 66 67 ret = uclass_get_device_by_driver(UCLASS_MISC, 68 DM_DRIVER_GET(rockchip_efuse), &dev); 69 if (ret) { 70 printf("%s: no misc-device found\n", __func__); 71 return 0; 72 } 73 74 for (i = 0; true; i += sizeof(data)) { 75 ret = misc_read(dev, i, &data, sizeof(data)); 76 if (ret <= 0) 77 return 0; 78 79 print_buffer(i, data, 1, sizeof(data), sizeof(data)); 80 } 81 82 return 0; 83} 84 85U_BOOT_CMD( 86 dump_efuse, 1, 1, dump_efuse, 87 "Dump the content of the efuse", 88 "" 89); 90#endif 91 92static int rockchip_rk3036_efuse_read(struct udevice *dev, int offset, 93 void *buf, int size) 94{ 95 struct rockchip_efuse_plat *efuse = dev_get_plat(dev); 96 u8 *buffer = buf; 97 98 /* Switch to read mode */ 99 writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL); 100 udelay(2); 101 102 while (size--) { 103 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3036_A_MASK, 104 RK3036_ADDR(offset++)); 105 udelay(2); 106 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 107 udelay(2); 108 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF); 109 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 110 udelay(2); 111 } 112 113 /* Switch to inactive mode */ 114 writel(0x0, efuse->base + EFUSE_CTRL); 115 116 return 0; 117} 118 119static int rockchip_rk3128_efuse_read(struct udevice *dev, int offset, 120 void *buf, int size) 121{ 122 struct rockchip_efuse_plat *efuse = dev_get_plat(dev); 123 u8 *buffer = buf; 124 125 /* Switch to read mode */ 126 writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL); 127 udelay(2); 128 129 while (size--) { 130 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3128_A_MASK, 131 RK3128_ADDR(offset++)); 132 udelay(2); 133 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 134 udelay(2); 135 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF); 136 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 137 udelay(2); 138 } 139 140 /* Switch to inactive mode */ 141 writel(0x0, efuse->base + EFUSE_CTRL); 142 143 return 0; 144} 145 146static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset, 147 void *buf, int size) 148{ 149 struct rockchip_efuse_plat *efuse = dev_get_plat(dev); 150 u8 *buffer = buf; 151 152 /* Switch to read mode */ 153 writel(EFUSE_CSB, efuse->base + EFUSE_CTRL); 154 writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + EFUSE_CTRL); 155 udelay(2); 156 157 while (size--) { 158 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3288_A_MASK, 159 RK3288_ADDR(offset++)); 160 udelay(2); 161 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 162 udelay(2); 163 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF); 164 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 165 udelay(2); 166 } 167 168 /* Switch to standby mode */ 169 writel(EFUSE_CSB | EFUSE_PGENB, efuse->base + EFUSE_CTRL); 170 171 return 0; 172} 173 174static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset, 175 void *buf, int size) 176{ 177 struct rockchip_efuse_plat *efuse = dev_get_plat(dev); 178 u32 status, *buffer = buf; 179 int ret; 180 181 while (size--) { 182 writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | RK3399_ADDR(offset++), 183 efuse->base + RK3328_AUTO_CTRL); 184 udelay(1); 185 186 ret = readl_poll_sleep_timeout(efuse->base + RK3328_INT_STATUS, 187 status, (status & RK3328_INT_FINISH), 1, 50); 188 if (ret) 189 return ret; 190 191 *buffer++ = readl(efuse->base + RK3328_DOUT); 192 writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS); 193 } 194 195 return 0; 196} 197 198static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset, 199 void *buf, int size) 200{ 201 struct rockchip_efuse_plat *efuse = dev_get_plat(dev); 202 u32 *buffer = buf; 203 204 /* Switch to array read mode */ 205 writel(EFUSE_LOAD | EFUSE_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 206 efuse->base + EFUSE_CTRL); 207 udelay(1); 208 209 while (size--) { 210 setbits_le32(efuse->base + EFUSE_CTRL, 211 EFUSE_STROBE | RK3399_ADDR(offset++)); 212 udelay(1); 213 *buffer++ = readl(efuse->base + EFUSE_DOUT); 214 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE); 215 udelay(1); 216 } 217 218 /* Switch to power-down mode */ 219 writel(RK3399_PD | EFUSE_CSB, efuse->base + EFUSE_CTRL); 220 221 return 0; 222} 223 224static int rockchip_efuse_read(struct udevice *dev, int offset, 225 void *buf, int size) 226{ 227 const struct rockchip_efuse_data *data = 228 (void *)dev_get_driver_data(dev); 229 u32 block_start, block_end, block_offset, blocks; 230 u8 *buffer; 231 int ret; 232 233 if (offset < 0 || !buf || size <= 0 || offset + size > data->size) 234 return -EINVAL; 235 236 if (!data->read) 237 return -ENOSYS; 238 239 offset += data->offset; 240 241 if (data->block_size <= 1) { 242 ret = data->read(dev, offset, buf, size); 243 goto done; 244 } 245 246 block_start = offset / data->block_size; 247 block_offset = offset % data->block_size; 248 block_end = DIV_ROUND_UP(offset + size, data->block_size); 249 blocks = block_end - block_start; 250 251 buffer = calloc(blocks, data->block_size); 252 if (!buffer) 253 return -ENOMEM; 254 255 ret = data->read(dev, block_start, buffer, blocks); 256 if (!ret) 257 memcpy(buf, buffer + block_offset, size); 258 259 free(buffer); 260 261done: 262 return ret < 0 ? ret : size; 263} 264 265static const struct misc_ops rockchip_efuse_ops = { 266 .read = rockchip_efuse_read, 267}; 268 269static int rockchip_efuse_of_to_plat(struct udevice *dev) 270{ 271 struct rockchip_efuse_plat *plat = dev_get_plat(dev); 272 273 plat->base = dev_read_addr_ptr(dev); 274 275 return 0; 276} 277 278static const struct rockchip_efuse_data rk3036_data = { 279 .read = rockchip_rk3036_efuse_read, 280 .size = 0x20, 281}; 282 283static const struct rockchip_efuse_data rk3128_data = { 284 .read = rockchip_rk3128_efuse_read, 285 .size = 0x40, 286}; 287 288static const struct rockchip_efuse_data rk3288_data = { 289 .read = rockchip_rk3288_efuse_read, 290 .size = 0x20, 291}; 292 293static const struct rockchip_efuse_data rk3328_data = { 294 .read = rockchip_rk3328_efuse_read, 295 .offset = 0x60, 296 .size = 0x20, 297 .block_size = 4, 298}; 299 300static const struct rockchip_efuse_data rk3399_data = { 301 .read = rockchip_rk3399_efuse_read, 302 .size = 0x80, 303 .block_size = 4, 304}; 305 306static const struct udevice_id rockchip_efuse_ids[] = { 307 { 308 .compatible = "rockchip,rk3036-efuse", 309 .data = (ulong)&rk3036_data, 310 }, 311 { 312 .compatible = "rockchip,rk3066a-efuse", 313 .data = (ulong)&rk3288_data, 314 }, 315 { 316 .compatible = "rockchip,rk3128-efuse", 317 .data = (ulong)&rk3128_data, 318 }, 319 { 320 .compatible = "rockchip,rk3188-efuse", 321 .data = (ulong)&rk3288_data, 322 }, 323 { 324 .compatible = "rockchip,rk3228-efuse", 325 .data = (ulong)&rk3288_data, 326 }, 327 { 328 .compatible = "rockchip,rk3288-efuse", 329 .data = (ulong)&rk3288_data, 330 }, 331 { 332 .compatible = "rockchip,rk3328-efuse", 333 .data = (ulong)&rk3328_data, 334 }, 335 { 336 .compatible = "rockchip,rk3399-efuse", 337 .data = (ulong)&rk3399_data, 338 }, 339 {} 340}; 341 342U_BOOT_DRIVER(rockchip_efuse) = { 343 .name = "rockchip_efuse", 344 .id = UCLASS_MISC, 345 .of_match = rockchip_efuse_ids, 346 .of_to_plat = rockchip_efuse_of_to_plat, 347 .plat_auto = sizeof(struct rockchip_efuse_plat), 348 .ops = &rockchip_efuse_ops, 349}; 350