diff --git a/drivers/hwinfo/CMakeLists.txt b/drivers/hwinfo/CMakeLists.txt index fd31ca80391..076bb69a9c0 100644 --- a/drivers/hwinfo/CMakeLists.txt +++ b/drivers/hwinfo/CMakeLists.txt @@ -5,5 +5,6 @@ zephyr_sources_ifdef(CONFIG_HWINFO_STM32 hwinfo_stm32.c) zephyr_sources_ifdef(CONFIG_HWINFO_NRF hwinfo_nrf.c) zephyr_sources_ifdef(CONFIG_HWINFO_MCUX_SIM hwinfo_mcux_sim.c) zephyr_sources_ifdef(CONFIG_HWINFO_IMXRT hwinfo_imxrt.c) +zephyr_sources_ifdef(CONFIG_HWINFO_SAM hwinfo_sam.c) zephyr_sources_ifdef(CONFIG_HWINFO_SHELL hwinfo_shell.c) diff --git a/drivers/hwinfo/Kconfig b/drivers/hwinfo/Kconfig index d41832276b8..c6120cbd2a3 100644 --- a/drivers/hwinfo/Kconfig +++ b/drivers/hwinfo/Kconfig @@ -48,4 +48,11 @@ config HWINFO_IMXRT help Enable NXP i.mx RT hwinfo driver. +config HWINFO_SAM + bool "Atmel SAM device ID" + default y + depends on SOC_FAMILY_SAM + help + Enable Atmel SAM hwinfo driver. + endif diff --git a/drivers/hwinfo/hwinfo_sam.c b/drivers/hwinfo/hwinfo_sam.c new file mode 100644 index 00000000000..a2706b8ea44 --- /dev/null +++ b/drivers/hwinfo/hwinfo_sam.c @@ -0,0 +1,90 @@ +/* + * 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);