14973d825SFinley Xiao // SPDX-License-Identifier: GPL-2.0 24973d825SFinley Xiao /* 34973d825SFinley Xiao * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd 44973d825SFinley Xiao */ 54973d825SFinley Xiao 64973d825SFinley Xiao #include <common.h> 74973d825SFinley Xiao #include <asm/io.h> 84973d825SFinley Xiao #include <command.h> 94973d825SFinley Xiao #include <dm.h> 104973d825SFinley Xiao #include <linux/bitops.h> 114973d825SFinley Xiao #include <linux/delay.h> 124973d825SFinley Xiao #include <misc.h> 134973d825SFinley Xiao 144973d825SFinley Xiao /* OTP Register Offsets */ 154973d825SFinley Xiao #define OTPC_SBPI_CTRL 0x0020 164973d825SFinley Xiao #define OTPC_SBPI_CMD_VALID_PRE 0x0024 174973d825SFinley Xiao #define OTPC_SBPI_CS_VALID_PRE 0x0028 184973d825SFinley Xiao #define OTPC_SBPI_STATUS 0x002C 194973d825SFinley Xiao #define OTPC_USER_CTRL 0x0100 204973d825SFinley Xiao #define OTPC_USER_ADDR 0x0104 214973d825SFinley Xiao #define OTPC_USER_ENABLE 0x0108 224973d825SFinley Xiao #define OTPC_USER_Q 0x0124 234973d825SFinley Xiao #define OTPC_INT_STATUS 0x0304 244973d825SFinley Xiao #define OTPC_SBPI_CMD0_OFFSET 0x1000 254973d825SFinley Xiao #define OTPC_SBPI_CMD1_OFFSET 0x1004 264973d825SFinley Xiao 274973d825SFinley Xiao /* OTP Register bits and masks */ 284973d825SFinley Xiao #define OTPC_USER_ADDR_MASK GENMASK(31, 16) 294973d825SFinley Xiao #define OTPC_USE_USER BIT(0) 304973d825SFinley Xiao #define OTPC_USE_USER_MASK GENMASK(16, 16) 314973d825SFinley Xiao #define OTPC_USER_FSM_ENABLE BIT(0) 324973d825SFinley Xiao #define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) 334973d825SFinley Xiao #define OTPC_SBPI_DONE BIT(1) 344973d825SFinley Xiao #define OTPC_USER_DONE BIT(2) 354973d825SFinley Xiao 364973d825SFinley Xiao #define SBPI_DAP_ADDR 0x02 374973d825SFinley Xiao #define SBPI_DAP_ADDR_SHIFT 8 384973d825SFinley Xiao #define SBPI_DAP_ADDR_MASK GENMASK(31, 24) 394973d825SFinley Xiao #define SBPI_CMD_VALID_MASK GENMASK(31, 16) 404973d825SFinley Xiao #define SBPI_DAP_CMD_WRF 0xC0 414973d825SFinley Xiao #define SBPI_DAP_REG_ECC 0x3A 424973d825SFinley Xiao #define SBPI_ECC_ENABLE 0x00 434973d825SFinley Xiao #define SBPI_ECC_DISABLE 0x09 444973d825SFinley Xiao #define SBPI_ENABLE BIT(0) 454973d825SFinley Xiao #define SBPI_ENABLE_MASK GENMASK(16, 16) 464973d825SFinley Xiao 474973d825SFinley Xiao #define OTPC_TIMEOUT 10000 484973d825SFinley Xiao 494973d825SFinley Xiao typedef int (*OTP_READ)(struct udevice *dev, int offset, void *buf, int size); 504973d825SFinley Xiao 514973d825SFinley Xiao struct rockchip_otp_platdata { 524973d825SFinley Xiao void __iomem *base; 534973d825SFinley Xiao }; 544973d825SFinley Xiao 554973d825SFinley Xiao #if defined(DEBUG) 564973d825SFinley Xiao static int dump_otps(cmd_tbl_t *cmdtp, int flag, 574973d825SFinley Xiao int argc, char * const argv[]) 584973d825SFinley Xiao { 594973d825SFinley Xiao struct udevice *dev; 604973d825SFinley Xiao u8 otps[64] = {0}; 614973d825SFinley Xiao int ret; 624973d825SFinley Xiao 634973d825SFinley Xiao /* retrieve the device */ 644973d825SFinley Xiao ret = uclass_get_device_by_driver(UCLASS_MISC, 654973d825SFinley Xiao DM_GET_DRIVER(rockchip_otp), &dev); 664973d825SFinley Xiao if (ret) { 674973d825SFinley Xiao printf("%s: no misc-device found\n", __func__); 684973d825SFinley Xiao return 0; 694973d825SFinley Xiao } 704973d825SFinley Xiao 714973d825SFinley Xiao ret = misc_read(dev, 0, &otps, sizeof(otps)); 724973d825SFinley Xiao if (ret) { 734973d825SFinley Xiao printf("%s: misc_read failed\n", __func__); 744973d825SFinley Xiao return 0; 754973d825SFinley Xiao } 764973d825SFinley Xiao 774973d825SFinley Xiao printf("otp-contents:\n"); 784973d825SFinley Xiao print_buffer(0, otps, 1, 64, 16); 794973d825SFinley Xiao 804973d825SFinley Xiao return 0; 814973d825SFinley Xiao } 824973d825SFinley Xiao 834973d825SFinley Xiao U_BOOT_CMD( 844973d825SFinley Xiao rockchip_dump_otps, 1, 1, dump_otps, 854973d825SFinley Xiao "Dump the content of the otps", 864973d825SFinley Xiao "" 874973d825SFinley Xiao ); 884973d825SFinley Xiao #endif 894973d825SFinley Xiao 904973d825SFinley Xiao static int rockchip_otp_wait_status(struct rockchip_otp_platdata *otp, 914973d825SFinley Xiao u32 flag) 924973d825SFinley Xiao { 934973d825SFinley Xiao int delay = OTPC_TIMEOUT; 944973d825SFinley Xiao 954973d825SFinley Xiao while (!(readl(otp->base + OTPC_INT_STATUS) & flag)) { 964973d825SFinley Xiao udelay(1); 974973d825SFinley Xiao delay--; 984973d825SFinley Xiao if (delay <= 0) { 994973d825SFinley Xiao printf("%s: wait init status timeout\n", __func__); 1004973d825SFinley Xiao return -ETIMEDOUT; 1014973d825SFinley Xiao } 1024973d825SFinley Xiao } 1034973d825SFinley Xiao 1044973d825SFinley Xiao /* clean int status */ 1054973d825SFinley Xiao writel(flag, otp->base + OTPC_INT_STATUS); 1064973d825SFinley Xiao 1074973d825SFinley Xiao return 0; 1084973d825SFinley Xiao } 1094973d825SFinley Xiao 1104973d825SFinley Xiao static int rockchip_otp_ecc_enable(struct rockchip_otp_platdata *otp, 1114973d825SFinley Xiao bool enable) 1124973d825SFinley Xiao { 1134973d825SFinley Xiao int ret = 0; 1144973d825SFinley Xiao 1154973d825SFinley Xiao writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), 1164973d825SFinley Xiao otp->base + OTPC_SBPI_CTRL); 1174973d825SFinley Xiao 1184973d825SFinley Xiao writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); 1194973d825SFinley Xiao writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, 1204973d825SFinley Xiao otp->base + OTPC_SBPI_CMD0_OFFSET); 1214973d825SFinley Xiao if (enable) 1224973d825SFinley Xiao writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 1234973d825SFinley Xiao else 1244973d825SFinley Xiao writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 1254973d825SFinley Xiao 1264973d825SFinley Xiao writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); 1274973d825SFinley Xiao 1284973d825SFinley Xiao ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); 1294973d825SFinley Xiao if (ret < 0) 1304973d825SFinley Xiao printf("%s timeout during ecc_enable\n", __func__); 1314973d825SFinley Xiao 1324973d825SFinley Xiao return ret; 1334973d825SFinley Xiao } 1344973d825SFinley Xiao 1354973d825SFinley Xiao static int rockchip_px30_otp_read(struct udevice *dev, int offset, 1364973d825SFinley Xiao void *buf, int size) 1374973d825SFinley Xiao { 1384973d825SFinley Xiao struct rockchip_otp_platdata *otp = dev_get_platdata(dev); 1394973d825SFinley Xiao u8 *buffer = buf; 1404973d825SFinley Xiao int ret = 0; 1414973d825SFinley Xiao 1424973d825SFinley Xiao ret = rockchip_otp_ecc_enable(otp, false); 1434973d825SFinley Xiao if (ret < 0) { 1444973d825SFinley Xiao printf("%s rockchip_otp_ecc_enable err\n", __func__); 1454973d825SFinley Xiao return ret; 1464973d825SFinley Xiao } 1474973d825SFinley Xiao 1484973d825SFinley Xiao writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 1494973d825SFinley Xiao udelay(5); 1504973d825SFinley Xiao while (size--) { 1514973d825SFinley Xiao writel(offset++ | OTPC_USER_ADDR_MASK, 1524973d825SFinley Xiao otp->base + OTPC_USER_ADDR); 1534973d825SFinley Xiao writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, 1544973d825SFinley Xiao otp->base + OTPC_USER_ENABLE); 1554973d825SFinley Xiao ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); 1564973d825SFinley Xiao if (ret < 0) { 1574973d825SFinley Xiao printf("%s timeout during read setup\n", __func__); 1584973d825SFinley Xiao goto read_end; 1594973d825SFinley Xiao } 1604973d825SFinley Xiao *buffer++ = readb(otp->base + OTPC_USER_Q); 1614973d825SFinley Xiao } 1624973d825SFinley Xiao 1634973d825SFinley Xiao read_end: 1644973d825SFinley Xiao writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 1654973d825SFinley Xiao 1664973d825SFinley Xiao return ret; 1674973d825SFinley Xiao } 1684973d825SFinley Xiao 1694973d825SFinley Xiao static int rockchip_otp_read(struct udevice *dev, int offset, 1704973d825SFinley Xiao void *buf, int size) 1714973d825SFinley Xiao { 1724973d825SFinley Xiao OTP_READ otp_read = NULL; 1734973d825SFinley Xiao 1744973d825SFinley Xiao otp_read = (OTP_READ)dev_get_driver_data(dev); 1754973d825SFinley Xiao if (!otp_read) 1764973d825SFinley Xiao return -ENOSYS; 1774973d825SFinley Xiao 1784973d825SFinley Xiao return (*otp_read)(dev, offset, buf, size); 1794973d825SFinley Xiao } 1804973d825SFinley Xiao 1814973d825SFinley Xiao static const struct misc_ops rockchip_otp_ops = { 1824973d825SFinley Xiao .read = rockchip_otp_read, 1834973d825SFinley Xiao }; 1844973d825SFinley Xiao 1854973d825SFinley Xiao static int rockchip_otp_ofdata_to_platdata(struct udevice *dev) 1864973d825SFinley Xiao { 1874973d825SFinley Xiao struct rockchip_otp_platdata *otp = dev_get_platdata(dev); 1884973d825SFinley Xiao 1894973d825SFinley Xiao otp->base = dev_read_addr_ptr(dev); 1904973d825SFinley Xiao 1914973d825SFinley Xiao return 0; 1924973d825SFinley Xiao } 1934973d825SFinley Xiao 1944973d825SFinley Xiao static const struct udevice_id rockchip_otp_ids[] = { 1954973d825SFinley Xiao { 1964973d825SFinley Xiao .compatible = "rockchip,px30-otp", 1974973d825SFinley Xiao .data = (ulong)&rockchip_px30_otp_read, 1984973d825SFinley Xiao }, 199*6be53b63SFinley Xiao { 200*6be53b63SFinley Xiao .compatible = "rockchip,rk3308-otp", 201*6be53b63SFinley Xiao .data = (ulong)&rockchip_px30_otp_read, 202*6be53b63SFinley Xiao }, 2034973d825SFinley Xiao {} 2044973d825SFinley Xiao }; 2054973d825SFinley Xiao 2064973d825SFinley Xiao U_BOOT_DRIVER(rockchip_otp) = { 2074973d825SFinley Xiao .name = "rockchip_otp", 2084973d825SFinley Xiao .id = UCLASS_MISC, 2094973d825SFinley Xiao .of_match = rockchip_otp_ids, 2104973d825SFinley Xiao .ops = &rockchip_otp_ops, 2114973d825SFinley Xiao .ofdata_to_platdata = rockchip_otp_ofdata_to_platdata, 2124973d825SFinley Xiao .platdata_auto_alloc_size = sizeof(struct rockchip_otp_platdata), 2134973d825SFinley Xiao }; 214