1 /*
2  * Copyright (c) 2019 Aurelien Jarno
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <device.h>
8 #include <drivers/hwinfo.h>
9 #include <init.h>
10 #include <soc.h>
11 #include <string.h>
12 
13 static uint8_t sam_uid[16];
14 
z_impl_hwinfo_get_device_id(uint8_t * buffer,size_t length)15 ssize_t z_impl_hwinfo_get_device_id(uint8_t *buffer, size_t length)
16 {
17 	if (length > sizeof(sam_uid)) {
18 		length = sizeof(sam_uid);
19 	}
20 
21 	memcpy(buffer, sam_uid, length);
22 
23 	return length;
24 }
25 
26 /* On the Atmel SAM SoC series, the device id is located in the flash
27  * controller. The controller can either present the flash area containing
28  * the code, the unique identifier or the user signature area at the flash
29  * location. Therefore the function reading the device id must be executed
30  * from RAM with the interrupts disabled. To avoid executing this complex
31  * code each time the device id is requested, we do this at boot time at save
32  * the 128-bit value into RAM.
33  */
hwinfo_sam_read_device_id(void)34 __ramfunc static void hwinfo_sam_read_device_id(void)
35 {
36 	Efc *efc = (Efc *)DT_REG_ADDR(DT_INST(0, atmel_sam_flash_controller));
37 	uint8_t *flash = (uint8_t *)CONFIG_FLASH_BASE_ADDRESS;
38 	int i;
39 
40 	/* Switch the flash controller to the unique identifier area. The flash
41 	 * is not available anymore, hence we have to wait for it to be *NOT*
42 	 * ready.
43 	 */
44 	efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_STUI;
45 	while ((efc->EEFC_FSR & EEFC_FSR_FRDY) == EEFC_FSR_FRDY) {
46 		/* Wait */
47 	}
48 
49 	/* Copy the 128-bit unique ID. We cannot use memcpy as it would
50 	 * execute code from flash.
51 	 */
52 	for (i = 0; i < sizeof(sam_uid); i++) {
53 		sam_uid[i] = flash[i];
54 	}
55 
56 	/* Switch back the controller to the flash area and wait for it to
57 	 * be ready.
58 	 */
59 	efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_SPUI;
60 	while ((efc->EEFC_FSR & EEFC_FSR_FRDY) != EEFC_FSR_FRDY) {
61 		/* Wait */
62 	}
63 }
64 
hwinfo_sam_init(const struct device * arg)65 static int hwinfo_sam_init(const struct device *arg)
66 {
67 	Efc *efc = (Efc *)DT_REG_ADDR(DT_INST(0, atmel_sam_flash_controller));
68 	uint32_t fmr;
69 	int key;
70 
71 	/* Disable interrupts. */
72 	key = irq_lock();
73 
74 	/* Disable code loop optimization and sequential code optimization. */
75 	fmr = efc->EEFC_FMR;
76 
77 #ifndef CONFIG_SOC_SERIES_SAM3X
78 	efc->EEFC_FMR = (fmr & (~EEFC_FMR_CLOE)) | EEFC_FMR_SCOD;
79 #else
80 	/* SAM3x does not have loop optimization (EEFC_FMR_CLOE) */
81 	efc->EEFC_FMR |= EEFC_FMR_SCOD;
82 #endif
83 
84 	/* Read the device ID using code in RAM */
85 	hwinfo_sam_read_device_id();
86 
87 	/* Restore code optimization settings. */
88 	efc->EEFC_FMR = fmr;
89 
90 	/* Re-enable interrupts */
91 	irq_unlock(key);
92 
93 	return 0;
94 }
95 
96 SYS_INIT(hwinfo_sam_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
97