|  | /* | 
|  | * Copyright (c) 2019 Aurelien Jarno | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <device.h> | 
|  | #include <drivers/hwinfo.h> | 
|  | #include <init.h> | 
|  | #include <soc.h> | 
|  | #include <string.h> | 
|  |  | 
|  | static u8_t sam_uid[16]; | 
|  |  | 
|  | ssize_t z_impl_hwinfo_get_device_id(u8_t *buffer, size_t length) | 
|  | { | 
|  | if (length > sizeof(sam_uid)) { | 
|  | length = sizeof(sam_uid); | 
|  | } | 
|  |  | 
|  | memcpy(buffer, sam_uid, length); | 
|  |  | 
|  | return length; | 
|  | } | 
|  |  | 
|  | /* On the Atmel SAM SoC series, the device id is located in the flash | 
|  | * controller. The controller can either present the flash area containing | 
|  | * the code, the unique identifier or the user signature area at the flash | 
|  | * location. Therefore the function reading the device id must be executed | 
|  | * from RAM with the interrupts disabled. To avoid executing this complex | 
|  | * code each time the device id is requested, we do this at boot time at save | 
|  | * the 128-bit value into RAM. | 
|  | */ | 
|  | __ramfunc static void hwinfo_sam_read_device_id(void) | 
|  | { | 
|  | Efc *efc = (Efc *)DT_FLASH_DEV_BASE_ADDRESS; | 
|  | u8_t *flash = (u8_t *)CONFIG_FLASH_BASE_ADDRESS; | 
|  | int i; | 
|  |  | 
|  | /* Switch the flash controller to the unique identifier area. The flash | 
|  | * is not available anymore, hence we have to wait for it to be *NOT* | 
|  | * ready. | 
|  | */ | 
|  | efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_STUI; | 
|  | while ((efc->EEFC_FSR & EEFC_FSR_FRDY) == EEFC_FSR_FRDY) { | 
|  | /* Wait */ | 
|  | } | 
|  |  | 
|  | /* Copy the 128-bit unique ID. We cannot use memcpy as it would | 
|  | * execute code from flash. | 
|  | */ | 
|  | for (i = 0; i < sizeof(sam_uid); i++) { | 
|  | sam_uid[i] = flash[i]; | 
|  | } | 
|  |  | 
|  | /* Switch back the controller to the flash area and wait for it to | 
|  | * be ready. | 
|  | */ | 
|  | efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_SPUI; | 
|  | while ((efc->EEFC_FSR & EEFC_FSR_FRDY) != EEFC_FSR_FRDY) { | 
|  | /* Wait */ | 
|  | } | 
|  | } | 
|  |  | 
|  | static int hwinfo_sam_init(struct device *arg) | 
|  | { | 
|  | Efc *efc = (Efc *)DT_FLASH_DEV_BASE_ADDRESS; | 
|  | u32_t fmr; | 
|  | int key; | 
|  |  | 
|  | /* Disable interrupts. */ | 
|  | key = irq_lock(); | 
|  |  | 
|  | /* Disable code loop optimization and sequential code optimization. */ | 
|  | fmr = efc->EEFC_FMR; | 
|  | efc->EEFC_FMR = (fmr & (~EEFC_FMR_CLOE)) | EEFC_FMR_SCOD; | 
|  |  | 
|  | /* Read the device ID using code in RAM */ | 
|  | hwinfo_sam_read_device_id(); | 
|  |  | 
|  | /* Restore code optimization settings. */ | 
|  | efc->EEFC_FMR = fmr; | 
|  |  | 
|  | /* Re-enable interrupts */ | 
|  | irq_unlock(key); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | SYS_INIT(hwinfo_sam_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |