diff options
Diffstat (limited to 'arch/arm/mach-stm32mp')
-rw-r--r-- | arch/arm/mach-stm32mp/Kconfig | 15 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/Makefile | 8 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/bsec.c | 431 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/cpu.c | 95 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/include/mach/stm32.h | 17 | ||||
-rw-r--r-- | arch/arm/mach-stm32mp/spl.c | 3 |
6 files changed, 563 insertions, 6 deletions
diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig index ccbeb5c3881..abceeded24a 100644 --- a/arch/arm/mach-stm32mp/Kconfig +++ b/arch/arm/mach-stm32mp/Kconfig @@ -54,4 +54,19 @@ config SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION_MMC2 source "board/st/stm32mp1/Kconfig" +# currently activated for debug / should be deactivated for real product +if DEBUG_UART + +config DEBUG_UART_BOARD_INIT + default y + +# debug on UART4 by default +config DEBUG_UART_BASE + default 0x40010000 + +# clock source is HSI on reset +config DEBUG_UART_CLOCK + default 64000000 +endif + endif diff --git a/arch/arm/mach-stm32mp/Makefile b/arch/arm/mach-stm32mp/Makefile index 08ee642d908..f59ced5ee1b 100644 --- a/arch/arm/mach-stm32mp/Makefile +++ b/arch/arm/mach-stm32mp/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +# SPDX-License-Identifier: GPL-2.0+ # # Copyright (C) 2018, STMicroelectronics - All Rights Reserved # @@ -7,6 +7,10 @@ obj-y += cpu.o obj-y += dram_init.o obj-y += syscon.o -obj-$(CONFIG_SPL_BUILD) += spl.o +ifdef CONFIG_SPL_BUILD +obj-y += spl.o +else +obj-y += bsec.o +endif obj-$(CONFIG_ARMV7_PSCI) += psci.o obj-$(CONFIG_$(SPL_)DM_REGULATOR) += pwr_regulator.o diff --git a/arch/arm/mach-stm32mp/bsec.c b/arch/arm/mach-stm32mp/bsec.c new file mode 100644 index 00000000000..0e152efc045 --- /dev/null +++ b/arch/arm/mach-stm32mp/bsec.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <dm.h> +#include <misc.h> +#include <asm/io.h> +#include <linux/iopoll.h> + +#define BSEC_OTP_MAX_VALUE 95 + +#define BSEC_TIMEOUT_US 10000 + +/* BSEC REGISTER OFFSET (base relative) */ +#define BSEC_OTP_CONF_OFF 0x000 +#define BSEC_OTP_CTRL_OFF 0x004 +#define BSEC_OTP_WRDATA_OFF 0x008 +#define BSEC_OTP_STATUS_OFF 0x00C +#define BSEC_OTP_LOCK_OFF 0x010 +#define BSEC_DISTURBED_OFF 0x01C +#define BSEC_ERROR_OFF 0x034 +#define BSEC_SPLOCK_OFF 0x064 /* Program safmem sticky lock */ +#define BSEC_SWLOCK_OFF 0x07C /* write in OTP sticky lock */ +#define BSEC_SRLOCK_OFF 0x094 /* shadowing sticky lock */ +#define BSEC_OTP_DATA_OFF 0x200 + +/* BSEC_CONFIGURATION Register MASK */ +#define BSEC_CONF_POWER_UP 0x001 + +/* BSEC_CONTROL Register */ +#define BSEC_READ 0x000 +#define BSEC_WRITE 0x100 + +/* LOCK Register */ +#define OTP_LOCK_MASK 0x1F +#define OTP_LOCK_BANK_SHIFT 0x05 +#define OTP_LOCK_BIT_MASK 0x01 + +/* STATUS Register */ +#define BSEC_MODE_BUSY_MASK 0x08 +#define BSEC_MODE_PROGFAIL_MASK 0x10 +#define BSEC_MODE_PWR_MASK 0x20 + +/* + * OTP Lock services definition + * Value must corresponding to the bit number in the register + */ +#define BSEC_LOCK_PROGRAM 0x04 + +/** + * bsec_check_error() - Check status of one otp + * @base: base address of bsec IP + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: 0 if no error, -EAGAIN or -ENOTSUPP + */ +static u32 bsec_check_error(u32 base, u32 otp) +{ + u32 bit; + u32 bank; + + bit = 1 << (otp & OTP_LOCK_MASK); + bank = ((otp >> OTP_LOCK_BANK_SHIFT) & OTP_LOCK_MASK) * sizeof(u32); + + if (readl(base + BSEC_DISTURBED_OFF + bank) & bit) + return -EAGAIN; + else if (readl(base + BSEC_ERROR_OFF + bank) & bit) + return -ENOTSUPP; + + return 0; +} + +/** + * bsec_lock() - manage lock for each type SR/SP/SW + * @address: address of bsec IP register + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: true if locked else false + */ +static bool bsec_read_lock(u32 address, u32 otp) +{ + u32 bit; + u32 bank; + + bit = 1 << (otp & OTP_LOCK_MASK); + bank = ((otp >> OTP_LOCK_BANK_SHIFT) & OTP_LOCK_MASK) * sizeof(u32); + + return !!(readl(address + bank) & bit); +} + +/** + * bsec_read_SR_lock() - read SR lock (Shadowing) + * @base: base address of bsec IP + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: true if locked else false + */ +static bool bsec_read_SR_lock(u32 base, u32 otp) +{ + return bsec_read_lock(base + BSEC_SRLOCK_OFF, otp); +} + +/** + * bsec_read_SP_lock() - read SP lock (program Lock) + * @base: base address of bsec IP + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: true if locked else false + */ +static bool bsec_read_SP_lock(u32 base, u32 otp) +{ + return bsec_read_lock(base + BSEC_SPLOCK_OFF, otp); +} + +/** + * bsec_SW_lock() - manage SW lock (Write in Shadow) + * @base: base address of bsec IP + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: true if locked else false + */ +static bool bsec_read_SW_lock(u32 base, u32 otp) +{ + return bsec_read_lock(base + BSEC_SWLOCK_OFF, otp); +} + +/** + * bsec_power_safmem() - Activate or deactivate safmem power + * @base: base address of bsec IP + * @power: true to power up , false to power down + * Return: 0 if succeed + */ +static int bsec_power_safmem(u32 base, bool power) +{ + u32 val; + u32 mask; + + if (power) { + setbits_le32(base + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP); + mask = BSEC_MODE_PWR_MASK; + } else { + clrbits_le32(base + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP); + mask = 0; + } + + /* waiting loop */ + return readl_poll_timeout(base + BSEC_OTP_STATUS_OFF, + val, (val & BSEC_MODE_PWR_MASK) == mask, + BSEC_TIMEOUT_US); +} + +/** + * bsec_shadow_register() - copy safmen otp to bsec data + * @base: base address of bsec IP + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: 0 if no error + */ +static int bsec_shadow_register(u32 base, u32 otp) +{ + u32 val; + int ret; + bool power_up = false; + + /* check if shadowing of otp is locked */ + if (bsec_read_SR_lock(base, otp)) + pr_debug("bsec : OTP %d is locked and refreshed with 0\n", otp); + + /* check if safemem is power up */ + val = readl(base + BSEC_OTP_STATUS_OFF); + if (!(val & BSEC_MODE_PWR_MASK)) { + ret = bsec_power_safmem(base, true); + if (ret) + return ret; + power_up = 1; + } + /* set BSEC_OTP_CTRL_OFF with the otp value*/ + writel(otp | BSEC_READ, base + BSEC_OTP_CTRL_OFF); + + /* check otp status*/ + ret = readl_poll_timeout(base + BSEC_OTP_STATUS_OFF, + val, (val & BSEC_MODE_BUSY_MASK) == 0, + BSEC_TIMEOUT_US); + if (ret) + return ret; + + ret = bsec_check_error(base, otp); + + if (power_up) + bsec_power_safmem(base, false); + + return ret; +} + +/** + * bsec_read_shadow() - read an otp data value from shadow + * @base: base address of bsec IP + * @val: read value + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: 0 if no error + */ +static int bsec_read_shadow(u32 base, u32 *val, u32 otp) +{ + *val = readl(base + BSEC_OTP_DATA_OFF + otp * sizeof(u32)); + + return bsec_check_error(base, otp); +} + +/** + * bsec_write_shadow() - write value in BSEC data register in shadow + * @base: base address of bsec IP + * @val: value to write + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * Return: 0 if no error + */ +static int bsec_write_shadow(u32 base, u32 val, u32 otp) +{ + /* check if programming of otp is locked */ + if (bsec_read_SW_lock(base, otp)) + pr_debug("bsec : OTP %d is lock, write will be ignore\n", otp); + + writel(val, base + BSEC_OTP_DATA_OFF + otp * sizeof(u32)); + + return bsec_check_error(base, otp); +} + +/** + * bsec_program_otp() - program a bit in SAFMEM + * @base: base address of bsec IP + * @val: value to program + * @otp: otp number (0 - BSEC_OTP_MAX_VALUE) + * after the function the otp data is not refreshed in shadow + * Return: 0 if no error + */ +static int bsec_program_otp(long base, u32 val, u32 otp) +{ + u32 ret; + bool power_up = false; + + if (bsec_read_SP_lock(base, otp)) + pr_debug("bsec : OTP %d locked, prog will be ignore\n", otp); + + if (readl(base + BSEC_OTP_LOCK_OFF) & (1 << BSEC_LOCK_PROGRAM)) + pr_debug("bsec : Global lock, prog will be ignore\n"); + + /* check if safemem is power up */ + if (!(readl(base + BSEC_OTP_STATUS_OFF) & BSEC_MODE_PWR_MASK)) { + ret = bsec_power_safmem(base, true); + if (ret) + return ret; + + power_up = true; + } + /* set value in write register*/ + writel(val, base + BSEC_OTP_WRDATA_OFF); + + /* set BSEC_OTP_CTRL_OFF with the otp value */ + writel(otp | BSEC_WRITE, base + BSEC_OTP_CTRL_OFF); + + /* check otp status*/ + ret = readl_poll_timeout(base + BSEC_OTP_STATUS_OFF, + val, (val & BSEC_MODE_BUSY_MASK) == 0, + BSEC_TIMEOUT_US); + if (ret) + return ret; + + if (val & BSEC_MODE_PROGFAIL_MASK) + ret = -EACCES; + else + ret = bsec_check_error(base, otp); + + if (power_up) + bsec_power_safmem(base, false); + + return ret; +} + +/* BSEC MISC driver *******************************************************/ +struct stm32mp_bsec_platdata { + u32 base; +}; + +static int stm32mp_bsec_read_otp(struct udevice *dev, u32 *val, u32 otp) +{ + struct stm32mp_bsec_platdata *plat = dev_get_platdata(dev); + u32 tmp_data = 0; + int ret; + + /* read current shadow value */ + ret = bsec_read_shadow(plat->base, &tmp_data, otp); + if (ret) + return ret; + + /* copy otp in shadow */ + ret = bsec_shadow_register(plat->base, otp); + if (ret) + return ret; + + ret = bsec_read_shadow(plat->base, val, otp); + if (ret) + return ret; + + /* restore shadow value */ + ret = bsec_write_shadow(plat->base, tmp_data, otp); + return ret; +} + +static int stm32mp_bsec_read_shadow(struct udevice *dev, u32 *val, u32 otp) +{ + struct stm32mp_bsec_platdata *plat = dev_get_platdata(dev); + + return bsec_read_shadow(plat->base, val, otp); +} + +static int stm32mp_bsec_write_otp(struct udevice *dev, u32 val, u32 otp) +{ + struct stm32mp_bsec_platdata *plat = dev_get_platdata(dev); + + return bsec_program_otp(plat->base, val, otp); +} + +static int stm32mp_bsec_write_shadow(struct udevice *dev, u32 val, u32 otp) +{ + struct stm32mp_bsec_platdata *plat = dev_get_platdata(dev); + + return bsec_write_shadow(plat->base, val, otp); +} + +static int stm32mp_bsec_read(struct udevice *dev, int offset, + void *buf, int size) +{ + int ret; + int i; + bool shadow = true; + int nb_otp = size / sizeof(u32); + int otp; + + if (offset >= STM32_BSEC_OTP_OFFSET) { + offset -= STM32_BSEC_OTP_OFFSET; + shadow = false; + } + otp = offset / sizeof(u32); + + if (otp < 0 || (otp + nb_otp - 1) > BSEC_OTP_MAX_VALUE) { + dev_err(dev, "wrong value for otp, max value : %i\n", + BSEC_OTP_MAX_VALUE); + return -EINVAL; + } + + for (i = otp; i < (otp + nb_otp); i++) { + u32 *addr = &((u32 *)buf)[i - otp]; + + if (shadow) + ret = stm32mp_bsec_read_shadow(dev, addr, i); + else + ret = stm32mp_bsec_read_otp(dev, addr, i); + + if (ret) + break; + } + return ret; +} + +static int stm32mp_bsec_write(struct udevice *dev, int offset, + const void *buf, int size) +{ + int ret = 0; + int i; + bool shadow = true; + int nb_otp = size / sizeof(u32); + int otp; + + if (offset >= STM32_BSEC_OTP_OFFSET) { + offset -= STM32_BSEC_OTP_OFFSET; + shadow = false; + } + otp = offset / sizeof(u32); + + if (otp < 0 || (otp + nb_otp - 1) > BSEC_OTP_MAX_VALUE) { + dev_err(dev, "wrong value for otp, max value : %d\n", + BSEC_OTP_MAX_VALUE); + return -EINVAL; + } + + for (i = otp; i < otp + nb_otp; i++) { + u32 *val = &((u32 *)buf)[i - otp]; + + if (shadow) + ret = stm32mp_bsec_write_shadow(dev, *val, i); + else + ret = stm32mp_bsec_write_otp(dev, *val, i); + if (ret) + break; + } + return ret; +} + +static const struct misc_ops stm32mp_bsec_ops = { + .read = stm32mp_bsec_read, + .write = stm32mp_bsec_write, +}; + +static int stm32mp_bsec_ofdata_to_platdata(struct udevice *dev) +{ + struct stm32mp_bsec_platdata *plat = dev_get_platdata(dev); + + plat->base = (u32)dev_read_addr_ptr(dev); + + return 0; +} + +static const struct udevice_id stm32mp_bsec_ids[] = { + { .compatible = "st,stm32mp-bsec" }, + {} +}; + +U_BOOT_DRIVER(stm32mp_bsec) = { + .name = "stm32mp_bsec", + .id = UCLASS_MISC, + .of_match = stm32mp_bsec_ids, + .ofdata_to_platdata = stm32mp_bsec_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct stm32mp_bsec_platdata), + .ops = &stm32mp_bsec_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +/* bsec IP is not present in device tee, manage IP address by platdata */ +static struct stm32mp_bsec_platdata stm32_bsec_platdata = { + .base = STM32_BSEC_BASE, +}; + +U_BOOT_DEVICE(stm32mp_bsec) = { + .name = "stm32mp_bsec", + .platdata = &stm32_bsec_platdata, +}; diff --git a/arch/arm/mach-stm32mp/cpu.c b/arch/arm/mach-stm32mp/cpu.c index dfcbbd23146..0e01f8e6138 100644 --- a/arch/arm/mach-stm32mp/cpu.c +++ b/arch/arm/mach-stm32mp/cpu.c @@ -4,9 +4,13 @@ */ #include <common.h> #include <clk.h> +#include <debug_uart.h> +#include <environment.h> +#include <misc.h> #include <asm/io.h> #include <asm/arch/stm32.h> #include <asm/arch/sys_proto.h> +#include <dm/device.h> #include <dm/uclass.h> /* RCC register */ @@ -50,6 +54,10 @@ #define BOOTROM_INSTANCE_MASK GENMASK(31, 16) #define BOOTROM_INSTANCE_SHIFT 16 +/* BSEC OTP index */ +#define BSEC_OTP_SERIAL 13 +#define BSEC_OTP_MAC 57 + #if !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD) static void security_init(void) { @@ -152,6 +160,8 @@ static u32 get_bootmode(void) */ int arch_cpu_init(void) { + u32 boot_mode; + /* early armv7 timer init: needed for polling */ timer_init(); @@ -160,8 +170,17 @@ int arch_cpu_init(void) security_init(); #endif + /* get bootmode from BootRom context: saved in TAMP register */ - get_bootmode(); + boot_mode = get_bootmode(); + + if ((boot_mode & TAMP_BOOT_DEVICE_MASK) == BOOT_SERIAL_UART) + gd->flags |= GD_FLG_SILENT | GD_FLG_DISABLE_CONSOLE; +#if defined(CONFIG_DEBUG_UART) && \ + (!defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD)) + else + debug_uart_init(); +#endif return 0; } @@ -262,9 +281,83 @@ static void setup_boot_mode(void) } } +/* + * If there is no MAC address in the environment, then it will be initialized + * (silently) from the value in the OTP. + */ +static int setup_mac_address(void) +{ +#if defined(CONFIG_NET) + int ret; + int i; + u32 otp[2]; + uchar enetaddr[6]; + struct udevice *dev; + + /* MAC already in environment */ + if (eth_env_get_enetaddr("ethaddr", enetaddr)) + return 0; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stm32mp_bsec), + &dev); + if (ret) + return ret; + + ret = misc_read(dev, BSEC_OTP_MAC * 4 + STM32_BSEC_OTP_OFFSET, + otp, sizeof(otp)); + if (ret) + return ret; + + for (i = 0; i < 6; i++) + enetaddr[i] = ((uint8_t *)&otp)[i]; + + if (!is_valid_ethaddr(enetaddr)) { + pr_err("invalid MAC address in OTP %pM", enetaddr); + return -EINVAL; + } + pr_debug("OTP MAC address = %pM\n", enetaddr); + ret = !eth_env_set_enetaddr("ethaddr", enetaddr); + if (!ret) + pr_err("Failed to set mac address %pM from OTP: %d\n", + enetaddr, ret); +#endif + + return 0; +} + +static int setup_serial_number(void) +{ + char serial_string[25]; + u32 otp[3] = {0, 0, 0 }; + struct udevice *dev; + int ret; + + if (env_get("serial#")) + return 0; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stm32mp_bsec), + &dev); + if (ret) + return ret; + + ret = misc_read(dev, BSEC_OTP_SERIAL * 4 + STM32_BSEC_OTP_OFFSET, + otp, sizeof(otp)); + if (ret) + return ret; + + sprintf(serial_string, "%08x%08x%08x", otp[0], otp[1], otp[2]); + env_set("serial#", serial_string); + + return 0; +} + int arch_misc_init(void) { setup_boot_mode(); + setup_mac_address(); + setup_serial_number(); return 0; } diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index a8142013b08..5d0bdca1787 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -13,10 +13,23 @@ #define STM32_RCC_BASE 0x50000000 #define STM32_PWR_BASE 0x50001000 #define STM32_DBGMCU_BASE 0x50081000 +#define STM32_BSEC_BASE 0x5C005000 #define STM32_TZC_BASE 0x5C006000 #define STM32_ETZPC_BASE 0x5C007000 #define STM32_TAMP_BASE 0x5C00A000 +#ifdef CONFIG_DEBUG_UART_BASE +/* hardcoded value can be only used for DEBUG UART */ +#define STM32_USART1_BASE 0x5C000000 +#define STM32_USART2_BASE 0x4000E000 +#define STM32_USART3_BASE 0x4000F000 +#define STM32_UART4_BASE 0x40010000 +#define STM32_UART5_BASE 0x40011000 +#define STM32_USART6_BASE 0x44003000 +#define STM32_UART7_BASE 0x40018000 +#define STM32_UART8_BASE 0x40019000 +#endif + #define STM32_SYSRAM_BASE 0x2FFC0000 #define STM32_SYSRAM_SIZE SZ_256K @@ -83,5 +96,9 @@ enum boot_device { #define TAMP_BOOT_DEVICE_MASK GENMASK(7, 4) #define TAMP_BOOT_INSTANCE_MASK GENMASK(3, 0) +/* offset used for BSEC driver: misc_read and misc_write */ +#define STM32_BSEC_SHADOW_OFFSET 0x0 +#define STM32_BSEC_OTP_OFFSET 0x80000000 + #endif /* __ASSEMBLY__*/ #endif /* _MACH_STM32_H_ */ diff --git a/arch/arm/mach-stm32mp/spl.c b/arch/arm/mach-stm32mp/spl.c index b56f0c5381c..790973e8b6e 100644 --- a/arch/arm/mach-stm32mp/spl.c +++ b/arch/arm/mach-stm32mp/spl.c @@ -14,9 +14,6 @@ u32 spl_boot_device(void) boot_mode = (readl(TAMP_BOOT_CONTEXT) & TAMP_BOOT_MODE_MASK) >> TAMP_BOOT_MODE_SHIFT; - clrsetbits_le32(TAMP_BOOT_CONTEXT, - TAMP_BOOT_MODE_MASK, - boot_mode << TAMP_BOOT_MODE_SHIFT); switch (boot_mode) { case BOOT_FLASH_SD_1: |