From bf59fcb78c92d46e24ed0fc5cc76eb89049f375c Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Sun, 10 Feb 2019 13:26:55 +0100 Subject: [PATCH] drivers: hwinfo: add driver support for Atmel SAM device ID Add driver support for Atmel SAM device ID, which is 16-bytes long. On this SoC family, the device ID is part of the flash controller and complex to read. Therefore the driver reads it once at boot time and then just returned the copy saved in RAM. Signed-off-by: Aurelien Jarno --- drivers/hwinfo/CMakeLists.txt | 1 + drivers/hwinfo/Kconfig | 7 +++ drivers/hwinfo/hwinfo_sam.c | 90 +++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 drivers/hwinfo/hwinfo_sam.c 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);