/* * Copyright (c) 2019 Aurelien Jarno * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include 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);