xref: /rk3399_rockchip-uboot/drivers/misc/rockchip-otp.c (revision a4c57e8a07c4c5a8dde85a50b2506600ebf3af2f)
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