5106 lines
131 KiB
Diff
5106 lines
131 KiB
Diff
From c4b103412f1a8cd3472b9d82f7d73737803cf7e7 Mon Sep 17 00:00:00 2001
|
||
From: Romuald JEANNE <romuald.jeanne@st.com>
|
||
Date: Fri, 5 Jun 2020 13:41:19 +0200
|
||
Subject: [PATCH 1/5] ARM v2020.01-stm32mp-r1 MACHINE
|
||
|
||
---
|
||
.gitignore | 3 +
|
||
CONTRIBUTING.md | 30 +
|
||
MAINTAINERS | 1 +
|
||
Makefile | 2 +-
|
||
arch/arm/lib/bootm.c | 3 +
|
||
arch/arm/lib/crt0.S | 3 +-
|
||
arch/arm/mach-stm32mp/Kconfig | 79 +-
|
||
arch/arm/mach-stm32mp/Makefile | 4 +-
|
||
arch/arm/mach-stm32mp/boot_params.c | 45 +
|
||
arch/arm/mach-stm32mp/bsec.c | 121 +-
|
||
arch/arm/mach-stm32mp/cmd_stm32prog/Makefile | 9 +
|
||
.../arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c | 191 +++
|
||
arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c | 1743 ++++++++++++++++++++
|
||
arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h | 185 +++
|
||
.../mach-stm32mp/cmd_stm32prog/stm32prog_serial.c | 993 +++++++++++
|
||
.../arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c | 232 +++
|
||
arch/arm/mach-stm32mp/cpu.c | 107 +-
|
||
arch/arm/mach-stm32mp/dram_init.c | 18 +
|
||
arch/arm/mach-stm32mp/fdt.c | 132 +-
|
||
arch/arm/mach-stm32mp/include/mach/bsec.h | 7 +
|
||
arch/arm/mach-stm32mp/include/mach/ddr.h | 6 +-
|
||
arch/arm/mach-stm32mp/include/mach/stm32.h | 24 +-
|
||
arch/arm/mach-stm32mp/include/mach/stm32mp1_smc.h | 13 +-
|
||
arch/arm/mach-stm32mp/include/mach/stm32prog.h | 16 +
|
||
arch/arm/mach-stm32mp/include/mach/sys_proto.h | 24 +-
|
||
arch/arm/mach-stm32mp/psci.c | 22 +
|
||
arch/arm/mach-stm32mp/pwr_regulator.c | 31 +-
|
||
arch/arm/mach-stm32mp/spl.c | 43 +-
|
||
arch/arm/mach-stm32mp/syscon.c | 1 -
|
||
arch/sandbox/dts/test.dts | 85 +-
|
||
arch/sandbox/include/asm/gpio.h | 38 +-
|
||
31 files changed, 4044 insertions(+), 167 deletions(-)
|
||
create mode 100644 CONTRIBUTING.md
|
||
create mode 100644 arch/arm/mach-stm32mp/boot_params.c
|
||
create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
|
||
create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
|
||
create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
|
||
create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
|
||
create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c
|
||
create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
|
||
create mode 100644 arch/arm/mach-stm32mp/include/mach/bsec.h
|
||
create mode 100644 arch/arm/mach-stm32mp/include/mach/stm32prog.h
|
||
|
||
diff --git a/.gitignore b/.gitignore
|
||
index 2e1c8bf..bb2dfee 100644
|
||
--- a/.gitignore
|
||
+++ b/.gitignore
|
||
@@ -92,3 +92,6 @@ GTAGS
|
||
*.orig
|
||
*~
|
||
\#*#
|
||
+
|
||
+/oe-*
|
||
+bitbake-cookerdaemon.log
|
||
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
|
||
new file mode 100644
|
||
index 0000000..3d1bacd
|
||
--- /dev/null
|
||
+++ b/CONTRIBUTING.md
|
||
@@ -0,0 +1,30 @@
|
||
+# Contributing guide
|
||
+
|
||
+This document serves as a checklist before contributing to this repository. It includes links to read up on if topics are unclear to you.
|
||
+
|
||
+This guide mainly focuses on the proper use of Git.
|
||
+
|
||
+## 1. Issues
|
||
+
|
||
+STM32MPU projects do not activate "Github issues" feature for the time being. If you need to report an issue or question about this project deliverables, you can report them using [ ST Support Center ](https://my.st.com/ols#/ols/newrequest) or [ ST Community MPU Forum ](https://community.st.com/s/topic/0TO0X0000003u2AWAQ/stm32-mpus).
|
||
+
|
||
+## 2. Pull Requests
|
||
+
|
||
+STMicrolectronics is happy to receive contributions from the community, based on an initial Contributor License Agreement (CLA) procedure.
|
||
+
|
||
+* If you are an individual writing original source code and you are sure **you own the intellectual property**, then you need to sign an Individual CLA (https://cla.st.com).
|
||
+* If you work for a company that wants also to allow you to contribute with your work, your company needs to provide a Corporate CLA (https://cla.st.com) mentioning your GitHub account name.
|
||
+* If you are not sure that a CLA (Individual or Corporate) has been signed for your GitHub account you can check here (https://cla.st.com).
|
||
+
|
||
+Please note that:
|
||
+* The Corporate CLA will always take precedence over the Individual CLA.
|
||
+* One CLA submission is sufficient, for any project proposed by STMicroelectronics.
|
||
+
|
||
+__How to proceed__
|
||
+
|
||
+* We recommend to fork the project in your GitHub account to further develop your contribution. Please use the latest commit version.
|
||
+* Please, submit one Pull Request for one new feature or proposal. This will ease the analysis and final merge if accepted.
|
||
+
|
||
+__Note__
|
||
+
|
||
+Merge will not be done directly in GitHub but it will need first to follow internal integration process before public deliver in a standard release. The Pull request will stay open until it is merged and delivered.
|
||
diff --git a/MAINTAINERS b/MAINTAINERS
|
||
index 438fb22..afc2f19 100644
|
||
--- a/MAINTAINERS
|
||
+++ b/MAINTAINERS
|
||
@@ -337,6 +337,7 @@ L: uboot-stm32@st-md-mailman.stormreply.com (moderated for non-subscribers)
|
||
T: git https://gitlab.denx.de/u-boot/custodians/u-boot-stm.git
|
||
S: Maintained
|
||
F: arch/arm/mach-stm32mp/
|
||
+F: doc/board/st/
|
||
F: drivers/adc/stm32-adc*
|
||
F: drivers/clk/clk_stm32mp1.c
|
||
F: drivers/gpio/stm32_gpio.c
|
||
diff --git a/Makefile b/Makefile
|
||
index 1766f5a..8b390bc 100644
|
||
--- a/Makefile
|
||
+++ b/Makefile
|
||
@@ -3,7 +3,7 @@
|
||
VERSION = 2020
|
||
PATCHLEVEL = 01
|
||
SUBLEVEL =
|
||
-EXTRAVERSION =
|
||
+EXTRAVERSION = -stm32mp-r1
|
||
NAME =
|
||
|
||
# *DOCUMENTATION*
|
||
diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
|
||
index 769a642..9453c09 100644
|
||
--- a/arch/arm/lib/bootm.c
|
||
+++ b/arch/arm/lib/bootm.c
|
||
@@ -74,6 +74,9 @@ void arch_lmb_reserve(struct lmb *lmb)
|
||
gd->bd->bi_dram[bank].size - 1;
|
||
if (sp > bank_end)
|
||
continue;
|
||
+ if (bank_end > gd->ram_top)
|
||
+ bank_end = gd->ram_top - 1;
|
||
+
|
||
lmb_reserve(lmb, sp, bank_end - sp + 1);
|
||
break;
|
||
}
|
||
diff --git a/arch/arm/lib/crt0.S b/arch/arm/lib/crt0.S
|
||
index fb6c37c..df9dd83 100644
|
||
--- a/arch/arm/lib/crt0.S
|
||
+++ b/arch/arm/lib/crt0.S
|
||
@@ -127,8 +127,7 @@ ENTRY(_main)
|
||
ldr r0, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
|
||
bic r0, r0, #7 /* 8-byte alignment for ABI compliance */
|
||
mov sp, r0
|
||
- ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
|
||
- sub r9, r9, #GD_SIZE /* new GD is below bd */
|
||
+ ldr r9, [r9, #GD_NEW_GD] /* r9 <- gd->new_gd */
|
||
|
||
adr lr, here
|
||
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
|
||
diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig
|
||
index ae28f6e..f9f7943 100644
|
||
--- a/arch/arm/mach-stm32mp/Kconfig
|
||
+++ b/arch/arm/mach-stm32mp/Kconfig
|
||
@@ -33,8 +33,8 @@ config SYS_MALLOC_LEN
|
||
config ENV_SIZE
|
||
default 0x2000
|
||
|
||
-config TARGET_STM32MP1
|
||
- bool "Support stm32mp1xx"
|
||
+config STM32MP15x
|
||
+ bool "Support STMicroelectronics STM32MP15x Soc"
|
||
select ARCH_SUPPORT_PSCI if !STM32MP1_TRUSTED
|
||
select CPU_V7A
|
||
select CPU_V7_HAS_NONSEC if !STM32MP1_TRUSTED
|
||
@@ -45,19 +45,47 @@ config TARGET_STM32MP1
|
||
select STM32_RESET
|
||
select STM32_SERIAL
|
||
select SYS_ARCH_TIMER
|
||
+ imply CMD_NVEDIT_INFO
|
||
+ imply SYSRESET_PSCI if STM32MP1_TRUSTED
|
||
+ imply SYSRESET_SYSCON if !STM32MP1_TRUSTED
|
||
+ help
|
||
+ support of STMicroelectronics SOC STM32MP15x family
|
||
+ STM32MP157, STM32MP153 or STM32MP151
|
||
+ STMicroelectronics MPU with core ARMv7
|
||
+ dual core A7 for STM32MP157/3, monocore for STM32MP151
|
||
+ target all the STMicroelectronics board with SOC STM32MP1 family
|
||
+
|
||
+choice
|
||
+ prompt "STM32MP15x board select"
|
||
+ optional
|
||
+
|
||
+config TARGET_ST_STM32MP15x
|
||
+ bool "STMicroelectronics STM32MP15x boards"
|
||
+ select STM32MP15x
|
||
imply BOOTCOUNT_LIMIT
|
||
+ imply BOOTSTAGE
|
||
imply CMD_BOOTCOUNT
|
||
+ imply CMD_BOOTSTAGE
|
||
imply CMD_CLS if CMD_BMP
|
||
imply DISABLE_CONSOLE
|
||
imply PRE_CONSOLE_BUFFER
|
||
imply SILENT_CONSOLE
|
||
- imply SYSRESET_PSCI if STM32MP1_TRUSTED
|
||
- imply SYSRESET_SYSCON if !STM32MP1_TRUSTED
|
||
+ imply VERSION_VARIABLE
|
||
help
|
||
- target STMicroelectronics SOC STM32MP1 family
|
||
- STM32MP157, STM32MP153 or STM32MP151
|
||
- STMicroelectronics MPU with core ARMv7
|
||
- dual core A7 for STM32MP157/3, monocore for STM32MP151
|
||
+ target the STMicroelectronics board with SOC STM32MP15x
|
||
+ managed by board/st/stm32mp1:
|
||
+ Evalulation board (EV1) or Discovery board (DK1 and DK2).
|
||
+ The difference between board are managed with devicetree
|
||
+
|
||
+config TARGET_DH_STM32MP1_PDK2
|
||
+ bool "DH STM32MP1 PDK2"
|
||
+ select STM32MP15x
|
||
+ imply BOOTCOUNT_LIMIT
|
||
+ imply CMD_BOOTCOUNT
|
||
+ help
|
||
+ Target the DH PDK2 development kit with STM32MP15x SoM.
|
||
+
|
||
+endchoice
|
||
|
||
config STM32MP1_TRUSTED
|
||
bool "Support trusted boot with TF-A"
|
||
@@ -69,23 +97,8 @@ config STM32MP1_TRUSTED
|
||
BootRom => TF-A.stm32 (clock & DDR) => U-Boot.stm32
|
||
TF-A monitor provides proprietary SMC to manage secure devices
|
||
|
||
-config STM32MP1_OPTEE
|
||
- bool "Support trusted boot with TF-A and OP-TEE"
|
||
- depends on STM32MP1_TRUSTED
|
||
- default n
|
||
- help
|
||
- Say Y here to enable boot with TF-A and OP-TEE
|
||
- Trusted boot chain is :
|
||
- BootRom => TF-A.stm32 (clock & DDR) => OP-TEE => U-Boot.stm32
|
||
- OP-TEE monitor provides ST SMC to access to secure resources
|
||
-
|
||
config SYS_TEXT_BASE
|
||
- prompt "U-Boot base address"
|
||
default 0xC0100000
|
||
- help
|
||
- configure the U-Boot base address
|
||
- when DDR driver is used:
|
||
- DDR + 1MB (0xC0100000)
|
||
|
||
config NR_DRAM_BANKS
|
||
default 1
|
||
@@ -100,11 +113,28 @@ config SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION_MMC2
|
||
|
||
config STM32_ETZPC
|
||
bool "STM32 Extended TrustZone Protection"
|
||
- depends on TARGET_STM32MP1
|
||
+ depends on STM32MP15x
|
||
default y
|
||
help
|
||
Say y to enable STM32 Extended TrustZone Protection
|
||
|
||
+config CMD_STM32PROG
|
||
+ bool "command stm32prog for STM32CudeProgrammer"
|
||
+ select DFU
|
||
+ select DFU_RAM
|
||
+ select DFU_VIRT
|
||
+ select PARTITION_TYPE_GUID
|
||
+ imply CMD_GPT if MMC
|
||
+ imply CMD_MTD if MTD
|
||
+ imply DFU_MMC if MMC
|
||
+ imply DFU_MTD if MTD
|
||
+ help
|
||
+ activate a specific command stm32prog for STM32MP soc family
|
||
+ witch update the device with the tools STM32CubeProgrammer,
|
||
+ using UART with STM32 protocol or USB with DFU protocol
|
||
+ NB: access to not volatile memory (NOR/NAND/SD/eMMC) is based
|
||
+ on U-Boot DFU framework
|
||
+
|
||
config CMD_STM32KEY
|
||
bool "command stm32key to fuse public key hash"
|
||
default y
|
||
@@ -147,5 +177,6 @@ config DEBUG_UART_CLOCK
|
||
endif
|
||
|
||
source "board/st/stm32mp1/Kconfig"
|
||
+source "board/dhelectronics/dh_stm32mp1/Kconfig"
|
||
|
||
endif
|
||
diff --git a/arch/arm/mach-stm32mp/Makefile b/arch/arm/mach-stm32mp/Makefile
|
||
index eee39c2..1fdacf4 100644
|
||
--- a/arch/arm/mach-stm32mp/Makefile
|
||
+++ b/arch/arm/mach-stm32mp/Makefile
|
||
@@ -6,13 +6,15 @@
|
||
obj-y += cpu.o
|
||
obj-y += dram_init.o
|
||
obj-y += syscon.o
|
||
+obj-y += bsec.o
|
||
|
||
ifdef CONFIG_SPL_BUILD
|
||
obj-y += spl.o
|
||
else
|
||
-obj-y += bsec.o
|
||
+obj-$(CONFIG_CMD_STM32PROG) += cmd_stm32prog/
|
||
obj-$(CONFIG_CMD_STM32KEY) += cmd_stm32key.o
|
||
obj-$(CONFIG_ARMV7_PSCI) += psci.o
|
||
+obj-$(CONFIG_STM32MP1_TRUSTED) += boot_params.o
|
||
endif
|
||
|
||
obj-$(CONFIG_$(SPL_)DM_REGULATOR) += pwr_regulator.o
|
||
diff --git a/arch/arm/mach-stm32mp/boot_params.c b/arch/arm/mach-stm32mp/boot_params.c
|
||
new file mode 100644
|
||
index 0000000..e4351de
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/boot_params.c
|
||
@@ -0,0 +1,45 @@
|
||
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
||
+/*
|
||
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#include <common.h>
|
||
+#include <asm/sections.h>
|
||
+#include <asm/system.h>
|
||
+
|
||
+/*
|
||
+ * Force data-section, as .bss will not be valid
|
||
+ * when save_boot_params is invoked.
|
||
+ */
|
||
+static unsigned long nt_fw_dtb __section(".data");
|
||
+
|
||
+/*
|
||
+ * Save the FDT address provided by TF-A in r2 at boot time
|
||
+ * This function is called from start.S
|
||
+ */
|
||
+void save_boot_params(unsigned long r0, unsigned long r1, unsigned long r2,
|
||
+ unsigned long r3)
|
||
+{
|
||
+ nt_fw_dtb = r2;
|
||
+
|
||
+ save_boot_params_ret();
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Use the saved FDT address provided by TF-A at boot time (NT_FW_CONFIG =
|
||
+ * Non Trusted Firmware configuration file) when the pointer is valid
|
||
+ */
|
||
+void *board_fdt_blob_setup(void)
|
||
+{
|
||
+ debug("%s: nt_fw_dtb=%lx\n", __func__, nt_fw_dtb);
|
||
+
|
||
+ /* use external device tree only if address is valid */
|
||
+ if (nt_fw_dtb >= STM32_DDR_BASE) {
|
||
+ if (fdt_magic(nt_fw_dtb) == FDT_MAGIC)
|
||
+ return (void *)nt_fw_dtb;
|
||
+ debug("%s: DTB not found.\n", __func__);
|
||
+ }
|
||
+ debug("%s: fall back to builtin DTB, %p\n", __func__, &_end);
|
||
+
|
||
+ return (void *)&_end;
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/bsec.c b/arch/arm/mach-stm32mp/bsec.c
|
||
index a77c706..7e04ddd 100644
|
||
--- a/arch/arm/mach-stm32mp/bsec.c
|
||
+++ b/arch/arm/mach-stm32mp/bsec.c
|
||
@@ -7,13 +7,12 @@
|
||
#include <dm.h>
|
||
#include <misc.h>
|
||
#include <asm/io.h>
|
||
+#include <asm/arch/bsec.h>
|
||
#include <asm/arch/stm32mp1_smc.h>
|
||
#include <linux/arm-smccc.h>
|
||
#include <linux/iopoll.h>
|
||
|
||
#define BSEC_OTP_MAX_VALUE 95
|
||
-
|
||
-#ifndef CONFIG_STM32MP1_TRUSTED
|
||
#define BSEC_TIMEOUT_US 10000
|
||
|
||
/* BSEC REGISTER OFFSET (base relative) */
|
||
@@ -22,11 +21,13 @@
|
||
#define BSEC_OTP_WRDATA_OFF 0x008
|
||
#define BSEC_OTP_STATUS_OFF 0x00C
|
||
#define BSEC_OTP_LOCK_OFF 0x010
|
||
+#define BSEC_DENABLE_OFF 0x014
|
||
#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_WRLOCK_OFF 0x04C /* OTP write permananet lock */
|
||
+#define BSEC_SPLOCK_OFF 0x064 /* OTP write sticky lock */
|
||
+#define BSEC_SWLOCK_OFF 0x07C /* shadow write sticky lock */
|
||
+#define BSEC_SRLOCK_OFF 0x094 /* shadow read sticky lock */
|
||
#define BSEC_OTP_DATA_OFF 0x200
|
||
|
||
/* BSEC_CONFIGURATION Register MASK */
|
||
@@ -46,6 +47,9 @@
|
||
#define BSEC_MODE_PROGFAIL_MASK 0x10
|
||
#define BSEC_MODE_PWR_MASK 0x20
|
||
|
||
+/* DENABLE Register */
|
||
+#define BSEC_DENABLE_DBGSWENABLE BIT(10)
|
||
+
|
||
/*
|
||
* OTP Lock services definition
|
||
* Value must corresponding to the bit number in the register
|
||
@@ -53,12 +57,12 @@
|
||
#define BSEC_LOCK_PROGRAM 0x04
|
||
|
||
/**
|
||
- * bsec_check_error() - Check status of one otp
|
||
- * @base: base address of bsec IP
|
||
+ * 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: 0 if no error, -EAGAIN or -ENOTSUPP
|
||
+ * Return: true if locked else false
|
||
*/
|
||
-static u32 bsec_check_error(u32 base, u32 otp)
|
||
+static bool bsec_read_lock(u32 address, u32 otp)
|
||
{
|
||
u32 bit;
|
||
u32 bank;
|
||
@@ -66,21 +70,17 @@ static u32 bsec_check_error(u32 base, u32 otp)
|
||
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;
|
||
+ return !!(readl(address + bank) & bit);
|
||
}
|
||
|
||
+#ifndef CONFIG_STM32MP1_TRUSTED
|
||
/**
|
||
- * bsec_lock() - manage lock for each type SR/SP/SW
|
||
- * @address: address of bsec IP register
|
||
+ * bsec_check_error() - Check status of one otp
|
||
+ * @base: base address of bsec IP
|
||
* @otp: otp number (0 - BSEC_OTP_MAX_VALUE)
|
||
- * Return: true if locked else false
|
||
+ * Return: 0 if no error, -EAGAIN or -ENOTSUPP
|
||
*/
|
||
-static bool bsec_read_lock(u32 address, u32 otp)
|
||
+static u32 bsec_check_error(u32 base, u32 otp)
|
||
{
|
||
u32 bit;
|
||
u32 bank;
|
||
@@ -88,7 +88,12 @@ static bool bsec_read_lock(u32 address, u32 otp)
|
||
bit = 1 << (otp & OTP_LOCK_MASK);
|
||
bank = ((otp >> OTP_LOCK_BANK_SHIFT) & OTP_LOCK_MASK) * sizeof(u32);
|
||
|
||
- return !!(readl(address + bank) & bit);
|
||
+ if (readl(base + BSEC_DISTURBED_OFF + bank) & bit)
|
||
+ return -EAGAIN;
|
||
+ else if (readl(base + BSEC_ERROR_OFF + bank) & bit)
|
||
+ return -ENOTSUPP;
|
||
+
|
||
+ return 0;
|
||
}
|
||
|
||
/**
|
||
@@ -324,6 +329,16 @@ static int stm32mp_bsec_read_shadow(struct udevice *dev, u32 *val, u32 otp)
|
||
#endif
|
||
}
|
||
|
||
+static int stm32mp_bsec_read_lock(struct udevice *dev, u32 *val, u32 otp)
|
||
+{
|
||
+ struct stm32mp_bsec_platdata *plat = dev_get_platdata(dev);
|
||
+
|
||
+ /* return OTP permanent write lock status */
|
||
+ *val = bsec_read_lock(plat->base + BSEC_WRLOCK_OFF, otp);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
static int stm32mp_bsec_write_otp(struct udevice *dev, u32 val, u32 otp)
|
||
{
|
||
#ifdef CONFIG_STM32MP1_TRUSTED
|
||
@@ -350,22 +365,41 @@ static int stm32mp_bsec_write_shadow(struct udevice *dev, u32 val, u32 otp)
|
||
#endif
|
||
}
|
||
|
||
+static int stm32mp_bsec_write_lock(struct udevice *dev, u32 val, u32 otp)
|
||
+{
|
||
+#ifdef CONFIG_STM32MP1_TRUSTED
|
||
+ if (val == 1)
|
||
+ return stm32_smc_exec(STM32_SMC_BSEC,
|
||
+ STM32_SMC_WRLOCK_OTP,
|
||
+ otp, 0);
|
||
+ if (val == 0)
|
||
+ return 0; /* nothing to do */
|
||
+
|
||
+ return -EINVAL;
|
||
+#else
|
||
+ return -ENOTSUPP;
|
||
+#endif
|
||
+}
|
||
+
|
||
static int stm32mp_bsec_read(struct udevice *dev, int offset,
|
||
void *buf, int size)
|
||
{
|
||
int ret;
|
||
int i;
|
||
- bool shadow = true;
|
||
+ bool shadow = true, lock = false;
|
||
int nb_otp = size / sizeof(u32);
|
||
int otp;
|
||
unsigned int offs = offset;
|
||
|
||
- if (offs >= STM32_BSEC_OTP_OFFSET) {
|
||
+ if (offs >= STM32_BSEC_LOCK_OFFSET) {
|
||
+ offs -= STM32_BSEC_LOCK_OFFSET;
|
||
+ lock = true;
|
||
+ } else if (offs >= STM32_BSEC_OTP_OFFSET) {
|
||
offs -= STM32_BSEC_OTP_OFFSET;
|
||
shadow = false;
|
||
}
|
||
|
||
- if (offs < 0 || (offs % 4) || (size % 4))
|
||
+ if ((offs % 4) || (size % 4))
|
||
return -EINVAL;
|
||
|
||
otp = offs / sizeof(u32);
|
||
@@ -373,7 +407,9 @@ static int stm32mp_bsec_read(struct udevice *dev, int offset,
|
||
for (i = otp; i < (otp + nb_otp) && i <= BSEC_OTP_MAX_VALUE; i++) {
|
||
u32 *addr = &((u32 *)buf)[i - otp];
|
||
|
||
- if (shadow)
|
||
+ if (lock)
|
||
+ ret = stm32mp_bsec_read_lock(dev, addr, i);
|
||
+ else if (shadow)
|
||
ret = stm32mp_bsec_read_shadow(dev, addr, i);
|
||
else
|
||
ret = stm32mp_bsec_read_otp(dev, addr, i);
|
||
@@ -392,17 +428,20 @@ static int stm32mp_bsec_write(struct udevice *dev, int offset,
|
||
{
|
||
int ret = 0;
|
||
int i;
|
||
- bool shadow = true;
|
||
+ bool shadow = true, lock = false;
|
||
int nb_otp = size / sizeof(u32);
|
||
int otp;
|
||
unsigned int offs = offset;
|
||
|
||
- if (offs >= STM32_BSEC_OTP_OFFSET) {
|
||
+ if (offs >= STM32_BSEC_LOCK_OFFSET) {
|
||
+ offs -= STM32_BSEC_LOCK_OFFSET;
|
||
+ lock = true;
|
||
+ } else if (offs >= STM32_BSEC_OTP_OFFSET) {
|
||
offs -= STM32_BSEC_OTP_OFFSET;
|
||
shadow = false;
|
||
}
|
||
|
||
- if (offs < 0 || (offs % 4) || (size % 4))
|
||
+ if ((offs % 4) || (size % 4))
|
||
return -EINVAL;
|
||
|
||
otp = offs / sizeof(u32);
|
||
@@ -410,7 +449,9 @@ static int stm32mp_bsec_write(struct udevice *dev, int offset,
|
||
for (i = otp; i < otp + nb_otp && i <= BSEC_OTP_MAX_VALUE; i++) {
|
||
u32 *val = &((u32 *)buf)[i - otp];
|
||
|
||
- if (shadow)
|
||
+ if (lock)
|
||
+ ret = stm32mp_bsec_write_lock(dev, *val, i);
|
||
+ else if (shadow)
|
||
ret = stm32mp_bsec_write_shadow(dev, *val, i);
|
||
else
|
||
ret = stm32mp_bsec_write_otp(dev, *val, i);
|
||
@@ -437,7 +478,7 @@ static int stm32mp_bsec_ofdata_to_platdata(struct udevice *dev)
|
||
return 0;
|
||
}
|
||
|
||
-#ifndef CONFIG_STM32MP1_TRUSTED
|
||
+#if !defined(CONFIG_STM32MP1_TRUSTED) && !defined(CONFIG_SPL_BUILD)
|
||
static int stm32mp_bsec_probe(struct udevice *dev)
|
||
{
|
||
int otp;
|
||
@@ -464,7 +505,27 @@ U_BOOT_DRIVER(stm32mp_bsec) = {
|
||
.ofdata_to_platdata = stm32mp_bsec_ofdata_to_platdata,
|
||
.platdata_auto_alloc_size = sizeof(struct stm32mp_bsec_platdata),
|
||
.ops = &stm32mp_bsec_ops,
|
||
-#ifndef CONFIG_STM32MP1_TRUSTED
|
||
+#if !defined(CONFIG_STM32MP1_TRUSTED) && !defined(CONFIG_SPL_BUILD)
|
||
.probe = stm32mp_bsec_probe,
|
||
#endif
|
||
};
|
||
+
|
||
+bool bsec_dbgswenable(void)
|
||
+{
|
||
+ struct udevice *dev;
|
||
+ struct stm32mp_bsec_platdata *plat;
|
||
+ int ret;
|
||
+
|
||
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||
+ DM_GET_DRIVER(stm32mp_bsec), &dev);
|
||
+ if (ret || !dev) {
|
||
+ pr_debug("bsec driver not available\n");
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ plat = dev_get_platdata(dev);
|
||
+ if (readl(plat->base + BSEC_DENABLE_OFF) & BSEC_DENABLE_DBGSWENABLE)
|
||
+ return true;
|
||
+
|
||
+ return false;
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
|
||
new file mode 100644
|
||
index 0000000..548a378
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
|
||
@@ -0,0 +1,9 @@
|
||
+# SPDX-License-Identifier: GPL-2.0+
|
||
+#
|
||
+# Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+#
|
||
+
|
||
+obj-y += cmd_stm32prog.o
|
||
+obj-y += stm32prog.o
|
||
+obj-y += stm32prog_serial.o
|
||
+obj-y += stm32prog_usb.o
|
||
diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
|
||
new file mode 100644
|
||
index 0000000..db1f3a7
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
|
||
@@ -0,0 +1,191 @@
|
||
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#include <common.h>
|
||
+#include <command.h>
|
||
+#include <dfu.h>
|
||
+#include <image.h>
|
||
+#include <asm/arch/stm32prog.h>
|
||
+#include "stm32prog.h"
|
||
+
|
||
+struct stm32prog_data *stm32prog_data;
|
||
+
|
||
+static void enable_vidconsole(void)
|
||
+{
|
||
+#ifdef CONFIG_DM_VIDEO
|
||
+ char *stdname;
|
||
+ char buf[64];
|
||
+
|
||
+ stdname = env_get("stdout");
|
||
+ if (!stdname || !strstr(stdname, "vidconsole")) {
|
||
+ if (!stdname)
|
||
+ snprintf(buf, sizeof(buf), "serial,vidconsole");
|
||
+ else
|
||
+ snprintf(buf, sizeof(buf), "%s,vidconsole", stdname);
|
||
+ env_set("stdout", buf);
|
||
+ }
|
||
+
|
||
+ stdname = env_get("stderr");
|
||
+ if (!stdname || !strstr(stdname, "vidconsole")) {
|
||
+ if (!stdname)
|
||
+ snprintf(buf, sizeof(buf), "serial,vidconsole");
|
||
+ else
|
||
+ snprintf(buf, sizeof(buf), "%s,vidconsole", stdname);
|
||
+ env_set("stderr", buf);
|
||
+ }
|
||
+#endif
|
||
+}
|
||
+
|
||
+static int do_stm32prog(cmd_tbl_t *cmdtp, int flag, int argc,
|
||
+ char * const argv[])
|
||
+{
|
||
+ ulong addr, size;
|
||
+ int dev, ret;
|
||
+ enum stm32prog_link_t link = LINK_UNDEFINED;
|
||
+ bool reset = false;
|
||
+ struct image_header_s header;
|
||
+ struct stm32prog_data *data;
|
||
+ u32 uimage, dtb;
|
||
+
|
||
+ if (argc < 3 || argc > 5)
|
||
+ return CMD_RET_USAGE;
|
||
+
|
||
+ if (!strcmp(argv[1], "usb"))
|
||
+ link = LINK_USB;
|
||
+ else if (!strcmp(argv[1], "serial"))
|
||
+ link = LINK_SERIAL;
|
||
+
|
||
+ if (link == LINK_UNDEFINED) {
|
||
+ pr_err("not supported link=%s\n", argv[1]);
|
||
+ return CMD_RET_USAGE;
|
||
+ }
|
||
+
|
||
+ dev = (int)simple_strtoul(argv[2], NULL, 10);
|
||
+
|
||
+ addr = STM32_DDR_BASE;
|
||
+ size = 0;
|
||
+ if (argc > 3) {
|
||
+ addr = simple_strtoul(argv[3], NULL, 16);
|
||
+ if (!addr)
|
||
+ return CMD_RET_FAILURE;
|
||
+ }
|
||
+ if (argc > 4)
|
||
+ size = simple_strtoul(argv[4], NULL, 16);
|
||
+
|
||
+ /* check STM32IMAGE presence */
|
||
+ if (size == 0 &&
|
||
+ !stm32prog_header_check((struct raw_header_s *)addr, &header)) {
|
||
+ size = header.image_length + BL_HEADER_SIZE;
|
||
+
|
||
+ /* uImage detected in STM32IMAGE, execute the script */
|
||
+ if (IMAGE_FORMAT_LEGACY ==
|
||
+ genimg_get_format((void *)(addr + BL_HEADER_SIZE)))
|
||
+ return source(addr + BL_HEADER_SIZE, "script@1");
|
||
+ }
|
||
+
|
||
+ enable_vidconsole();
|
||
+
|
||
+ data = (struct stm32prog_data *)malloc(sizeof(*data));
|
||
+
|
||
+ if (!data) {
|
||
+ pr_err("Alloc failed.");
|
||
+ return CMD_RET_FAILURE;
|
||
+ }
|
||
+ stm32prog_data = data;
|
||
+
|
||
+ ret = stm32prog_init(data, addr, size);
|
||
+ if (ret)
|
||
+ printf("Invalid or missing layout file.");
|
||
+
|
||
+ /* prepare DFU for device read/write */
|
||
+ ret = stm32prog_dfu_init(data);
|
||
+ if (ret)
|
||
+ goto cleanup;
|
||
+
|
||
+ switch (link) {
|
||
+ case LINK_SERIAL:
|
||
+ ret = stm32prog_serial_init(data, dev);
|
||
+ if (ret)
|
||
+ goto cleanup;
|
||
+ reset = stm32prog_serial_loop(data);
|
||
+ break;
|
||
+ case LINK_USB:
|
||
+ reset = stm32prog_usb_loop(data, dev);
|
||
+ break;
|
||
+ default:
|
||
+ goto cleanup;
|
||
+ }
|
||
+
|
||
+ uimage = data->uimage;
|
||
+ dtb = data->dtb;
|
||
+
|
||
+ stm32prog_clean(data);
|
||
+ free(stm32prog_data);
|
||
+ stm32prog_data = NULL;
|
||
+
|
||
+ puts("Download done\n");
|
||
+
|
||
+ if (uimage) {
|
||
+ char boot_addr_start[20];
|
||
+ char dtb_addr[20];
|
||
+ char *bootm_argv[5] = {
|
||
+ "bootm", boot_addr_start, "-", dtb_addr, NULL
|
||
+ };
|
||
+ if (!dtb)
|
||
+ bootm_argv[3] = env_get("fdtcontroladdr");
|
||
+ else
|
||
+ snprintf(dtb_addr, sizeof(dtb_addr) - 1,
|
||
+ "0x%x", dtb);
|
||
+
|
||
+ snprintf(boot_addr_start, sizeof(boot_addr_start) - 1,
|
||
+ "0x%x", uimage);
|
||
+ printf("Booting kernel at %s - %s...\n\n\n",
|
||
+ boot_addr_start, bootm_argv[3]);
|
||
+ /* Try bootm for legacy and FIT format image */
|
||
+ if (genimg_get_format((void *)uimage) != IMAGE_FORMAT_INVALID)
|
||
+ do_bootm(cmdtp, 0, 4, bootm_argv);
|
||
+ else if CONFIG_IS_ENABLED(CMD_BOOTZ)
|
||
+ do_bootz(cmdtp, 0, 4, bootm_argv);
|
||
+ }
|
||
+
|
||
+ if (reset) {
|
||
+ puts("Reset...\n");
|
||
+ run_command("reset", 0);
|
||
+ }
|
||
+
|
||
+ return CMD_RET_SUCCESS;
|
||
+
|
||
+cleanup:
|
||
+ stm32prog_clean(data);
|
||
+ free(stm32prog_data);
|
||
+ stm32prog_data = NULL;
|
||
+
|
||
+ return CMD_RET_FAILURE;
|
||
+}
|
||
+
|
||
+U_BOOT_CMD(stm32prog, 5, 0, do_stm32prog,
|
||
+ "<link> <dev> [<addr>] [<size>]\n"
|
||
+ "start communication with tools STM32Cubeprogrammer on <link> with Flashlayout at <addr>",
|
||
+ "<link> = serial|usb\n"
|
||
+ "<dev> = device instance\n"
|
||
+ "<addr> = address of flashlayout\n"
|
||
+ "<size> = size of flashlayout\n"
|
||
+);
|
||
+
|
||
+bool stm32prog_get_tee_partitions(void)
|
||
+{
|
||
+ if (stm32prog_data)
|
||
+ return stm32prog_data->tee_detected;
|
||
+
|
||
+ return false;
|
||
+}
|
||
+
|
||
+bool stm32prog_get_fsbl_nor(void)
|
||
+{
|
||
+ if (stm32prog_data)
|
||
+ return stm32prog_data->fsbl_nor_detected;
|
||
+
|
||
+ return false;
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
|
||
new file mode 100644
|
||
index 0000000..f2f9ed9
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
|
||
@@ -0,0 +1,1743 @@
|
||
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#include <common.h>
|
||
+#include <console.h>
|
||
+#include <dfu.h>
|
||
+#include <malloc.h>
|
||
+#include <misc.h>
|
||
+#include <mmc.h>
|
||
+#include <part.h>
|
||
+#include <asm/arch/stm32mp1_smc.h>
|
||
+#include <dm/uclass.h>
|
||
+#include <jffs2/load_kernel.h>
|
||
+#include <linux/list.h>
|
||
+#include <linux/list_sort.h>
|
||
+#include <linux/mtd/mtd.h>
|
||
+#include <linux/sizes.h>
|
||
+
|
||
+#include "stm32prog.h"
|
||
+
|
||
+/* Primary GPT header size for 128 entries : 17kB = 34 LBA of 512B */
|
||
+#define GPT_HEADER_SZ 34
|
||
+
|
||
+#define OPT_SELECT BIT(0)
|
||
+#define OPT_EMPTY BIT(1)
|
||
+#define OPT_DELETE BIT(2)
|
||
+
|
||
+#define IS_SELECT(part) ((part)->option & OPT_SELECT)
|
||
+#define IS_EMPTY(part) ((part)->option & OPT_EMPTY)
|
||
+#define IS_DELETE(part) ((part)->option & OPT_DELETE)
|
||
+
|
||
+#define ALT_BUF_LEN SZ_1K
|
||
+
|
||
+#define ROOTFS_MMC0_UUID \
|
||
+ EFI_GUID(0xE91C4E10, 0x16E6, 0x4C0E, \
|
||
+ 0xBD, 0x0E, 0x77, 0xBE, 0xCF, 0x4A, 0x35, 0x82)
|
||
+
|
||
+#define ROOTFS_MMC1_UUID \
|
||
+ EFI_GUID(0x491F6117, 0x415D, 0x4F53, \
|
||
+ 0x88, 0xC9, 0x6E, 0x0D, 0xE5, 0x4D, 0xEA, 0xC6)
|
||
+
|
||
+#define ROOTFS_MMC2_UUID \
|
||
+ EFI_GUID(0xFD58F1C7, 0xBE0D, 0x4338, \
|
||
+ 0x88, 0xE9, 0xAD, 0x8F, 0x05, 0x0A, 0xEB, 0x18)
|
||
+
|
||
+/* RAW parttion (binary / bootloader) used Linux - reserved UUID */
|
||
+#define LINUX_RESERVED_UUID "8DA63339-0007-60C0-C436-083AC8230908"
|
||
+
|
||
+/*
|
||
+ * unique partition guid (uuid) for partition named "rootfs"
|
||
+ * on each MMC instance = SD Card or eMMC
|
||
+ * allow fixed kernel bootcmd: "rootf=PARTUID=e91c4e10-..."
|
||
+ */
|
||
+static const efi_guid_t uuid_mmc[3] = {
|
||
+ ROOTFS_MMC0_UUID,
|
||
+ ROOTFS_MMC1_UUID,
|
||
+ ROOTFS_MMC2_UUID
|
||
+};
|
||
+
|
||
+DECLARE_GLOBAL_DATA_PTR;
|
||
+
|
||
+/* order of column in flash layout file */
|
||
+enum stm32prog_col_t {
|
||
+ COL_OPTION,
|
||
+ COL_ID,
|
||
+ COL_NAME,
|
||
+ COL_TYPE,
|
||
+ COL_IP,
|
||
+ COL_OFFSET,
|
||
+ COL_NB_STM32
|
||
+};
|
||
+
|
||
+/* partition handling routines : CONFIG_CMD_MTDPARTS */
|
||
+int mtdparts_init(void);
|
||
+int find_dev_and_part(const char *id, struct mtd_device **dev,
|
||
+ u8 *part_num, struct part_info **part);
|
||
+
|
||
+char *stm32prog_get_error(struct stm32prog_data *data)
|
||
+{
|
||
+ static const char error_msg[] = "Unspecified";
|
||
+
|
||
+ if (strlen(data->error) == 0)
|
||
+ strcpy(data->error, error_msg);
|
||
+
|
||
+ return data->error;
|
||
+}
|
||
+
|
||
+u8 stm32prog_header_check(struct raw_header_s *raw_header,
|
||
+ struct image_header_s *header)
|
||
+{
|
||
+ unsigned int i;
|
||
+
|
||
+ header->present = 0;
|
||
+ header->image_checksum = 0x0;
|
||
+ header->image_length = 0x0;
|
||
+
|
||
+ if (!raw_header || !header) {
|
||
+ pr_debug("%s:no header data\n", __func__);
|
||
+ return -1;
|
||
+ }
|
||
+ if (raw_header->magic_number !=
|
||
+ (('S' << 0) | ('T' << 8) | ('M' << 16) | (0x32 << 24))) {
|
||
+ pr_debug("%s:invalid magic number : 0x%x\n",
|
||
+ __func__, raw_header->magic_number);
|
||
+ return -2;
|
||
+ }
|
||
+ /* only header v1.0 supported */
|
||
+ if (raw_header->header_version != 0x00010000) {
|
||
+ pr_debug("%s:invalid header version : 0x%x\n",
|
||
+ __func__, raw_header->header_version);
|
||
+ return -3;
|
||
+ }
|
||
+ if (raw_header->reserved1 != 0x0 || raw_header->reserved2) {
|
||
+ pr_debug("%s:invalid reserved field\n", __func__);
|
||
+ return -4;
|
||
+ }
|
||
+ for (i = 0; i < (sizeof(raw_header->padding) / 4); i++) {
|
||
+ if (raw_header->padding[i] != 0) {
|
||
+ pr_debug("%s:invalid padding field\n", __func__);
|
||
+ return -5;
|
||
+ }
|
||
+ }
|
||
+ header->present = 1;
|
||
+ header->image_checksum = le32_to_cpu(raw_header->image_checksum);
|
||
+ header->image_length = le32_to_cpu(raw_header->image_length);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static u32 stm32prog_header_checksum(u32 addr, struct image_header_s *header)
|
||
+{
|
||
+ u32 i, checksum;
|
||
+ u8 *payload;
|
||
+
|
||
+ /* compute checksum on payload */
|
||
+ payload = (u8 *)addr;
|
||
+ checksum = 0;
|
||
+ for (i = header->image_length; i > 0; i--)
|
||
+ checksum += *(payload++);
|
||
+
|
||
+ return checksum;
|
||
+}
|
||
+
|
||
+/* FLASHLAYOUT PARSING *****************************************/
|
||
+static int parse_option(struct stm32prog_data *data,
|
||
+ int i, char *p, struct stm32prog_part_t *part)
|
||
+{
|
||
+ int result = 0;
|
||
+ char *c = p;
|
||
+
|
||
+ part->option = 0;
|
||
+ if (!strcmp(p, "-"))
|
||
+ return 0;
|
||
+
|
||
+ while (*c) {
|
||
+ switch (*c) {
|
||
+ case 'P':
|
||
+ part->option |= OPT_SELECT;
|
||
+ break;
|
||
+ case 'E':
|
||
+ part->option |= OPT_EMPTY;
|
||
+ break;
|
||
+ case 'D':
|
||
+ part->option |= OPT_DELETE;
|
||
+ break;
|
||
+ default:
|
||
+ result = -EINVAL;
|
||
+ stm32prog_err("Layout line %d: invalid option '%c' in %s)",
|
||
+ i, *c, p);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ c++;
|
||
+ }
|
||
+ if (!(part->option & OPT_SELECT)) {
|
||
+ stm32prog_err("Layout line %d: missing 'P' in option %s", i, p);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+static int parse_id(struct stm32prog_data *data,
|
||
+ int i, char *p, struct stm32prog_part_t *part)
|
||
+{
|
||
+ int result = 0;
|
||
+ unsigned long value;
|
||
+
|
||
+ result = strict_strtoul(p, 0, &value);
|
||
+ part->id = value;
|
||
+ if (result || value > PHASE_LAST_USER) {
|
||
+ stm32prog_err("Layout line %d: invalid phase value = %s", i, p);
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+static int parse_name(struct stm32prog_data *data,
|
||
+ int i, char *p, struct stm32prog_part_t *part)
|
||
+{
|
||
+ int result = 0;
|
||
+
|
||
+ if (strlen(p) < sizeof(part->name)) {
|
||
+ strcpy(part->name, p);
|
||
+ } else {
|
||
+ stm32prog_err("Layout line %d: partition name too long [%d]: %s",
|
||
+ i, strlen(p), p);
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+static int parse_type(struct stm32prog_data *data,
|
||
+ int i, char *p, struct stm32prog_part_t *part)
|
||
+{
|
||
+ int result = 0;
|
||
+ int len = 0;
|
||
+
|
||
+ part->bin_nb = 0;
|
||
+ if (!strncmp(p, "Binary", 6)) {
|
||
+ part->part_type = PART_BINARY;
|
||
+
|
||
+ /* search for Binary(X) case */
|
||
+ len = strlen(p);
|
||
+ part->bin_nb = 1;
|
||
+ if (len > 6) {
|
||
+ if (len < 8 ||
|
||
+ (p[6] != '(') ||
|
||
+ (p[len - 1] != ')'))
|
||
+ result = -EINVAL;
|
||
+ else
|
||
+ part->bin_nb =
|
||
+ simple_strtoul(&p[7], NULL, 10);
|
||
+ }
|
||
+ } else if (!strcmp(p, "System")) {
|
||
+ part->part_type = PART_SYSTEM;
|
||
+ } else if (!strcmp(p, "FileSystem")) {
|
||
+ part->part_type = PART_FILESYSTEM;
|
||
+ } else if (!strcmp(p, "RawImage")) {
|
||
+ part->part_type = RAW_IMAGE;
|
||
+ } else {
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+ if (result)
|
||
+ stm32prog_err("Layout line %d: type parsing error : '%s'",
|
||
+ i, p);
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+static int parse_ip(struct stm32prog_data *data,
|
||
+ int i, char *p, struct stm32prog_part_t *part)
|
||
+{
|
||
+ int result = 0;
|
||
+ unsigned int len = 0;
|
||
+
|
||
+ part->dev_id = 0;
|
||
+ if (!strcmp(p, "none")) {
|
||
+ part->target = STM32PROG_NONE;
|
||
+ } else if (!strncmp(p, "mmc", 3)) {
|
||
+ part->target = STM32PROG_MMC;
|
||
+ len = 3;
|
||
+ } else if (!strncmp(p, "nor", 3)) {
|
||
+ part->target = STM32PROG_NOR;
|
||
+ len = 3;
|
||
+ } else if (!strncmp(p, "nand", 4)) {
|
||
+ part->target = STM32PROG_NAND;
|
||
+ len = 4;
|
||
+ } else if (!strncmp(p, "spi-nand", 8)) {
|
||
+ part->target = STM32PROG_SPI_NAND;
|
||
+ len = 8;
|
||
+ } else if (!strncmp(p, "ram", 3)) {
|
||
+ part->target = STM32PROG_RAM;
|
||
+ len = 0;
|
||
+ } else {
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+ if (len) {
|
||
+ /* only one digit allowed for device id */
|
||
+ if (strlen(p) != len + 1) {
|
||
+ result = -EINVAL;
|
||
+ } else {
|
||
+ part->dev_id = p[len] - '0';
|
||
+ if (part->dev_id > 9)
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ if (result)
|
||
+ stm32prog_err("Layout line %d: ip parsing error: '%s'", i, p);
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+static int parse_offset(struct stm32prog_data *data,
|
||
+ int i, char *p, struct stm32prog_part_t *part)
|
||
+{
|
||
+ int result = 0;
|
||
+ char *tail;
|
||
+
|
||
+ part->part_id = 0;
|
||
+ part->addr = 0;
|
||
+ part->size = 0;
|
||
+ /* eMMC boot parttion */
|
||
+ if (!strncmp(p, "boot", 4)) {
|
||
+ if (strlen(p) != 5) {
|
||
+ result = -EINVAL;
|
||
+ } else {
|
||
+ if (p[4] == '1')
|
||
+ part->part_id = -1;
|
||
+ else if (p[4] == '2')
|
||
+ part->part_id = -2;
|
||
+ else
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+ if (result)
|
||
+ stm32prog_err("Layout line %d: invalid part '%s'",
|
||
+ i, p);
|
||
+ } else {
|
||
+ part->addr = simple_strtoull(p, &tail, 0);
|
||
+ if (tail == p || *tail != '\0') {
|
||
+ stm32prog_err("Layout line %d: invalid offset '%s'",
|
||
+ i, p);
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+static
|
||
+int (* const parse[COL_NB_STM32])(struct stm32prog_data *data, int i, char *p,
|
||
+ struct stm32prog_part_t *part) = {
|
||
+ [COL_OPTION] = parse_option,
|
||
+ [COL_ID] = parse_id,
|
||
+ [COL_NAME] = parse_name,
|
||
+ [COL_TYPE] = parse_type,
|
||
+ [COL_IP] = parse_ip,
|
||
+ [COL_OFFSET] = parse_offset,
|
||
+};
|
||
+
|
||
+static int parse_flash_layout(struct stm32prog_data *data,
|
||
+ ulong addr,
|
||
+ ulong size)
|
||
+{
|
||
+ int column = 0, part_nb = 0, ret;
|
||
+ bool end_of_line, eof;
|
||
+ char *p, *start, *last, *col;
|
||
+ struct stm32prog_part_t *part;
|
||
+ int part_list_size;
|
||
+ int i;
|
||
+
|
||
+ data->part_nb = 0;
|
||
+
|
||
+ /* check if STM32image is detected */
|
||
+ if (!stm32prog_header_check((struct raw_header_s *)addr,
|
||
+ &data->header)) {
|
||
+ u32 checksum;
|
||
+
|
||
+ addr = addr + BL_HEADER_SIZE;
|
||
+ size = data->header.image_length;
|
||
+
|
||
+ checksum = stm32prog_header_checksum(addr, &data->header);
|
||
+ if (checksum != data->header.image_checksum) {
|
||
+ stm32prog_err("Layout: invalid checksum : 0x%x expected 0x%x",
|
||
+ checksum, data->header.image_checksum);
|
||
+ return -EIO;
|
||
+ }
|
||
+ }
|
||
+ if (!size)
|
||
+ return -EINVAL;
|
||
+
|
||
+ start = (char *)addr;
|
||
+ last = start + size;
|
||
+
|
||
+ *last = 0x0; /* force null terminated string */
|
||
+ pr_debug("flash layout =\n%s\n", start);
|
||
+
|
||
+ /* calculate expected number of partitions */
|
||
+ part_list_size = 1;
|
||
+ p = start;
|
||
+ while (*p && (p < last)) {
|
||
+ if (*p++ == '\n') {
|
||
+ part_list_size++;
|
||
+ if (p < last && *p == '#')
|
||
+ part_list_size--;
|
||
+ }
|
||
+ }
|
||
+ if (part_list_size > PHASE_LAST_USER) {
|
||
+ stm32prog_err("Layout: too many partition (%d)",
|
||
+ part_list_size);
|
||
+ return -1;
|
||
+ }
|
||
+ part = calloc(sizeof(struct stm32prog_part_t), part_list_size);
|
||
+ if (!part) {
|
||
+ stm32prog_err("Layout: alloc failed");
|
||
+ return -ENOMEM;
|
||
+ }
|
||
+ data->part_array = part;
|
||
+
|
||
+ /* main parsing loop */
|
||
+ i = 1;
|
||
+ eof = false;
|
||
+ p = start;
|
||
+ col = start; /* 1st column */
|
||
+ end_of_line = false;
|
||
+ while (!eof) {
|
||
+ switch (*p) {
|
||
+ /* CR is ignored and replaced by NULL character */
|
||
+ case '\r':
|
||
+ *p = '\0';
|
||
+ p++;
|
||
+ continue;
|
||
+ case '\0':
|
||
+ end_of_line = true;
|
||
+ eof = true;
|
||
+ break;
|
||
+ case '\n':
|
||
+ end_of_line = true;
|
||
+ break;
|
||
+ case '\t':
|
||
+ break;
|
||
+ case '#':
|
||
+ /* comment line is skipped */
|
||
+ if (column == 0 && p == col) {
|
||
+ while ((p < last) && *p)
|
||
+ if (*p++ == '\n')
|
||
+ break;
|
||
+ col = p;
|
||
+ i++;
|
||
+ if (p >= last || !*p) {
|
||
+ eof = true;
|
||
+ end_of_line = true;
|
||
+ }
|
||
+ continue;
|
||
+ }
|
||
+ /* fall through */
|
||
+ /* by default continue with the next character */
|
||
+ default:
|
||
+ p++;
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ /* replace by \0: allow string parsing for each column */
|
||
+ *p = '\0';
|
||
+ p++;
|
||
+ if (p >= last) {
|
||
+ eof = true;
|
||
+ end_of_line = true;
|
||
+ }
|
||
+
|
||
+ /* skip empty line and multiple TAB in tsv file */
|
||
+ if (strlen(col) == 0) {
|
||
+ col = p;
|
||
+ /* skip empty line */
|
||
+ if (column == 0 && end_of_line) {
|
||
+ end_of_line = false;
|
||
+ i++;
|
||
+ }
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ if (column < COL_NB_STM32) {
|
||
+ ret = parse[column](data, i, col, part);
|
||
+ if (ret)
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ /* save the beginning of the next column */
|
||
+ column++;
|
||
+ col = p;
|
||
+
|
||
+ if (!end_of_line)
|
||
+ continue;
|
||
+
|
||
+ /* end of the line detected */
|
||
+ end_of_line = false;
|
||
+
|
||
+ if (column < COL_NB_STM32) {
|
||
+ stm32prog_err("Layout line %d: no enought column", i);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ column = 0;
|
||
+ part_nb++;
|
||
+ part++;
|
||
+ i++;
|
||
+ if (part_nb >= part_list_size) {
|
||
+ part = NULL;
|
||
+ if (!eof) {
|
||
+ stm32prog_err("Layout: no enought memory for %d part",
|
||
+ part_nb);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ data->part_nb = part_nb;
|
||
+ if (data->part_nb == 0) {
|
||
+ stm32prog_err("Layout: no partition found");
|
||
+ return -ENODEV;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int __init part_cmp(void *priv, struct list_head *a, struct list_head *b)
|
||
+{
|
||
+ struct stm32prog_part_t *parta, *partb;
|
||
+
|
||
+ parta = container_of(a, struct stm32prog_part_t, list);
|
||
+ partb = container_of(b, struct stm32prog_part_t, list);
|
||
+
|
||
+ if (parta->part_id != partb->part_id)
|
||
+ return parta->part_id - partb->part_id;
|
||
+ else
|
||
+ return parta->addr > partb->addr ? 1 : -1;
|
||
+}
|
||
+
|
||
+static void get_mtd_by_target(char *string, enum stm32prog_target target,
|
||
+ int dev_id)
|
||
+{
|
||
+ const char *dev_str;
|
||
+
|
||
+ switch (target) {
|
||
+ case STM32PROG_NOR:
|
||
+ dev_str = "nor";
|
||
+ break;
|
||
+ case STM32PROG_NAND:
|
||
+ dev_str = "nand";
|
||
+ break;
|
||
+ case STM32PROG_SPI_NAND:
|
||
+ dev_str = "spi-nand";
|
||
+ break;
|
||
+ default:
|
||
+ dev_str = "invalid";
|
||
+ break;
|
||
+ }
|
||
+ sprintf(string, "%s%d", dev_str, dev_id);
|
||
+}
|
||
+
|
||
+static int init_device(struct stm32prog_data *data,
|
||
+ struct stm32prog_dev_t *dev)
|
||
+{
|
||
+ struct mmc *mmc = NULL;
|
||
+ struct blk_desc *block_dev = NULL;
|
||
+#ifdef CONFIG_MTD
|
||
+ struct mtd_info *mtd = NULL;
|
||
+ char mtd_id[16];
|
||
+#endif
|
||
+ int part_id;
|
||
+ int ret;
|
||
+ u64 first_addr = 0, last_addr = 0;
|
||
+ struct stm32prog_part_t *part, *next_part;
|
||
+ u64 part_addr, part_size;
|
||
+ bool part_found;
|
||
+ const char *part_name;
|
||
+
|
||
+ switch (dev->target) {
|
||
+#ifdef CONFIG_MMC
|
||
+ case STM32PROG_MMC:
|
||
+ mmc = find_mmc_device(dev->dev_id);
|
||
+ if (mmc_init(mmc)) {
|
||
+ stm32prog_err("mmc device %d not found", dev->dev_id);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ block_dev = mmc_get_blk_desc(mmc);
|
||
+ if (!block_dev) {
|
||
+ stm32prog_err("mmc device %d not probed", dev->dev_id);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ dev->erase_size = mmc->erase_grp_size * block_dev->blksz;
|
||
+ dev->mmc = mmc;
|
||
+
|
||
+ /* reserve a full erase group for each GTP headers */
|
||
+ if (mmc->erase_grp_size > GPT_HEADER_SZ) {
|
||
+ first_addr = dev->erase_size;
|
||
+ last_addr = (u64)(block_dev->lba -
|
||
+ mmc->erase_grp_size) *
|
||
+ block_dev->blksz;
|
||
+ } else {
|
||
+ first_addr = (u64)GPT_HEADER_SZ * block_dev->blksz;
|
||
+ last_addr = (u64)(block_dev->lba - GPT_HEADER_SZ - 1) *
|
||
+ block_dev->blksz;
|
||
+ }
|
||
+ pr_debug("MMC %d: lba=%ld blksz=%ld\n", dev->dev_id,
|
||
+ block_dev->lba, block_dev->blksz);
|
||
+ pr_debug(" available address = 0x%llx..0x%llx\n",
|
||
+ first_addr, last_addr);
|
||
+ pr_debug(" full_update = %d\n", dev->full_update);
|
||
+ break;
|
||
+#endif
|
||
+#ifdef CONFIG_MTD
|
||
+ case STM32PROG_NOR:
|
||
+ case STM32PROG_NAND:
|
||
+ case STM32PROG_SPI_NAND:
|
||
+ get_mtd_by_target(mtd_id, dev->target, dev->dev_id);
|
||
+ pr_debug("%s\n", mtd_id);
|
||
+
|
||
+ mtdparts_init();
|
||
+ mtd = get_mtd_device_nm(mtd_id);
|
||
+ if (IS_ERR(mtd)) {
|
||
+ stm32prog_err("MTD device %s not found", mtd_id);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ first_addr = 0;
|
||
+ last_addr = mtd->size;
|
||
+ dev->erase_size = mtd->erasesize;
|
||
+ pr_debug("MTD device %s: size=%lld erasesize=%d\n",
|
||
+ mtd_id, mtd->size, mtd->erasesize);
|
||
+ pr_debug(" available address = 0x%llx..0x%llx\n",
|
||
+ first_addr, last_addr);
|
||
+ dev->mtd = mtd;
|
||
+ break;
|
||
+#endif
|
||
+ case STM32PROG_RAM:
|
||
+ first_addr = gd->bd->bi_dram[0].start;
|
||
+ last_addr = first_addr + gd->bd->bi_dram[0].size;
|
||
+ dev->erase_size = 1;
|
||
+ break;
|
||
+ default:
|
||
+ stm32prog_err("unknown device type = %d", dev->target);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ pr_debug(" erase size = 0x%x\n", dev->erase_size);
|
||
+ pr_debug(" full_update = %d\n", dev->full_update);
|
||
+
|
||
+ /* order partition list in offset order */
|
||
+ list_sort(NULL, &dev->part_list, &part_cmp);
|
||
+ part_id = 1;
|
||
+ pr_debug("id : Opt Phase Name target.n dev.n addr size part_off part_size\n");
|
||
+ list_for_each_entry(part, &dev->part_list, list) {
|
||
+ if (part->bin_nb > 1) {
|
||
+ if ((dev->target != STM32PROG_NAND &&
|
||
+ dev->target != STM32PROG_SPI_NAND) ||
|
||
+ part->id >= PHASE_FIRST_USER ||
|
||
+ strncmp(part->name, "fsbl", 4)) {
|
||
+ stm32prog_err("%s (0x%x): multiple binary %d not supported",
|
||
+ part->name, part->id,
|
||
+ part->bin_nb);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ if (part->part_type == RAW_IMAGE) {
|
||
+ part->part_id = 0x0;
|
||
+ part->addr = 0x0;
|
||
+ if (block_dev)
|
||
+ part->size = block_dev->lba * block_dev->blksz;
|
||
+ else
|
||
+ part->size = last_addr;
|
||
+ pr_debug("-- : %1d %02x %14s %02d.%d %02d.%02d %08llx %08llx\n",
|
||
+ part->option, part->id, part->name,
|
||
+ part->part_type, part->bin_nb, part->target,
|
||
+ part->dev_id, part->addr, part->size);
|
||
+ continue;
|
||
+ }
|
||
+ if (part->part_id < 0) { /* boot hw partition for eMMC */
|
||
+ if (mmc) {
|
||
+ part->size = mmc->capacity_boot;
|
||
+ } else {
|
||
+ stm32prog_err("%s (0x%x): hw partition not expected : %d",
|
||
+ part->name, part->id,
|
||
+ part->part_id);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ } else {
|
||
+ part->part_id = part_id++;
|
||
+
|
||
+ /* last partition : size to the end of the device */
|
||
+ if (part->list.next != &dev->part_list) {
|
||
+ next_part =
|
||
+ container_of(part->list.next,
|
||
+ struct stm32prog_part_t,
|
||
+ list);
|
||
+ if (part->addr < next_part->addr) {
|
||
+ part->size = next_part->addr -
|
||
+ part->addr;
|
||
+ } else {
|
||
+ stm32prog_err("%s (0x%x): same address : 0x%llx == %s (0x%x): 0x%llx",
|
||
+ part->name, part->id,
|
||
+ part->addr,
|
||
+ next_part->name,
|
||
+ next_part->id,
|
||
+ next_part->addr);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ } else {
|
||
+ if (part->addr <= last_addr) {
|
||
+ part->size = last_addr - part->addr;
|
||
+ } else {
|
||
+ stm32prog_err("%s (0x%x): invalid address 0x%llx (max=0x%llx)",
|
||
+ part->name, part->id,
|
||
+ part->addr, last_addr);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ if (part->addr < first_addr) {
|
||
+ stm32prog_err("%s (0x%x): invalid address 0x%llx (min=0x%llx)",
|
||
+ part->name, part->id,
|
||
+ part->addr, first_addr);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ if ((part->addr & ((u64)part->dev->erase_size - 1)) != 0) {
|
||
+ stm32prog_err("%s (0x%x): not aligned address : 0x%llx on erase size 0x%x",
|
||
+ part->name, part->id, part->addr,
|
||
+ part->dev->erase_size);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ pr_debug("%02d : %1d %02x %14s %02d.%d %02d.%02d %08llx %08llx",
|
||
+ part->part_id, part->option, part->id, part->name,
|
||
+ part->part_type, part->bin_nb, part->target,
|
||
+ part->dev_id, part->addr, part->size);
|
||
+
|
||
+ part_addr = 0;
|
||
+ part_size = 0;
|
||
+ part_found = false;
|
||
+
|
||
+ /* check coherency with existing partition */
|
||
+ if (block_dev) {
|
||
+ /*
|
||
+ * block devices with GPT: check user partition size
|
||
+ * only for partial update, the GPT partions are be
|
||
+ * created for full update
|
||
+ */
|
||
+ if (dev->full_update || part->part_id < 0) {
|
||
+ pr_debug("\n");
|
||
+ continue;
|
||
+ }
|
||
+ disk_partition_t partinfo;
|
||
+
|
||
+ ret = part_get_info(block_dev, part->part_id,
|
||
+ &partinfo);
|
||
+
|
||
+ if (ret) {
|
||
+ stm32prog_err("%s (0x%x):Couldn't find part %d on device mmc %d",
|
||
+ part->name, part->id,
|
||
+ part_id, part->dev_id);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ part_addr = (u64)partinfo.start * partinfo.blksz;
|
||
+ part_size = (u64)partinfo.size * partinfo.blksz;
|
||
+ part_name = (char *)partinfo.name;
|
||
+ part_found = true;
|
||
+ }
|
||
+
|
||
+#ifdef CONFIG_MTD
|
||
+ if (mtd) {
|
||
+ char mtd_part_id[32];
|
||
+ struct part_info *mtd_part;
|
||
+ struct mtd_device *mtd_dev;
|
||
+ u8 part_num;
|
||
+
|
||
+ sprintf(mtd_part_id, "%s,%d", mtd_id,
|
||
+ part->part_id - 1);
|
||
+ ret = find_dev_and_part(mtd_part_id, &mtd_dev,
|
||
+ &part_num, &mtd_part);
|
||
+ if (ret != 0) {
|
||
+ stm32prog_err("%s (0x%x): Invalid MTD partition %s",
|
||
+ part->name, part->id,
|
||
+ mtd_part_id);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ part_addr = mtd_part->offset;
|
||
+ part_size = mtd_part->size;
|
||
+ part_name = mtd_part->name;
|
||
+ part_found = true;
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ /* no partition for this device */
|
||
+ if (!part_found) {
|
||
+ pr_debug("\n");
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ pr_debug(" %08llx %08llx\n", part_addr, part_size);
|
||
+
|
||
+ if (part->addr != part_addr) {
|
||
+ stm32prog_err("%s (0x%x): Bad address for partition %d (%s) = 0x%llx <> 0x%llx expected",
|
||
+ part->name, part->id, part->part_id,
|
||
+ part_name, part->addr, part_addr);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ if (part->size != part_size) {
|
||
+ stm32prog_err("%s (0x%x): Bad size for partition %d (%s) at 0x%llx = 0x%llx <> 0x%llx expected",
|
||
+ part->name, part->id, part->part_id,
|
||
+ part_name, part->addr, part->size,
|
||
+ part_size);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int treat_partition_list(struct stm32prog_data *data)
|
||
+{
|
||
+ int i, j;
|
||
+ struct stm32prog_part_t *part;
|
||
+
|
||
+ for (j = 0; j < STM32PROG_MAX_DEV; j++) {
|
||
+ data->dev[j].target = STM32PROG_NONE;
|
||
+ INIT_LIST_HEAD(&data->dev[j].part_list);
|
||
+ }
|
||
+
|
||
+ data->tee_detected = false;
|
||
+ data->fsbl_nor_detected = false;
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ part = &data->part_array[i];
|
||
+ part->alt_id = -1;
|
||
+
|
||
+ /* skip partition with IP="none" */
|
||
+ if (part->target == STM32PROG_NONE) {
|
||
+ if (IS_SELECT(part)) {
|
||
+ stm32prog_err("Layout: selected none phase = 0x%x",
|
||
+ part->id);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ if (part->id == PHASE_FLASHLAYOUT ||
|
||
+ part->id > PHASE_LAST_USER) {
|
||
+ stm32prog_err("Layout: invalid phase = 0x%x",
|
||
+ part->id);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ for (j = i + 1; j < data->part_nb; j++) {
|
||
+ if (part->id == data->part_array[j].id) {
|
||
+ stm32prog_err("Layout: duplicated phase 0x%x at line %d and %d",
|
||
+ part->id, i, j);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ for (j = 0; j < STM32PROG_MAX_DEV; j++) {
|
||
+ if (data->dev[j].target == STM32PROG_NONE) {
|
||
+ /* new device found */
|
||
+ data->dev[j].target = part->target;
|
||
+ data->dev[j].dev_id = part->dev_id;
|
||
+ data->dev[j].full_update = true;
|
||
+ data->dev_nb++;
|
||
+ break;
|
||
+ } else if ((part->target == data->dev[j].target) &&
|
||
+ (part->dev_id == data->dev[j].dev_id)) {
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if (j == STM32PROG_MAX_DEV) {
|
||
+ stm32prog_err("Layout: too many device");
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ switch (part->target) {
|
||
+ case STM32PROG_NOR:
|
||
+ if (!data->fsbl_nor_detected &&
|
||
+ !strncmp(part->name, "fsbl", 4))
|
||
+ data->fsbl_nor_detected = true;
|
||
+ /* fallthrough */
|
||
+ case STM32PROG_NAND:
|
||
+ case STM32PROG_SPI_NAND:
|
||
+ if (!data->tee_detected &&
|
||
+ !strncmp(part->name, "tee", 3))
|
||
+ data->tee_detected = true;
|
||
+ break;
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+ part->dev = &data->dev[j];
|
||
+ if (!IS_SELECT(part))
|
||
+ part->dev->full_update = false;
|
||
+ list_add_tail(&part->list, &data->dev[j].part_list);
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int create_partitions(struct stm32prog_data *data)
|
||
+{
|
||
+#ifdef CONFIG_MMC
|
||
+ int offset = 0;
|
||
+ const int buflen = SZ_8K;
|
||
+ char *buf;
|
||
+ char uuid[UUID_STR_LEN + 1];
|
||
+ unsigned char *uuid_bin;
|
||
+ unsigned int mmc_id;
|
||
+ int i;
|
||
+ bool rootfs_found;
|
||
+ struct stm32prog_part_t *part;
|
||
+
|
||
+ buf = malloc(buflen);
|
||
+ if (!buf)
|
||
+ return -ENOMEM;
|
||
+
|
||
+ puts("partitions : ");
|
||
+ /* initialize the selected device */
|
||
+ for (i = 0; i < data->dev_nb; i++) {
|
||
+ /* create gpt partition support only for full update on MMC */
|
||
+ if (data->dev[i].target != STM32PROG_MMC ||
|
||
+ !data->dev[i].full_update)
|
||
+ continue;
|
||
+
|
||
+ offset = 0;
|
||
+ rootfs_found = false;
|
||
+ memset(buf, 0, buflen);
|
||
+
|
||
+ list_for_each_entry(part, &data->dev[i].part_list, list) {
|
||
+ /* skip eMMC boot partitions */
|
||
+ if (part->part_id < 0)
|
||
+ continue;
|
||
+ /* skip Raw Image */
|
||
+ if (part->part_type == RAW_IMAGE)
|
||
+ continue;
|
||
+
|
||
+ if (offset + 100 > buflen) {
|
||
+ pr_debug("\n%s: buffer too small, %s skippped",
|
||
+ __func__, part->name);
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ if (!offset)
|
||
+ offset += sprintf(buf, "gpt write mmc %d \"",
|
||
+ data->dev[i].dev_id);
|
||
+
|
||
+ offset += snprintf(buf + offset, buflen - offset,
|
||
+ "name=%s,start=0x%llx,size=0x%llx",
|
||
+ part->name,
|
||
+ part->addr,
|
||
+ part->size);
|
||
+
|
||
+ if (part->part_type == PART_BINARY)
|
||
+ offset += snprintf(buf + offset,
|
||
+ buflen - offset,
|
||
+ ",type="
|
||
+ LINUX_RESERVED_UUID);
|
||
+ else
|
||
+ offset += snprintf(buf + offset,
|
||
+ buflen - offset,
|
||
+ ",type=linux");
|
||
+
|
||
+ if (part->part_type == PART_SYSTEM)
|
||
+ offset += snprintf(buf + offset,
|
||
+ buflen - offset,
|
||
+ ",bootable");
|
||
+
|
||
+ if (!rootfs_found && !strcmp(part->name, "rootfs")) {
|
||
+ mmc_id = part->dev_id;
|
||
+ rootfs_found = true;
|
||
+ if (mmc_id < ARRAY_SIZE(uuid_mmc)) {
|
||
+ uuid_bin =
|
||
+ (unsigned char *)uuid_mmc[mmc_id].b;
|
||
+ uuid_bin_to_str(uuid_bin, uuid,
|
||
+ UUID_STR_FORMAT_GUID);
|
||
+ offset += snprintf(buf + offset,
|
||
+ buflen - offset,
|
||
+ ",uuid=%s", uuid);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ offset += snprintf(buf + offset, buflen - offset, ";");
|
||
+ }
|
||
+
|
||
+ if (offset) {
|
||
+ offset += snprintf(buf + offset, buflen - offset, "\"");
|
||
+ pr_debug("\ncmd: %s\n", buf);
|
||
+ if (run_command(buf, 0)) {
|
||
+ stm32prog_err("GPT partitionning fail: %s",
|
||
+ buf);
|
||
+ free(buf);
|
||
+
|
||
+ return -1;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (data->dev[i].mmc)
|
||
+ part_init(mmc_get_blk_desc(data->dev[i].mmc));
|
||
+
|
||
+#ifdef DEBUG
|
||
+ sprintf(buf, "gpt verify mmc %d", data->dev[i].dev_id);
|
||
+ pr_debug("\ncmd: %s", buf);
|
||
+ if (run_command(buf, 0))
|
||
+ printf("fail !\n");
|
||
+ else
|
||
+ printf("OK\n");
|
||
+
|
||
+ sprintf(buf, "part list mmc %d", data->dev[i].dev_id);
|
||
+ run_command(buf, 0);
|
||
+#endif
|
||
+ }
|
||
+ puts("done\n");
|
||
+
|
||
+#ifdef DEBUG
|
||
+ run_command("mtd list", 0);
|
||
+#endif
|
||
+ free(buf);
|
||
+#endif
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int stm32prog_alt_add(struct stm32prog_data *data,
|
||
+ struct dfu_entity *dfu,
|
||
+ struct stm32prog_part_t *part)
|
||
+{
|
||
+ int ret = 0;
|
||
+ int offset = 0;
|
||
+ char devstr[10];
|
||
+ char dfustr[10];
|
||
+ char buf[ALT_BUF_LEN];
|
||
+ u32 size;
|
||
+ char multiplier, type;
|
||
+
|
||
+ /* max 3 digit for sector size */
|
||
+ if (part->size > SZ_1M) {
|
||
+ size = (u32)(part->size / SZ_1M);
|
||
+ multiplier = 'M';
|
||
+ } else if (part->size > SZ_1K) {
|
||
+ size = (u32)(part->size / SZ_1K);
|
||
+ multiplier = 'K';
|
||
+ } else {
|
||
+ size = (u32)part->size;
|
||
+ multiplier = 'B';
|
||
+ }
|
||
+ if (IS_SELECT(part) && !IS_EMPTY(part))
|
||
+ type = 'e'; /*Readable and Writeable*/
|
||
+ else
|
||
+ type = 'a';/*Readable*/
|
||
+
|
||
+ memset(buf, 0, sizeof(buf));
|
||
+ offset = snprintf(buf, ALT_BUF_LEN - offset,
|
||
+ "@%s/0x%02x/1*%d%c%c ",
|
||
+ part->name, part->id,
|
||
+ size, multiplier, type);
|
||
+
|
||
+ if (part->target == STM32PROG_RAM) {
|
||
+ offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
|
||
+ "ram 0x%llx 0x%llx",
|
||
+ part->addr, part->size);
|
||
+ } else if (part->part_type == RAW_IMAGE) {
|
||
+ u64 dfu_size;
|
||
+
|
||
+ if (part->dev->target == STM32PROG_MMC)
|
||
+ dfu_size = part->size / part->dev->mmc->read_bl_len;
|
||
+ else
|
||
+ dfu_size = part->size;
|
||
+ offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
|
||
+ "raw 0x0 0x%llx", dfu_size);
|
||
+ } else if (part->part_id < 0) {
|
||
+ u64 nb_blk = part->size / part->dev->mmc->read_bl_len;
|
||
+
|
||
+ offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
|
||
+ "raw 0x%llx 0x%llx",
|
||
+ part->addr, nb_blk);
|
||
+ offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
|
||
+ " mmcpart %d;", -(part->part_id));
|
||
+ } else {
|
||
+ if (part->part_type == PART_SYSTEM &&
|
||
+ (part->target == STM32PROG_NAND ||
|
||
+ part->target == STM32PROG_NOR ||
|
||
+ part->target == STM32PROG_SPI_NAND))
|
||
+ offset += snprintf(buf + offset,
|
||
+ ALT_BUF_LEN - offset,
|
||
+ "partubi");
|
||
+ else
|
||
+ offset += snprintf(buf + offset,
|
||
+ ALT_BUF_LEN - offset,
|
||
+ "part");
|
||
+ /* dev_id requested by DFU MMC */
|
||
+ if (part->target == STM32PROG_MMC)
|
||
+ offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
|
||
+ " %d", part->dev_id);
|
||
+ offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
|
||
+ " %d;", part->part_id);
|
||
+ }
|
||
+ switch (part->target) {
|
||
+#ifdef CONFIG_MMC
|
||
+ case STM32PROG_MMC:
|
||
+ sprintf(dfustr, "mmc");
|
||
+ sprintf(devstr, "%d", part->dev_id);
|
||
+ break;
|
||
+#endif
|
||
+#ifdef CONFIG_MTD
|
||
+ case STM32PROG_NAND:
|
||
+ case STM32PROG_NOR:
|
||
+ case STM32PROG_SPI_NAND:
|
||
+ sprintf(dfustr, "mtd");
|
||
+ get_mtd_by_target(devstr, part->target, part->dev_id);
|
||
+ break;
|
||
+#endif
|
||
+ case STM32PROG_RAM:
|
||
+ sprintf(dfustr, "ram");
|
||
+ sprintf(devstr, "0");
|
||
+ break;
|
||
+ default:
|
||
+ stm32prog_err("invalid target: %d", part->target);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ pr_debug("dfu_alt_add(%s,%s,%s)\n", dfustr, devstr, buf);
|
||
+ ret = dfu_alt_add(dfu, dfustr, devstr, buf);
|
||
+ pr_debug("dfu_alt_add(%s,%s,%s) result %d\n",
|
||
+ dfustr, devstr, buf, ret);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static int stm32prog_alt_add_virt(struct dfu_entity *dfu,
|
||
+ char *name, int phase, int size)
|
||
+{
|
||
+ int ret = 0;
|
||
+ char devstr[4];
|
||
+ char buf[ALT_BUF_LEN];
|
||
+
|
||
+ sprintf(devstr, "%d", phase);
|
||
+ sprintf(buf, "@%s/0x%02x/1*%dBe", name, phase, size);
|
||
+ ret = dfu_alt_add(dfu, "virt", devstr, buf);
|
||
+ pr_debug("dfu_alt_add(virt,%s,%s) result %d\n", devstr, buf, ret);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static int dfu_init_entities(struct stm32prog_data *data)
|
||
+{
|
||
+ int ret = 0;
|
||
+ int phase, i, alt_id;
|
||
+ struct stm32prog_part_t *part;
|
||
+ struct dfu_entity *dfu;
|
||
+ int alt_nb;
|
||
+
|
||
+ alt_nb = 3; /* number of virtual = CMD, OTP, PMIC*/
|
||
+ if (data->part_nb == 0)
|
||
+ alt_nb++; /* +1 for FlashLayout */
|
||
+ else
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ if (data->part_array[i].target != STM32PROG_NONE)
|
||
+ alt_nb++;
|
||
+ }
|
||
+
|
||
+ if (dfu_alt_init(alt_nb, &dfu))
|
||
+ return -ENODEV;
|
||
+
|
||
+ puts("DFU alt info setting: ");
|
||
+ if (data->part_nb) {
|
||
+ alt_id = 0;
|
||
+ for (phase = 1;
|
||
+ (phase <= PHASE_LAST_USER) &&
|
||
+ (alt_id < alt_nb) && !ret;
|
||
+ phase++) {
|
||
+ /* ordering alt setting by phase id */
|
||
+ part = NULL;
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ if (phase == data->part_array[i].id) {
|
||
+ part = &data->part_array[i];
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if (!part)
|
||
+ continue;
|
||
+ if (part->target == STM32PROG_NONE)
|
||
+ continue;
|
||
+ part->alt_id = alt_id;
|
||
+ alt_id++;
|
||
+
|
||
+ ret = stm32prog_alt_add(data, dfu, part);
|
||
+ }
|
||
+ } else {
|
||
+ char buf[ALT_BUF_LEN];
|
||
+
|
||
+ sprintf(buf, "@FlashLayout/0x%02x/1*256Ke ram %x 40000",
|
||
+ PHASE_FLASHLAYOUT, STM32_DDR_BASE);
|
||
+ ret = dfu_alt_add(dfu, "ram", NULL, buf);
|
||
+ pr_debug("dfu_alt_add(ram, NULL,%s) result %d\n", buf, ret);
|
||
+ }
|
||
+
|
||
+ if (!ret)
|
||
+ ret = stm32prog_alt_add_virt(dfu, "virtual", PHASE_CMD, 512);
|
||
+
|
||
+ if (!ret)
|
||
+ ret = stm32prog_alt_add_virt(dfu, "OTP", PHASE_OTP, 512);
|
||
+
|
||
+ if (!ret && CONFIG_IS_ENABLED(DM_PMIC))
|
||
+ ret = stm32prog_alt_add_virt(dfu, "PMIC", PHASE_PMIC, 8);
|
||
+
|
||
+ if (ret)
|
||
+ stm32prog_err("dfu init failed: %d", ret);
|
||
+ puts("done\n");
|
||
+
|
||
+#ifdef DEBUG
|
||
+ dfu_show_entities();
|
||
+#endif
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+int stm32prog_otp_write(struct stm32prog_data *data, u32 offset, u8 *buffer,
|
||
+ long *size)
|
||
+{
|
||
+ pr_debug("%s: %x %lx\n", __func__, offset, *size);
|
||
+
|
||
+ if (!data->otp_part) {
|
||
+ data->otp_part = memalign(CONFIG_SYS_CACHELINE_SIZE, OTP_SIZE);
|
||
+ if (!data->otp_part)
|
||
+ return -ENOMEM;
|
||
+ }
|
||
+
|
||
+ if (!offset)
|
||
+ memset(data->otp_part, 0, OTP_SIZE);
|
||
+
|
||
+ if (offset + *size > OTP_SIZE)
|
||
+ *size = OTP_SIZE - offset;
|
||
+
|
||
+ memcpy((void *)((u32)data->otp_part + offset), buffer, *size);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+int stm32prog_otp_read(struct stm32prog_data *data, u32 offset, u8 *buffer,
|
||
+ long *size)
|
||
+{
|
||
+#ifndef CONFIG_ARM_SMCCC
|
||
+ stm32prog_err("OTP update not supported");
|
||
+
|
||
+ return -1;
|
||
+#else
|
||
+ int result = 0;
|
||
+
|
||
+ pr_debug("%s: %x %lx\n", __func__, offset, *size);
|
||
+ /* alway read for first packet */
|
||
+ if (!offset) {
|
||
+ if (!data->otp_part)
|
||
+ data->otp_part =
|
||
+ memalign(CONFIG_SYS_CACHELINE_SIZE, OTP_SIZE);
|
||
+
|
||
+ if (!data->otp_part) {
|
||
+ result = -ENOMEM;
|
||
+ goto end_otp_read;
|
||
+ }
|
||
+
|
||
+ /* init struct with 0 */
|
||
+ memset(data->otp_part, 0, OTP_SIZE);
|
||
+
|
||
+ /* call the service */
|
||
+ result = stm32_smc_exec(STM32_SMC_BSEC, STM32_SMC_READ_ALL,
|
||
+ (u32)data->otp_part, 0);
|
||
+ if (result)
|
||
+ goto end_otp_read;
|
||
+ }
|
||
+
|
||
+ if (!data->otp_part) {
|
||
+ result = -ENOMEM;
|
||
+ goto end_otp_read;
|
||
+ }
|
||
+
|
||
+ if (offset + *size > OTP_SIZE)
|
||
+ *size = OTP_SIZE - offset;
|
||
+ memcpy(buffer, (void *)((u32)data->otp_part + offset), *size);
|
||
+
|
||
+end_otp_read:
|
||
+ pr_debug("%s: result %i\n", __func__, result);
|
||
+
|
||
+ return result;
|
||
+#endif
|
||
+}
|
||
+
|
||
+int stm32prog_otp_start(struct stm32prog_data *data)
|
||
+{
|
||
+#ifndef CONFIG_ARM_SMCCC
|
||
+ stm32prog_err("OTP update not supported");
|
||
+
|
||
+ return -1;
|
||
+#else
|
||
+ int result = 0;
|
||
+ struct arm_smccc_res res;
|
||
+
|
||
+ if (!data->otp_part) {
|
||
+ stm32prog_err("start OTP without data");
|
||
+ return -1;
|
||
+ }
|
||
+
|
||
+ arm_smccc_smc(STM32_SMC_BSEC, STM32_SMC_WRITE_ALL,
|
||
+ (u32)data->otp_part, 0, 0, 0, 0, 0, &res);
|
||
+
|
||
+ if (!res.a0) {
|
||
+ switch (res.a1) {
|
||
+ case 0:
|
||
+ result = 0;
|
||
+ break;
|
||
+ case 1:
|
||
+ stm32prog_err("Provisioning");
|
||
+ result = 0;
|
||
+ break;
|
||
+ default:
|
||
+ pr_err("%s: OTP incorrect value (err = %ld)\n",
|
||
+ __func__, res.a1);
|
||
+ result = -EINVAL;
|
||
+ break;
|
||
+ }
|
||
+ } else {
|
||
+ pr_err("%s: Failed to exec svc=%x op=%x in secure mode (err = %ld)\n",
|
||
+ __func__, STM32_SMC_BSEC, STM32_SMC_WRITE_ALL, res.a0);
|
||
+ result = -EINVAL;
|
||
+ }
|
||
+
|
||
+ free(data->otp_part);
|
||
+ data->otp_part = NULL;
|
||
+ pr_debug("%s: result %i\n", __func__, result);
|
||
+
|
||
+ return result;
|
||
+#endif
|
||
+}
|
||
+
|
||
+int stm32prog_pmic_write(struct stm32prog_data *data, u32 offset, u8 *buffer,
|
||
+ long *size)
|
||
+{
|
||
+ pr_debug("%s: %x %lx\n", __func__, offset, *size);
|
||
+
|
||
+ if (!offset)
|
||
+ memset(data->pmic_part, 0, PMIC_SIZE);
|
||
+
|
||
+ if (offset + *size > PMIC_SIZE)
|
||
+ *size = PMIC_SIZE - offset;
|
||
+
|
||
+ memcpy(&data->pmic_part[offset], buffer, *size);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+int stm32prog_pmic_read(struct stm32prog_data *data, u32 offset, u8 *buffer,
|
||
+ long *size)
|
||
+{
|
||
+ int result = 0, ret;
|
||
+ struct udevice *dev;
|
||
+
|
||
+ if (!CONFIG_IS_ENABLED(PMIC_STPMIC1)) {
|
||
+ stm32prog_err("PMIC update not supported");
|
||
+
|
||
+ return -EOPNOTSUPP;
|
||
+ }
|
||
+
|
||
+ pr_debug("%s: %x %lx\n", __func__, offset, *size);
|
||
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||
+ DM_GET_DRIVER(stpmic1_nvm),
|
||
+ &dev);
|
||
+ if (ret)
|
||
+ return ret;
|
||
+
|
||
+ /* alway request PMIC for first packet */
|
||
+ if (!offset) {
|
||
+ /* init struct with 0 */
|
||
+ memset(data->pmic_part, 0, PMIC_SIZE);
|
||
+
|
||
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||
+ DM_GET_DRIVER(stpmic1_nvm),
|
||
+ &dev);
|
||
+ if (ret)
|
||
+ return ret;
|
||
+
|
||
+ ret = misc_read(dev, 0xF8, data->pmic_part, PMIC_SIZE);
|
||
+ if (ret < 0) {
|
||
+ result = ret;
|
||
+ goto end_pmic_read;
|
||
+ }
|
||
+ if (ret != PMIC_SIZE) {
|
||
+ result = -EACCES;
|
||
+ goto end_pmic_read;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (offset + *size > PMIC_SIZE)
|
||
+ *size = PMIC_SIZE - offset;
|
||
+
|
||
+ memcpy(buffer, &data->pmic_part[offset], *size);
|
||
+
|
||
+end_pmic_read:
|
||
+ pr_debug("%s: result %i\n", __func__, result);
|
||
+ return result;
|
||
+}
|
||
+
|
||
+int stm32prog_pmic_start(struct stm32prog_data *data)
|
||
+{
|
||
+ int ret;
|
||
+ struct udevice *dev;
|
||
+
|
||
+ if (!CONFIG_IS_ENABLED(PMIC_STPMIC1)) {
|
||
+ stm32prog_err("PMIC update not supported");
|
||
+
|
||
+ return -EOPNOTSUPP;
|
||
+ }
|
||
+
|
||
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||
+ DM_GET_DRIVER(stpmic1_nvm),
|
||
+ &dev);
|
||
+ if (ret)
|
||
+ return ret;
|
||
+
|
||
+ return misc_write(dev, 0xF8, data->pmic_part, PMIC_SIZE);
|
||
+}
|
||
+
|
||
+/* copy FSBL on NAND to improve reliability on NAND */
|
||
+static int stm32prog_copy_fsbl(struct stm32prog_part_t *part)
|
||
+{
|
||
+ int ret, i;
|
||
+ void *fsbl;
|
||
+ struct image_header_s header;
|
||
+ struct raw_header_s raw_header;
|
||
+ struct dfu_entity *dfu;
|
||
+ long size, offset;
|
||
+
|
||
+ if (part->target != STM32PROG_NAND &&
|
||
+ part->target != STM32PROG_SPI_NAND)
|
||
+ return -1;
|
||
+
|
||
+ dfu = dfu_get_entity(part->alt_id);
|
||
+
|
||
+ /* read header */
|
||
+ dfu_transaction_cleanup(dfu);
|
||
+ size = BL_HEADER_SIZE;
|
||
+ ret = dfu->read_medium(dfu, 0, (void *)&raw_header, &size);
|
||
+ if (ret)
|
||
+ return ret;
|
||
+ if (stm32prog_header_check(&raw_header, &header))
|
||
+ return -1;
|
||
+
|
||
+ /* read header + payload */
|
||
+ size = header.image_length + BL_HEADER_SIZE;
|
||
+ size = round_up(size, part->dev->mtd->erasesize);
|
||
+ fsbl = calloc(1, size);
|
||
+ if (!fsbl)
|
||
+ return -ENOMEM;
|
||
+ ret = dfu->read_medium(dfu, 0, fsbl, &size);
|
||
+ pr_debug("%s read size=%lx ret=%d\n", __func__, size, ret);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+
|
||
+ dfu_transaction_cleanup(dfu);
|
||
+ offset = 0;
|
||
+ for (i = part->bin_nb - 1; i > 0; i--) {
|
||
+ offset += size;
|
||
+ /* write to the next erase block */
|
||
+ ret = dfu->write_medium(dfu, offset, fsbl, &size);
|
||
+ pr_debug("%s copy at ofset=%lx size=%lx ret=%d",
|
||
+ __func__, offset, size, ret);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+ }
|
||
+
|
||
+error:
|
||
+ free(fsbl);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static void stm32prog_end_phase(struct stm32prog_data *data)
|
||
+{
|
||
+ if (data->phase == PHASE_FLASHLAYOUT) {
|
||
+ if (parse_flash_layout(data, STM32_DDR_BASE, 0))
|
||
+ stm32prog_err("Layout: invalid FlashLayout");
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ if (!data->cur_part)
|
||
+ return;
|
||
+
|
||
+ if (data->cur_part->target == STM32PROG_RAM) {
|
||
+ if (data->cur_part->part_type == PART_SYSTEM)
|
||
+ data->uimage = data->cur_part->addr;
|
||
+ if (data->cur_part->part_type == PART_FILESYSTEM)
|
||
+ data->dtb = data->cur_part->addr;
|
||
+ }
|
||
+
|
||
+ if (CONFIG_IS_ENABLED(MMC) &&
|
||
+ data->cur_part->part_id < 0) {
|
||
+ char cmdbuf[60];
|
||
+
|
||
+ sprintf(cmdbuf, "mmc bootbus %d 0 0 0; mmc partconf %d 1 %d 0",
|
||
+ data->cur_part->dev_id, data->cur_part->dev_id,
|
||
+ -(data->cur_part->part_id));
|
||
+ if (run_command(cmdbuf, 0)) {
|
||
+ stm32prog_err("commands '%s' failed", cmdbuf);
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (CONFIG_IS_ENABLED(MTD) &&
|
||
+ data->cur_part->bin_nb > 1) {
|
||
+ if (stm32prog_copy_fsbl(data->cur_part)) {
|
||
+ stm32prog_err("%s (0x%x): copy of fsbl failed",
|
||
+ data->cur_part->name, data->cur_part->id);
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+void stm32prog_do_reset(struct stm32prog_data *data)
|
||
+{
|
||
+ if (data->phase == PHASE_RESET) {
|
||
+ data->phase = PHASE_DO_RESET;
|
||
+ puts("Reset requested\n");
|
||
+ }
|
||
+}
|
||
+
|
||
+void stm32prog_next_phase(struct stm32prog_data *data)
|
||
+{
|
||
+ int phase, i;
|
||
+ struct stm32prog_part_t *part;
|
||
+ bool found;
|
||
+
|
||
+ phase = data->phase;
|
||
+ switch (phase) {
|
||
+ case PHASE_RESET:
|
||
+ case PHASE_END:
|
||
+ case PHASE_DO_RESET:
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* found next selected partition */
|
||
+ data->dfu_seq = 0;
|
||
+ data->cur_part = NULL;
|
||
+ data->phase = PHASE_END;
|
||
+ found = false;
|
||
+ do {
|
||
+ phase++;
|
||
+ if (phase > PHASE_LAST_USER)
|
||
+ break;
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ part = &data->part_array[i];
|
||
+ if (part->id == phase) {
|
||
+ if (IS_SELECT(part) && !IS_EMPTY(part)) {
|
||
+ data->cur_part = part;
|
||
+ data->phase = phase;
|
||
+ found = true;
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ } while (!found);
|
||
+
|
||
+ if (data->phase == PHASE_END)
|
||
+ puts("Phase=END\n");
|
||
+}
|
||
+
|
||
+static int part_delete(struct stm32prog_data *data,
|
||
+ struct stm32prog_part_t *part)
|
||
+{
|
||
+ int ret = 0;
|
||
+#ifdef CONFIG_MMC
|
||
+ unsigned long blks, blks_offset, blks_size;
|
||
+ struct blk_desc *block_dev = NULL;
|
||
+ #endif
|
||
+#ifdef CONFIG_MTD
|
||
+ char cmdbuf[40];
|
||
+ char devstr[10];
|
||
+#endif
|
||
+
|
||
+ printf("Erasing %s ", part->name);
|
||
+ switch (part->target) {
|
||
+#ifdef CONFIG_MMC
|
||
+ case STM32PROG_MMC:
|
||
+ printf("on mmc %d: ", part->dev->dev_id);
|
||
+ block_dev = mmc_get_blk_desc(part->dev->mmc);
|
||
+ blks_offset = lldiv(part->addr, part->dev->mmc->read_bl_len);
|
||
+ blks_size = lldiv(part->size, part->dev->mmc->read_bl_len);
|
||
+ /* -1 or -2 : delete boot partition of MMC
|
||
+ * need to switch to associated hwpart 1 or 2
|
||
+ */
|
||
+ if (part->part_id < 0)
|
||
+ if (blk_select_hwpart_devnum(IF_TYPE_MMC,
|
||
+ part->dev->dev_id,
|
||
+ -part->part_id))
|
||
+ return -1;
|
||
+ blks = blk_derase(block_dev, blks_offset, blks_size);
|
||
+ /* return to user partition */
|
||
+ if (part->part_id < 0)
|
||
+ blk_select_hwpart_devnum(IF_TYPE_MMC,
|
||
+ part->dev->dev_id, 0);
|
||
+ if (blks != blks_size) {
|
||
+ ret = -1;
|
||
+ stm32prog_err("%s (0x%x): MMC erase failed",
|
||
+ part->name, part->id);
|
||
+ }
|
||
+ break;
|
||
+#endif
|
||
+#ifdef CONFIG_MTD
|
||
+ case STM32PROG_NOR:
|
||
+ case STM32PROG_NAND:
|
||
+ case STM32PROG_SPI_NAND:
|
||
+ get_mtd_by_target(devstr, part->target, part->dev->dev_id);
|
||
+ printf("on %s: ", devstr);
|
||
+ sprintf(cmdbuf, "mtd erase %s 0x%llx 0x%llx",
|
||
+ devstr, part->addr, part->size);
|
||
+ if (run_command(cmdbuf, 0)) {
|
||
+ ret = -1;
|
||
+ stm32prog_err("%s (0x%x): MTD erase commands failed (%s)",
|
||
+ part->name, part->id, cmdbuf);
|
||
+ }
|
||
+ break;
|
||
+#endif
|
||
+ case STM32PROG_RAM:
|
||
+ printf("on ram: ");
|
||
+ memset((void *)(uintptr_t)part->addr, 0, (size_t)part->size);
|
||
+ break;
|
||
+ default:
|
||
+ ret = -1;
|
||
+ stm32prog_err("%s (0x%x): erase invalid", part->name, part->id);
|
||
+ break;
|
||
+ }
|
||
+ if (!ret)
|
||
+ printf("done\n");
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static void stm32prog_devices_init(struct stm32prog_data *data)
|
||
+{
|
||
+ int i;
|
||
+ int ret;
|
||
+ struct stm32prog_part_t *part;
|
||
+
|
||
+ ret = treat_partition_list(data);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+
|
||
+ /* initialize the selected device */
|
||
+ for (i = 0; i < data->dev_nb; i++) {
|
||
+ ret = init_device(data, &data->dev[i]);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+ }
|
||
+
|
||
+ /* delete RAW partition before create partition */
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ part = &data->part_array[i];
|
||
+
|
||
+ if (part->part_type != RAW_IMAGE)
|
||
+ continue;
|
||
+
|
||
+ if (!IS_SELECT(part) || !IS_DELETE(part))
|
||
+ continue;
|
||
+
|
||
+ ret = part_delete(data, part);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+ }
|
||
+
|
||
+ ret = create_partitions(data);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+
|
||
+ /* delete partition GPT or MTD */
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ part = &data->part_array[i];
|
||
+
|
||
+ if (part->part_type == RAW_IMAGE)
|
||
+ continue;
|
||
+
|
||
+ if (!IS_SELECT(part) || !IS_DELETE(part))
|
||
+ continue;
|
||
+
|
||
+ ret = part_delete(data, part);
|
||
+ if (ret)
|
||
+ goto error;
|
||
+ }
|
||
+
|
||
+ return;
|
||
+
|
||
+error:
|
||
+ data->part_nb = 0;
|
||
+}
|
||
+
|
||
+int stm32prog_dfu_init(struct stm32prog_data *data)
|
||
+{
|
||
+ /* init device if no error */
|
||
+ if (data->part_nb)
|
||
+ stm32prog_devices_init(data);
|
||
+
|
||
+ if (data->part_nb)
|
||
+ stm32prog_next_phase(data);
|
||
+
|
||
+ /* prepare DFU for device read/write */
|
||
+ dfu_free_entities();
|
||
+ return dfu_init_entities(data);
|
||
+}
|
||
+
|
||
+int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size)
|
||
+{
|
||
+ memset(data, 0x0, sizeof(*data));
|
||
+ data->read_phase = PHASE_RESET;
|
||
+ data->phase = PHASE_FLASHLAYOUT;
|
||
+
|
||
+ return parse_flash_layout(data, addr, size);
|
||
+}
|
||
+
|
||
+void stm32prog_clean(struct stm32prog_data *data)
|
||
+{
|
||
+ /* clean */
|
||
+ dfu_free_entities();
|
||
+ free(data->part_array);
|
||
+ free(data->otp_part);
|
||
+ free(data->buffer);
|
||
+ free(data->header_data);
|
||
+}
|
||
+
|
||
+/* DFU callback: used after serial and direct DFU USB access */
|
||
+void dfu_flush_callback(struct dfu_entity *dfu)
|
||
+{
|
||
+ if (!stm32prog_data)
|
||
+ return;
|
||
+
|
||
+ if (dfu->dev_type == DFU_DEV_VIRT) {
|
||
+ if (dfu->data.virt.dev_num == PHASE_OTP)
|
||
+ stm32prog_otp_start(stm32prog_data);
|
||
+ else if (dfu->data.virt.dev_num == PHASE_PMIC)
|
||
+ stm32prog_pmic_start(stm32prog_data);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ if (dfu->dev_type == DFU_DEV_RAM) {
|
||
+ if (dfu->alt == 0 &&
|
||
+ stm32prog_data->phase == PHASE_FLASHLAYOUT) {
|
||
+ stm32prog_end_phase(stm32prog_data);
|
||
+ /* waiting DFU DETACH for reenumeration */
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (!stm32prog_data->cur_part)
|
||
+ return;
|
||
+
|
||
+ if (dfu->alt == stm32prog_data->cur_part->alt_id) {
|
||
+ stm32prog_end_phase(stm32prog_data);
|
||
+ stm32prog_next_phase(stm32prog_data);
|
||
+ }
|
||
+}
|
||
+
|
||
+void dfu_initiated_callback(struct dfu_entity *dfu)
|
||
+{
|
||
+ if (!stm32prog_data)
|
||
+ return;
|
||
+
|
||
+ if (!stm32prog_data->cur_part)
|
||
+ return;
|
||
+
|
||
+ /* force the saved offset for the current partition */
|
||
+ if (dfu->alt == stm32prog_data->cur_part->alt_id) {
|
||
+ dfu->offset = stm32prog_data->offset;
|
||
+ stm32prog_data->dfu_seq = 0;
|
||
+ pr_debug("dfu offset = 0x%llx\n", dfu->offset);
|
||
+ }
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
|
||
new file mode 100644
|
||
index 0000000..bae4e91
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
|
||
@@ -0,0 +1,185 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#ifndef _STM32PROG_H_
|
||
+#define _STM32PROG_H_
|
||
+
|
||
+/* - phase defines ------------------------------------------------*/
|
||
+#define PHASE_FLASHLAYOUT 0x00
|
||
+#define PHASE_FIRST_USER 0x10
|
||
+#define PHASE_LAST_USER 0xF0
|
||
+#define PHASE_CMD 0xF1
|
||
+#define PHASE_OTP 0xF2
|
||
+#define PHASE_PMIC 0xF4
|
||
+#define PHASE_END 0xFE
|
||
+#define PHASE_RESET 0xFF
|
||
+#define PHASE_DO_RESET 0x1FF
|
||
+
|
||
+#define DEFAULT_ADDRESS 0xFFFFFFFF
|
||
+
|
||
+#define OTP_SIZE 1024
|
||
+#define PMIC_SIZE 8
|
||
+
|
||
+enum stm32prog_target {
|
||
+ STM32PROG_NONE,
|
||
+ STM32PROG_MMC,
|
||
+ STM32PROG_NAND,
|
||
+ STM32PROG_NOR,
|
||
+ STM32PROG_SPI_NAND,
|
||
+ STM32PROG_RAM
|
||
+};
|
||
+
|
||
+enum stm32prog_link_t {
|
||
+ LINK_SERIAL,
|
||
+ LINK_USB,
|
||
+ LINK_UNDEFINED,
|
||
+};
|
||
+
|
||
+struct image_header_s {
|
||
+ bool present;
|
||
+ u32 image_checksum;
|
||
+ u32 image_length;
|
||
+};
|
||
+
|
||
+struct raw_header_s {
|
||
+ u32 magic_number;
|
||
+ u32 image_signature[64 / 4];
|
||
+ u32 image_checksum;
|
||
+ u32 header_version;
|
||
+ u32 image_length;
|
||
+ u32 image_entry_point;
|
||
+ u32 reserved1;
|
||
+ u32 load_address;
|
||
+ u32 reserved2;
|
||
+ u32 version_number;
|
||
+ u32 option_flags;
|
||
+ u32 ecdsa_algorithm;
|
||
+ u32 ecdsa_public_key[64 / 4];
|
||
+ u32 padding[83 / 4];
|
||
+ u32 binary_type;
|
||
+};
|
||
+
|
||
+#define BL_HEADER_SIZE sizeof(struct raw_header_s)
|
||
+
|
||
+/* partition type in flashlayout file */
|
||
+enum stm32prog_part_type {
|
||
+ PART_BINARY,
|
||
+ PART_SYSTEM,
|
||
+ PART_FILESYSTEM,
|
||
+ RAW_IMAGE
|
||
+};
|
||
+
|
||
+/* device information */
|
||
+struct stm32prog_dev_t {
|
||
+ enum stm32prog_target target;
|
||
+ char dev_id;
|
||
+ u32 erase_size;
|
||
+ struct mmc *mmc;
|
||
+ struct mtd_info *mtd;
|
||
+ /* list of partition for this device / ordered in offset */
|
||
+ struct list_head part_list;
|
||
+ bool full_update;
|
||
+};
|
||
+
|
||
+/* partition information build from FlashLayout and device */
|
||
+struct stm32prog_part_t {
|
||
+ /* FlashLayout information */
|
||
+ int option;
|
||
+ int id;
|
||
+ enum stm32prog_part_type part_type;
|
||
+ enum stm32prog_target target;
|
||
+ char dev_id;
|
||
+
|
||
+ /* partition name
|
||
+ * (16 char in gpt, + 1 for null terminated string
|
||
+ */
|
||
+ char name[16 + 1];
|
||
+ u64 addr;
|
||
+ u64 size;
|
||
+ enum stm32prog_part_type bin_nb; /* SSBL repeatition */
|
||
+
|
||
+ /* information on associated device */
|
||
+ struct stm32prog_dev_t *dev; /* pointer to device */
|
||
+ s16 part_id; /* partition id in device */
|
||
+ int alt_id; /* alt id in usb/dfu */
|
||
+
|
||
+ struct list_head list;
|
||
+};
|
||
+
|
||
+#define STM32PROG_MAX_DEV 5
|
||
+struct stm32prog_data {
|
||
+ /* Layout information */
|
||
+ int dev_nb; /* device number*/
|
||
+ struct stm32prog_dev_t dev[STM32PROG_MAX_DEV]; /* array of device */
|
||
+ int part_nb; /* nb of partition */
|
||
+ struct stm32prog_part_t *part_array; /* array of partition */
|
||
+ bool tee_detected;
|
||
+ bool fsbl_nor_detected;
|
||
+
|
||
+ /* command internal information */
|
||
+ unsigned int phase;
|
||
+ u32 offset;
|
||
+ char error[255];
|
||
+ struct stm32prog_part_t *cur_part;
|
||
+ u32 *otp_part;
|
||
+ u8 pmic_part[PMIC_SIZE];
|
||
+
|
||
+ /* STM32 header information */
|
||
+ struct raw_header_s *header_data;
|
||
+ struct image_header_s header;
|
||
+
|
||
+ /* SERIAL information */
|
||
+ u32 cursor;
|
||
+ u32 packet_number;
|
||
+ u32 checksum;
|
||
+ u8 *buffer; /* size = USART_RAM_BUFFER_SIZE*/
|
||
+ int dfu_seq;
|
||
+ u8 read_phase;
|
||
+
|
||
+ /* bootm information */
|
||
+ u32 uimage;
|
||
+ u32 dtb;
|
||
+};
|
||
+
|
||
+extern struct stm32prog_data *stm32prog_data;
|
||
+
|
||
+/* OTP access */
|
||
+int stm32prog_otp_write(struct stm32prog_data *data, u32 offset,
|
||
+ u8 *buffer, long *size);
|
||
+int stm32prog_otp_read(struct stm32prog_data *data, u32 offset,
|
||
+ u8 *buffer, long *size);
|
||
+int stm32prog_otp_start(struct stm32prog_data *data);
|
||
+
|
||
+/* PMIC access */
|
||
+int stm32prog_pmic_write(struct stm32prog_data *data, u32 offset,
|
||
+ u8 *buffer, long *size);
|
||
+int stm32prog_pmic_read(struct stm32prog_data *data, u32 offset,
|
||
+ u8 *buffer, long *size);
|
||
+int stm32prog_pmic_start(struct stm32prog_data *data);
|
||
+
|
||
+/* generic part*/
|
||
+u8 stm32prog_header_check(struct raw_header_s *raw_header,
|
||
+ struct image_header_s *header);
|
||
+int stm32prog_dfu_init(struct stm32prog_data *data);
|
||
+void stm32prog_next_phase(struct stm32prog_data *data);
|
||
+void stm32prog_do_reset(struct stm32prog_data *data);
|
||
+
|
||
+char *stm32prog_get_error(struct stm32prog_data *data);
|
||
+
|
||
+#define stm32prog_err(args...) {\
|
||
+ if (data->phase != PHASE_RESET) { \
|
||
+ sprintf(data->error, args); \
|
||
+ data->phase = PHASE_RESET; \
|
||
+ pr_err("Error: %s\n", data->error); } \
|
||
+ }
|
||
+
|
||
+/* Main function */
|
||
+int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size);
|
||
+int stm32prog_serial_init(struct stm32prog_data *data, int link_dev);
|
||
+bool stm32prog_serial_loop(struct stm32prog_data *data);
|
||
+bool stm32prog_usb_loop(struct stm32prog_data *data, int dev);
|
||
+void stm32prog_clean(struct stm32prog_data *data);
|
||
+
|
||
+#endif
|
||
diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c
|
||
new file mode 100644
|
||
index 0000000..5a16979
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c
|
||
@@ -0,0 +1,993 @@
|
||
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#include <common.h>
|
||
+#include <console.h>
|
||
+#include <dfu.h>
|
||
+#include <malloc.h>
|
||
+#include <serial.h>
|
||
+#include <watchdog.h>
|
||
+#include <dm/lists.h>
|
||
+#include <dm/device-internal.h>
|
||
+#include "stm32prog.h"
|
||
+
|
||
+/* - configuration part -----------------------------*/
|
||
+#define USART_BL_VERSION 0x40 /* USART bootloader version V4.0*/
|
||
+#define UBOOT_BL_VERSION 0x03 /* bootloader version V0.3*/
|
||
+#define DEVICE_ID_BYTE1 0x05 /* MSB byte of device ID*/
|
||
+#define DEVICE_ID_BYTE2 0x00 /* LSB byte of device ID*/
|
||
+#define USART_RAM_BUFFER_SIZE 256 /* Size of USART_RAM_Buf buffer*/
|
||
+
|
||
+/* - Commands -----------------------------*/
|
||
+#define GET_CMD_COMMAND 0x00 /* Get CMD command*/
|
||
+#define GET_VER_COMMAND 0x01 /* Get Version command*/
|
||
+#define GET_ID_COMMAND 0x02 /* Get ID command*/
|
||
+#define GET_PHASE_COMMAND 0x03 /* Get Phase command*/
|
||
+#define RM_COMMAND 0x11 /* Read Memory command*/
|
||
+#define READ_PART_COMMAND 0x12 /* Read Partition command*/
|
||
+#define START_COMMAND 0x21 /* START command (Go)*/
|
||
+#define DOWNLOAD_COMMAND 0x31 /* Download command*/
|
||
+/* existing command for other STM32 but not used */
|
||
+/* ERASE 0x43 */
|
||
+/* EXTENDED_ERASE 0x44 */
|
||
+/* WRITE_UNPROTECTED 0x73 */
|
||
+/* READOUT_PROTECT 0x82 */
|
||
+/* READOUT_UNPROTECT 0x92 */
|
||
+
|
||
+/* - miscellaneous defines ----------------------------------------*/
|
||
+#define INIT_BYTE 0x7F /*Init Byte ID*/
|
||
+#define ACK_BYTE 0x79 /*Acknowlede Byte ID*/
|
||
+#define NACK_BYTE 0x1F /*No Acknowlede Byte ID*/
|
||
+#define ABORT_BYTE 0x5F /*ABORT*/
|
||
+
|
||
+struct udevice *down_serial_dev;
|
||
+
|
||
+const u8 cmd_id[] = {
|
||
+ GET_CMD_COMMAND,
|
||
+ GET_VER_COMMAND,
|
||
+ GET_ID_COMMAND,
|
||
+ GET_PHASE_COMMAND,
|
||
+ RM_COMMAND,
|
||
+ READ_PART_COMMAND,
|
||
+ START_COMMAND,
|
||
+ DOWNLOAD_COMMAND
|
||
+};
|
||
+
|
||
+#define NB_CMD sizeof(cmd_id)
|
||
+
|
||
+/* DFU support for serial *********************************************/
|
||
+static struct dfu_entity *stm32prog_get_entity(struct stm32prog_data *data)
|
||
+{
|
||
+ int alt_id;
|
||
+
|
||
+ if (!data->cur_part)
|
||
+ if (data->phase == PHASE_FLASHLAYOUT)
|
||
+ alt_id = 0;
|
||
+ else
|
||
+ return NULL;
|
||
+ else
|
||
+ alt_id = data->cur_part->alt_id;
|
||
+
|
||
+ return dfu_get_entity(alt_id);
|
||
+}
|
||
+
|
||
+static int stm32prog_write(struct stm32prog_data *data, u8 *buffer,
|
||
+ u32 buffer_size)
|
||
+{
|
||
+ struct dfu_entity *dfu_entity;
|
||
+ u8 ret = 0;
|
||
+
|
||
+ dfu_entity = stm32prog_get_entity(data);
|
||
+ if (!dfu_entity)
|
||
+ return -ENODEV;
|
||
+
|
||
+ ret = dfu_write(dfu_entity,
|
||
+ buffer,
|
||
+ buffer_size,
|
||
+ data->dfu_seq);
|
||
+
|
||
+ if (ret) {
|
||
+ stm32prog_err("DFU write failed [%d] cnt: %d",
|
||
+ ret, data->dfu_seq);
|
||
+ }
|
||
+ data->dfu_seq++;
|
||
+ /* handle rollover as in driver/dfu/dfu.c */
|
||
+ data->dfu_seq &= 0xffff;
|
||
+ if (buffer_size == 0)
|
||
+ data->dfu_seq = 0; /* flush done */
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static int stm32prog_read(struct stm32prog_data *data, u8 phase, u32 offset,
|
||
+ u8 *buffer, u32 buffer_size)
|
||
+{
|
||
+ struct dfu_entity *dfu_entity;
|
||
+ struct stm32prog_part_t *part;
|
||
+ u32 size;
|
||
+ int ret, i;
|
||
+
|
||
+ if (data->dfu_seq) {
|
||
+ stm32prog_err("DFU write pending for phase %d, seq %d",
|
||
+ data->phase, data->dfu_seq);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ if (phase == PHASE_FLASHLAYOUT || phase > PHASE_LAST_USER) {
|
||
+ stm32prog_err("read failed : phase %d is invalid", phase);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ if (data->read_phase <= PHASE_LAST_USER &&
|
||
+ phase != data->read_phase) {
|
||
+ /* clear previous read session */
|
||
+ dfu_entity = dfu_get_entity(data->read_phase - 1);
|
||
+ if (dfu_entity)
|
||
+ dfu_transaction_cleanup(dfu_entity);
|
||
+ }
|
||
+
|
||
+ dfu_entity = NULL;
|
||
+ /* found partition for the expected phase */
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ part = &data->part_array[i];
|
||
+ if (part->id == phase)
|
||
+ dfu_entity = dfu_get_entity(part->alt_id);
|
||
+ }
|
||
+ if (!dfu_entity) {
|
||
+ stm32prog_err("read failed : phase %d is unknown", phase);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+
|
||
+ /* clear pending read before to force offset */
|
||
+ if (dfu_entity->inited &&
|
||
+ (data->read_phase != phase || data->offset != offset))
|
||
+ dfu_transaction_cleanup(dfu_entity);
|
||
+
|
||
+ /* initiate before to force offset */
|
||
+ if (!dfu_entity->inited) {
|
||
+ ret = dfu_transaction_initiate(dfu_entity, true);
|
||
+ if (ret < 0) {
|
||
+ stm32prog_err("DFU read init failed [%d] phase = %d offset = 0x%08x",
|
||
+ ret, phase, offset);
|
||
+ return ret;
|
||
+ }
|
||
+ }
|
||
+ /* force new offset */
|
||
+ if (dfu_entity->offset != offset)
|
||
+ dfu_entity->offset = offset;
|
||
+ data->offset = offset;
|
||
+ data->read_phase = phase;
|
||
+ pr_debug("\nSTM32 download read %s offset=0x%x\n",
|
||
+ dfu_entity->name, offset);
|
||
+ ret = dfu_read(dfu_entity, buffer, buffer_size,
|
||
+ dfu_entity->i_blk_seq_num);
|
||
+ if (ret < 0) {
|
||
+ stm32prog_err("DFU read failed [%d] phase = %d offset = 0x%08x",
|
||
+ ret, phase, offset);
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ size = ret;
|
||
+
|
||
+ if (size < buffer_size) {
|
||
+ data->offset = 0;
|
||
+ data->read_phase = PHASE_END;
|
||
+ memset(buffer + size, 0, buffer_size - size);
|
||
+ } else {
|
||
+ data->offset += size;
|
||
+ }
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+/* UART access ***************************************************/
|
||
+int stm32prog_serial_init(struct stm32prog_data *data, int link_dev)
|
||
+{
|
||
+ struct udevice *dev = NULL;
|
||
+ int node;
|
||
+ char alias[10];
|
||
+ const char *path;
|
||
+ struct dm_serial_ops *ops;
|
||
+ /* no parity, 8 bits, 1 stop */
|
||
+ u32 serial_config = SERIAL_DEFAULT_CONFIG;
|
||
+
|
||
+ down_serial_dev = NULL;
|
||
+
|
||
+ sprintf(alias, "serial%d", link_dev);
|
||
+ path = fdt_get_alias(gd->fdt_blob, alias);
|
||
+ if (!path) {
|
||
+ pr_err("%s alias not found", alias);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ node = fdt_path_offset(gd->fdt_blob, path);
|
||
+ if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node,
|
||
+ &dev)) {
|
||
+ down_serial_dev = dev;
|
||
+ } else if (node > 0 &&
|
||
+ !lists_bind_fdt(gd->dm_root, offset_to_ofnode(node),
|
||
+ &dev, false)) {
|
||
+ if (!device_probe(dev))
|
||
+ down_serial_dev = dev;
|
||
+ }
|
||
+ if (!down_serial_dev) {
|
||
+ pr_err("%s = %s device not found", alias, path);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+
|
||
+ /* force silent console on uart only when used */
|
||
+ if (gd->cur_serial_dev == down_serial_dev)
|
||
+ gd->flags |= GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT;
|
||
+ else
|
||
+ gd->flags &= ~(GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT);
|
||
+
|
||
+ ops = serial_get_ops(down_serial_dev);
|
||
+
|
||
+ if (!ops) {
|
||
+ pr_err("%s = %s missing ops", alias, path);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ if (!ops->setconfig) {
|
||
+ pr_err("%s = %s missing setconfig", alias, path);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+
|
||
+ clrsetbits_le32(&serial_config, SERIAL_PAR_MASK, SERIAL_PAR_EVEN);
|
||
+
|
||
+ data->buffer = memalign(CONFIG_SYS_CACHELINE_SIZE,
|
||
+ USART_RAM_BUFFER_SIZE);
|
||
+
|
||
+ return ops->setconfig(down_serial_dev, serial_config);
|
||
+}
|
||
+
|
||
+static void stm32prog_serial_flush(void)
|
||
+{
|
||
+ struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
|
||
+ int err;
|
||
+
|
||
+ do {
|
||
+ err = ops->getc(down_serial_dev);
|
||
+ } while (err != -EAGAIN);
|
||
+}
|
||
+
|
||
+static int stm32prog_serial_getc_err(void)
|
||
+{
|
||
+ struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
|
||
+ int err;
|
||
+
|
||
+ do {
|
||
+ err = ops->getc(down_serial_dev);
|
||
+ if (err == -EAGAIN) {
|
||
+ ctrlc();
|
||
+ WATCHDOG_RESET();
|
||
+ }
|
||
+ } while ((err == -EAGAIN) && (!had_ctrlc()));
|
||
+
|
||
+ return err;
|
||
+}
|
||
+
|
||
+static u8 stm32prog_serial_getc(void)
|
||
+{
|
||
+ int err;
|
||
+
|
||
+ err = stm32prog_serial_getc_err();
|
||
+
|
||
+ return err >= 0 ? err : 0;
|
||
+}
|
||
+
|
||
+static bool stm32prog_serial_get_buffer(u8 *buffer, u32 *count)
|
||
+{
|
||
+ struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
|
||
+ int err;
|
||
+
|
||
+ do {
|
||
+ err = ops->getc(down_serial_dev);
|
||
+ if (err >= 0) {
|
||
+ *buffer++ = err;
|
||
+ *count -= 1;
|
||
+ } else if (err == -EAGAIN) {
|
||
+ ctrlc();
|
||
+ WATCHDOG_RESET();
|
||
+ } else {
|
||
+ break;
|
||
+ }
|
||
+ } while (*count && !had_ctrlc());
|
||
+
|
||
+ return !!(err < 0);
|
||
+}
|
||
+
|
||
+static void stm32prog_serial_putc(u8 w_byte)
|
||
+{
|
||
+ struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
|
||
+ int err;
|
||
+
|
||
+ do {
|
||
+ err = ops->putc(down_serial_dev, w_byte);
|
||
+ } while (err == -EAGAIN);
|
||
+}
|
||
+
|
||
+/* Helper function ************************************************/
|
||
+
|
||
+static u8 stm32prog_header(struct stm32prog_data *data)
|
||
+{
|
||
+ u8 ret;
|
||
+ u8 boot = 0;
|
||
+ struct dfu_entity *dfu_entity;
|
||
+ u64 size = 0;
|
||
+
|
||
+ dfu_entity = stm32prog_get_entity(data);
|
||
+ if (!dfu_entity)
|
||
+ return -ENODEV;
|
||
+
|
||
+ printf("\nSTM32 download write %s\n", dfu_entity->name);
|
||
+
|
||
+ /* force cleanup to avoid issue with previous read */
|
||
+ dfu_transaction_cleanup(dfu_entity);
|
||
+
|
||
+ ret = stm32prog_header_check(data->header_data,
|
||
+ &data->header);
|
||
+
|
||
+ /* no header : max size is partition size */
|
||
+ if (ret) {
|
||
+ dfu_entity->get_medium_size(dfu_entity, &size);
|
||
+ data->header.image_length = size;
|
||
+ }
|
||
+
|
||
+ /**** Flash the header if necessary for boot partition */
|
||
+ if (data->phase < PHASE_FIRST_USER)
|
||
+ boot = 1;
|
||
+
|
||
+ /* write header if boot partition */
|
||
+ if (boot) {
|
||
+ if (ret) {
|
||
+ stm32prog_err("invalid header (error %d)", ret);
|
||
+ } else {
|
||
+ ret = stm32prog_write(data,
|
||
+ (u8 *)data->header_data,
|
||
+ BL_HEADER_SIZE);
|
||
+ }
|
||
+ } else {
|
||
+ if (ret)
|
||
+ printf(" partition without checksum\n");
|
||
+ ret = 0;
|
||
+ }
|
||
+
|
||
+ free(data->header_data);
|
||
+ data->header_data = NULL;
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static u8 stm32prog_start(struct stm32prog_data *data, u32 address)
|
||
+{
|
||
+ u8 ret = 0;
|
||
+ struct dfu_entity *dfu_entity;
|
||
+
|
||
+ if (address < 0x100) {
|
||
+ if (address == PHASE_OTP)
|
||
+ return stm32prog_otp_start(data);
|
||
+
|
||
+ if (address == PHASE_PMIC)
|
||
+ return stm32prog_pmic_start(data);
|
||
+
|
||
+ if (address == PHASE_RESET || address == PHASE_END) {
|
||
+ data->cur_part = NULL;
|
||
+ data->dfu_seq = 0;
|
||
+ data->phase = address;
|
||
+ return 0;
|
||
+ }
|
||
+ if (address != data->phase) {
|
||
+ stm32prog_err("invalid received phase id %d, current phase is %d",
|
||
+ (u8)address, (u8)data->phase);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ }
|
||
+ /* check the last loaded partition */
|
||
+ if (address == DEFAULT_ADDRESS || address == data->phase) {
|
||
+ switch (data->phase) {
|
||
+ case PHASE_END:
|
||
+ case PHASE_RESET:
|
||
+ case PHASE_DO_RESET:
|
||
+ data->cur_part = NULL;
|
||
+ data->phase = PHASE_DO_RESET;
|
||
+ return 0;
|
||
+ }
|
||
+ dfu_entity = stm32prog_get_entity(data);
|
||
+ if (!dfu_entity)
|
||
+ return -ENODEV;
|
||
+
|
||
+ if (data->dfu_seq) {
|
||
+ ret = dfu_flush(dfu_entity, NULL, 0, data->dfu_seq);
|
||
+ data->dfu_seq = 0;
|
||
+ if (ret) {
|
||
+ stm32prog_err("DFU flush failed [%d]", ret);
|
||
+ return ret;
|
||
+ }
|
||
+ }
|
||
+ printf("\n received length = 0x%x\n", data->cursor);
|
||
+ if (data->header.present) {
|
||
+ if (data->cursor !=
|
||
+ (data->header.image_length + BL_HEADER_SIZE)) {
|
||
+ stm32prog_err("transmission interrupted (length=0x%x expected=0x%x)",
|
||
+ data->cursor,
|
||
+ data->header.image_length +
|
||
+ BL_HEADER_SIZE);
|
||
+ return -EIO;
|
||
+ }
|
||
+ if (data->header.image_checksum != data->checksum) {
|
||
+ stm32prog_err("invalid checksum received (0x%x expected 0x%x)",
|
||
+ data->checksum,
|
||
+ data->header.image_checksum);
|
||
+ return -EIO;
|
||
+ }
|
||
+ printf("\n checksum OK (0x%x)\n", data->checksum);
|
||
+ }
|
||
+
|
||
+ /* update DFU with received flashlayout */
|
||
+ if (data->phase == PHASE_FLASHLAYOUT)
|
||
+ stm32prog_dfu_init(data);
|
||
+ } else {
|
||
+ void (*entry)(void) = (void *)address;
|
||
+
|
||
+ printf("## Starting application at 0x%x ...\n", address);
|
||
+ (*entry)();
|
||
+ printf("## Application terminated\n");
|
||
+ ret = -ENOEXEC;
|
||
+ }
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+/**
|
||
+ * get_address() - Get address if it is valid
|
||
+ *
|
||
+ * @tmp_xor: Current xor value to update
|
||
+ * @return The address area
|
||
+ */
|
||
+static u32 get_address(u8 *tmp_xor)
|
||
+{
|
||
+ u32 address = 0x0;
|
||
+ u8 data;
|
||
+
|
||
+ data = stm32prog_serial_getc();
|
||
+ *tmp_xor ^= data;
|
||
+ address |= ((u32)data) << 24;
|
||
+
|
||
+ data = stm32prog_serial_getc();
|
||
+ address |= ((u32)data) << 16;
|
||
+ *tmp_xor ^= data;
|
||
+
|
||
+ data = stm32prog_serial_getc();
|
||
+ address |= ((u32)data) << 8;
|
||
+ *tmp_xor ^= data;
|
||
+
|
||
+ data = stm32prog_serial_getc();
|
||
+ address |= ((u32)data);
|
||
+ *tmp_xor ^= data;
|
||
+
|
||
+ return address;
|
||
+}
|
||
+
|
||
+static void stm32prog_serial_result(u8 result)
|
||
+{
|
||
+ /* always flush fifo before to send result */
|
||
+ stm32prog_serial_flush();
|
||
+ stm32prog_serial_putc(result);
|
||
+}
|
||
+
|
||
+/* Command -----------------------------------------------*/
|
||
+/**
|
||
+ * get_cmd_command() - Respond to Get command
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void get_cmd_command(struct stm32prog_data *data)
|
||
+{
|
||
+ u32 counter = 0x0;
|
||
+
|
||
+ stm32prog_serial_putc(NB_CMD);
|
||
+ stm32prog_serial_putc(USART_BL_VERSION);
|
||
+
|
||
+ for (counter = 0; counter < NB_CMD; counter++)
|
||
+ stm32prog_serial_putc(cmd_id[counter]);
|
||
+
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * get_version_command() - Respond to Get Version command
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void get_version_command(struct stm32prog_data *data)
|
||
+{
|
||
+ stm32prog_serial_putc(UBOOT_BL_VERSION);
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * get_id_command() - Respond to Get ID command
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void get_id_command(struct stm32prog_data *data)
|
||
+{
|
||
+ /* Send Device IDCode */
|
||
+ stm32prog_serial_putc(0x1);
|
||
+ stm32prog_serial_putc(DEVICE_ID_BYTE1);
|
||
+ stm32prog_serial_putc(DEVICE_ID_BYTE2);
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * get_phase_command() - Respond to Get phase
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void get_phase_command(struct stm32prog_data *data)
|
||
+{
|
||
+ char *err_msg = NULL;
|
||
+ u8 i, length = 0;
|
||
+ u32 destination = DEFAULT_ADDRESS; /* destination address */
|
||
+ int phase = data->phase;
|
||
+
|
||
+ if (phase == PHASE_RESET || phase == PHASE_DO_RESET) {
|
||
+ err_msg = stm32prog_get_error(data);
|
||
+ length = strlen(err_msg);
|
||
+ }
|
||
+ if (phase == PHASE_FLASHLAYOUT)
|
||
+ destination = STM32_DDR_BASE;
|
||
+
|
||
+ stm32prog_serial_putc(length + 5); /* Total length */
|
||
+ stm32prog_serial_putc(phase & 0xFF); /* partition ID */
|
||
+ stm32prog_serial_putc(destination); /* byte 1 of address */
|
||
+ stm32prog_serial_putc(destination >> 8); /* byte 2 of address */
|
||
+ stm32prog_serial_putc(destination >> 16); /* byte 3 of address */
|
||
+ stm32prog_serial_putc(destination >> 24); /* byte 4 of address */
|
||
+
|
||
+ stm32prog_serial_putc(length); /* Information length */
|
||
+ for (i = 0; i < length; i++)
|
||
+ stm32prog_serial_putc(err_msg[i]);
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+
|
||
+ if (phase == PHASE_RESET)
|
||
+ stm32prog_do_reset(data);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * read_memory_command() - Read data from memory
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void read_memory_command(struct stm32prog_data *data)
|
||
+{
|
||
+ u32 address = 0x0;
|
||
+ u8 rcv_data = 0x0, tmp_xor = 0x0;
|
||
+ u32 counter = 0x0;
|
||
+
|
||
+ /* Read memory address */
|
||
+ address = get_address(&tmp_xor);
|
||
+
|
||
+ /* If address memory is not received correctly */
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+ if (rcv_data != tmp_xor) {
|
||
+ stm32prog_serial_result(NACK_BYTE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+
|
||
+ /* Read the number of bytes to be received:
|
||
+ * Max NbrOfData = Data + 1 = 256
|
||
+ */
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+ tmp_xor = ~rcv_data;
|
||
+ if (stm32prog_serial_getc() != tmp_xor) {
|
||
+ stm32prog_serial_result(NACK_BYTE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* If checksum is correct send ACK */
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+
|
||
+ /* Send data to the host:
|
||
+ * Number of data to read = data + 1
|
||
+ */
|
||
+ for (counter = (rcv_data + 1); counter != 0; counter--)
|
||
+ stm32prog_serial_putc(*(u8 *)(address++));
|
||
+}
|
||
+
|
||
+/**
|
||
+ * start_command() - Respond to start command
|
||
+ *
|
||
+ * Jump to user application in RAM or partition check
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void start_command(struct stm32prog_data *data)
|
||
+{
|
||
+ u32 address = 0;
|
||
+ u8 tmp_xor = 0x0;
|
||
+ u8 ret, rcv_data;
|
||
+
|
||
+ /* Read memory address */
|
||
+ address = get_address(&tmp_xor);
|
||
+
|
||
+ /* If address memory is not received correctly */
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+ if (rcv_data != tmp_xor) {
|
||
+ stm32prog_serial_result(NACK_BYTE);
|
||
+ return;
|
||
+ }
|
||
+ /* validate partition */
|
||
+ ret = stm32prog_start(data,
|
||
+ address);
|
||
+
|
||
+ if (ret)
|
||
+ stm32prog_serial_result(ABORT_BYTE);
|
||
+ else
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * download_command() - Respond to download command
|
||
+ *
|
||
+ * Write data to not volatile memory, Flash
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void download_command(struct stm32prog_data *data)
|
||
+{
|
||
+ u32 address = 0x0;
|
||
+ u8 my_xor = 0x0;
|
||
+ u8 rcv_xor;
|
||
+ u32 counter = 0x0, codesize = 0x0;
|
||
+ u8 *ramaddress = 0;
|
||
+ u8 rcv_data = 0x0;
|
||
+ struct image_header_s *image_header = &data->header;
|
||
+ u32 cursor = data->cursor;
|
||
+ long size = 0;
|
||
+ u8 operation;
|
||
+ u32 packet_number;
|
||
+ u32 result = ACK_BYTE;
|
||
+ u8 ret;
|
||
+ unsigned int i;
|
||
+ bool error;
|
||
+ int rcv;
|
||
+
|
||
+ address = get_address(&my_xor);
|
||
+
|
||
+ /* If address memory is not received correctly */
|
||
+ rcv_xor = stm32prog_serial_getc();
|
||
+ if (rcv_xor != my_xor) {
|
||
+ result = NACK_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ /* If address valid send ACK */
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+
|
||
+ /* get packet number and operation type */
|
||
+ operation = (u8)((u32)address >> 24);
|
||
+ packet_number = ((u32)(((u32)address << 8))) >> 8;
|
||
+
|
||
+ switch (operation) {
|
||
+ /* supported operation */
|
||
+ case PHASE_FLASHLAYOUT:
|
||
+ case PHASE_OTP:
|
||
+ case PHASE_PMIC:
|
||
+ break;
|
||
+ default:
|
||
+ result = NACK_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+ /* check the packet number */
|
||
+ if (packet_number == 0) {
|
||
+ /* erase: re-initialize the image_header struct */
|
||
+ data->packet_number = 0;
|
||
+ if (data->header_data)
|
||
+ memset(data->header_data, 0, BL_HEADER_SIZE);
|
||
+ else
|
||
+ data->header_data = calloc(1, BL_HEADER_SIZE);
|
||
+ cursor = 0;
|
||
+ data->cursor = 0;
|
||
+ data->checksum = 0;
|
||
+ /*idx = cursor;*/
|
||
+ } else {
|
||
+ data->packet_number++;
|
||
+ }
|
||
+
|
||
+ /* Check with the number of current packet if the device receive
|
||
+ * the true packet
|
||
+ */
|
||
+ if (packet_number != data->packet_number) {
|
||
+ data->packet_number--;
|
||
+ result = NACK_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ /*-- Read number of bytes to be written and data -----------*/
|
||
+
|
||
+ /* Read the number of bytes to be written:
|
||
+ * Max NbrOfData = data + 1 <= 256
|
||
+ */
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+
|
||
+ /* NbrOfData to write = data + 1 */
|
||
+ codesize = rcv_data + 0x01;
|
||
+
|
||
+ if (codesize > USART_RAM_BUFFER_SIZE) {
|
||
+ result = NACK_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ /* Checksum Initialization */
|
||
+ my_xor = rcv_data;
|
||
+
|
||
+ /* UART receive data and send to Buffer */
|
||
+ counter = codesize;
|
||
+ error = stm32prog_serial_get_buffer(data->buffer, &counter);
|
||
+
|
||
+ /* read checksum */
|
||
+ if (!error) {
|
||
+ rcv = stm32prog_serial_getc_err();
|
||
+ error = !!(rcv < 0);
|
||
+ rcv_xor = rcv;
|
||
+ }
|
||
+
|
||
+ if (error) {
|
||
+ printf("transmission error on packet %d, byte %d\n",
|
||
+ packet_number, codesize - counter);
|
||
+ /* waiting end of packet before flush & NACK */
|
||
+ mdelay(30);
|
||
+ data->packet_number--;
|
||
+ result = NACK_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ /* Compute Checksum */
|
||
+ ramaddress = data->buffer;
|
||
+ for (counter = codesize; counter != 0; counter--)
|
||
+ my_xor ^= *(ramaddress++);
|
||
+
|
||
+ /* If Checksum is incorrect */
|
||
+ if (rcv_xor != my_xor) {
|
||
+ printf("checksum error on packet %d\n",
|
||
+ packet_number);
|
||
+ /* wait to be sure that all data are received
|
||
+ * in the FIFO before flush
|
||
+ */
|
||
+ mdelay(30);
|
||
+ data->packet_number--;
|
||
+ result = NACK_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ /* Update current position in buffer */
|
||
+ data->cursor += codesize;
|
||
+
|
||
+ if (operation == PHASE_OTP) {
|
||
+ size = data->cursor - cursor;
|
||
+ /* no header for OTP */
|
||
+ if (stm32prog_otp_write(data, cursor,
|
||
+ data->buffer, &size))
|
||
+ result = ABORT_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ if (operation == PHASE_PMIC) {
|
||
+ size = data->cursor - cursor;
|
||
+ /* no header for PMIC */
|
||
+ if (stm32prog_pmic_write(data, cursor,
|
||
+ data->buffer, &size))
|
||
+ result = ABORT_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ if (cursor < BL_HEADER_SIZE) {
|
||
+ /* size = portion of header in this chunck */
|
||
+ if (data->cursor >= BL_HEADER_SIZE)
|
||
+ size = BL_HEADER_SIZE - cursor;
|
||
+ else
|
||
+ size = data->cursor - cursor;
|
||
+ memcpy((void *)((u32)(data->header_data) + cursor),
|
||
+ data->buffer, size);
|
||
+ cursor += size;
|
||
+
|
||
+ if (cursor == BL_HEADER_SIZE) {
|
||
+ /* Check and Write the header */
|
||
+ if (stm32prog_header(data)) {
|
||
+ result = ABORT_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+ } else {
|
||
+ goto end;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (image_header->present) {
|
||
+ if (data->cursor <= BL_HEADER_SIZE)
|
||
+ goto end;
|
||
+ /* compute checksum on payload */
|
||
+ for (i = (unsigned long)size; i < codesize; i++)
|
||
+ data->checksum += data->buffer[i];
|
||
+
|
||
+ if (data->cursor >
|
||
+ image_header->image_length + BL_HEADER_SIZE) {
|
||
+ pr_err("expected size exceeded\n");
|
||
+ result = ABORT_BYTE;
|
||
+ goto end;
|
||
+ }
|
||
+
|
||
+ /* write data (payload) */
|
||
+ ret = stm32prog_write(data,
|
||
+ &data->buffer[size],
|
||
+ codesize - size);
|
||
+ } else {
|
||
+ /* write all */
|
||
+ ret = stm32prog_write(data,
|
||
+ data->buffer,
|
||
+ codesize);
|
||
+ }
|
||
+ if (ret)
|
||
+ result = ABORT_BYTE;
|
||
+
|
||
+end:
|
||
+ stm32prog_serial_result(result);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * read_partition() - Respond to read command
|
||
+ *
|
||
+ * Read data from not volatile memory, Flash
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ */
|
||
+static void read_partition_command(struct stm32prog_data *data)
|
||
+{
|
||
+ u32 i, part_id, codesize, offset = 0, rcv_data;
|
||
+ long size;
|
||
+ u8 tmp_xor;
|
||
+ int res;
|
||
+ u8 buffer[256];
|
||
+
|
||
+ part_id = stm32prog_serial_getc();
|
||
+ tmp_xor = part_id;
|
||
+
|
||
+ offset = get_address(&tmp_xor);
|
||
+
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+ if (rcv_data != tmp_xor) {
|
||
+ pr_debug("1st checksum received = %x, computed %x\n",
|
||
+ rcv_data, tmp_xor);
|
||
+ goto error;
|
||
+ }
|
||
+ stm32prog_serial_putc(ACK_BYTE);
|
||
+
|
||
+ /* NbrOfData to read = data + 1 */
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+ codesize = rcv_data + 0x01;
|
||
+ tmp_xor = rcv_data;
|
||
+
|
||
+ rcv_data = stm32prog_serial_getc();
|
||
+ if ((rcv_data ^ tmp_xor) != 0xFF) {
|
||
+ pr_debug("2nd checksum received = %x, computed %x\n",
|
||
+ rcv_data, tmp_xor);
|
||
+ goto error;
|
||
+ }
|
||
+
|
||
+ pr_debug("%s : %x\n", __func__, part_id);
|
||
+ rcv_data = 0;
|
||
+ switch (part_id) {
|
||
+ case PHASE_OTP:
|
||
+ size = codesize;
|
||
+ if (!stm32prog_otp_read(data, offset, buffer, &size))
|
||
+ rcv_data = size;
|
||
+ break;
|
||
+ case PHASE_PMIC:
|
||
+ size = codesize;
|
||
+ if (!stm32prog_pmic_read(data, offset, buffer, &size))
|
||
+ rcv_data = size;
|
||
+ break;
|
||
+ default:
|
||
+ res = stm32prog_read(data, part_id, offset,
|
||
+ buffer, codesize);
|
||
+ if (res > 0)
|
||
+ rcv_data = res;
|
||
+ break;
|
||
+ }
|
||
+ if (rcv_data > 0) {
|
||
+ stm32prog_serial_putc(ACK_BYTE);
|
||
+ /*----------- Send data to the host -----------*/
|
||
+ for (i = 0; i < rcv_data; i++)
|
||
+ stm32prog_serial_putc(buffer[i]);
|
||
+ /*----------- Send filler to the host -----------*/
|
||
+ for (; i < codesize; i++)
|
||
+ stm32prog_serial_putc(0x0);
|
||
+ return;
|
||
+ }
|
||
+ stm32prog_serial_result(ABORT_BYTE);
|
||
+ return;
|
||
+
|
||
+error:
|
||
+ stm32prog_serial_result(NACK_BYTE);
|
||
+}
|
||
+
|
||
+/* MAIN function = SERIAL LOOP ***********************************************/
|
||
+
|
||
+/**
|
||
+ * stm32prog_serial_loop() - USART bootloader Loop routine
|
||
+ *
|
||
+ * @data: Current command context
|
||
+ * @return true if reset is needed after loop
|
||
+ */
|
||
+bool stm32prog_serial_loop(struct stm32prog_data *data)
|
||
+{
|
||
+ u32 counter = 0x0;
|
||
+ u8 command = 0x0;
|
||
+ u8 found;
|
||
+ int phase = data->phase;
|
||
+
|
||
+ /* element of cmd_func need to aligned with cmd_id[]*/
|
||
+ void (*cmd_func[NB_CMD])(struct stm32prog_data *) = {
|
||
+ /* GET_CMD_COMMAND */ get_cmd_command,
|
||
+ /* GET_VER_COMMAND */ get_version_command,
|
||
+ /* GET_ID_COMMAND */ get_id_command,
|
||
+ /* GET_PHASE_COMMAND */ get_phase_command,
|
||
+ /* RM_COMMAND */ read_memory_command,
|
||
+ /* READ_PART_COMMAND */ read_partition_command,
|
||
+ /* START_COMMAND */ start_command,
|
||
+ /* DOWNLOAD_COMMAND */ download_command
|
||
+ };
|
||
+
|
||
+ /* flush and NACK pending command received during u-boot init
|
||
+ * request command reemit
|
||
+ */
|
||
+ stm32prog_serial_result(NACK_BYTE);
|
||
+
|
||
+ clear_ctrlc(); /* forget any previous Control C */
|
||
+ while (!had_ctrlc()) {
|
||
+ phase = data->phase;
|
||
+
|
||
+ if (phase == PHASE_DO_RESET)
|
||
+ return true;
|
||
+
|
||
+ /* Get the user command: read first byte */
|
||
+ command = stm32prog_serial_getc();
|
||
+
|
||
+ if (command == INIT_BYTE) {
|
||
+ puts("\nConnected\n");
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ found = 0;
|
||
+ for (counter = 0; counter < NB_CMD; counter++)
|
||
+ if (cmd_id[counter] == command) {
|
||
+ found = 1;
|
||
+ break;
|
||
+ }
|
||
+ if (found)
|
||
+ if ((command ^ stm32prog_serial_getc()) != 0xFF)
|
||
+ found = 0;
|
||
+ if (!found) {
|
||
+ /* wait to be sure that all data are received
|
||
+ * in the FIFO before flush (CMD and XOR)
|
||
+ */
|
||
+ mdelay(3);
|
||
+ stm32prog_serial_result(NACK_BYTE);
|
||
+ } else {
|
||
+ stm32prog_serial_result(ACK_BYTE);
|
||
+ cmd_func[counter](data);
|
||
+ }
|
||
+ WATCHDOG_RESET();
|
||
+ }
|
||
+
|
||
+ /* clean device */
|
||
+ if (gd->cur_serial_dev == down_serial_dev) {
|
||
+ /* restore console on uart */
|
||
+ gd->flags &= ~(GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT);
|
||
+ }
|
||
+ down_serial_dev = NULL;
|
||
+
|
||
+ return false; /* no reset after ctrlc */
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
|
||
new file mode 100644
|
||
index 0000000..30547f9
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
|
||
@@ -0,0 +1,232 @@
|
||
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#include <common.h>
|
||
+#include <dfu.h>
|
||
+#include <g_dnl.h>
|
||
+#include <usb.h>
|
||
+#include <asm/arch/stm32prog.h>
|
||
+#include <asm/arch/sys_proto.h>
|
||
+#include "stm32prog.h"
|
||
+
|
||
+static int stm32prog_set_phase(struct stm32prog_data *data, u8 phase,
|
||
+ u32 offset)
|
||
+{
|
||
+ struct stm32prog_part_t *part;
|
||
+ int i;
|
||
+
|
||
+ if (phase == data->phase) {
|
||
+ data->offset = offset;
|
||
+ data->dfu_seq = 0;
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
+ /* found partition for phase */
|
||
+ for (i = 0; i < data->part_nb; i++) {
|
||
+ part = &data->part_array[i];
|
||
+ if (part->id == phase) {
|
||
+ data->cur_part = part;
|
||
+ data->phase = phase;
|
||
+ data->offset = offset;
|
||
+ data->dfu_seq = 0;
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return -EINVAL;
|
||
+}
|
||
+
|
||
+static int stm32prog_cmd_write(u64 offset, void *buf, long *len)
|
||
+{
|
||
+ u8 phase;
|
||
+ u32 address;
|
||
+ u8 *pt = buf;
|
||
+ void (*entry)(void);
|
||
+ int ret;
|
||
+
|
||
+ if (*len < 5) {
|
||
+ pr_err("size not allowed\n");
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ if (offset) {
|
||
+ pr_err("invalid offset\n");
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ phase = pt[0];
|
||
+ address = (pt[1] << 24) | (pt[2] << 16) | (pt[3] << 8) | pt[4];
|
||
+ if (phase == PHASE_RESET) {
|
||
+ entry = (void *)address;
|
||
+ printf("## Starting application at 0x%x ...\n", address);
|
||
+ (*entry)();
|
||
+ printf("## Application terminated\n");
|
||
+ return 0;
|
||
+ }
|
||
+ /* set phase and offset */
|
||
+ ret = stm32prog_set_phase(stm32prog_data, phase, address);
|
||
+ if (ret)
|
||
+ pr_err("failed: %d\n", ret);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+#define PHASE_MIN_SIZE 9
|
||
+static int stm32prog_cmd_read(u64 offset, void *buf, long *len)
|
||
+{
|
||
+ u32 destination = DEFAULT_ADDRESS; /* destination address */
|
||
+ u32 dfu_offset;
|
||
+ u8 *pt_buf = buf;
|
||
+ int phase;
|
||
+ char *err_msg;
|
||
+ int length;
|
||
+
|
||
+ if (*len < PHASE_MIN_SIZE) {
|
||
+ pr_err("request exceeds allowed area\n");
|
||
+ return -EINVAL;
|
||
+ }
|
||
+ if (offset) {
|
||
+ *len = 0; /* EOF for second request */
|
||
+ return 0;
|
||
+ }
|
||
+ phase = stm32prog_data->phase;
|
||
+ if (phase == PHASE_FLASHLAYOUT)
|
||
+ destination = STM32_DDR_BASE;
|
||
+ dfu_offset = stm32prog_data->offset;
|
||
+
|
||
+ /* mandatory header, size = PHASE_MIN_SIZE */
|
||
+ *pt_buf++ = (u8)(phase & 0xFF);
|
||
+ *pt_buf++ = (u8)(destination);
|
||
+ *pt_buf++ = (u8)(destination >> 8);
|
||
+ *pt_buf++ = (u8)(destination >> 16);
|
||
+ *pt_buf++ = (u8)(destination >> 24);
|
||
+ *pt_buf++ = (u8)(dfu_offset);
|
||
+ *pt_buf++ = (u8)(dfu_offset >> 8);
|
||
+ *pt_buf++ = (u8)(dfu_offset >> 16);
|
||
+ *pt_buf++ = (u8)(dfu_offset >> 24);
|
||
+
|
||
+ if (phase == PHASE_RESET || phase == PHASE_DO_RESET) {
|
||
+ err_msg = stm32prog_get_error(stm32prog_data);
|
||
+ length = strlen(err_msg);
|
||
+ if (length + PHASE_MIN_SIZE > *len)
|
||
+ length = *len - PHASE_MIN_SIZE;
|
||
+
|
||
+ memcpy(pt_buf, err_msg, length);
|
||
+ *len = PHASE_MIN_SIZE + length;
|
||
+ stm32prog_do_reset(stm32prog_data);
|
||
+ } else if (phase == PHASE_FLASHLAYOUT) {
|
||
+ *pt_buf++ = stm32prog_data->part_nb ? 1 : 0;
|
||
+ *len = PHASE_MIN_SIZE + 1;
|
||
+ } else {
|
||
+ *len = PHASE_MIN_SIZE;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+int stm32prog_write_medium_virt(struct dfu_entity *dfu, u64 offset,
|
||
+ void *buf, long *len)
|
||
+{
|
||
+ if (dfu->dev_type != DFU_DEV_VIRT)
|
||
+ return -EINVAL;
|
||
+
|
||
+ switch (dfu->data.virt.dev_num) {
|
||
+ case PHASE_CMD:
|
||
+ return stm32prog_cmd_write(offset, buf, len);
|
||
+
|
||
+ case PHASE_OTP:
|
||
+ return stm32prog_otp_write(stm32prog_data, (u32)offset,
|
||
+ buf, len);
|
||
+
|
||
+ case PHASE_PMIC:
|
||
+ return stm32prog_pmic_write(stm32prog_data, (u32)offset,
|
||
+ buf, len);
|
||
+ }
|
||
+ *len = 0;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+int stm32prog_read_medium_virt(struct dfu_entity *dfu, u64 offset,
|
||
+ void *buf, long *len)
|
||
+{
|
||
+ if (dfu->dev_type != DFU_DEV_VIRT)
|
||
+ return -EINVAL;
|
||
+
|
||
+ switch (dfu->data.virt.dev_num) {
|
||
+ case PHASE_CMD:
|
||
+ return stm32prog_cmd_read(offset, buf, len);
|
||
+
|
||
+ case PHASE_OTP:
|
||
+ return stm32prog_otp_read(stm32prog_data, (u32)offset,
|
||
+ buf, len);
|
||
+
|
||
+ case PHASE_PMIC:
|
||
+ return stm32prog_pmic_read(stm32prog_data, (u32)offset,
|
||
+ buf, len);
|
||
+ }
|
||
+ *len = 0;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+int stm32prog_get_medium_size_virt(struct dfu_entity *dfu, u64 *size)
|
||
+{
|
||
+ if (dfu->dev_type != DFU_DEV_VIRT) {
|
||
+ *size = 0;
|
||
+ pr_debug("%s, invalid dev_type = %d\n",
|
||
+ __func__, dfu->dev_type);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ switch (dfu->data.virt.dev_num) {
|
||
+ case PHASE_CMD:
|
||
+ *size = 512;
|
||
+ break;
|
||
+ case PHASE_OTP:
|
||
+ *size = OTP_SIZE;
|
||
+ break;
|
||
+ case PHASE_PMIC:
|
||
+ *size = PMIC_SIZE;
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+bool stm32prog_usb_loop(struct stm32prog_data *data, int dev)
|
||
+{
|
||
+ int ret;
|
||
+ bool result;
|
||
+ /* USB download gadget for STM32 Programmer */
|
||
+ char product[128];
|
||
+ char name[SOC_NAME_SIZE];
|
||
+
|
||
+ get_soc_name(name);
|
||
+ snprintf(product, sizeof(product),
|
||
+ "USB download gadget@Device ID /0x%03X, @Revision ID /0x%04X, @Name /%s,",
|
||
+ get_cpu_dev(), get_cpu_rev(), name);
|
||
+ g_dnl_set_product(product);
|
||
+
|
||
+ if (stm32prog_data->phase == PHASE_FLASHLAYOUT) {
|
||
+ ret = run_usb_dnl_gadget(dev, "usb_dnl_dfu");
|
||
+ if (ret || stm32prog_data->phase == PHASE_DO_RESET)
|
||
+ return ret;
|
||
+ /* prepare the second enumeration with the FlashLayout */
|
||
+ if (stm32prog_data->phase == PHASE_FLASHLAYOUT)
|
||
+ stm32prog_dfu_init(data);
|
||
+ /* found next selected partition */
|
||
+ stm32prog_next_phase(data);
|
||
+ }
|
||
+
|
||
+ ret = run_usb_dnl_gadget(dev, "usb_dnl_dfu");
|
||
+
|
||
+ result = !!(ret) || (stm32prog_data->phase == PHASE_DO_RESET);
|
||
+
|
||
+ g_dnl_set_product(NULL);
|
||
+
|
||
+ return result;
|
||
+}
|
||
+
|
||
+int g_dnl_get_board_bcd_device_number(int gcnum)
|
||
+{
|
||
+ pr_debug("%s\n", __func__);
|
||
+ return 0x200;
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/cpu.c b/arch/arm/mach-stm32mp/cpu.c
|
||
index ed7d9f6..305534f 100644
|
||
--- a/arch/arm/mach-stm32mp/cpu.c
|
||
+++ b/arch/arm/mach-stm32mp/cpu.c
|
||
@@ -9,6 +9,7 @@
|
||
#include <env.h>
|
||
#include <misc.h>
|
||
#include <asm/io.h>
|
||
+#include <asm/arch/bsec.h>
|
||
#include <asm/arch/stm32.h>
|
||
#include <asm/arch/sys_proto.h>
|
||
#include <dm/device.h>
|
||
@@ -35,7 +36,9 @@
|
||
#define TAMP_CR1 (STM32_TAMP_BASE + 0x00)
|
||
|
||
#define PWR_CR1 (STM32_PWR_BASE + 0x00)
|
||
+#define PWR_MCUCR (STM32_PWR_BASE + 0x14)
|
||
#define PWR_CR1_DBP BIT(8)
|
||
+#define PWR_MCUCR_SBF BIT(6)
|
||
|
||
/* DBGMCU register */
|
||
#define DBGMCU_IDC (STM32_DBGMCU_BASE + 0x00)
|
||
@@ -59,12 +62,6 @@
|
||
#define BOOTROM_INSTANCE_MASK GENMASK(31, 16)
|
||
#define BOOTROM_INSTANCE_SHIFT 16
|
||
|
||
-/* BSEC OTP index */
|
||
-#define BSEC_OTP_RPN 1
|
||
-#define BSEC_OTP_SERIAL 13
|
||
-#define BSEC_OTP_PKG 16
|
||
-#define BSEC_OTP_MAC 57
|
||
-
|
||
/* Device Part Number (RPN) = OTP_DATA1 lower 8 bits */
|
||
#define RPN_SHIFT 0
|
||
#define RPN_MASK GENMASK(7, 0)
|
||
@@ -149,8 +146,13 @@ static void dbgmcu_init(void)
|
||
{
|
||
setbits_le32(RCC_DBGCFGR, RCC_DBGCFGR_DBGCKEN);
|
||
|
||
- /* Freeze IWDG2 if Cortex-A7 is in debug mode */
|
||
- setbits_le32(DBGMCU_APB4FZ1, DBGMCU_APB4FZ1_IWDG2);
|
||
+ /*
|
||
+ * Freeze IWDG2 if Cortex-A7 is in debug mode
|
||
+ * done in TF-A for TRUSTED boot and
|
||
+ * DBGMCU access is controlled by BSEC_DENABLE.DBGSWENABLE
|
||
+ */
|
||
+ if (!CONFIG_IS_ENABLED(STM32MP1_TRUSTED) && bsec_dbgswenable())
|
||
+ setbits_le32(DBGMCU_APB4FZ1, DBGMCU_APB4FZ1_IWDG2);
|
||
}
|
||
#endif /* !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD) */
|
||
|
||
@@ -206,6 +208,11 @@ int arch_cpu_init(void)
|
||
security_init();
|
||
update_bootmode();
|
||
#endif
|
||
+ /* Reset Coprocessor state unless it wakes up from Standby power mode */
|
||
+ if (!(readl(PWR_MCUCR) & PWR_MCUCR_SBF)) {
|
||
+ writel(TAMP_COPRO_STATE_OFF, TAMP_COPRO_STATE);
|
||
+ writel(0, TAMP_COPRO_RSC_TBL_ADDRESS);
|
||
+ }
|
||
#endif
|
||
|
||
boot_mode = get_bootmode();
|
||
@@ -230,9 +237,22 @@ void enable_caches(void)
|
||
|
||
static u32 read_idc(void)
|
||
{
|
||
- setbits_le32(RCC_DBGCFGR, RCC_DBGCFGR_DBGCKEN);
|
||
+ /* DBGMCU access is controlled by BSEC_DENABLE.DBGSWENABLE */
|
||
+ if (bsec_dbgswenable()) {
|
||
+ setbits_le32(RCC_DBGCFGR, RCC_DBGCFGR_DBGCKEN);
|
||
+
|
||
+ return readl(DBGMCU_IDC);
|
||
+ }
|
||
|
||
- return readl(DBGMCU_IDC);
|
||
+ if (CONFIG_IS_ENABLED(STM32MP15x))
|
||
+ return CPU_DEV_STM32MP15; /* STM32MP15x and unknown revision */
|
||
+ else
|
||
+ return 0x0;
|
||
+}
|
||
+
|
||
+u32 get_cpu_dev(void)
|
||
+{
|
||
+ return (read_idc() & DBGMCU_IDC_DEV_ID_MASK) >> DBGMCU_IDC_DEV_ID_SHIFT;
|
||
}
|
||
|
||
u32 get_cpu_rev(void)
|
||
@@ -265,11 +285,7 @@ static u32 get_cpu_rpn(void)
|
||
|
||
u32 get_cpu_type(void)
|
||
{
|
||
- u32 id;
|
||
-
|
||
- id = (read_idc() & DBGMCU_IDC_DEV_ID_MASK) >> DBGMCU_IDC_DEV_ID_SHIFT;
|
||
-
|
||
- return (id << 16) | get_cpu_rpn();
|
||
+ return (get_cpu_dev() << 16) | get_cpu_rpn();
|
||
}
|
||
|
||
/* Get Package options from OTP */
|
||
@@ -278,25 +294,42 @@ u32 get_cpu_package(void)
|
||
return get_otp(BSEC_OTP_PKG, PKG_SHIFT, PKG_MASK);
|
||
}
|
||
|
||
-#if defined(CONFIG_DISPLAY_CPUINFO)
|
||
-int print_cpuinfo(void)
|
||
+void get_soc_name(char name[SOC_NAME_SIZE])
|
||
{
|
||
char *cpu_s, *cpu_r, *pkg;
|
||
|
||
/* MPUs Part Numbers */
|
||
switch (get_cpu_type()) {
|
||
+ case CPU_STM32MP157Fxx:
|
||
+ cpu_s = "157F";
|
||
+ break;
|
||
+ case CPU_STM32MP157Dxx:
|
||
+ cpu_s = "157D";
|
||
+ break;
|
||
case CPU_STM32MP157Cxx:
|
||
cpu_s = "157C";
|
||
break;
|
||
case CPU_STM32MP157Axx:
|
||
cpu_s = "157A";
|
||
break;
|
||
+ case CPU_STM32MP153Fxx:
|
||
+ cpu_s = "153F";
|
||
+ break;
|
||
+ case CPU_STM32MP153Dxx:
|
||
+ cpu_s = "153D";
|
||
+ break;
|
||
case CPU_STM32MP153Cxx:
|
||
cpu_s = "153C";
|
||
break;
|
||
case CPU_STM32MP153Axx:
|
||
cpu_s = "153A";
|
||
break;
|
||
+ case CPU_STM32MP151Fxx:
|
||
+ cpu_s = "151F";
|
||
+ break;
|
||
+ case CPU_STM32MP151Dxx:
|
||
+ cpu_s = "151D";
|
||
+ break;
|
||
case CPU_STM32MP151Cxx:
|
||
cpu_s = "151C";
|
||
break;
|
||
@@ -335,12 +368,24 @@ int print_cpuinfo(void)
|
||
case CPU_REVB:
|
||
cpu_r = "B";
|
||
break;
|
||
+ case CPU_REVZ:
|
||
+ cpu_r = "Z";
|
||
+ break;
|
||
default:
|
||
cpu_r = "?";
|
||
break;
|
||
}
|
||
|
||
- printf("CPU: STM32MP%s%s Rev.%s\n", cpu_s, pkg, cpu_r);
|
||
+ snprintf(name, SOC_NAME_SIZE, "STM32MP%s%s Rev.%s", cpu_s, pkg, cpu_r);
|
||
+}
|
||
+
|
||
+#if defined(CONFIG_DISPLAY_CPUINFO)
|
||
+int print_cpuinfo(void)
|
||
+{
|
||
+ char name[SOC_NAME_SIZE];
|
||
+
|
||
+ get_soc_name(name);
|
||
+ printf("CPU: %s\n", name);
|
||
|
||
return 0;
|
||
}
|
||
@@ -405,6 +450,10 @@ static void setup_boot_mode(void)
|
||
env_set("boot_device", "nand");
|
||
env_set("boot_instance", "0");
|
||
break;
|
||
+ case BOOT_FLASH_SPINAND:
|
||
+ env_set("boot_device", "spi-nand");
|
||
+ env_set("boot_instance", "0");
|
||
+ break;
|
||
case BOOT_FLASH_NOR:
|
||
env_set("boot_device", "nor");
|
||
env_set("boot_instance", "0");
|
||
@@ -449,7 +498,7 @@ 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)
|
||
+__weak int setup_mac_address(void)
|
||
{
|
||
#if defined(CONFIG_NET)
|
||
int ret;
|
||
@@ -481,8 +530,9 @@ static int setup_mac_address(void)
|
||
return -EINVAL;
|
||
}
|
||
pr_debug("OTP MAC address = %pM\n", enetaddr);
|
||
- ret = !eth_env_set_enetaddr("ethaddr", enetaddr);
|
||
- if (!ret)
|
||
+
|
||
+ ret = eth_env_set_enetaddr("ethaddr", enetaddr);
|
||
+ if (ret)
|
||
pr_err("Failed to set mac address %pM from OTP: %d\n",
|
||
enetaddr, ret);
|
||
#endif
|
||
@@ -492,13 +542,13 @@ static int setup_mac_address(void)
|
||
|
||
static int setup_serial_number(void)
|
||
{
|
||
+ char *serial_env;
|
||
char serial_string[25];
|
||
u32 otp[3] = {0, 0, 0 };
|
||
struct udevice *dev;
|
||
int ret;
|
||
|
||
- if (env_get("serial#"))
|
||
- return 0;
|
||
+ serial_env = env_get("serial#");
|
||
|
||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||
DM_GET_DRIVER(stm32mp_bsec),
|
||
@@ -512,6 +562,15 @@ static int setup_serial_number(void)
|
||
return ret;
|
||
|
||
sprintf(serial_string, "%08X%08X%08X", otp[0], otp[1], otp[2]);
|
||
+
|
||
+ if (serial_env) {
|
||
+ if (!strcmp(serial_string, serial_env))
|
||
+ return 0;
|
||
+ /* For invalid enviromnent (serial# change), reset to default */
|
||
+ env_set_default("serial number mismatch", 0);
|
||
+ }
|
||
+
|
||
+ /* save serial number */
|
||
env_set("serial#", serial_string);
|
||
|
||
return 0;
|
||
@@ -519,9 +578,9 @@ static int setup_serial_number(void)
|
||
|
||
int arch_misc_init(void)
|
||
{
|
||
+ setup_serial_number();
|
||
setup_boot_mode();
|
||
setup_mac_address();
|
||
- setup_serial_number();
|
||
|
||
return 0;
|
||
}
|
||
diff --git a/arch/arm/mach-stm32mp/dram_init.c b/arch/arm/mach-stm32mp/dram_init.c
|
||
index 7688b3e..3233415 100644
|
||
--- a/arch/arm/mach-stm32mp/dram_init.c
|
||
+++ b/arch/arm/mach-stm32mp/dram_init.c
|
||
@@ -5,6 +5,7 @@
|
||
|
||
#include <common.h>
|
||
#include <dm.h>
|
||
+#include <lmb.h>
|
||
#include <ram.h>
|
||
|
||
DECLARE_GLOBAL_DATA_PTR;
|
||
@@ -31,3 +32,20 @@ int dram_init(void)
|
||
|
||
return 0;
|
||
}
|
||
+
|
||
+ulong board_get_usable_ram_top(ulong total_size)
|
||
+{
|
||
+ phys_addr_t reg;
|
||
+ struct lmb lmb;
|
||
+
|
||
+ /* found enough not-reserved memory to relocated U-Boot */
|
||
+ lmb_init(&lmb);
|
||
+ lmb_add(&lmb, gd->ram_base, gd->ram_size);
|
||
+ boot_fdt_add_mem_rsv_regions(&lmb, (void *)gd->fdt_blob);
|
||
+ reg = lmb_alloc(&lmb, CONFIG_SYS_MALLOC_LEN + total_size, SZ_4K);
|
||
+
|
||
+ if (reg)
|
||
+ return ALIGN(reg + CONFIG_SYS_MALLOC_LEN + total_size, SZ_4K);
|
||
+
|
||
+ return gd->ram_top;
|
||
+}
|
||
diff --git a/arch/arm/mach-stm32mp/fdt.c b/arch/arm/mach-stm32mp/fdt.c
|
||
index 82c430b..21b5f09 100644
|
||
--- a/arch/arm/mach-stm32mp/fdt.c
|
||
+++ b/arch/arm/mach-stm32mp/fdt.c
|
||
@@ -5,6 +5,7 @@
|
||
|
||
#include <common.h>
|
||
#include <fdt_support.h>
|
||
+#include <tee.h>
|
||
#include <asm/arch/sys_proto.h>
|
||
#include <dt-bindings/pinctrl/stm32-pinfunc.h>
|
||
#include <linux/io.h>
|
||
@@ -23,6 +24,12 @@
|
||
|
||
#define ETZPC_RESERVED 0xffffffff
|
||
|
||
+#define STM32_FDCAN_BASE 0x4400e000
|
||
+#define STM32_CRYP2_BASE 0x4c005000
|
||
+#define STM32_CRYP1_BASE 0x54001000
|
||
+#define STM32_GPU_BASE 0x59000000
|
||
+#define STM32_DSI_BASE 0x5a000000
|
||
+
|
||
static const u32 stm32mp1_ip_addr[] = {
|
||
0x5c008000, /* 00 stgenc */
|
||
0x54000000, /* 01 bkpsram */
|
||
@@ -33,7 +40,7 @@ static const u32 stm32mp1_ip_addr[] = {
|
||
ETZPC_RESERVED, /* 06 reserved */
|
||
0x54003000, /* 07 rng1 */
|
||
0x54002000, /* 08 hash1 */
|
||
- 0x54001000, /* 09 cryp1 */
|
||
+ STM32_CRYP1_BASE, /* 09 cryp1 */
|
||
0x5a003000, /* 0A ddrctrl */
|
||
0x5a004000, /* 0B ddrphyc */
|
||
0x5c009000, /* 0C i2c6 */
|
||
@@ -86,7 +93,7 @@ static const u32 stm32mp1_ip_addr[] = {
|
||
0x4400b000, /* 3B sai2 */
|
||
0x4400c000, /* 3C sai3 */
|
||
0x4400d000, /* 3D dfsdm */
|
||
- 0x4400e000, /* 3E tt_fdcan */
|
||
+ STM32_FDCAN_BASE, /* 3E tt_fdcan */
|
||
ETZPC_RESERVED, /* 3F reserved */
|
||
0x50021000, /* 40 lptim2 */
|
||
0x50022000, /* 41 lptim3 */
|
||
@@ -99,7 +106,7 @@ static const u32 stm32mp1_ip_addr[] = {
|
||
0x48003000, /* 48 adc */
|
||
0x4c002000, /* 49 hash2 */
|
||
0x4c003000, /* 4A rng2 */
|
||
- 0x4c005000, /* 4B cryp2 */
|
||
+ STM32_CRYP2_BASE, /* 4B cryp2 */
|
||
ETZPC_RESERVED, /* 4C reserved */
|
||
ETZPC_RESERVED, /* 4D reserved */
|
||
ETZPC_RESERVED, /* 4E reserved */
|
||
@@ -126,11 +133,13 @@ static const u32 stm32mp1_ip_addr[] = {
|
||
static bool fdt_disable_subnode_by_address(void *fdt, int offset, u32 addr)
|
||
{
|
||
int node;
|
||
+ fdt_addr_t regs;
|
||
|
||
for (node = fdt_first_subnode(fdt, offset);
|
||
node >= 0;
|
||
node = fdt_next_subnode(fdt, node)) {
|
||
- if (addr == (u32)fdt_getprop(fdt, node, "reg", 0)) {
|
||
+ regs = fdtdec_get_addr(fdt, node, "reg");
|
||
+ if (addr == regs) {
|
||
if (fdtdec_get_is_enabled(fdt, node)) {
|
||
fdt_status_disabled(fdt, node);
|
||
|
||
@@ -143,11 +152,11 @@ static bool fdt_disable_subnode_by_address(void *fdt, int offset, u32 addr)
|
||
return false;
|
||
}
|
||
|
||
-static int stm32_fdt_fixup_etzpc(void *fdt)
|
||
+static int stm32_fdt_fixup_etzpc(void *fdt, int soc_node)
|
||
{
|
||
const u32 *array;
|
||
int array_size, i;
|
||
- int soc_node, offset, shift;
|
||
+ int offset, shift;
|
||
u32 addr, status, decprot[ETZPC_DECPROT_NB];
|
||
|
||
array = stm32mp1_ip_addr;
|
||
@@ -156,10 +165,6 @@ static int stm32_fdt_fixup_etzpc(void *fdt)
|
||
for (i = 0; i < ETZPC_DECPROT_NB; i++)
|
||
decprot[i] = readl(ETZPC_DECPROT(i));
|
||
|
||
- soc_node = fdt_path_offset(fdt, "/soc");
|
||
- if (soc_node < 0)
|
||
- return soc_node;
|
||
-
|
||
for (i = 0; i < array_size; i++) {
|
||
offset = i / NB_PROT_PER_REG;
|
||
shift = (i % NB_PROT_PER_REG) * DECPROT_NB_BITS;
|
||
@@ -180,6 +185,60 @@ static int stm32_fdt_fixup_etzpc(void *fdt)
|
||
return 0;
|
||
}
|
||
|
||
+/* deactivate all the cpu except core 0 */
|
||
+static void stm32_fdt_fixup_cpu(void *blob, char *name)
|
||
+{
|
||
+ int off;
|
||
+ u32 reg;
|
||
+
|
||
+ off = fdt_path_offset(blob, "/cpus");
|
||
+ if (off < 0) {
|
||
+ printf("%s: couldn't find /cpus node\n", __func__);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4);
|
||
+ while (off != -FDT_ERR_NOTFOUND) {
|
||
+ reg = fdtdec_get_addr(blob, off, "reg");
|
||
+ if (reg != 0) {
|
||
+ fdt_del_node(blob, off);
|
||
+ printf("FDT: cpu %d node remove for %s\n", reg, name);
|
||
+ /* after delete we can't trust the offsets anymore */
|
||
+ off = -1;
|
||
+ }
|
||
+ off = fdt_node_offset_by_prop_value(blob, off,
|
||
+ "device_type", "cpu", 4);
|
||
+ }
|
||
+}
|
||
+
|
||
+static void stm32_fdt_disable(void *fdt, int offset, u32 addr,
|
||
+ const char *string, const char *name)
|
||
+{
|
||
+ if (fdt_disable_subnode_by_address(fdt, offset, addr))
|
||
+ printf("FDT: %s@%08x node disabled for %s\n",
|
||
+ string, addr, name);
|
||
+}
|
||
+
|
||
+static void stm32_fdt_disable_optee(void *blob)
|
||
+{
|
||
+ int off, node;
|
||
+
|
||
+ off = fdt_node_offset_by_compatible(blob, -1, "linaro,optee-tz");
|
||
+ if (off >= 0 && fdtdec_get_is_enabled(blob, off))
|
||
+ fdt_status_disabled(blob, off);
|
||
+
|
||
+ /* Disabled "optee@..." reserved-memory node */
|
||
+ off = fdt_path_offset(blob, "/reserved-memory/");
|
||
+ if (off < 0)
|
||
+ return;
|
||
+ for (node = fdt_first_subnode(blob, off);
|
||
+ node >= 0;
|
||
+ node = fdt_next_subnode(blob, node)) {
|
||
+ if (!strncmp(fdt_get_name(blob, node, NULL), "optee@", 6))
|
||
+ fdt_status_disabled(blob, node);
|
||
+ }
|
||
+}
|
||
+
|
||
/*
|
||
* This function is called right before the kernel is booted. "blob" is the
|
||
* device tree that will be passed to the kernel.
|
||
@@ -187,14 +246,59 @@ static int stm32_fdt_fixup_etzpc(void *fdt)
|
||
int ft_system_setup(void *blob, bd_t *bd)
|
||
{
|
||
int ret = 0;
|
||
- u32 pkg;
|
||
+ int soc;
|
||
+ u32 pkg, cpu;
|
||
+ char name[SOC_NAME_SIZE];
|
||
+
|
||
+ soc = fdt_path_offset(blob, "/soc");
|
||
+ if (soc < 0)
|
||
+ return soc;
|
||
|
||
if (CONFIG_IS_ENABLED(STM32_ETZPC)) {
|
||
- ret = stm32_fdt_fixup_etzpc(blob);
|
||
+ ret = stm32_fdt_fixup_etzpc(blob, soc);
|
||
if (ret)
|
||
return ret;
|
||
}
|
||
|
||
+ /* MPUs Part Numbers and name*/
|
||
+ cpu = get_cpu_type();
|
||
+ get_soc_name(name);
|
||
+
|
||
+ switch (cpu) {
|
||
+ case CPU_STM32MP151Fxx:
|
||
+ case CPU_STM32MP151Dxx:
|
||
+ case CPU_STM32MP151Cxx:
|
||
+ case CPU_STM32MP151Axx:
|
||
+ stm32_fdt_fixup_cpu(blob, name);
|
||
+ /* after cpu delete we can't trust the soc offsets anymore */
|
||
+ soc = fdt_path_offset(blob, "/soc");
|
||
+ stm32_fdt_disable(blob, soc, STM32_FDCAN_BASE, "can", name);
|
||
+ /* fall through */
|
||
+ case CPU_STM32MP153Fxx:
|
||
+ case CPU_STM32MP153Dxx:
|
||
+ case CPU_STM32MP153Cxx:
|
||
+ case CPU_STM32MP153Axx:
|
||
+ stm32_fdt_disable(blob, soc, STM32_GPU_BASE, "gpu", name);
|
||
+ stm32_fdt_disable(blob, soc, STM32_DSI_BASE, "dsi", name);
|
||
+ break;
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ switch (cpu) {
|
||
+ case CPU_STM32MP157Dxx:
|
||
+ case CPU_STM32MP157Axx:
|
||
+ case CPU_STM32MP153Dxx:
|
||
+ case CPU_STM32MP153Axx:
|
||
+ case CPU_STM32MP151Dxx:
|
||
+ case CPU_STM32MP151Axx:
|
||
+ stm32_fdt_disable(blob, soc, STM32_CRYP1_BASE, "cryp", name);
|
||
+ stm32_fdt_disable(blob, soc, STM32_CRYP2_BASE, "cryp", name);
|
||
+ break;
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
switch (get_cpu_package()) {
|
||
case PKG_AA_LBGA448:
|
||
pkg = STM32MP_PKG_AA;
|
||
@@ -219,5 +323,9 @@ int ft_system_setup(void *blob, bd_t *bd)
|
||
"st,package", pkg, false);
|
||
}
|
||
|
||
+ if (!CONFIG_IS_ENABLED(OPTEE) ||
|
||
+ !tee_find_device(NULL, NULL, NULL, NULL))
|
||
+ stm32_fdt_disable_optee(blob);
|
||
+
|
||
return ret;
|
||
}
|
||
diff --git a/arch/arm/mach-stm32mp/include/mach/bsec.h b/arch/arm/mach-stm32mp/include/mach/bsec.h
|
||
new file mode 100644
|
||
index 0000000..252eac3
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/include/mach/bsec.h
|
||
@@ -0,0 +1,7 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+/* check self hosted debug status = BSEC_DENABLE.DBGSWENABLE */
|
||
+bool bsec_dbgswenable(void);
|
||
diff --git a/arch/arm/mach-stm32mp/include/mach/ddr.h b/arch/arm/mach-stm32mp/include/mach/ddr.h
|
||
index b8a17cf..bfc42a7 100644
|
||
--- a/arch/arm/mach-stm32mp/include/mach/ddr.h
|
||
+++ b/arch/arm/mach-stm32mp/include/mach/ddr.h
|
||
@@ -9,8 +9,10 @@
|
||
/* DDR power initializations */
|
||
enum ddr_type {
|
||
STM32MP_DDR3,
|
||
- STM32MP_LPDDR2,
|
||
- STM32MP_LPDDR3,
|
||
+ STM32MP_LPDDR2_16,
|
||
+ STM32MP_LPDDR2_32,
|
||
+ STM32MP_LPDDR3_16,
|
||
+ STM32MP_LPDDR3_32,
|
||
};
|
||
|
||
int board_ddr_power_init(enum ddr_type ddr_type);
|
||
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h
|
||
index b3e9ccc..8820bc4 100644
|
||
--- a/arch/arm/mach-stm32mp/include/mach/stm32.h
|
||
+++ b/arch/arm/mach-stm32mp/include/mach/stm32.h
|
||
@@ -37,7 +37,6 @@
|
||
/* enumerated used to identify the SYSCON driver instance */
|
||
enum {
|
||
STM32MP_SYSCON_UNKNOWN,
|
||
- STM32MP_SYSCON_PWR,
|
||
STM32MP_SYSCON_SYSCFG,
|
||
};
|
||
|
||
@@ -80,15 +79,29 @@ enum boot_device {
|
||
|
||
BOOT_SERIAL_USB = 0x60,
|
||
BOOT_SERIAL_USB_OTG = 0x62,
|
||
+
|
||
+ BOOT_FLASH_SPINAND = 0x70,
|
||
+ BOOT_FLASH_SPINAND_1 = 0x71,
|
||
};
|
||
|
||
/* TAMP registers */
|
||
#define TAMP_BACKUP_REGISTER(x) (STM32_TAMP_BASE + 0x100 + 4 * x)
|
||
+/* secure access */
|
||
#define TAMP_BACKUP_MAGIC_NUMBER TAMP_BACKUP_REGISTER(4)
|
||
#define TAMP_BACKUP_BRANCH_ADDRESS TAMP_BACKUP_REGISTER(5)
|
||
+/* non secure access */
|
||
+#define TAMP_COPRO_RSC_TBL_ADDRESS TAMP_BACKUP_REGISTER(17)
|
||
+#define TAMP_COPRO_STATE TAMP_BACKUP_REGISTER(18)
|
||
#define TAMP_BOOT_CONTEXT TAMP_BACKUP_REGISTER(20)
|
||
#define TAMP_BOOTCOUNT TAMP_BACKUP_REGISTER(21)
|
||
|
||
+#define TAMP_COPRO_STATE_OFF 0
|
||
+#define TAMP_COPRO_STATE_INIT 1
|
||
+#define TAMP_COPRO_STATE_CRUN 2
|
||
+#define TAMP_COPRO_STATE_CSTOP 3
|
||
+#define TAMP_COPRO_STATE_STANDBY 4
|
||
+#define TAMP_COPRO_STATE_CRASH 5
|
||
+
|
||
#define TAMP_BOOT_MODE_MASK GENMASK(15, 8)
|
||
#define TAMP_BOOT_MODE_SHIFT 8
|
||
#define TAMP_BOOT_DEVICE_MASK GENMASK(7, 4)
|
||
@@ -111,7 +124,14 @@ enum forced_boot_mode {
|
||
#define STM32_BSEC_SHADOW(id) (STM32_BSEC_SHADOW_OFFSET + (id) * 4)
|
||
#define STM32_BSEC_OTP_OFFSET 0x80000000
|
||
#define STM32_BSEC_OTP(id) (STM32_BSEC_OTP_OFFSET + (id) * 4)
|
||
-
|
||
+#define STM32_BSEC_LOCK_OFFSET 0xC0000000
|
||
+#define STM32_BSEC_LOCK(id) (STM32_BSEC_LOCK_OFFSET + (id) * 4)
|
||
+
|
||
+/* BSEC OTP index */
|
||
+#define BSEC_OTP_RPN 1
|
||
+#define BSEC_OTP_SERIAL 13
|
||
+#define BSEC_OTP_PKG 16
|
||
+#define BSEC_OTP_MAC 57
|
||
#define BSEC_OTP_BOARD 59
|
||
|
||
#endif /* __ASSEMBLY__*/
|
||
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32mp1_smc.h b/arch/arm/mach-stm32mp/include/mach/stm32mp1_smc.h
|
||
index 8130546..dea5b4a 100644
|
||
--- a/arch/arm/mach-stm32mp/include/mach/stm32mp1_smc.h
|
||
+++ b/arch/arm/mach-stm32mp/include/mach/stm32mp1_smc.h
|
||
@@ -18,8 +18,16 @@
|
||
#define STM32_SMC_VERSION 0x82000000
|
||
|
||
/* Secure Service access from Non-secure */
|
||
+#define STM32_SMC_RCC 0x82001000
|
||
+#define STM32_SMC_PWR 0x82001001
|
||
+#define STM32_SMC_RTC 0x82001002
|
||
#define STM32_SMC_BSEC 0x82001003
|
||
|
||
+/* Register access service use for RCC/RTC/PWR */
|
||
+#define STM32_SMC_REG_WRITE 0x1
|
||
+#define STM32_SMC_REG_SET 0x2
|
||
+#define STM32_SMC_REG_CLEAR 0x3
|
||
+
|
||
/* Service for BSEC */
|
||
#define STM32_SMC_READ_SHADOW 0x01
|
||
#define STM32_SMC_PROG_OTP 0x02
|
||
@@ -27,6 +35,7 @@
|
||
#define STM32_SMC_READ_OTP 0x04
|
||
#define STM32_SMC_READ_ALL 0x05
|
||
#define STM32_SMC_WRITE_ALL 0x06
|
||
+#define STM32_SMC_WRLOCK_OTP 0x07
|
||
|
||
/* SMC error codes */
|
||
#define STM32_SMC_OK 0x0
|
||
@@ -45,8 +54,8 @@ static inline u32 stm32_smc(u32 svc, u8 op, u32 data1, u32 data2, u32 *result)
|
||
arm_smccc_smc(svc, op, data1, data2, 0, 0, 0, 0, &res);
|
||
|
||
if (res.a0) {
|
||
- pr_err("%s: Failed to exec in secure mode (err = %ld)\n",
|
||
- __func__, res.a0);
|
||
+ pr_err("%s: Failed to exec svc=%x op=%x in secure mode (err = %ld)\n",
|
||
+ __func__, svc, op, res.a0);
|
||
return -EINVAL;
|
||
}
|
||
if (result)
|
||
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32prog.h b/arch/arm/mach-stm32mp/include/mach/stm32prog.h
|
||
new file mode 100644
|
||
index 0000000..c080b9c
|
||
--- /dev/null
|
||
+++ b/arch/arm/mach-stm32mp/include/mach/stm32prog.h
|
||
@@ -0,0 +1,16 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
|
||
+/*
|
||
+ * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
||
+ */
|
||
+
|
||
+#define STM32PROG_VIRT_FIRST_DEV_NUM 0xF1
|
||
+
|
||
+int stm32prog_write_medium_virt(struct dfu_entity *dfu, u64 offset,
|
||
+ void *buf, long *len);
|
||
+int stm32prog_read_medium_virt(struct dfu_entity *dfu, u64 offset,
|
||
+ void *buf, long *len);
|
||
+int stm32prog_get_medium_size_virt(struct dfu_entity *dfu, u64 *size);
|
||
+
|
||
+bool stm32prog_get_tee_partitions(void);
|
||
+
|
||
+bool stm32prog_get_fsbl_nor(void);
|
||
diff --git a/arch/arm/mach-stm32mp/include/mach/sys_proto.h b/arch/arm/mach-stm32mp/include/mach/sys_proto.h
|
||
index 99eefab..b6ad3c6 100644
|
||
--- a/arch/arm/mach-stm32mp/include/mach/sys_proto.h
|
||
+++ b/arch/arm/mach-stm32mp/include/mach/sys_proto.h
|
||
@@ -3,19 +3,31 @@
|
||
* Copyright (C) 2015-2017, STMicroelectronics - All Rights Reserved
|
||
*/
|
||
|
||
-/* ID = Device Version (bit31:16) + Device Part Number (RPN) (bit15:0)*/
|
||
+/* ID = Device Version (bit31:16) + Device Part Number (RPN) (bit7:0) */
|
||
#define CPU_STM32MP157Cxx 0x05000000
|
||
#define CPU_STM32MP157Axx 0x05000001
|
||
#define CPU_STM32MP153Cxx 0x05000024
|
||
#define CPU_STM32MP153Axx 0x05000025
|
||
#define CPU_STM32MP151Cxx 0x0500002E
|
||
#define CPU_STM32MP151Axx 0x0500002F
|
||
+#define CPU_STM32MP157Fxx 0x05000080
|
||
+#define CPU_STM32MP157Dxx 0x05000081
|
||
+#define CPU_STM32MP153Fxx 0x050000A4
|
||
+#define CPU_STM32MP153Dxx 0x050000A5
|
||
+#define CPU_STM32MP151Fxx 0x050000AE
|
||
+#define CPU_STM32MP151Dxx 0x050000AF
|
||
|
||
/* return CPU_STMP32MP...Xxx constants */
|
||
u32 get_cpu_type(void);
|
||
|
||
+#define CPU_DEV_STM32MP15 0x500
|
||
+
|
||
+/* return CPU_DEV constants */
|
||
+u32 get_cpu_dev(void);
|
||
+
|
||
#define CPU_REVA 0x1000
|
||
#define CPU_REVB 0x2000
|
||
+#define CPU_REVZ 0x2001
|
||
|
||
/* return CPU_REV constants */
|
||
u32 get_cpu_rev(void);
|
||
@@ -28,5 +40,15 @@ u32 get_cpu_package(void);
|
||
#define PKG_AC_TFBGA361 2
|
||
#define PKG_AD_TFBGA257 1
|
||
|
||
+/* Get SOC name */
|
||
+#define SOC_NAME_SIZE 20
|
||
+void get_soc_name(char name[SOC_NAME_SIZE]);
|
||
+
|
||
/* return boot mode */
|
||
u32 get_bootmode(void);
|
||
+
|
||
+int setup_mac_address(void);
|
||
+
|
||
+/* board power management : configure vddcore according OPP */
|
||
+void board_vddcore_init(u32 voltage_mv);
|
||
+int board_vddcore_set(void);
|
||
diff --git a/arch/arm/mach-stm32mp/psci.c b/arch/arm/mach-stm32mp/psci.c
|
||
index 1d91b2d..3fb038d 100644
|
||
--- a/arch/arm/mach-stm32mp/psci.c
|
||
+++ b/arch/arm/mach-stm32mp/psci.c
|
||
@@ -30,6 +30,22 @@ u8 psci_state[STM32MP1_PSCI_NR_CPUS] __secure_data = {
|
||
PSCI_AFFINITY_LEVEL_ON,
|
||
PSCI_AFFINITY_LEVEL_OFF};
|
||
|
||
+static u32 __secure_data cntfrq;
|
||
+
|
||
+static u32 __secure cp15_read_cntfrq(void)
|
||
+{
|
||
+ u32 frq;
|
||
+
|
||
+ asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (frq));
|
||
+
|
||
+ return frq;
|
||
+}
|
||
+
|
||
+static void __secure cp15_write_cntfrq(u32 frq)
|
||
+{
|
||
+ asm volatile ("mcr p15, 0, %0, c14, c0, 0" : : "r" (frq));
|
||
+}
|
||
+
|
||
static inline void psci_set_state(int cpu, u8 state)
|
||
{
|
||
psci_state[cpu] = state;
|
||
@@ -63,6 +79,9 @@ void __secure psci_arch_cpu_entry(void)
|
||
|
||
psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON);
|
||
|
||
+ /* write the saved cntfrq */
|
||
+ cp15_write_cntfrq(cntfrq);
|
||
+
|
||
/* reset magic in TAMP register */
|
||
writel(0xFFFFFFFF, TAMP_BACKUP_MAGIC_NUMBER);
|
||
}
|
||
@@ -130,6 +149,9 @@ s32 __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
|
||
if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON)
|
||
return ARM_PSCI_RET_ALREADY_ON;
|
||
|
||
+ /* read and save cntfrq of current cpu to write on target cpu */
|
||
+ cntfrq = cp15_read_cntfrq();
|
||
+
|
||
/* reset magic in TAMP register */
|
||
if (readl(TAMP_BACKUP_MAGIC_NUMBER))
|
||
writel(0xFFFFFFFF, TAMP_BACKUP_MAGIC_NUMBER);
|
||
diff --git a/arch/arm/mach-stm32mp/pwr_regulator.c b/arch/arm/mach-stm32mp/pwr_regulator.c
|
||
index 9484645..7219af5 100644
|
||
--- a/arch/arm/mach-stm32mp/pwr_regulator.c
|
||
+++ b/arch/arm/mach-stm32mp/pwr_regulator.c
|
||
@@ -6,8 +6,9 @@
|
||
#include <common.h>
|
||
#include <dm.h>
|
||
#include <errno.h>
|
||
-#include <regmap.h>
|
||
#include <syscon.h>
|
||
+#include <asm/io.h>
|
||
+#include <asm/arch/stm32mp1_smc.h>
|
||
#include <power/pmic.h>
|
||
#include <power/regulator.h>
|
||
|
||
@@ -26,19 +27,28 @@ struct stm32mp_pwr_reg_info {
|
||
};
|
||
|
||
struct stm32mp_pwr_priv {
|
||
- struct regmap *regmap;
|
||
+ fdt_addr_t base;
|
||
};
|
||
|
||
static int stm32mp_pwr_write(struct udevice *dev, uint reg,
|
||
const uint8_t *buff, int len)
|
||
{
|
||
+#ifndef CONFIG_STM32MP1_TRUSTED
|
||
struct stm32mp_pwr_priv *priv = dev_get_priv(dev);
|
||
+#endif
|
||
u32 val = *(u32 *)buff;
|
||
|
||
if (len != 4)
|
||
return -EINVAL;
|
||
|
||
- return regmap_write(priv->regmap, STM32MP_PWR_CR3, val);
|
||
+#ifdef CONFIG_STM32MP1_TRUSTED
|
||
+ return stm32_smc_exec(STM32_SMC_PWR, STM32_SMC_REG_WRITE,
|
||
+ STM32MP_PWR_CR3, val);
|
||
+#else /* CONFIG_STM32MP1_TRUSTED */
|
||
+ writel(val, priv->base + STM32MP_PWR_CR3);
|
||
+
|
||
+ return 0;
|
||
+#endif /* CONFIG_STM32MP1_TRUSTED */
|
||
}
|
||
|
||
static int stm32mp_pwr_read(struct udevice *dev, uint reg, uint8_t *buff,
|
||
@@ -49,21 +59,18 @@ static int stm32mp_pwr_read(struct udevice *dev, uint reg, uint8_t *buff,
|
||
if (len != 4)
|
||
return -EINVAL;
|
||
|
||
- return regmap_read(priv->regmap, STM32MP_PWR_CR3, (u32 *)buff);
|
||
+ *(u32 *)buff = readl(priv->base + STM32MP_PWR_CR3);
|
||
+
|
||
+ return 0;
|
||
}
|
||
|
||
static int stm32mp_pwr_ofdata_to_platdata(struct udevice *dev)
|
||
{
|
||
struct stm32mp_pwr_priv *priv = dev_get_priv(dev);
|
||
- struct regmap *regmap;
|
||
|
||
- regmap = syscon_get_regmap_by_driver_data(STM32MP_SYSCON_PWR);
|
||
- if (IS_ERR(regmap)) {
|
||
- pr_err("%s: unable to find regmap (%ld)\n", __func__,
|
||
- PTR_ERR(regmap));
|
||
- return PTR_ERR(regmap);
|
||
- }
|
||
- priv->regmap = regmap;
|
||
+ priv->base = dev_read_addr(dev);
|
||
+ if (priv->base == FDT_ADDR_T_NONE)
|
||
+ return -EINVAL;
|
||
|
||
return 0;
|
||
}
|
||
diff --git a/arch/arm/mach-stm32mp/spl.c b/arch/arm/mach-stm32mp/spl.c
|
||
index a3b0d6f..f4b4c3b 100644
|
||
--- a/arch/arm/mach-stm32mp/spl.c
|
||
+++ b/arch/arm/mach-stm32mp/spl.c
|
||
@@ -38,6 +38,8 @@ u32 spl_boot_device(void)
|
||
return BOOT_DEVICE_NAND;
|
||
case BOOT_FLASH_NOR_QSPI:
|
||
return BOOT_DEVICE_SPI;
|
||
+ case BOOT_FLASH_SPINAND_1:
|
||
+ return BOOT_DEVICE_NONE; /* SPINAND not supported in SPL */
|
||
}
|
||
|
||
return BOOT_DEVICE_MMC1;
|
||
@@ -75,40 +77,47 @@ void spl_display_print(void)
|
||
}
|
||
#endif
|
||
|
||
+__weak int board_vddcore_set(void)
|
||
+{
|
||
+ return 0;
|
||
+}
|
||
+
|
||
void board_init_f(ulong dummy)
|
||
{
|
||
struct udevice *dev;
|
||
- int ret;
|
||
+ int ret, clk, reset, pinctrl, power;
|
||
|
||
arch_cpu_init();
|
||
|
||
ret = spl_early_init();
|
||
if (ret) {
|
||
- debug("spl_early_init() failed: %d\n", ret);
|
||
+ debug("%s: spl_early_init() failed: %d\n", __func__, ret);
|
||
hang();
|
||
}
|
||
|
||
- ret = uclass_get_device(UCLASS_CLK, 0, &dev);
|
||
- if (ret) {
|
||
- debug("Clock init failed: %d\n", ret);
|
||
- return;
|
||
- }
|
||
+ clk = uclass_get_device(UCLASS_CLK, 0, &dev);
|
||
+ if (clk)
|
||
+ debug("%s: Clock init failed: %d\n", __func__, clk);
|
||
|
||
- ret = uclass_get_device(UCLASS_RESET, 0, &dev);
|
||
- if (ret) {
|
||
- debug("Reset init failed: %d\n", ret);
|
||
- return;
|
||
- }
|
||
+ reset = uclass_get_device(UCLASS_RESET, 0, &dev);
|
||
+ if (reset)
|
||
+ debug("%s: Reset init failed: %d\n", __func__, reset);
|
||
|
||
- ret = uclass_get_device(UCLASS_PINCTRL, 0, &dev);
|
||
- if (ret) {
|
||
- debug("%s: Cannot find pinctrl device\n", __func__);
|
||
- return;
|
||
- }
|
||
+ pinctrl = uclass_get_device(UCLASS_PINCTRL, 0, &dev);
|
||
+ if (pinctrl)
|
||
+ debug("%s: Cannot find pinctrl device: %d\n",
|
||
+ __func__, pinctrl);
|
||
|
||
/* enable console uart printing */
|
||
preloader_console_init();
|
||
|
||
+ /* change vddcore if needed after clock tree init */
|
||
+ power = board_vddcore_set();
|
||
+
|
||
+ if (clk || reset || pinctrl || power)
|
||
+ printf("%s: probe failed clk=%d reset=%d pinctrl=%d power=%d\n",
|
||
+ __func__, clk, reset, pinctrl, power);
|
||
+
|
||
ret = uclass_get_device(UCLASS_RAM, 0, &dev);
|
||
if (ret) {
|
||
printf("DRAM init failed: %d\n", ret);
|
||
diff --git a/arch/arm/mach-stm32mp/syscon.c b/arch/arm/mach-stm32mp/syscon.c
|
||
index 6070837..3e61ce4 100644
|
||
--- a/arch/arm/mach-stm32mp/syscon.c
|
||
+++ b/arch/arm/mach-stm32mp/syscon.c
|
||
@@ -9,7 +9,6 @@
|
||
#include <asm/arch/stm32.h>
|
||
|
||
static const struct udevice_id stm32mp_syscon_ids[] = {
|
||
- { .compatible = "st,stm32mp1-pwr", .data = STM32MP_SYSCON_PWR },
|
||
{ .compatible = "st,stm32mp157-syscfg",
|
||
.data = STM32MP_SYSCON_SYSCFG },
|
||
{ }
|
||
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
|
||
index fdb08f2..b69f0da 100644
|
||
--- a/arch/sandbox/dts/test.dts
|
||
+++ b/arch/sandbox/dts/test.dts
|
||
@@ -1,5 +1,8 @@
|
||
/dts-v1/;
|
||
|
||
+#include <dt-bindings/gpio/gpio.h>
|
||
+#include <dt-bindings/gpio/sandbox-gpio.h>
|
||
+
|
||
/ {
|
||
model = "sandbox";
|
||
compatible = "sandbox";
|
||
@@ -13,6 +16,7 @@
|
||
eth5 = ð_5;
|
||
gpio1 = &gpio_a;
|
||
gpio2 = &gpio_b;
|
||
+ gpio3 = &gpio_c;
|
||
i2c0 = "/i2c@0";
|
||
mmc0 = "/mmc0";
|
||
mmc1 = "/mmc1";
|
||
@@ -86,11 +90,21 @@
|
||
ping-expect = <0>;
|
||
ping-add = <0>;
|
||
u-boot,dm-pre-reloc;
|
||
- test-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 5 0 3 2 1>,
|
||
+ test-gpios = <&gpio_a 1>, <&gpio_a 4>,
|
||
+ <&gpio_b 5 GPIO_ACTIVE_HIGH 3 2 1>,
|
||
<0>, <&gpio_a 12>;
|
||
- test2-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 6 1 3 2 1>,
|
||
- <&gpio_b 7 2 3 2 1>, <&gpio_b 8 4 3 2 1>,
|
||
- <&gpio_b 9 0xc 3 2 1>;
|
||
+ test2-gpios = <&gpio_a 1>, <&gpio_a 4>,
|
||
+ <&gpio_b 6 GPIO_ACTIVE_LOW 3 2 1>,
|
||
+ <&gpio_b 7 GPIO_IN 3 2 1>,
|
||
+ <&gpio_b 8 GPIO_OUT 3 2 1>,
|
||
+ <&gpio_b 9 (GPIO_OUT|GPIO_OUT_ACTIVE) 3 2 1>;
|
||
+ test3-gpios =
|
||
+ <&gpio_c 0 (GPIO_OUT|GPIO_OPEN_DRAIN)>,
|
||
+ <&gpio_c 1 (GPIO_OUT|GPIO_OPEN_SOURCE)>,
|
||
+ <&gpio_c 2 GPIO_OUT>,
|
||
+ <&gpio_c 3 (GPIO_IN|GPIO_PULL_UP)>,
|
||
+ <&gpio_c 4 (GPIO_IN|GPIO_PULL_DOWN)>,
|
||
+ <&gpio_c 5 GPIO_IN>;
|
||
int-value = <1234>;
|
||
uint-value = <(-1234)>;
|
||
};
|
||
@@ -274,20 +288,32 @@
|
||
};
|
||
};
|
||
|
||
- gpio_a: base-gpios {
|
||
- compatible = "sandbox,gpio";
|
||
- gpio-controller;
|
||
- #gpio-cells = <1>;
|
||
- gpio-bank-name = "a";
|
||
- sandbox,gpio-count = <20>;
|
||
- };
|
||
+ pinctrl-gpio {
|
||
+ compatible = "sandbox,pinctrl-gpio";
|
||
+
|
||
+ gpio_a: base-gpios {
|
||
+ compatible = "sandbox,gpio";
|
||
+ gpio-controller;
|
||
+ #gpio-cells = <1>;
|
||
+ gpio-bank-name = "a";
|
||
+ sandbox,gpio-count = <20>;
|
||
+ };
|
||
|
||
- gpio_b: extra-gpios {
|
||
- compatible = "sandbox,gpio";
|
||
- gpio-controller;
|
||
- #gpio-cells = <5>;
|
||
- gpio-bank-name = "b";
|
||
- sandbox,gpio-count = <10>;
|
||
+ gpio_b: extra-gpios {
|
||
+ compatible = "sandbox,gpio";
|
||
+ gpio-controller;
|
||
+ #gpio-cells = <5>;
|
||
+ gpio-bank-name = "b";
|
||
+ sandbox,gpio-count = <10>;
|
||
+ };
|
||
+
|
||
+ gpio_c: pinmux-gpios {
|
||
+ compatible = "sandbox,gpio";
|
||
+ gpio-controller;
|
||
+ #gpio-cells = <2>;
|
||
+ gpio-bank-name = "c";
|
||
+ sandbox,gpio-count = <10>;
|
||
+ };
|
||
};
|
||
|
||
i2c@0 {
|
||
@@ -851,6 +877,31 @@
|
||
|
||
pinctrl {
|
||
compatible = "sandbox,pinctrl";
|
||
+
|
||
+ pinctrl-names = "default";
|
||
+ pinctrl-0 = <&gpios>;
|
||
+
|
||
+ gpios: gpios {
|
||
+ gpio0 {
|
||
+ pins = "GPIO0";
|
||
+ bias-pull-up;
|
||
+ input-disable;
|
||
+ };
|
||
+ gpio1 {
|
||
+ pins = "GPIO1";
|
||
+ output-high;
|
||
+ drive-open-drain;
|
||
+ };
|
||
+ gpio2 {
|
||
+ pins = "GPIO2";
|
||
+ bias-pull-down;
|
||
+ input-enable;
|
||
+ };
|
||
+ gpio3 {
|
||
+ pins = "GPIO3";
|
||
+ bias-disable;
|
||
+ };
|
||
+ };
|
||
};
|
||
|
||
hwspinlock@0 {
|
||
diff --git a/arch/sandbox/include/asm/gpio.h b/arch/sandbox/include/asm/gpio.h
|
||
index de8ac37..df4ba4f 100644
|
||
--- a/arch/sandbox/include/asm/gpio.h
|
||
+++ b/arch/sandbox/include/asm/gpio.h
|
||
@@ -43,43 +43,43 @@ int sandbox_gpio_get_value(struct udevice *dev, unsigned int offset);
|
||
int sandbox_gpio_set_value(struct udevice *dev, unsigned int offset, int value);
|
||
|
||
/**
|
||
- * Set or reset the simulated open drain mode of a GPIO (used only in sandbox
|
||
- * test code)
|
||
+ * Return the simulated direction of a GPIO (used only in sandbox test code)
|
||
*
|
||
- * @param gp GPIO number
|
||
- * @param value value to set (0 for enabled open drain mode, non-zero for
|
||
- * disabled)
|
||
- * @return -1 on error, 0 if ok
|
||
+ * @param dev device to use
|
||
+ * @param offset GPIO offset within bank
|
||
+ * @return -1 on error, 0 if GPIO is input, >0 if output
|
||
*/
|
||
-int sandbox_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value);
|
||
+int sandbox_gpio_get_direction(struct udevice *dev, unsigned int offset);
|
||
|
||
/**
|
||
- * Return the state of the simulated open drain mode of a GPIO (used only in
|
||
- * sandbox test code)
|
||
+ * Set the simulated direction of a GPIO (used only in sandbox test code)
|
||
*
|
||
- * @param gp GPIO number
|
||
- * @return -1 on error, 0 if GPIO is input, >0 if output
|
||
+ * @param dev device to use
|
||
+ * @param offset GPIO offset within bank
|
||
+ * @param output 0 to set as input, 1 to set as output
|
||
+ * @return -1 on error, 0 if ok
|
||
*/
|
||
-int sandbox_gpio_get_open_drain(struct udevice *dev, unsigned offset);
|
||
+int sandbox_gpio_set_direction(struct udevice *dev, unsigned int offset,
|
||
+ int output);
|
||
|
||
/**
|
||
- * Return the simulated direction of a GPIO (used only in sandbox test code)
|
||
+ * Return the simulated flags of a GPIO (used only in sandbox test code)
|
||
*
|
||
* @param dev device to use
|
||
* @param offset GPIO offset within bank
|
||
- * @return -1 on error, 0 if GPIO is input, >0 if output
|
||
+ * @return dir_flags: bitfield accesses by GPIOD_ defines
|
||
*/
|
||
-int sandbox_gpio_get_direction(struct udevice *dev, unsigned int offset);
|
||
+ulong sandbox_gpio_get_dir_flags(struct udevice *dev, unsigned int offset);
|
||
|
||
/**
|
||
- * Set the simulated direction of a GPIO (used only in sandbox test code)
|
||
+ * Set the simulated flags of a GPIO (used only in sandbox test code)
|
||
*
|
||
* @param dev device to use
|
||
* @param offset GPIO offset within bank
|
||
- * @param output 0 to set as input, 1 to set as output
|
||
+ * @param flags dir_flags: bitfield accesses by GPIOD_ defines
|
||
* @return -1 on error, 0 if ok
|
||
*/
|
||
-int sandbox_gpio_set_direction(struct udevice *dev, unsigned int offset,
|
||
- int output);
|
||
+int sandbox_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
|
||
+ ulong flags);
|
||
|
||
#endif
|
||
--
|
||
2.7.4
|
||
|