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 <aurelien@aurel32.net>
This commit is contained in:
Aurelien Jarno 2019-02-10 13:26:55 +01:00 committed by Anas Nashif
commit bf59fcb78c
3 changed files with 98 additions and 0 deletions

View file

@ -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)

View file

@ -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

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2019 Aurelien Jarno
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <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);