xref: /rk3399_rockchip-uboot/drivers/keylad/rk_keylad.c (revision 4132a4ff1de7d3ffd1cd0a2e791b6542e00b73f7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2025 Rockchip Electronics Co., Ltd
4  */
5 
6 #include <common.h>
7 #include <clk.h>
8 #include <keylad.h>
9 #include <dm.h>
10 #include <misc.h>
11 #include <asm/io.h>
12 #include <clk-uclass.h>
13 #include <asm/arch/hardware.h>
14 #include <asm/arch/clock.h>
15 
16 #define KEYLAD_APB_CMD			0x0450
17 #define REG_APB_CMD_EN			BIT(0)
18 #define VALUE_APB_CMD_DISABLE		0
19 #define VALUE_APB_CMD_ENABLE		BIT(0)
20 
21 #define KEYLAD_APB_PADDR		0x0454
22 #define KEYLAD_APB_PWDATA		0x0458
23 #define KEYLAD_APB_PWRITE		0x045C
24 #define KEYLAD_DATA_CTL			0x0460
25 #define VALUE_DATA_CTL_EN		BIT(15)
26 
27 #define KEYLAD_KEY_SEL			0x0610
28 #define VALUE_KEY_SEL_OUTER_KEY		0x00000000
29 
30 #define KEYLAD_LOCKSTEP_FLAG		0x0618
31 #define KEYLAD_LOCKSTEP_EN		0x061C
32 
33 #define KEY_LADDER_OTP_KEY_REQ		0x0640
34 #define KL_OTP_KEY_REQ_DST_ADDR(addr)	((addr) & 0x3) // 256bit algin address
35 #define KL_OTP_KEY_REQ_BYTE_SWAP	BIT(4)
36 #define KL_OTP_KEY_REQ_WORD_SWAP	BIT(5)
37 #define KL_OTP_KEY_REQ_EN		BIT(8)
38 #define KL_OTP_KEY_ECC_ST		BIT(12)
39 #define KL_OTP_KEY_REQ_SRC_ADDR(addr)	(((addr) & 0xffff) << 16)// byte address, dword align
40 
41 #define KEY_LADDER_KEY_LEN		0x0648
42 #define KL_KEY_LEN(len)			((len) & 0x3f)
43 
44 #define KEYLAD_KEY_REG_SIZE_BYTES	4
45 #define KEYLAD_KEY_REG_NUM		32
46 #define KEYLAD_AREA_NUM			2
47 
48 #define RK_KEYLAD_TIME_OUT		10000  /* max 10ms */
49 
50 #define KEYLAD_POLL_TIMEOUT(condition, timeout, ret) do { \
51 	u32 time_out = timeout; \
52 	while (condition) { \
53 		if (time_out-- == 0) { \
54 			printf("[%s] %d: time out!\n", __func__, __LINE__); \
55 			ret = -ETIMEDOUT; \
56 			break; \
57 		} \
58 		udelay(1); \
59 	} \
60 } while (0)
61 
62 struct rockchip_keylad_priv {
63 	fdt_addr_t			reg;
64 	char				*clocks;
65 	u32				nclocks;
66 };
67 
68 fdt_addr_t keylad_base;
69 
70 static int rk_keylad_do_enable_clk(struct udevice *dev, int enable)
71 {
72 	struct rockchip_keylad_priv *priv = dev_get_priv(dev);
73 	struct clk clk;
74 	int i, ret;
75 
76 	for (i = 0; i < priv->nclocks; i++) {
77 		ret = clk_get_by_index(dev, i, &clk);
78 		if (ret < 0) {
79 			printf("Keylad failed to get clk index %d, ret=%d\n", i, ret);
80 			return ret;
81 		}
82 
83 		if (enable)
84 			ret = clk_enable(&clk);
85 		else
86 			ret = clk_disable(&clk);
87 
88 		if (ret < 0 && ret != -ENOSYS) {
89 			printf("Keylad failed to enable(%d) clk(%ld): ret=%d\n",
90 			       enable, clk.id, ret);
91 			return ret;
92 		}
93 	}
94 
95 	return 0;
96 }
97 
98 static int rk_keylad_enable_clk(struct udevice *dev)
99 {
100 	return rk_keylad_do_enable_clk(dev, 1);
101 }
102 
103 static int rk_keylad_disable_clk(struct udevice *dev)
104 {
105 	return rk_keylad_do_enable_clk(dev, 0);
106 }
107 
108 static inline u32 keylad_read(u32 offset)
109 {
110 	return readl(keylad_base + offset);
111 }
112 
113 static inline void keylad_write(u32 offset, u32 val)
114 {
115 	writel(val, keylad_base + offset);
116 }
117 
118 static int rk_get_fwkey_param(u32 keyid, u32 *offset, u32 *max_len)
119 {
120 	switch (keyid) {
121 	case RK_FW_KEY0:
122 		*offset  = OTP_FW_ENC_KEY_ADDR;
123 		*max_len = OTP_FW_ENC_KEY_SIZE;
124 		break;
125 	default:
126 		return -EINVAL;
127 	}
128 
129 	return 0;
130 }
131 
132 static int rk_keylad_send_key(u32 key_reg, u32 n_words, ulong dst_addr)
133 {
134 	int ret = 0;
135 
136 	/* key_reg of 32bits can be 0-31 */
137 	if ((key_reg + n_words) > KEYLAD_KEY_REG_NUM)
138 		return -EINVAL;
139 
140 	for (u32 i = 0; i < n_words; i++) {
141 		/* set destination addr */
142 		keylad_write(KEYLAD_APB_PADDR,
143 			     (dst_addr & 0xffffffff) + (i * KEYLAD_KEY_REG_SIZE_BYTES));
144 		/* select which word of key table to be sent */
145 		keylad_write(KEYLAD_APB_PWDATA, key_reg + i);
146 
147 		keylad_write(KEYLAD_APB_CMD, VALUE_APB_CMD_ENABLE);
148 		KEYLAD_POLL_TIMEOUT((keylad_read(KEYLAD_APB_CMD) & REG_APB_CMD_EN) ==
149 				    VALUE_APB_CMD_ENABLE, RK_KEYLAD_TIME_OUT, ret);
150 	}
151 
152 	return ret;
153 }
154 
155 static int rk_otp_keylad_read_init(void)
156 {
157 	struct udevice *dev;
158 
159 	dev = misc_otp_get_device(OTP_S);
160 	if (!dev)
161 		return -ENODEV;
162 
163 	misc_otp_ioctl(dev, IOCTL_REQ_KEYLAD_INIT, NULL);
164 
165 	return 0;
166 }
167 
168 static int rk_otp_keylad_read_deinit(void)
169 {
170 	struct udevice *dev;
171 
172 	dev = misc_otp_get_device(OTP_S);
173 	if (!dev)
174 		return -ENODEV;
175 
176 	misc_otp_ioctl(dev, IOCTL_REQ_KEYLAD_DEINIT, NULL);
177 
178 	return 0;
179 }
180 
181 static int rk_keylad_read_otp_key(u32 otp_offset, u32 keylad_area, u32 keylen)
182 {
183 	int ret = 0;
184 	u32 val = 0;
185 	u32 nbytes = keylen;
186 
187 	/* keylad_area of 256bits can be 0-1 */
188 	if (keylad_area >= KEYLAD_AREA_NUM)
189 		return -EINVAL;
190 
191 	ret = rk_otp_keylad_read_init();
192 	if (ret) {
193 		printf("Keyladder read otp key init err: 0x%x.", ret);
194 		return ret;
195 	}
196 
197 	/* src use byte address, dst use keytable block address */
198 	val = KL_OTP_KEY_REQ_SRC_ADDR(otp_offset / 2) |
199 	      KL_OTP_KEY_REQ_DST_ADDR(keylad_area) |
200 	      KL_OTP_KEY_REQ_BYTE_SWAP |
201 	      KL_OTP_KEY_REQ_EN;
202 
203 	keylad_write(KEYLAD_KEY_SEL, VALUE_KEY_SEL_OUTER_KEY);
204 
205 	keylad_write(KEY_LADDER_KEY_LEN, KL_KEY_LEN(nbytes));
206 
207 	keylad_write(KEY_LADDER_OTP_KEY_REQ, val);
208 
209 	KEYLAD_POLL_TIMEOUT(keylad_read(KEY_LADDER_OTP_KEY_REQ) & KL_OTP_KEY_REQ_EN,
210 			    RK_KEYLAD_TIME_OUT, ret);
211 
212 	val = keylad_read(KEY_LADDER_OTP_KEY_REQ);
213 	if (val & KL_OTP_KEY_ECC_ST) {
214 		printf("KEYLAD transfer OTP key ECC check error!");
215 		ret = -EIO;
216 	}
217 
218 	rk_otp_keylad_read_deinit();
219 
220 	return ret;
221 }
222 
223 static int rockchip_keylad_transfer_fwkey(struct udevice *dev, ulong dst,
224 					  u32 fw_keyid, u32 keylen)
225 {
226 	int res = 0;
227 	u32 fw_key_offset;
228 	u32 max_key_len = 0;
229 
230 	if (keylen % 4) {
231 		printf("key_len(%u) must be multiple of 4 error.", keylen);
232 		return -EINVAL;
233 	}
234 
235 	res = rk_get_fwkey_param(fw_keyid, &fw_key_offset, &max_key_len);
236 	if (res)
237 		return res;
238 
239 	if (keylen > max_key_len) {
240 		printf("key_len(%u) > %u error.", keylen, max_key_len);
241 		return -EINVAL;
242 	}
243 
244 	rk_keylad_enable_clk(dev);
245 
246 	res = rk_keylad_read_otp_key(fw_key_offset, 0, keylen);
247 	if (res) {
248 		printf("Keyladder read otp key err: 0x%x.", res);
249 		return res;
250 	}
251 
252 	res = rk_keylad_send_key(0, keylen / 4, dst);
253 
254 	rk_keylad_disable_clk(dev);
255 
256 	if (res) {
257 		printf("Keyladder transfer key err: 0x%x.", res);
258 		goto exit;
259 	}
260 
261 exit:
262 	return res;
263 }
264 
265 static const struct dm_keylad_ops rockchip_keylad_ops = {
266 	.transfer_fwkey   = rockchip_keylad_transfer_fwkey,
267 };
268 
269 static int rockchip_keylad_ofdata_to_platdata(struct udevice *dev)
270 {
271 	struct rockchip_keylad_priv *priv = dev_get_priv(dev);
272 	int len = 0;
273 	int ret = 0;
274 
275 	memset(priv, 0x00, sizeof(*priv));
276 
277 	priv->reg = (fdt_addr_t)dev_read_addr_ptr(dev);
278 	if (priv->reg == FDT_ADDR_T_NONE)
279 		return -EINVAL;
280 
281 	keylad_base = priv->reg;
282 
283 	/* if there is no clocks in dts, just skip it */
284 	if (!dev_read_prop(dev, "clocks", &len)) {
285 		printf("Keylad \"clocks\" property not set.\n");
286 		return 0;
287 	}
288 
289 	priv->clocks = malloc(len);
290 	if (!priv->clocks)
291 		return -ENOMEM;
292 
293 	priv->nclocks = len / (2 * sizeof(u32));
294 	if (dev_read_u32_array(dev, "clocks", (u32 *)priv->clocks,
295 			       priv->nclocks)) {
296 		printf("Keylad can't read \"clocks\" property.\n");
297 		ret = -EINVAL;
298 		goto exit;
299 	}
300 
301 	return 0;
302 exit:
303 	if (priv->clocks)
304 		free(priv->clocks);
305 
306 	return ret;
307 }
308 
309 static int rockchip_keylad_probe(struct udevice *dev)
310 {
311 	rk_keylad_disable_clk(dev);
312 
313 	return 0;
314 }
315 
316 static const struct udevice_id rockchip_keylad_ids[] = {
317 	{
318 		.compatible = "rockchip,keylad",
319 	},
320 };
321 
322 U_BOOT_DRIVER(rockchip_keylad) = {
323 	.name		= "rockchip_keylad",
324 	.id		= UCLASS_KEYLAD,
325 	.of_match	= rockchip_keylad_ids,
326 	.ops		= &rockchip_keylad_ops,
327 	.probe		= rockchip_keylad_probe,
328 	.ofdata_to_platdata = rockchip_keylad_ofdata_to_platdata,
329 	.priv_auto_alloc_size = sizeof(struct rockchip_keylad_priv),
330 };
331