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> 12*a4c57e8aSFinley Xiao #include <linux/iopoll.h> 134973d825SFinley Xiao #include <misc.h> 146e12c1ddSJason Zhu #include <rockchip-otp.h> 154973d825SFinley Xiao 16*a4c57e8aSFinley Xiao struct otp_data { 17*a4c57e8aSFinley Xiao int (*init)(struct udevice *dev); 18*a4c57e8aSFinley Xiao int (*read)(struct udevice *dev, int offset, void *buf, int size); 19*a4c57e8aSFinley Xiao }; 20*a4c57e8aSFinley Xiao 214973d825SFinley Xiao static int rockchip_otp_wait_status(struct rockchip_otp_platdata *otp, 224973d825SFinley Xiao u32 flag) 234973d825SFinley Xiao { 244973d825SFinley Xiao int delay = OTPC_TIMEOUT; 254973d825SFinley Xiao 264973d825SFinley Xiao while (!(readl(otp->base + OTPC_INT_STATUS) & flag)) { 274973d825SFinley Xiao udelay(1); 284973d825SFinley Xiao delay--; 294973d825SFinley Xiao if (delay <= 0) { 304973d825SFinley Xiao printf("%s: wait init status timeout\n", __func__); 314973d825SFinley Xiao return -ETIMEDOUT; 324973d825SFinley Xiao } 334973d825SFinley Xiao } 344973d825SFinley Xiao 354973d825SFinley Xiao /* clean int status */ 364973d825SFinley Xiao writel(flag, otp->base + OTPC_INT_STATUS); 374973d825SFinley Xiao 384973d825SFinley Xiao return 0; 394973d825SFinley Xiao } 404973d825SFinley Xiao 414973d825SFinley Xiao static int rockchip_otp_ecc_enable(struct rockchip_otp_platdata *otp, 424973d825SFinley Xiao bool enable) 434973d825SFinley Xiao { 444973d825SFinley Xiao int ret = 0; 454973d825SFinley Xiao 464973d825SFinley Xiao writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), 474973d825SFinley Xiao otp->base + OTPC_SBPI_CTRL); 484973d825SFinley Xiao 494973d825SFinley Xiao writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); 504973d825SFinley Xiao writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, 514973d825SFinley Xiao otp->base + OTPC_SBPI_CMD0_OFFSET); 524973d825SFinley Xiao if (enable) 534973d825SFinley Xiao writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 544973d825SFinley Xiao else 554973d825SFinley Xiao writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 564973d825SFinley Xiao 574973d825SFinley Xiao writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); 584973d825SFinley Xiao 594973d825SFinley Xiao ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); 604973d825SFinley Xiao if (ret < 0) 614973d825SFinley Xiao printf("%s timeout during ecc_enable\n", __func__); 624973d825SFinley Xiao 634973d825SFinley Xiao return ret; 644973d825SFinley Xiao } 654973d825SFinley Xiao 664973d825SFinley Xiao static int rockchip_px30_otp_read(struct udevice *dev, int offset, 674973d825SFinley Xiao void *buf, int size) 684973d825SFinley Xiao { 694973d825SFinley Xiao struct rockchip_otp_platdata *otp = dev_get_platdata(dev); 704973d825SFinley Xiao u8 *buffer = buf; 714973d825SFinley Xiao int ret = 0; 724973d825SFinley Xiao 734973d825SFinley Xiao ret = rockchip_otp_ecc_enable(otp, false); 744973d825SFinley Xiao if (ret < 0) { 754973d825SFinley Xiao printf("%s rockchip_otp_ecc_enable err\n", __func__); 764973d825SFinley Xiao return ret; 774973d825SFinley Xiao } 784973d825SFinley Xiao 794973d825SFinley Xiao writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 804973d825SFinley Xiao udelay(5); 814973d825SFinley Xiao while (size--) { 824973d825SFinley Xiao writel(offset++ | OTPC_USER_ADDR_MASK, 834973d825SFinley Xiao otp->base + OTPC_USER_ADDR); 844973d825SFinley Xiao writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, 854973d825SFinley Xiao otp->base + OTPC_USER_ENABLE); 864973d825SFinley Xiao ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); 874973d825SFinley Xiao if (ret < 0) { 884973d825SFinley Xiao printf("%s timeout during read setup\n", __func__); 894973d825SFinley Xiao goto read_end; 904973d825SFinley Xiao } 914973d825SFinley Xiao *buffer++ = readb(otp->base + OTPC_USER_Q); 924973d825SFinley Xiao } 934973d825SFinley Xiao 944973d825SFinley Xiao read_end: 954973d825SFinley Xiao writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 964973d825SFinley Xiao 974973d825SFinley Xiao return ret; 984973d825SFinley Xiao } 994973d825SFinley Xiao 100*a4c57e8aSFinley Xiao static int rockchip_rv1126_otp_init(struct udevice *dev) 101*a4c57e8aSFinley Xiao { 102*a4c57e8aSFinley Xiao struct rockchip_otp_platdata *otp = dev_get_platdata(dev); 103*a4c57e8aSFinley Xiao u32 status = 0; 104*a4c57e8aSFinley Xiao int ret; 105*a4c57e8aSFinley Xiao 106*a4c57e8aSFinley Xiao writel(0x0, otp->base + RV1126_OTP_NVM_CEB); 107*a4c57e8aSFinley Xiao ret = readl_poll_timeout(otp->base + RV1126_OTP_NVM_ST, status, 108*a4c57e8aSFinley Xiao status & 0x1, OTPC_TIMEOUT); 109*a4c57e8aSFinley Xiao if (ret < 0) { 110*a4c57e8aSFinley Xiao printf("%s timeout during set ceb\n", __func__); 111*a4c57e8aSFinley Xiao return ret; 112*a4c57e8aSFinley Xiao } 113*a4c57e8aSFinley Xiao 114*a4c57e8aSFinley Xiao writel(0x1, otp->base + RV1126_OTP_NVM_RSTB); 115*a4c57e8aSFinley Xiao ret = readl_poll_timeout(otp->base + RV1126_OTP_NVM_ST, status, 116*a4c57e8aSFinley Xiao status & 0x4, OTPC_TIMEOUT); 117*a4c57e8aSFinley Xiao if (ret < 0) { 118*a4c57e8aSFinley Xiao printf("%s timeout during set rstb\n", __func__); 119*a4c57e8aSFinley Xiao return ret; 120*a4c57e8aSFinley Xiao } 121*a4c57e8aSFinley Xiao 122*a4c57e8aSFinley Xiao return 0; 123*a4c57e8aSFinley Xiao } 124*a4c57e8aSFinley Xiao 125*a4c57e8aSFinley Xiao static int rockchip_rv1126_otp_read(struct udevice *dev, int offset, void *buf, 126*a4c57e8aSFinley Xiao int size) 127*a4c57e8aSFinley Xiao { 128*a4c57e8aSFinley Xiao struct rockchip_otp_platdata *otp = dev_get_platdata(dev); 129*a4c57e8aSFinley Xiao u32 status = 0; 130*a4c57e8aSFinley Xiao u8 *buffer = buf; 131*a4c57e8aSFinley Xiao int ret = 0; 132*a4c57e8aSFinley Xiao 133*a4c57e8aSFinley Xiao while (size--) { 134*a4c57e8aSFinley Xiao writel(offset++, otp->base + RV1126_OTP_NVM_RADDR); 135*a4c57e8aSFinley Xiao writel(0x1, otp->base + RV1126_OTP_NVM_RSTART); 136*a4c57e8aSFinley Xiao ret = readl_poll_timeout(otp->base + RV1126_OTP_READ_ST, 137*a4c57e8aSFinley Xiao status, status == 0, OTPC_TIMEOUT); 138*a4c57e8aSFinley Xiao if (ret < 0) { 139*a4c57e8aSFinley Xiao printf("%s timeout during read setup\n", __func__); 140*a4c57e8aSFinley Xiao return ret; 141*a4c57e8aSFinley Xiao } 142*a4c57e8aSFinley Xiao 143*a4c57e8aSFinley Xiao *buffer++ = readb(otp->base + RV1126_OTP_NVM_RDATA); 144*a4c57e8aSFinley Xiao } 145*a4c57e8aSFinley Xiao 146*a4c57e8aSFinley Xiao return 0; 147*a4c57e8aSFinley Xiao } 148*a4c57e8aSFinley Xiao 1494973d825SFinley Xiao static int rockchip_otp_read(struct udevice *dev, int offset, 1504973d825SFinley Xiao void *buf, int size) 1514973d825SFinley Xiao { 152*a4c57e8aSFinley Xiao struct otp_data *data; 1534973d825SFinley Xiao 154*a4c57e8aSFinley Xiao data = (struct otp_data *)dev_get_driver_data(dev); 155*a4c57e8aSFinley Xiao if (!data) 1564973d825SFinley Xiao return -ENOSYS; 1574973d825SFinley Xiao 158*a4c57e8aSFinley Xiao return data->read(dev, offset, buf, size); 1594973d825SFinley Xiao } 1604973d825SFinley Xiao 1614973d825SFinley Xiao static const struct misc_ops rockchip_otp_ops = { 1624973d825SFinley Xiao .read = rockchip_otp_read, 1634973d825SFinley Xiao }; 1644973d825SFinley Xiao 1654973d825SFinley Xiao static int rockchip_otp_ofdata_to_platdata(struct udevice *dev) 1664973d825SFinley Xiao { 1674973d825SFinley Xiao struct rockchip_otp_platdata *otp = dev_get_platdata(dev); 1684973d825SFinley Xiao 1694973d825SFinley Xiao otp->base = dev_read_addr_ptr(dev); 1704973d825SFinley Xiao 1714973d825SFinley Xiao return 0; 1724973d825SFinley Xiao } 1734973d825SFinley Xiao 174*a4c57e8aSFinley Xiao static int rockchip_otp_probe(struct udevice *dev) 175*a4c57e8aSFinley Xiao { 176*a4c57e8aSFinley Xiao struct otp_data *data; 177*a4c57e8aSFinley Xiao 178*a4c57e8aSFinley Xiao data = (struct otp_data *)dev_get_driver_data(dev); 179*a4c57e8aSFinley Xiao if (!data) 180*a4c57e8aSFinley Xiao return -EINVAL; 181*a4c57e8aSFinley Xiao 182*a4c57e8aSFinley Xiao if (data->init) 183*a4c57e8aSFinley Xiao return data->init(dev); 184*a4c57e8aSFinley Xiao 185*a4c57e8aSFinley Xiao return 0; 186*a4c57e8aSFinley Xiao } 187*a4c57e8aSFinley Xiao 188*a4c57e8aSFinley Xiao static const struct otp_data px30_data = { 189*a4c57e8aSFinley Xiao .read = rockchip_px30_otp_read, 190*a4c57e8aSFinley Xiao }; 191*a4c57e8aSFinley Xiao 192*a4c57e8aSFinley Xiao static const struct otp_data rv1126_data = { 193*a4c57e8aSFinley Xiao .init = rockchip_rv1126_otp_init, 194*a4c57e8aSFinley Xiao .read = rockchip_rv1126_otp_read, 195*a4c57e8aSFinley Xiao }; 196*a4c57e8aSFinley Xiao 1974973d825SFinley Xiao static const struct udevice_id rockchip_otp_ids[] = { 1984973d825SFinley Xiao { 1994973d825SFinley Xiao .compatible = "rockchip,px30-otp", 200*a4c57e8aSFinley Xiao .data = (ulong)&px30_data, 2014973d825SFinley Xiao }, 2026be53b63SFinley Xiao { 2036be53b63SFinley Xiao .compatible = "rockchip,rk3308-otp", 204*a4c57e8aSFinley Xiao .data = (ulong)&px30_data, 205*a4c57e8aSFinley Xiao }, 206*a4c57e8aSFinley Xiao { 207*a4c57e8aSFinley Xiao .compatible = "rockchip,rv1126-otp", 208*a4c57e8aSFinley Xiao .data = (ulong)&rv1126_data, 2096be53b63SFinley Xiao }, 2104973d825SFinley Xiao {} 2114973d825SFinley Xiao }; 2124973d825SFinley Xiao 2134973d825SFinley Xiao U_BOOT_DRIVER(rockchip_otp) = { 2144973d825SFinley Xiao .name = "rockchip_otp", 2154973d825SFinley Xiao .id = UCLASS_MISC, 2164973d825SFinley Xiao .of_match = rockchip_otp_ids, 2174973d825SFinley Xiao .ops = &rockchip_otp_ops, 2184973d825SFinley Xiao .ofdata_to_platdata = rockchip_otp_ofdata_to_platdata, 2194973d825SFinley Xiao .platdata_auto_alloc_size = sizeof(struct rockchip_otp_platdata), 220*a4c57e8aSFinley Xiao .probe = rockchip_otp_probe, 2214973d825SFinley Xiao }; 222