]>
Commit | Line | Data |
---|---|---|
54f17fa0 KY |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2019 Rockchip Electronics Co., Ltd. | |
08d61f87 QS |
4 | * |
5 | * Copyright (C) 2019 Collabora Inc - https://www.collabora.com/ | |
6 | * Rohan Garg <[email protected]> | |
7 | * | |
8 | * Based on puma-rk3399.c: | |
9 | * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH | |
54f17fa0 KY |
10 | */ |
11 | #include <common.h> | |
12 | #include <clk.h> | |
9edefc27 | 13 | #include <cpu_func.h> |
08d61f87 | 14 | #include <env.h> |
54f17fa0 | 15 | #include <dm.h> |
bea9267d | 16 | #include <efi_loader.h> |
851737ab | 17 | #include <fastboot.h> |
08d61f87 | 18 | #include <hash.h> |
5255932f | 19 | #include <init.h> |
f7ae49fc | 20 | #include <log.h> |
bea9267d | 21 | #include <mmc.h> |
08d61f87 QS |
22 | #include <dm/uclass-internal.h> |
23 | #include <misc.h> | |
bea9267d | 24 | #include <part.h> |
54f17fa0 KY |
25 | #include <ram.h> |
26 | #include <syscon.h> | |
bea9267d | 27 | #include <uuid.h> |
08d61f87 QS |
28 | #include <u-boot/crc.h> |
29 | #include <u-boot/sha256.h> | |
90526e9f | 30 | #include <asm/cache.h> |
54f17fa0 KY |
31 | #include <asm/io.h> |
32 | #include <asm/arch-rockchip/boot_mode.h> | |
33 | #include <asm/arch-rockchip/clock.h> | |
34 | #include <asm/arch-rockchip/periph.h> | |
04825384 | 35 | #include <asm/arch-rockchip/misc.h> |
54f17fa0 KY |
36 | #include <power/regulator.h> |
37 | ||
bea9267d SG |
38 | #if defined(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) && defined(CONFIG_EFI_PARTITION) |
39 | ||
40 | #define DFU_ALT_BUF_LEN SZ_1K | |
41 | ||
42 | static struct efi_fw_image *fw_images; | |
43 | ||
44 | static bool updatable_image(struct disk_partition *info) | |
45 | { | |
46 | int i; | |
47 | bool ret = false; | |
48 | efi_guid_t image_type_guid; | |
49 | ||
50 | uuid_str_to_bin(info->type_guid, image_type_guid.b, | |
51 | UUID_STR_FORMAT_GUID); | |
52 | ||
cccea188 | 53 | for (i = 0; i < update_info.num_images; i++) { |
bea9267d SG |
54 | if (!guidcmp(&fw_images[i].image_type_id, &image_type_guid)) { |
55 | ret = true; | |
56 | break; | |
57 | } | |
58 | } | |
59 | ||
60 | return ret; | |
61 | } | |
62 | ||
63 | static void set_image_index(struct disk_partition *info, int index) | |
64 | { | |
65 | int i; | |
66 | efi_guid_t image_type_guid; | |
67 | ||
68 | uuid_str_to_bin(info->type_guid, image_type_guid.b, | |
69 | UUID_STR_FORMAT_GUID); | |
70 | ||
cccea188 | 71 | for (i = 0; i < update_info.num_images; i++) { |
bea9267d SG |
72 | if (!guidcmp(&fw_images[i].image_type_id, &image_type_guid)) { |
73 | fw_images[i].image_index = index; | |
74 | break; | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | static int get_mmc_desc(struct blk_desc **desc) | |
80 | { | |
81 | int ret; | |
82 | struct mmc *mmc; | |
83 | struct udevice *dev; | |
84 | ||
85 | /* | |
86 | * For now the firmware images are assumed to | |
87 | * be on the SD card | |
88 | */ | |
89 | ret = uclass_get_device(UCLASS_MMC, 1, &dev); | |
90 | if (ret) | |
91 | return -1; | |
92 | ||
93 | mmc = mmc_get_mmc_dev(dev); | |
94 | if (!mmc) | |
95 | return -ENODEV; | |
96 | ||
97 | if ((ret = mmc_init(mmc))) | |
98 | return ret; | |
99 | ||
100 | *desc = mmc_get_blk_desc(mmc); | |
101 | if (!*desc) | |
102 | return -1; | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
107 | void set_dfu_alt_info(char *interface, char *devstr) | |
108 | { | |
109 | const char *name; | |
110 | bool first = true; | |
111 | int p, len, devnum, ret; | |
112 | char buf[DFU_ALT_BUF_LEN]; | |
113 | struct disk_partition info; | |
114 | struct blk_desc *desc = NULL; | |
115 | ||
116 | ret = get_mmc_desc(&desc); | |
117 | if (ret) { | |
118 | log_err("Unable to get mmc desc\n"); | |
119 | return; | |
120 | } | |
121 | ||
122 | memset(buf, 0, sizeof(buf)); | |
123 | name = blk_get_uclass_name(desc->uclass_id); | |
124 | devnum = desc->devnum; | |
125 | len = strlen(buf); | |
126 | ||
127 | len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, | |
128 | "%s %d=", name, devnum); | |
129 | ||
130 | for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { | |
131 | if (part_get_info(desc, p, &info)) | |
132 | continue; | |
133 | ||
134 | /* Add entry to dfu_alt_info only for updatable images */ | |
135 | if (updatable_image(&info)) { | |
136 | if (!first) | |
137 | len += snprintf(buf + len, | |
138 | DFU_ALT_BUF_LEN - len, ";"); | |
139 | ||
140 | len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, | |
141 | "%s%d_%s part %d %d", | |
142 | name, devnum, info.name, devnum, p); | |
143 | first = false; | |
144 | } | |
145 | } | |
146 | ||
147 | log_debug("dfu_alt_info => %s\n", buf); | |
148 | env_set("dfu_alt_info", buf); | |
149 | } | |
150 | ||
151 | static void gpt_capsule_update_setup(void) | |
152 | { | |
153 | int p, i, ret; | |
154 | struct disk_partition info; | |
155 | struct blk_desc *desc = NULL; | |
156 | ||
157 | fw_images = update_info.images; | |
158 | rockchip_capsule_update_board_setup(); | |
159 | ||
160 | ret = get_mmc_desc(&desc); | |
161 | if (ret) { | |
162 | log_err("Unable to get mmc desc\n"); | |
163 | return; | |
164 | } | |
165 | ||
166 | for (p = 1, i = 1; p <= MAX_SEARCH_PARTITIONS; p++) { | |
167 | if (part_get_info(desc, p, &info)) | |
168 | continue; | |
169 | ||
170 | /* | |
171 | * Since we have a GPT partitioned device, the updatable | |
172 | * images could be stored in any order. Populate the | |
173 | * image_index at runtime. | |
174 | */ | |
175 | if (updatable_image(&info)) { | |
176 | set_image_index(&info, i); | |
177 | i++; | |
178 | } | |
179 | } | |
180 | } | |
181 | #endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT && CONFIG_EFI_PARTITION */ | |
182 | ||
54f17fa0 KY |
183 | __weak int rk_board_late_init(void) |
184 | { | |
bea9267d SG |
185 | #if defined(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) && defined(CONFIG_EFI_PARTITION) |
186 | gpt_capsule_update_setup(); | |
187 | #endif | |
188 | ||
54f17fa0 KY |
189 | return 0; |
190 | } | |
191 | ||
192 | int board_late_init(void) | |
193 | { | |
194 | setup_boot_mode(); | |
195 | ||
196 | return rk_board_late_init(); | |
197 | } | |
198 | ||
199 | int board_init(void) | |
200 | { | |
201 | int ret; | |
202 | ||
203 | #ifdef CONFIG_DM_REGULATOR | |
204 | ret = regulators_enable_boot_on(false); | |
205 | if (ret) | |
206 | debug("%s: Cannot enable boot on regulator\n", __func__); | |
207 | #endif | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | #if !defined(CONFIG_SYS_DCACHE_OFF) && !defined(CONFIG_ARM64) | |
213 | void enable_caches(void) | |
214 | { | |
215 | /* Enable D-cache. I-cache is already enabled in start.S */ | |
216 | dcache_enable(); | |
217 | } | |
218 | #endif | |
219 | ||
bcbd9a6f | 220 | #if defined(CONFIG_USB_GADGET) && defined(CONFIG_USB_GADGET_DWC2_OTG) |
54f17fa0 | 221 | #include <usb.h> |
51c54080 | 222 | #include <linux/usb/otg.h> |
54f17fa0 KY |
223 | #include <usb/dwc2_udc.h> |
224 | ||
225 | static struct dwc2_plat_otg_data otg_data = { | |
226 | .rx_fifo_sz = 512, | |
227 | .np_tx_fifo_sz = 16, | |
228 | .tx_fifo_sz = 128, | |
229 | }; | |
230 | ||
231 | int board_usb_init(int index, enum usb_init_type init) | |
232 | { | |
e76943ca | 233 | ofnode node; |
54f17fa0 | 234 | bool matched = false; |
54f17fa0 KY |
235 | |
236 | /* find the usb_otg node */ | |
e76943ca KY |
237 | node = ofnode_by_compatible(ofnode_null(), "snps,dwc2"); |
238 | while (ofnode_valid(node)) { | |
51c54080 JK |
239 | switch (usb_get_dr_mode(node)) { |
240 | case USB_DR_MODE_OTG: | |
35c27506 | 241 | case USB_DR_MODE_PERIPHERAL: |
54f17fa0 KY |
242 | matched = true; |
243 | break; | |
51c54080 JK |
244 | |
245 | default: | |
246 | break; | |
54f17fa0 KY |
247 | } |
248 | ||
51c54080 JK |
249 | if (matched) |
250 | break; | |
251 | ||
e76943ca | 252 | node = ofnode_by_compatible(node, "snps,dwc2"); |
54f17fa0 KY |
253 | } |
254 | if (!matched) { | |
255 | debug("Not found usb_otg device\n"); | |
256 | return -ENODEV; | |
257 | } | |
e76943ca | 258 | otg_data.regs_otg = ofnode_get_addr(node); |
54f17fa0 | 259 | |
e0479b71 | 260 | #ifdef CONFIG_ROCKCHIP_USB2_PHY |
17224b32 KY |
261 | int ret; |
262 | u32 phandle, offset; | |
263 | ofnode phy_node; | |
264 | ||
265 | ret = ofnode_read_u32(node, "phys", &phandle); | |
266 | if (ret) | |
267 | return ret; | |
268 | ||
269 | node = ofnode_get_by_phandle(phandle); | |
270 | if (!ofnode_valid(node)) { | |
271 | debug("Not found usb phy device\n"); | |
272 | return -ENODEV; | |
273 | } | |
274 | ||
275 | phy_node = ofnode_get_parent(node); | |
276 | if (!ofnode_valid(node)) { | |
277 | debug("Not found usb phy device\n"); | |
278 | return -ENODEV; | |
279 | } | |
280 | ||
281 | otg_data.phy_of_node = phy_node; | |
282 | ret = ofnode_read_u32(node, "reg", &offset); | |
283 | if (ret) | |
284 | return ret; | |
285 | otg_data.regs_phy = offset + | |
286 | (u32)syscon_get_first_range(ROCKCHIP_SYSCON_GRF); | |
287 | #endif | |
54f17fa0 KY |
288 | return dwc2_udc_probe(&otg_data); |
289 | } | |
290 | ||
291 | int board_usb_cleanup(int index, enum usb_init_type init) | |
292 | { | |
293 | return 0; | |
294 | } | |
c618bb00 JT |
295 | #endif /* CONFIG_USB_GADGET_DWC2_OTG */ |
296 | ||
41782a9c | 297 | #if IS_ENABLED(CONFIG_FASTBOOT) |
851737ab | 298 | int fastboot_set_reboot_flag(enum fastboot_reboot_reason reason) |
54f17fa0 | 299 | { |
851737ab RK |
300 | if (reason != FASTBOOT_REBOOT_REASON_BOOTLOADER) |
301 | return -ENOTSUPP; | |
302 | ||
54f17fa0 KY |
303 | printf("Setting reboot to fastboot flag ...\n"); |
304 | /* Set boot mode to fastboot */ | |
305 | writel(BOOT_FASTBOOT, CONFIG_ROCKCHIP_BOOT_MODE_REG); | |
306 | ||
307 | return 0; | |
308 | } | |
309 | #endif | |
04825384 RG |
310 | |
311 | #ifdef CONFIG_MISC_INIT_R | |
08d61f87 QS |
312 | int rockchip_setup_macaddr(void) |
313 | { | |
314 | #if CONFIG_IS_ENABLED(HASH) && CONFIG_IS_ENABLED(SHA256) | |
315 | int ret; | |
316 | const char *cpuid = env_get("cpuid#"); | |
317 | u8 hash[SHA256_SUM_LEN]; | |
318 | int size = sizeof(hash); | |
319 | u8 mac_addr[6]; | |
320 | ||
321 | /* Only generate a MAC address, if none is set in the environment */ | |
322 | if (env_get("ethaddr")) | |
323 | return 0; | |
324 | ||
325 | if (!cpuid) { | |
326 | debug("%s: could not retrieve 'cpuid#'\n", __func__); | |
327 | return -1; | |
328 | } | |
329 | ||
330 | ret = hash_block("sha256", (void *)cpuid, strlen(cpuid), hash, &size); | |
331 | if (ret) { | |
332 | debug("%s: failed to calculate SHA256\n", __func__); | |
333 | return -1; | |
334 | } | |
335 | ||
336 | /* Copy 6 bytes of the hash to base the MAC address on */ | |
337 | memcpy(mac_addr, hash, 6); | |
338 | ||
339 | /* Make this a valid MAC address and set it */ | |
340 | mac_addr[0] &= 0xfe; /* clear multicast bit */ | |
341 | mac_addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ | |
342 | eth_env_set_enetaddr("ethaddr", mac_addr); | |
343 | ||
344 | /* Make a valid MAC address for ethernet1 */ | |
345 | mac_addr[5] ^= 0x01; | |
346 | eth_env_set_enetaddr("eth1addr", mac_addr); | |
347 | #endif | |
348 | return 0; | |
349 | } | |
350 | ||
351 | int rockchip_cpuid_from_efuse(const u32 cpuid_offset, | |
352 | const u32 cpuid_length, | |
353 | u8 *cpuid) | |
354 | { | |
355 | #if IS_ENABLED(CONFIG_ROCKCHIP_EFUSE) || IS_ENABLED(CONFIG_ROCKCHIP_OTP) | |
356 | struct udevice *dev; | |
357 | int ret; | |
358 | ||
359 | /* retrieve the device */ | |
360 | #if IS_ENABLED(CONFIG_ROCKCHIP_EFUSE) | |
361 | ret = uclass_get_device_by_driver(UCLASS_MISC, | |
362 | DM_DRIVER_GET(rockchip_efuse), &dev); | |
363 | #elif IS_ENABLED(CONFIG_ROCKCHIP_OTP) | |
364 | ret = uclass_get_device_by_driver(UCLASS_MISC, | |
365 | DM_DRIVER_GET(rockchip_otp), &dev); | |
366 | #endif | |
367 | if (ret) { | |
368 | debug("%s: could not find efuse device\n", __func__); | |
369 | return -1; | |
370 | } | |
371 | ||
372 | /* read the cpu_id range from the efuses */ | |
373 | ret = misc_read(dev, cpuid_offset, cpuid, cpuid_length); | |
374 | if (ret < 0) { | |
375 | debug("%s: reading cpuid from the efuses failed\n", | |
376 | __func__); | |
377 | return -1; | |
378 | } | |
379 | #endif | |
380 | return 0; | |
381 | } | |
382 | ||
383 | int rockchip_cpuid_set(const u8 *cpuid, const u32 cpuid_length) | |
384 | { | |
385 | u8 low[cpuid_length / 2], high[cpuid_length / 2]; | |
386 | char cpuid_str[cpuid_length * 2 + 1]; | |
387 | u64 serialno; | |
388 | char serialno_str[17]; | |
389 | const char *oldid; | |
390 | int i; | |
391 | ||
392 | memset(cpuid_str, 0, sizeof(cpuid_str)); | |
393 | for (i = 0; i < cpuid_length; i++) | |
394 | sprintf(&cpuid_str[i * 2], "%02x", cpuid[i]); | |
395 | ||
396 | debug("cpuid: %s\n", cpuid_str); | |
397 | ||
398 | /* | |
399 | * Mix the cpuid bytes using the same rules as in | |
400 | * ${linux}/drivers/soc/rockchip/rockchip-cpuinfo.c | |
401 | */ | |
402 | for (i = 0; i < cpuid_length / 2; i++) { | |
403 | low[i] = cpuid[1 + (i << 1)]; | |
404 | high[i] = cpuid[i << 1]; | |
405 | } | |
406 | ||
407 | serialno = crc32_no_comp(0, low, cpuid_length / 2); | |
408 | serialno |= (u64)crc32_no_comp(serialno, high, cpuid_length / 2) << 32; | |
409 | snprintf(serialno_str, sizeof(serialno_str), "%016llx", serialno); | |
410 | ||
411 | oldid = env_get("cpuid#"); | |
412 | if (oldid && strcmp(oldid, cpuid_str) != 0) | |
413 | printf("cpuid: value %s present in env does not match hardware %s\n", | |
414 | oldid, cpuid_str); | |
415 | ||
416 | env_set("cpuid#", cpuid_str); | |
417 | ||
418 | /* Only generate serial# when none is set yet */ | |
419 | if (!env_get("serial#")) | |
420 | env_set("serial#", serialno_str); | |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
7400ed6a QS |
425 | __weak int rockchip_early_misc_init_r(void) |
426 | { | |
427 | return 0; | |
428 | } | |
429 | ||
04825384 RG |
430 | __weak int misc_init_r(void) |
431 | { | |
2eedb6d9 | 432 | const u32 cpuid_offset = CFG_CPUID_OFFSET; |
04825384 RG |
433 | const u32 cpuid_length = 0x10; |
434 | u8 cpuid[cpuid_length]; | |
435 | int ret; | |
436 | ||
7400ed6a QS |
437 | ret = rockchip_early_misc_init_r(); |
438 | if (ret) | |
439 | return ret; | |
440 | ||
04825384 RG |
441 | ret = rockchip_cpuid_from_efuse(cpuid_offset, cpuid_length, cpuid); |
442 | if (ret) | |
443 | return ret; | |
444 | ||
445 | ret = rockchip_cpuid_set(cpuid, cpuid_length); | |
446 | if (ret) | |
447 | return ret; | |
448 | ||
449 | ret = rockchip_setup_macaddr(); | |
450 | ||
451 | return ret; | |
452 | } | |
453 | #endif | |
d2048baf CM |
454 | |
455 | #if IS_ENABLED(CONFIG_BOARD_RNG_SEED) && IS_ENABLED(CONFIG_RNG_ROCKCHIP) | |
456 | #include <rng.h> | |
457 | ||
458 | /* Use hardware rng to seed Linux random. */ | |
459 | __weak int board_rng_seed(struct abuf *buf) | |
460 | { | |
461 | struct udevice *dev; | |
462 | size_t len = 0x8; | |
463 | u64 *data; | |
464 | ||
465 | data = malloc(len); | |
466 | if (!data) { | |
467 | printf("Out of memory\n"); | |
468 | return -ENOMEM; | |
469 | } | |
470 | ||
471 | if (uclass_get_device(UCLASS_RNG, 0, &dev) || !dev) { | |
472 | printf("No RNG device\n"); | |
473 | return -ENODEV; | |
474 | } | |
475 | ||
476 | if (dm_rng_read(dev, data, len)) { | |
477 | printf("Reading RNG failed\n"); | |
478 | return -EIO; | |
479 | } | |
480 | ||
481 | abuf_init_set(buf, data, len); | |
482 | ||
483 | return 0; | |
484 | } | |
485 | #endif |