xref: /optee_os/core/drivers/rockchip_otp.c (revision d2c909e8962b3b61cd3b8718221c7923857dd3f5)
1*d2c909e8SMichael Tretter // SPDX-License-Identifier: BSD-3-Clause
2*d2c909e8SMichael Tretter /*
3*d2c909e8SMichael Tretter  * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH
4*d2c909e8SMichael Tretter  * Copyright (c) 2024, Rockchip, Inc. All rights reserved.
5*d2c909e8SMichael Tretter  * Copyright (C) 2025, Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
6*d2c909e8SMichael Tretter  */
7*d2c909e8SMichael Tretter 
8*d2c909e8SMichael Tretter #include <common.h>
9*d2c909e8SMichael Tretter #include <drivers/rockchip_otp.h>
10*d2c909e8SMichael Tretter #include <io.h>
11*d2c909e8SMichael Tretter #include <kernel/panic.h>
12*d2c909e8SMichael Tretter #include <kernel/tee_common_otp.h>
13*d2c909e8SMichael Tretter #include <mm/core_memprot.h>
14*d2c909e8SMichael Tretter #include <utee_defines.h>
15*d2c909e8SMichael Tretter 
16*d2c909e8SMichael Tretter #define OTP_S_AUTO_CTRL	0x0004
17*d2c909e8SMichael Tretter #define OTP_S_AUTO_EN	0x0008
18*d2c909e8SMichael Tretter #define OTP_S_PROG_DATA	0x0010
19*d2c909e8SMichael Tretter #define OTP_S_DOUT	0x0020
20*d2c909e8SMichael Tretter #define OTP_S_INT_ST	0x0084
21*d2c909e8SMichael Tretter 
22*d2c909e8SMichael Tretter #define ADDR_SHIFT	16
23*d2c909e8SMichael Tretter #define BURST_SHIFT	8
24*d2c909e8SMichael Tretter #define CMD_READ	0
25*d2c909e8SMichael Tretter #define CMD_WRITE	2
26*d2c909e8SMichael Tretter #define EN_ENABLE	1
27*d2c909e8SMichael Tretter #define EN_DISABLE	0
28*d2c909e8SMichael Tretter 
29*d2c909e8SMichael Tretter #define MAX_INDEX	0x300
30*d2c909e8SMichael Tretter #define BURST_SIZE	8
31*d2c909e8SMichael Tretter #define OTP_WORD	1
32*d2c909e8SMichael Tretter 
33*d2c909e8SMichael Tretter #define OTP_S_ERROR_BIT		BIT32(4)
34*d2c909e8SMichael Tretter #define OTP_S_WR_DONE_BIT	BIT32(3)
35*d2c909e8SMichael Tretter #define OTP_S_VERIFY_BIT	BIT32(2)
36*d2c909e8SMichael Tretter #define OTP_S_RD_DONE_BIT	BIT32(1)
37*d2c909e8SMichael Tretter 
38*d2c909e8SMichael Tretter #define OTP_POLL_PERIOD_US	0
39*d2c909e8SMichael Tretter #define OTP_POLL_TIMEOUT_US	1000
40*d2c909e8SMichael Tretter 
41*d2c909e8SMichael Tretter register_phys_mem_pgdir(MEM_AREA_IO_SEC, OTP_S_BASE, OTP_S_SIZE);
42*d2c909e8SMichael Tretter 
rockchip_otp_read_secure(uint32_t * value,uint32_t index,uint32_t count)43*d2c909e8SMichael Tretter TEE_Result rockchip_otp_read_secure(uint32_t *value, uint32_t index,
44*d2c909e8SMichael Tretter 				    uint32_t count)
45*d2c909e8SMichael Tretter {
46*d2c909e8SMichael Tretter 	vaddr_t base = (vaddr_t)phys_to_virt(OTP_S_BASE, MEM_AREA_IO_SEC,
47*d2c909e8SMichael Tretter 					     OTP_S_SIZE);
48*d2c909e8SMichael Tretter 	uint32_t int_status = 0;
49*d2c909e8SMichael Tretter 	uint32_t i = 0;
50*d2c909e8SMichael Tretter 	uint32_t val = 0;
51*d2c909e8SMichael Tretter 	uint32_t auto_ctrl_val = 0;
52*d2c909e8SMichael Tretter 	TEE_Result res = TEE_SUCCESS;
53*d2c909e8SMichael Tretter 
54*d2c909e8SMichael Tretter 	if (!base)
55*d2c909e8SMichael Tretter 		panic("OTP_S base not mapped");
56*d2c909e8SMichael Tretter 
57*d2c909e8SMichael Tretter 	/* Check for invalid parameters or exceeding hardware burst limit */
58*d2c909e8SMichael Tretter 	if (!value || !count || count > BURST_SIZE ||
59*d2c909e8SMichael Tretter 	    (index + count > MAX_INDEX))
60*d2c909e8SMichael Tretter 		return TEE_ERROR_BAD_PARAMETERS;
61*d2c909e8SMichael Tretter 
62*d2c909e8SMichael Tretter 	/* Setup read: index, count, command = READ */
63*d2c909e8SMichael Tretter 	auto_ctrl_val = SHIFT_U32(index, ADDR_SHIFT) |
64*d2c909e8SMichael Tretter 			SHIFT_U32(count, BURST_SHIFT) |
65*d2c909e8SMichael Tretter 			CMD_READ;
66*d2c909e8SMichael Tretter 
67*d2c909e8SMichael Tretter 	/* Clear any pending interrupts by reading & writing back INT_ST */
68*d2c909e8SMichael Tretter 	io_write32(base + OTP_S_INT_ST, io_read32(base + OTP_S_INT_ST));
69*d2c909e8SMichael Tretter 
70*d2c909e8SMichael Tretter 	/* Set read command */
71*d2c909e8SMichael Tretter 	io_write32(base + OTP_S_AUTO_CTRL, auto_ctrl_val);
72*d2c909e8SMichael Tretter 
73*d2c909e8SMichael Tretter 	/* Enable read */
74*d2c909e8SMichael Tretter 	io_write32(base + OTP_S_AUTO_EN, EN_ENABLE);
75*d2c909e8SMichael Tretter 
76*d2c909e8SMichael Tretter 	/* Wait for RD_DONE or ERROR bits */
77*d2c909e8SMichael Tretter 	res = IO_READ32_POLL_TIMEOUT(base + OTP_S_INT_ST,
78*d2c909e8SMichael Tretter 				     int_status,
79*d2c909e8SMichael Tretter 				     (int_status & OTP_S_RD_DONE_BIT) ||
80*d2c909e8SMichael Tretter 				     (int_status & OTP_S_ERROR_BIT),
81*d2c909e8SMichael Tretter 				     OTP_POLL_PERIOD_US,
82*d2c909e8SMichael Tretter 				     OTP_POLL_TIMEOUT_US);
83*d2c909e8SMichael Tretter 
84*d2c909e8SMichael Tretter 	/* Clear the interrupt again */
85*d2c909e8SMichael Tretter 	io_write32(base + OTP_S_INT_ST, io_read32(base + OTP_S_INT_ST));
86*d2c909e8SMichael Tretter 
87*d2c909e8SMichael Tretter 	if (int_status & OTP_S_ERROR_BIT) {
88*d2c909e8SMichael Tretter 		EMSG("OTP_S Error");
89*d2c909e8SMichael Tretter 		return TEE_ERROR_GENERIC;
90*d2c909e8SMichael Tretter 	}
91*d2c909e8SMichael Tretter 	if (res) {
92*d2c909e8SMichael Tretter 		EMSG("OTP_S Timeout");
93*d2c909e8SMichael Tretter 		return TEE_ERROR_BUSY;
94*d2c909e8SMichael Tretter 	}
95*d2c909e8SMichael Tretter 
96*d2c909e8SMichael Tretter 	/* Read out the data */
97*d2c909e8SMichael Tretter 	for (i = 0; i < count; i++) {
98*d2c909e8SMichael Tretter 		val = io_read32(base + OTP_S_DOUT +
99*d2c909e8SMichael Tretter 				(i * sizeof(uint32_t)));
100*d2c909e8SMichael Tretter 		value[i] = val;
101*d2c909e8SMichael Tretter 	}
102*d2c909e8SMichael Tretter 
103*d2c909e8SMichael Tretter 	return TEE_SUCCESS;
104*d2c909e8SMichael Tretter }
105*d2c909e8SMichael Tretter 
rockchip_otp_write_secure(const uint32_t * value,uint32_t index,uint32_t count)106*d2c909e8SMichael Tretter TEE_Result rockchip_otp_write_secure(const uint32_t *value, uint32_t index,
107*d2c909e8SMichael Tretter 				     uint32_t count)
108*d2c909e8SMichael Tretter {
109*d2c909e8SMichael Tretter 	vaddr_t base = (vaddr_t)phys_to_virt(OTP_S_BASE, MEM_AREA_IO_SEC,
110*d2c909e8SMichael Tretter 					     OTP_S_SIZE);
111*d2c909e8SMichael Tretter 	uint32_t int_status = 0;
112*d2c909e8SMichael Tretter 	uint32_t i = 0;
113*d2c909e8SMichael Tretter 
114*d2c909e8SMichael Tretter 	if (!base)
115*d2c909e8SMichael Tretter 		panic("OTP_S base not mapped");
116*d2c909e8SMichael Tretter 
117*d2c909e8SMichael Tretter 	/* Check for invalid parameters or exceeding hardware limits */
118*d2c909e8SMichael Tretter 	if (!value || !count || count > BURST_SIZE ||
119*d2c909e8SMichael Tretter 	    (index + count > MAX_INDEX))
120*d2c909e8SMichael Tretter 		return TEE_ERROR_BAD_PARAMETERS;
121*d2c909e8SMichael Tretter 
122*d2c909e8SMichael Tretter 	/* Program OTP words */
123*d2c909e8SMichael Tretter 	for (i = 0; i < count; i++) {
124*d2c909e8SMichael Tretter 		uint32_t old_val = 0;
125*d2c909e8SMichael Tretter 		uint32_t new_val = 0;
126*d2c909e8SMichael Tretter 		uint32_t curr_idx = index + i;
127*d2c909e8SMichael Tretter 		TEE_Result res = TEE_SUCCESS;
128*d2c909e8SMichael Tretter 
129*d2c909e8SMichael Tretter 		/* Setup write: curr_idx, command = WRITE */
130*d2c909e8SMichael Tretter 		uint32_t auto_ctrl_val = SHIFT_U32(curr_idx, ADDR_SHIFT) |
131*d2c909e8SMichael Tretter 						   CMD_WRITE;
132*d2c909e8SMichael Tretter 
133*d2c909e8SMichael Tretter 		/* Read existing OTP word to see which bits can be set */
134*d2c909e8SMichael Tretter 		res = rockchip_otp_read_secure(&old_val, curr_idx, OTP_WORD);
135*d2c909e8SMichael Tretter 		if (res != TEE_SUCCESS)
136*d2c909e8SMichael Tretter 			return res;
137*d2c909e8SMichael Tretter 
138*d2c909e8SMichael Tretter 		/* Check if bits in value conflict with old_val */
139*d2c909e8SMichael Tretter 		if (~*value & old_val) {
140*d2c909e8SMichael Tretter 			EMSG("OTP_S Program fail");
141*d2c909e8SMichael Tretter 			return TEE_ERROR_GENERIC;
142*d2c909e8SMichael Tretter 		}
143*d2c909e8SMichael Tretter 
144*d2c909e8SMichael Tretter 		/* Only program bits that are currently 0 (0->1) */
145*d2c909e8SMichael Tretter 		new_val = *value & ~old_val;
146*d2c909e8SMichael Tretter 		value++;
147*d2c909e8SMichael Tretter 		if (!new_val)
148*d2c909e8SMichael Tretter 			continue;
149*d2c909e8SMichael Tretter 
150*d2c909e8SMichael Tretter 		/* Clear any pending interrupts */
151*d2c909e8SMichael Tretter 		io_write32(base + OTP_S_INT_ST, io_read32(base + OTP_S_INT_ST));
152*d2c909e8SMichael Tretter 
153*d2c909e8SMichael Tretter 		/* Set write command */
154*d2c909e8SMichael Tretter 		io_write32(base + OTP_S_AUTO_CTRL, auto_ctrl_val);
155*d2c909e8SMichael Tretter 
156*d2c909e8SMichael Tretter 		/* Write the new bits into PROG_DATA register */
157*d2c909e8SMichael Tretter 		io_write32(base + OTP_S_PROG_DATA, new_val);
158*d2c909e8SMichael Tretter 
159*d2c909e8SMichael Tretter 		/* Enable the write */
160*d2c909e8SMichael Tretter 		io_write32(base + OTP_S_AUTO_EN, EN_ENABLE);
161*d2c909e8SMichael Tretter 
162*d2c909e8SMichael Tretter 		/* Poll for WR_DONE or verify/error bits */
163*d2c909e8SMichael Tretter 		res = IO_READ32_POLL_TIMEOUT(base + OTP_S_INT_ST,
164*d2c909e8SMichael Tretter 					     int_status,
165*d2c909e8SMichael Tretter 					     (int_status & OTP_S_WR_DONE_BIT) ||
166*d2c909e8SMichael Tretter 					     (int_status & OTP_S_VERIFY_BIT) ||
167*d2c909e8SMichael Tretter 					     (int_status & OTP_S_ERROR_BIT),
168*d2c909e8SMichael Tretter 					     OTP_POLL_PERIOD_US,
169*d2c909e8SMichael Tretter 					     OTP_POLL_TIMEOUT_US);
170*d2c909e8SMichael Tretter 
171*d2c909e8SMichael Tretter 		/* Clear INT status bits */
172*d2c909e8SMichael Tretter 		io_write32(base + OTP_S_INT_ST, int_status);
173*d2c909e8SMichael Tretter 
174*d2c909e8SMichael Tretter 		/* Check for VERIFY_FAIL, ERROR or timeout */
175*d2c909e8SMichael Tretter 		if (int_status & OTP_S_VERIFY_BIT) {
176*d2c909e8SMichael Tretter 			EMSG("OTP_S Verification fail");
177*d2c909e8SMichael Tretter 			return TEE_ERROR_GENERIC;
178*d2c909e8SMichael Tretter 		}
179*d2c909e8SMichael Tretter 		if (int_status & OTP_S_ERROR_BIT) {
180*d2c909e8SMichael Tretter 			EMSG("OTP_S Error");
181*d2c909e8SMichael Tretter 			return TEE_ERROR_GENERIC;
182*d2c909e8SMichael Tretter 		}
183*d2c909e8SMichael Tretter 		if (res) {
184*d2c909e8SMichael Tretter 			EMSG("OTP_S Timeout");
185*d2c909e8SMichael Tretter 			return TEE_ERROR_BUSY;
186*d2c909e8SMichael Tretter 		}
187*d2c909e8SMichael Tretter 	}
188*d2c909e8SMichael Tretter 
189*d2c909e8SMichael Tretter 	return TEE_SUCCESS;
190*d2c909e8SMichael Tretter }
191