* [PATCH v2 1/6] ARM: i.MX6QDL: add imxcfg helper to configure the TZASC1/2
2025-06-26 14:45 [PATCH v2 0/6] i.MX6Q TZASC and OP-TEE early helpers Marco Felsch
@ 2025-06-26 14:45 ` Marco Felsch
2025-06-26 14:45 ` [PATCH v2 2/6] ARM: i.MX6Q: add imx6_get_mmdc_sdram_size Marco Felsch
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Marco Felsch @ 2025-06-26 14:45 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
On i.MX6 platforms we need to set the bypass mode within the DCD unlike
the i.MX8M platforms which uses the PBL lowlevel code
(imx8m_tzc380_init()).
Add a helper for this to make the integration easier.
Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
Changelog:
v2:
- Add Ahmad r-b
- Change subject
include/mach/imx/imx6q-tzasc.h | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 include/mach/imx/imx6q-tzasc.h
diff --git a/include/mach/imx/imx6q-tzasc.h b/include/mach/imx/imx6q-tzasc.h
new file mode 100644
index 000000000000..968b17d5ec3f
--- /dev/null
+++ b/include/mach/imx/imx6q-tzasc.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Per default all clocks are on, except for TZASC1/2 CG11/2, so enable them
+ * before activate the modules (disable the bypass mode).
+ */
+wm 32 0x020c4070 0xffffffff
+/* Disable TZASC1/2 bypass */
+wm 32 0x020E0024 0x00000003
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/6] ARM: i.MX6Q: add imx6_get_mmdc_sdram_size
2025-06-26 14:45 [PATCH v2 0/6] i.MX6Q TZASC and OP-TEE early helpers Marco Felsch
2025-06-26 14:45 ` [PATCH v2 1/6] ARM: i.MX6QDL: add imxcfg helper to configure the TZASC1/2 Marco Felsch
@ 2025-06-26 14:45 ` Marco Felsch
2025-06-26 14:45 ` [PATCH v2 3/6] ARM: mach-imx: tzasc: add region configure helpers Marco Felsch
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Marco Felsch @ 2025-06-26 14:45 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Add a helper to query the MMDC configured SDRAM size.
Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
Changelog:
v2:
- Add Ahmad r-b
arch/arm/mach-imx/esdctl.c | 5 +++++
include/mach/imx/esdctl.h | 1 +
2 files changed, 6 insertions(+)
diff --git a/arch/arm/mach-imx/esdctl.c b/arch/arm/mach-imx/esdctl.c
index 701ca0ac1f37..7a09f43071c2 100644
--- a/arch/arm/mach-imx/esdctl.c
+++ b/arch/arm/mach-imx/esdctl.c
@@ -286,6 +286,11 @@ static inline resource_size_t imx6_mmdc_sdram_size(void __iomem *mmdcbase)
return size;
}
+resource_size_t imx6_get_mmdc_sdram_size(void)
+{
+ return imx6_mmdc_sdram_size(IOMEM(MX6_MMDC_P0_BASE_ADDR));
+}
+
static int imx6_mmdc_add_mem(void *mmdcbase, struct imx_esdctl_data *data)
{
return arm_add_mem_device("ram0", data->base0,
diff --git a/include/mach/imx/esdctl.h b/include/mach/imx/esdctl.h
index d79bf17959e6..97bd444b1a4c 100644
--- a/include/mach/imx/esdctl.h
+++ b/include/mach/imx/esdctl.h
@@ -151,6 +151,7 @@ void __noreturn imx7d_barebox_entry(void *boarddata);
void __noreturn imx93_barebox_entry(void *boarddata);
#define imx6sx_barebox_entry(boarddata) imx6ul_barebox_entry(boarddata)
void imx_esdctl_disable(void);
+resource_size_t imx6_get_mmdc_sdram_size(void);
resource_size_t imx8m_barebox_earlymem_size(unsigned buswidth);
resource_size_t imx9_ddrc_sdram_size(void);
#endif
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/6] ARM: mach-imx: tzasc: add region configure helpers
2025-06-26 14:45 [PATCH v2 0/6] i.MX6Q TZASC and OP-TEE early helpers Marco Felsch
2025-06-26 14:45 ` [PATCH v2 1/6] ARM: i.MX6QDL: add imxcfg helper to configure the TZASC1/2 Marco Felsch
2025-06-26 14:45 ` [PATCH v2 2/6] ARM: i.MX6Q: add imx6_get_mmdc_sdram_size Marco Felsch
@ 2025-06-26 14:45 ` Marco Felsch
2025-06-26 14:45 ` [PATCH v2 4/6] ARM: mach-imx: tzasc: add imx6q_tzc380_early_ns_region1() Marco Felsch
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Marco Felsch @ 2025-06-26 14:45 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
At the moment the TZC380 driver is very limited and focused on minimal
platform setup unlike the TZC400 driver.
This commit adds helper functions to setup any number of TZASC regions
of any size which is required by later commits to setup an early
non-secure TZASC region1.
The code is based on the TZC400 barebox driver and the TZC380 OP-TEE
driver.
Acked-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
Changelog:
v2:
- Add Ahmad ack
- Drop "TrustZone :" from pr_debug
arch/arm/mach-imx/tzasc.c | 236 ++++++++++++++++++++++++++++++++++++++
1 file changed, 236 insertions(+)
diff --git a/arch/arm/mach-imx/tzasc.c b/arch/arm/mach-imx/tzasc.c
index 4cb4d7c5cffc..2a48f841af49 100644
--- a/arch/arm/mach-imx/tzasc.c
+++ b/arch/arm/mach-imx/tzasc.c
@@ -1,11 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) "tzc380: " fmt
+
+#include <common.h>
#include <mach/imx/generic.h>
#include <mach/imx/tzasc.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/log2.h>
+#include <linux/sizes.h>
#include <mach/imx/imx8m-regs.h>
#include <io.h>
+/*******************************************************************************
+ * TZC380 defines
+ ******************************************************************************/
+
+#define TZC380_BUILD_CONFIG 0x000
+#define TZC380_BUILD_CONFIG_AW GENMASK(13, 8)
+#define TZC380_BUILD_CONFIG_NR GENMASK(4, 0)
+
+/*
+ * All TZC region configuration registers are placed one after another. It
+ * depicts size of block of registers for programming each region.
+ */
+#define TZC380_REGION_REG_SIZE 0x10
+
+#define TZC380_REGION_SETUP_LOW_0 0x100
+#define TZC380_REGION_SETUP_HIGH_0 0x104
+#define TZC380_REGION_ATTR_0 0x108
+#define TZC380_REGION_SP GENMASK(31, 28)
+#define TZC380_SUBREGION_DIS_MASK GENMASK(15, 8)
+#define TZC380_REGION_SIZE GENMASK(6, 1)
+#define TZC380_REGION_EN BIT(0)
+
+/* ID Registers */
+#define TZC380_PID0_OFF 0xfe0
+#define TZC380_PID1_OFF 0xfe4
+#define TZC380_PERIPHERAL_ID 0x380
+#define TZC380_PID2_OFF 0xfe8
+#define TZC380_PID3_OFF 0xfec
+#define TZC380_PID4_OFF 0xfd0
+#define TZC380_CID0_OFF 0xff0
+#define TZC380_CID1_OFF 0xff4
+#define TZC380_CID2_OFF 0xff8
+
+#define TZC380_REGION_OFFSET(region_no) \
+ (TZC380_REGION_REG_SIZE * (region_no))
+#define TZC380_REGION_SETUP_LOW(region_no) \
+ (TZC380_REGION_OFFSET(region_no) + TZC380_REGION_SETUP_LOW_0)
+#define TZC380_REGION_SETUP_HIGH(region_no) \
+ (TZC380_REGION_OFFSET(region_no) + TZC380_REGION_SETUP_HIGH_0)
+#define TZC380_REGION_ATTR(region_no) \
+ (TZC380_REGION_OFFSET(region_no) + TZC380_REGION_ATTR_0)
+
+#define TZC380_REGION_SP_NS_W FIELD_PREP(TZC380_REGION_SP, BIT(0))
+#define TZC380_REGION_SP_NS_R FIELD_PREP(TZC380_REGION_SP, BIT(1))
+#define TZC380_REGION_SP_S_W FIELD_PREP(TZC380_REGION_SP, BIT(2))
+#define TZC380_REGION_SP_S_R FIELD_PREP(TZC380_REGION_SP, BIT(3))
+
+#define TZC380_REGION_SP_ALL \
+ (TZC380_REGION_SP_NS_W | TZC380_REGION_SP_NS_R | \
+ TZC380_REGION_SP_S_W | TZC380_REGION_SP_S_R)
+#define TZC380_REGION_SP_S_RW \
+ (TZC380_REGION_SP_S_W | TZC380_REGION_SP_S_R)
+#define TZC380_REGION_SP_NS_RW \
+ (TZC380_REGION_SP_NS_W | TZC380_REGION_SP_NS_R)
+
+/*******************************************************************************
+ * SoC specific defines
+ ******************************************************************************/
+
#define GPR_TZASC_EN BIT(0)
#define GPR_TZASC_ID_SWAP_BYPASS BIT(1)
#define GPR_TZASC_EN_LOCK BIT(16)
@@ -14,6 +79,177 @@
#define MX8M_TZASC_REGION_ATTRIBUTES_0 (MX8M_TZASC_BASE_ADDR + 0x108)
#define MX8M_TZASC_REGION_ATTRIBUTES_0_SP GENMASK(31, 28)
+/*
+ * Implementation defined values used to validate inputs later.
+ * Filters : max of 4 ; 0 to 3
+ * Regions : max of 9 ; 0 to 8
+ * Address width : Values between 32 to 64
+ */
+struct tzc380_instance {
+ void __iomem *base;
+ uint8_t addr_width;
+ uint8_t num_regions;
+};
+
+/* Some platforms like i.MX6 does have two tzc380 controllers */
+static struct tzc380_instance tzc380_inst[2];
+
+static inline unsigned int tzc_read_peripheral_id(void __iomem *base)
+{
+ unsigned int id;
+
+ id = in_le32(base + TZC380_PID0_OFF);
+ /* Masks DESC part in PID1 */
+ id |= ((in_le32(base + TZC380_PID1_OFF) & 0xFU) << 8U);
+
+ return id;
+}
+
+static struct tzc380_instance *tzc380_init(void __iomem *base)
+{
+ struct tzc380_instance *tzc380 = &tzc380_inst[0];
+ unsigned int tzc380_id;
+ unsigned int tzc380_build;
+
+ if (tzc380->base)
+ tzc380 = &tzc380_inst[1];
+
+ if (tzc380->base)
+ panic("TZC-380: No free memory\n");
+
+ tzc380->base = base;
+
+ tzc380_id = tzc_read_peripheral_id(base);
+ if (tzc380_id != TZC380_PERIPHERAL_ID)
+ panic("TZC-380 : Wrong device ID (0x%x).\n", tzc380_id);
+
+ /* Save values we will use later. */
+ tzc380_build = in_le32(base + TZC380_BUILD_CONFIG);
+ tzc380->addr_width = FIELD_GET(TZC380_BUILD_CONFIG_AW, tzc380_build) + 1;
+ tzc380->num_regions = FIELD_GET(TZC380_BUILD_CONFIG_NR, tzc380_build) + 1;
+
+ return tzc380;
+}
+
+static void
+tzc380_configure_region(struct tzc380_instance *tzc380, unsigned int region,
+ uint64_t region_base, unsigned int region_attr)
+{
+ void __iomem *base = tzc380->base;
+
+ /* Do range checks on regions */
+ ASSERT((region < tzc380->num_regions));
+
+ pr_debug("Configuring region %u\n", region);
+ pr_debug("... base = %#llx\n", region_base);
+ pr_debug("... sp = %#x\n",
+ (unsigned int)FIELD_GET(TZC380_REGION_SP, region_attr));
+ pr_debug("... subregion dis-mask = %#x\n",
+ (unsigned int)FIELD_GET(TZC380_SUBREGION_DIS_MASK, region_attr));
+ pr_debug("... size = %#x\n",
+ (unsigned int)FIELD_GET(TZC380_REGION_SIZE, region_attr));
+ pr_debug("... enable = %#x\n",
+ (unsigned int)FIELD_GET(TZC380_REGION_EN, region_attr));
+
+ /***************************************************/
+ /* Inputs look ok, start programming registers. */
+ /* The address registers are 32 bits wide and */
+ /* have a LOW and HIGH */
+ /* component used to construct an address up to a */
+ /* 64bit. */
+ /***************************************************/
+ out_le32(base + TZC380_REGION_SETUP_LOW(region), (uint32_t)region_base);
+ out_le32(base + TZC380_REGION_SETUP_HIGH(region), (uint32_t)(region_base >> 32));
+
+ /* Set region attributes */
+ out_le32(base + TZC380_REGION_ATTR(region), region_attr);
+}
+
+static int
+tzc380_auto_configure(struct tzc380_instance *tzc380, unsigned int region,
+ uint64_t base, uint64_t size,
+ unsigned int attr)
+{
+ uint64_t sub_region_size = 0;
+ uint64_t area = 0;
+ uint8_t lregion = region;
+ uint64_t region_size = 0;
+ uint64_t sub_address = 0;
+ uint64_t address = base;
+ uint64_t lsize = size;
+ unsigned int lattr;
+ uint32_t mask = 0;
+ uint64_t reminder;
+ int i = 0;
+ uint8_t pow = 0;
+
+ ASSERT(tzc380->base);
+
+ /*
+ * TZC380 RM
+ * For region_attributes_<n> registers, region_size:
+ * Note: The AXI address width, that is AXI_ADDRESS_MSB+1, controls the
+ * upper limit value of the field.
+ */
+ pow = tzc380->addr_width;
+
+ while (lsize != 0 && pow >= 15) {
+ region_size = 1ULL << pow;
+
+ /* Case region fits alignment and covers requested area */
+ if ((address % region_size == 0) &&
+ ((address + lsize) % region_size == 0)) {
+ lattr = attr;
+ lattr |= FIELD_PREP(TZC380_REGION_SIZE, (pow - 1));
+ lattr |= TZC380_REGION_EN;
+
+ tzc380_configure_region(tzc380, lregion, address, lattr);
+
+ lregion++;
+ address += region_size;
+ lsize -= region_size;
+ pow = tzc380->addr_width;
+ continue;
+ }
+
+ /* Cover area using several subregions */
+ sub_region_size = div_u64(region_size, 8);
+ div64_u64_rem(address, sub_region_size, &reminder);
+ if (reminder == 0 && lsize > 2 * sub_region_size) {
+ sub_address = div64_u64(address, region_size) * region_size;
+ mask = 0;
+ for (i = 0; i < 8; i++) {
+ area = (i + 1) * sub_region_size;
+ if (sub_address + area <= address ||
+ sub_address + area > address + lsize) {
+ mask |= FIELD_PREP(TZC380_SUBREGION_DIS_MASK, BIT(i));
+ } else {
+ address += sub_region_size;
+ lsize -= sub_region_size;
+ }
+ }
+
+ lattr = mask | attr;
+ lattr |= FIELD_PREP(TZC380_REGION_SIZE, (pow - 1));
+ lattr |= TZC380_REGION_EN;
+
+ tzc380_configure_region(tzc380, lregion, sub_address, lattr);
+
+ lregion++;
+ pow = tzc380->addr_width;
+ continue;
+ }
+ pow--;
+ }
+ ASSERT(lsize == 0);
+ ASSERT(address == base + size);
+ return lregion;
+}
+
+/******************************************************************************
+ * SoC specific helpers
+ ******************************************************************************/
+
void imx8m_tzc380_init(void)
{
u32 __iomem *gpr = IOMEM(MX8M_IOMUXC_GPR_BASE_ADDR);
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 4/6] ARM: mach-imx: tzasc: add imx6q_tzc380_early_ns_region1()
2025-06-26 14:45 [PATCH v2 0/6] i.MX6Q TZASC and OP-TEE early helpers Marco Felsch
` (2 preceding siblings ...)
2025-06-26 14:45 ` [PATCH v2 3/6] ARM: mach-imx: tzasc: add region configure helpers Marco Felsch
@ 2025-06-26 14:45 ` Marco Felsch
2025-06-26 14:45 ` [PATCH v2 5/6] ARM: mach-imx: tzasc: add imx6q_tzc380_is_enabled Marco Felsch
2025-06-26 14:45 ` [PATCH v2 6/6] ARM: optee-early: add mx6_start_optee_early helper Marco Felsch
5 siblings, 0 replies; 7+ messages in thread
From: Marco Felsch @ 2025-06-26 14:45 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Add a helper function which can be used by the board code to setup an
early non-secure TZASC region1 which covers the whole SDRAM size.
This eliminates the current workaround of configuring region0 as
non-secure/secure region.
Acked-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
Changelog:
v2:
- Add Ahmad ack
arch/arm/mach-imx/Makefile | 2 +-
arch/arm/mach-imx/tzasc.c | 44 ++++++++++++++++++++++++++++++++++++++
include/mach/imx/tzasc.h | 1 +
3 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 23f51fc66019..07e14b392d6c 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -21,7 +21,7 @@ obj-pbl-$(CONFIG_ARCH_IMX8M) += imx8m.o
obj-pbl-$(CONFIG_ARCH_IMX_SCRATCHMEM) += scratch.o
obj-$(CONFIG_ARCH_IMX9) += imx9.o imx-v3-image.o
lwl-$(CONFIG_ARCH_IMX_ATF) += atf.o
-obj-pbl-$(CONFIG_ARCH_IMX8M) += tzasc.o
+obj-pbl-y += tzasc.o
obj-pbl-$(CONFIG_ARCH_IMX_ROMAPI) += romapi.o
obj-$(CONFIG_IMX_IIM) += iim.o
obj-$(CONFIG_NAND_IMX) += nand.o
diff --git a/arch/arm/mach-imx/tzasc.c b/arch/arm/mach-imx/tzasc.c
index 2a48f841af49..54f7d1d49715 100644
--- a/arch/arm/mach-imx/tzasc.c
+++ b/arch/arm/mach-imx/tzasc.c
@@ -3,8 +3,10 @@
#define pr_fmt(fmt) "tzc380: " fmt
#include <common.h>
+#include <mach/imx/esdctl.h>
#include <mach/imx/generic.h>
#include <mach/imx/tzasc.h>
+#include <mach/imx/imx6-regs.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/log2.h>
@@ -71,6 +73,9 @@
* SoC specific defines
******************************************************************************/
+#define MX6_TZASC1_BASE 0x21d0000
+#define MX6_TZASC2_BASE 0x21d4000
+
#define GPR_TZASC_EN BIT(0)
#define GPR_TZASC_ID_SWAP_BYPASS BIT(1)
#define GPR_TZASC_EN_LOCK BIT(16)
@@ -250,6 +255,45 @@ tzc380_auto_configure(struct tzc380_instance *tzc380, unsigned int region,
* SoC specific helpers
******************************************************************************/
+static void imx_tzc380_init_and_setup(void __iomem *base, unsigned int region,
+ resource_size_t region_base,
+ resource_size_t region_size,
+ unsigned int region_attr)
+{
+ struct tzc380_instance *tzasc = tzc380_init(base);
+
+ tzc380_auto_configure(tzasc, region, region_base, region_size,
+ region_attr);
+}
+
+/*
+ * imx6q_tzc380_early_ns_region1 - configure the whole DRAM as non-secure
+ * region1
+ *
+ * Passing data between TEE and barebox need to follow some requirements:
+ * - the location can be accessed by the normal and secure world
+ * - the mapping in the normal and secure world must be the same to avoid
+ * manual cache maintenance.
+ *
+ * Therefore this function reads the DRAM size out of the MMDC controller and
+ * configures the whole size as non-secure TZC380 region1. This allows the
+ * early TEE code to map the location as non-secure to while writing the data
+ * e.g. device-tee-overlays. Later on the TEE may reconfigure and lock the
+ * TZC380 regions. The reconfiguration needs to ensure that the exchange data
+ * location is still accessible by the normal world.
+ */
+void imx6q_tzc380_early_ns_region1(void)
+{
+ resource_size_t ram_sz = imx6_get_mmdc_sdram_size();
+
+ imx_tzc380_init_and_setup(IOMEM(MX6_TZASC1_BASE), 1,
+ MX6_MMDC_PORT01_BASE_ADDR, ram_sz,
+ TZC380_REGION_SP_NS_RW);
+ imx_tzc380_init_and_setup(IOMEM(MX6_TZASC2_BASE), 1,
+ MX6_MMDC_PORT01_BASE_ADDR, ram_sz,
+ TZC380_REGION_SP_NS_RW);
+}
+
void imx8m_tzc380_init(void)
{
u32 __iomem *gpr = IOMEM(MX8M_IOMUXC_GPR_BASE_ADDR);
diff --git a/include/mach/imx/tzasc.h b/include/mach/imx/tzasc.h
index 51c86f168ee4..4d3f26fc82f1 100644
--- a/include/mach/imx/tzasc.h
+++ b/include/mach/imx/tzasc.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
#include <asm/system.h>
+void imx6q_tzc380_early_ns_region1(void);
void imx8m_tzc380_init(void);
bool imx8m_tzc380_is_enabled(void);
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 5/6] ARM: mach-imx: tzasc: add imx6q_tzc380_is_enabled
2025-06-26 14:45 [PATCH v2 0/6] i.MX6Q TZASC and OP-TEE early helpers Marco Felsch
` (3 preceding siblings ...)
2025-06-26 14:45 ` [PATCH v2 4/6] ARM: mach-imx: tzasc: add imx6q_tzc380_early_ns_region1() Marco Felsch
@ 2025-06-26 14:45 ` Marco Felsch
2025-06-26 14:45 ` [PATCH v2 6/6] ARM: optee-early: add mx6_start_optee_early helper Marco Felsch
5 siblings, 0 replies; 7+ messages in thread
From: Marco Felsch @ 2025-06-26 14:45 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
In preparation of adding an i.MX6QD optee-early helper add a helper to
check if the early code e.g. PBL/BootROM enabled the TZC380 controllers
on the i.MX6QDL.
Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
Changelog:
v2:
- Add Ahmad r-b
arch/arm/mach-imx/tzasc.c | 15 +++++++++++++++
include/mach/imx/tzasc.h | 1 +
2 files changed, 16 insertions(+)
diff --git a/arch/arm/mach-imx/tzasc.c b/arch/arm/mach-imx/tzasc.c
index 54f7d1d49715..b8270a4afdc8 100644
--- a/arch/arm/mach-imx/tzasc.c
+++ b/arch/arm/mach-imx/tzasc.c
@@ -76,6 +76,9 @@
#define MX6_TZASC1_BASE 0x21d0000
#define MX6_TZASC2_BASE 0x21d4000
+#define MX6_GPR_TZASC1_EN BIT(0)
+#define MX6_GPR_TZASC2_EN BIT(1)
+
#define GPR_TZASC_EN BIT(0)
#define GPR_TZASC_ID_SWAP_BYPASS BIT(1)
#define GPR_TZASC_EN_LOCK BIT(16)
@@ -294,6 +297,18 @@ void imx6q_tzc380_early_ns_region1(void)
TZC380_REGION_SP_NS_RW);
}
+bool imx6q_tzc380_is_enabled(void)
+{
+ u32 __iomem *gpr = IOMEM(MX6_IOMUXC_BASE_ADDR);
+
+ /*
+ * MX6_GPR_TZASC1_EN and MX6_GPR_TZASC2_EN are sticky bits which
+ * preserve their values once set until the next power-up cycle.
+ */
+ return (readl(&gpr[9]) & (MX6_GPR_TZASC1_EN | MX6_GPR_TZASC2_EN)) ==
+ (MX6_GPR_TZASC1_EN | MX6_GPR_TZASC2_EN);
+}
+
void imx8m_tzc380_init(void)
{
u32 __iomem *gpr = IOMEM(MX8M_IOMUXC_GPR_BASE_ADDR);
diff --git a/include/mach/imx/tzasc.h b/include/mach/imx/tzasc.h
index 4d3f26fc82f1..59ec56a5ec60 100644
--- a/include/mach/imx/tzasc.h
+++ b/include/mach/imx/tzasc.h
@@ -7,6 +7,7 @@
#include <asm/system.h>
void imx6q_tzc380_early_ns_region1(void);
+bool imx6q_tzc380_is_enabled(void);
void imx8m_tzc380_init(void);
bool imx8m_tzc380_is_enabled(void);
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 6/6] ARM: optee-early: add mx6_start_optee_early helper
2025-06-26 14:45 [PATCH v2 0/6] i.MX6Q TZASC and OP-TEE early helpers Marco Felsch
` (4 preceding siblings ...)
2025-06-26 14:45 ` [PATCH v2 5/6] ARM: mach-imx: tzasc: add imx6q_tzc380_is_enabled Marco Felsch
@ 2025-06-26 14:45 ` Marco Felsch
5 siblings, 0 replies; 7+ messages in thread
From: Marco Felsch @ 2025-06-26 14:45 UTC (permalink / raw)
To: barebox
Add a i.MX6 specific helper function which covers most of the steps
usually done within the board lowlevel code. All new i.MX6 boards are
encouraged to use this helper to load OP-TEE since the helper validates
that the TZC380 is enabled and setup the TZC380 region properly.
Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
Changelog:
v2:
- new in this series
Documentation/user/optee.rst | 19 ++++++++++++-------
arch/arm/lib32/optee-early.c | 30 ++++++++++++++++++++++++++++++
include/tee/optee.h | 2 ++
3 files changed, 44 insertions(+), 7 deletions(-)
diff --git a/Documentation/user/optee.rst b/Documentation/user/optee.rst
index 2729d21d2e44..451f9ad264bb 100644
--- a/Documentation/user/optee.rst
+++ b/Documentation/user/optee.rst
@@ -19,14 +19,19 @@ During the PBL
^^^^^^^^^^^^^^
To start OP-TEE during the lowlevel initialization of your board in the ``PBL``,
-enable the ``CONFIG_PBL_OPTEE`` configuration variable. your board should then
+enable the ``CONFIG_PBL_OPTEE`` configuration variable. Your board should then
call the function ``start_optee_early(void* tee, void* fdt)`` with a valid tee
-and FDT. Ensure that your OP-TEE is compiled with ``CFG_NS_ENTRY_ADDR`` unset,
-otherwise OP-TEE will not correctly return to barebox after startup.
-Since OP-TEE in the default configuration also modifies the device tree, don't
-pass the barebox internal device tree, instead copy it into a different memory
-location and pass it to OP-TEE afterwards.
-The modified device tree can then be passed to the main barebox start function.
+and FDT. If you're running on an i.MX6 platform your board code should call
+``int mx6_start_optee_early(void *fdt, void *tee, void *data_location, unsigned
+int data_location_size)`` instead since it validates that the TZASC is running
+and configured as expected by OP-TEE.
+
+Ensure that your OP-TEE is compiled with ``CFG_NS_ENTRY_ADDR`` unset, otherwise
+OP-TEE will not correctly return to barebox after startup. Since OP-TEE in the
+default configuration also modifies the device tree, don't pass the barebox
+internal device tree, instead copy it into a different memory location and pass
+it to OP-TEE afterwards. The modified device tree can then be passed to the
+main barebox start function.
Before Linux start
^^^^^^^^^^^^^^^^^^
diff --git a/arch/arm/lib32/optee-early.c b/arch/arm/lib32/optee-early.c
index 735d829c99fb..a3e2892a3291 100644
--- a/arch/arm/lib32/optee-early.c
+++ b/arch/arm/lib32/optee-early.c
@@ -10,6 +10,8 @@
#include <tee/optee.h>
#include <debug_ll.h>
#include <string.h>
+#include <mach/imx/imx6.h>
+#include <mach/imx/tzasc.h>
static jmp_buf tee_buf;
@@ -37,3 +39,31 @@ int start_optee_early(void *fdt, void *tee)
return 0;
}
+
+int mx6_start_optee_early(void *fdt, void *tee, void *data_location,
+ unsigned int data_location_size)
+{
+ switch (__imx6_cpu_type()) {
+ case IMX6_CPUTYPE_IMX6D:
+ case IMX6_CPUTYPE_IMX6Q:
+ if (!imx6q_tzc380_is_enabled())
+ panic("TZC380 is not enabled, abort OP-TEE loading\n");
+
+ /* Add early non-secure TZASC region1 to pass DTO */
+ imx6q_tzc380_early_ns_region1();
+
+ /*
+ * Set the OP-TEE <-> barebox exchange data location to zero.
+ * This is optional since recent OP-TEE versions perform the
+ * memset too.
+ */
+ if (data_location)
+ memset(data_location, 0, data_location_size);
+
+ break;
+ default:
+ panic("Unknown CPU type\n");
+ }
+
+ return start_optee_early(fdt, tee);
+}
diff --git a/include/tee/optee.h b/include/tee/optee.h
index f52775dab5b4..19d9a20c7cdc 100644
--- a/include/tee/optee.h
+++ b/include/tee/optee.h
@@ -54,6 +54,8 @@ static inline int optee_get_membase(u64 *membase)
#ifdef __PBL__
int start_optee_early(void* fdt, void* tee);
+int mx6_start_optee_early(void *fdt, void *tee, void *data_location,
+ unsigned int data_location_size);
#endif /* __PBL__ */
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread