23390 lines
613 KiB
Diff
23390 lines
613 KiB
Diff
From da84a97b7083d65144ed658e0f0def1232b7852c Mon Sep 17 00:00:00 2001
|
|
From: christophe montaud <christophe.montaud@st.com>
|
|
Date: Wed, 30 Jan 2019 10:41:28 +0100
|
|
Subject: [PATCH] st updates r1
|
|
|
|
---
|
|
core/arch/arm/fdts/stm32mp15-ddr.dtsi | 153 ++
|
|
.../arm/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi | 121 ++
|
|
.../arm/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi | 122 ++
|
|
core/arch/arm/fdts/stm32mp157-pinctrl.dtsi | 350 +++++
|
|
core/arch/arm/fdts/stm32mp157a-dk1.dts | 380 +++++
|
|
core/arch/arm/fdts/stm32mp157c-dk2.dts | 16 +
|
|
core/arch/arm/fdts/stm32mp157c-ed1.dts | 381 +++++
|
|
core/arch/arm/fdts/stm32mp157c-ev1.dts | 67 +
|
|
core/arch/arm/fdts/stm32mp157c-security.dtsi | 71 +
|
|
core/arch/arm/fdts/stm32mp157c.dtsi | 371 +++++
|
|
core/arch/arm/fdts/stm32mp157caa-pinctrl.dtsi | 90 ++
|
|
core/arch/arm/fdts/stm32mp157cab-pinctrl.dtsi | 62 +
|
|
core/arch/arm/fdts/stm32mp157cac-pinctrl.dtsi | 78 +
|
|
core/arch/arm/fdts/stm32mp157cad-pinctrl.dtsi | 62 +
|
|
core/arch/arm/include/arm32.h | 14 +-
|
|
core/arch/arm/include/kernel/delay.h | 8 +
|
|
core/arch/arm/include/mm/core_mmu.h | 3 +
|
|
core/arch/arm/include/sm/pm.h | 4 +
|
|
core/arch/arm/kernel/delay.c | 40 +-
|
|
core/arch/arm/kernel/generic_boot.c | 36 +-
|
|
core/arch/arm/mm/core_mmu.c | 3 +
|
|
core/arch/arm/mm/mobj.c | 3 +-
|
|
core/arch/arm/plat-stm32mp1/boot_api.h | 2 +
|
|
core/arch/arm/plat-stm32mp1/conf.mk | 81 +-
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32_reset.c | 60 +
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32_reset.h | 15 +
|
|
.../arm/plat-stm32mp1/drivers/stm32mp1_calib.c | 457 ++++++
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.c | 1527 ++++++++++++++++++
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.h | 62 +
|
|
.../arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.c | 340 ++++
|
|
.../arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.h | 33 +
|
|
.../arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.c | 513 ++++++
|
|
.../arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.h | 205 +++
|
|
.../arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.c | 614 ++++++++
|
|
.../arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.h | 33 +
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.c | 21 +
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.h | 48 +
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.c | 51 +
|
|
core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.h | 539 +++++++
|
|
core/arch/arm/plat-stm32mp1/drivers/sub.mk | 8 +
|
|
core/arch/arm/plat-stm32mp1/link.mk | 6 +-
|
|
core/arch/arm/plat-stm32mp1/main.c | 578 ++++++-
|
|
core/arch/arm/plat-stm32mp1/platform_config.h | 183 ++-
|
|
core/arch/arm/plat-stm32mp1/pm/context.c | 510 ++++++
|
|
core/arch/arm/plat-stm32mp1/pm/context.h | 100 ++
|
|
.../arm/plat-stm32mp1/pm/context_asm_defines.c | 28 +
|
|
core/arch/arm/plat-stm32mp1/pm/low_power.c | 431 ++++++
|
|
core/arch/arm/plat-stm32mp1/pm/pm_helpers.S | 635 ++++++++
|
|
core/arch/arm/plat-stm32mp1/pm/power.h | 26 +
|
|
core/arch/arm/plat-stm32mp1/pm/power_config.c | 212 +++
|
|
core/arch/arm/plat-stm32mp1/pm/psci.c | 427 +++++
|
|
core/arch/arm/plat-stm32mp1/pm/sub.mk | 7 +
|
|
core/arch/arm/plat-stm32mp1/reset.S | 28 +-
|
|
core/arch/arm/plat-stm32mp1/scripts/stm32image.py | 19 +-
|
|
core/arch/arm/plat-stm32mp1/service/bsec_svc.c | 65 +
|
|
core/arch/arm/plat-stm32mp1/service/bsec_svc.h | 14 +
|
|
.../arch/arm/plat-stm32mp1/service/low_power_svc.c | 153 ++
|
|
.../arch/arm/plat-stm32mp1/service/low_power_svc.h | 22 +
|
|
core/arch/arm/plat-stm32mp1/service/pwr_svc.c | 117 ++
|
|
core/arch/arm/plat-stm32mp1/service/pwr_svc.h | 11 +
|
|
core/arch/arm/plat-stm32mp1/service/rcc_svc.c | 440 ++++++
|
|
core/arch/arm/plat-stm32mp1/service/rcc_svc.h | 11 +
|
|
core/arch/arm/plat-stm32mp1/service/stm32mp1_smc.h | 211 +++
|
|
.../arm/plat-stm32mp1/service/stm32mp1_svc_setup.c | 129 ++
|
|
core/arch/arm/plat-stm32mp1/service/sub.mk | 7 +
|
|
core/arch/arm/plat-stm32mp1/shared_resources.c | 1007 ++++++++++++
|
|
core/arch/arm/plat-stm32mp1/stm32_util.h | 287 ++++
|
|
core/arch/arm/plat-stm32mp1/stm32mp1_dt.c | 338 ++++
|
|
core/arch/arm/plat-stm32mp1/stm32mp_dt.h | 39 +
|
|
core/arch/arm/plat-stm32mp1/stm32mp_pm.h | 49 +
|
|
core/arch/arm/plat-stm32mp1/sub.mk | 6 +
|
|
core/arch/arm/sm/pm_a32.S | 83 +-
|
|
core/arch/arm/tee/entry_std.c | 2 +-
|
|
core/drivers/gic.c | 210 ++-
|
|
core/drivers/stm32_bsec.c | 824 ++++++++++
|
|
core/drivers/stm32_etzpc.c | 337 ++++
|
|
core/drivers/stm32_gpio.c | 417 +++++
|
|
core/drivers/stm32_i2c.c | 1629 ++++++++++++++++++++
|
|
core/drivers/stm32_iwdg.c | 308 ++++
|
|
core/drivers/stm32_rng.c | 200 +++
|
|
core/drivers/stm32_rtc.c | 503 ++++++
|
|
core/drivers/stm32_timer.c | 272 ++++
|
|
core/drivers/stm32_uart.c | 142 +-
|
|
core/drivers/stpmic1.c | 954 ++++++++++++
|
|
core/drivers/sub.mk | 9 +
|
|
core/include/drivers/gic.h | 29 +
|
|
core/include/drivers/stm32_bsec.h | 143 ++
|
|
core/include/drivers/stm32_etzpc.h | 75 +
|
|
core/include/drivers/stm32_gpio.h | 107 ++
|
|
core/include/drivers/stm32_i2c.h | 377 +++++
|
|
core/include/drivers/stm32_iwdg.h | 17 +
|
|
core/include/drivers/stm32_rng.h | 14 +
|
|
core/include/drivers/stm32_rtc.h | 73 +
|
|
core/include/drivers/stm32_timer.h | 25 +
|
|
core/include/drivers/stm32_uart.h | 10 +-
|
|
core/include/drivers/stpmic1.h | 228 +++
|
|
core/include/dt-bindings/clock/stm32mp1-clks.h | 252 +++
|
|
core/include/dt-bindings/clock/stm32mp1-clksrc.h | 284 ++++
|
|
core/include/dt-bindings/etzpc/stm32-etzpc.h | 108 ++
|
|
.../dt-bindings/interrupt-controller/arm-gic.h | 21 +
|
|
core/include/dt-bindings/pinctrl/stm32-pinfunc.h | 41 +
|
|
core/include/dt-bindings/power/stm32mp1-power.h | 19 +
|
|
core/include/dt-bindings/reset/stm32mp1-resets.h | 108 ++
|
|
core/include/kernel/interrupt.h | 15 +
|
|
core/kernel/console.c | 9 +-
|
|
core/kernel/interrupt.c | 10 +
|
|
core/lib/libfdt/fdt_ro.c | 154 +-
|
|
core/lib/libfdt/fdt_rw.c | 11 +-
|
|
core/lib/libfdt/fdt_wip.c | 29 +-
|
|
core/lib/libfdt/include/fdt.h | 8 +
|
|
core/lib/libfdt/include/libfdt.h | 247 ++-
|
|
core/secure_dt.mk | 107 ++
|
|
core/sub.mk | 7 +
|
|
lib/libutils/ext/include/util.h | 7 +
|
|
mk/config.mk | 23 +-
|
|
scripts/bin_to_c.py | 58 +
|
|
116 files changed, 21451 insertions(+), 195 deletions(-)
|
|
create mode 100644 core/arch/arm/fdts/stm32mp15-ddr.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157-pinctrl.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157a-dk1.dts
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157c-dk2.dts
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157c-ed1.dts
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157c-ev1.dts
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157c-security.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157c.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157caa-pinctrl.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157cab-pinctrl.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157cac-pinctrl.dtsi
|
|
create mode 100644 core/arch/arm/fdts/stm32mp157cad-pinctrl.dtsi
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32_reset.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32_reset.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_calib.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/drivers/sub.mk
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/context.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/context.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/context_asm_defines.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/low_power.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/pm_helpers.S
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/power.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/power_config.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/psci.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/pm/sub.mk
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/bsec_svc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/bsec_svc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/low_power_svc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/low_power_svc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/pwr_svc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/pwr_svc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/rcc_svc.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/rcc_svc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/stm32mp1_smc.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/stm32mp1_svc_setup.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/service/sub.mk
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/shared_resources.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/stm32_util.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/stm32mp1_dt.c
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/stm32mp_dt.h
|
|
create mode 100644 core/arch/arm/plat-stm32mp1/stm32mp_pm.h
|
|
create mode 100644 core/drivers/stm32_bsec.c
|
|
create mode 100644 core/drivers/stm32_etzpc.c
|
|
create mode 100644 core/drivers/stm32_gpio.c
|
|
create mode 100644 core/drivers/stm32_i2c.c
|
|
create mode 100644 core/drivers/stm32_iwdg.c
|
|
create mode 100644 core/drivers/stm32_rng.c
|
|
create mode 100644 core/drivers/stm32_rtc.c
|
|
create mode 100644 core/drivers/stm32_timer.c
|
|
create mode 100644 core/drivers/stpmic1.c
|
|
create mode 100644 core/include/drivers/stm32_bsec.h
|
|
create mode 100644 core/include/drivers/stm32_etzpc.h
|
|
create mode 100644 core/include/drivers/stm32_gpio.h
|
|
create mode 100644 core/include/drivers/stm32_i2c.h
|
|
create mode 100644 core/include/drivers/stm32_iwdg.h
|
|
create mode 100644 core/include/drivers/stm32_rng.h
|
|
create mode 100644 core/include/drivers/stm32_rtc.h
|
|
create mode 100644 core/include/drivers/stm32_timer.h
|
|
create mode 100644 core/include/drivers/stpmic1.h
|
|
create mode 100644 core/include/dt-bindings/clock/stm32mp1-clks.h
|
|
create mode 100644 core/include/dt-bindings/clock/stm32mp1-clksrc.h
|
|
create mode 100644 core/include/dt-bindings/etzpc/stm32-etzpc.h
|
|
create mode 100644 core/include/dt-bindings/interrupt-controller/arm-gic.h
|
|
create mode 100644 core/include/dt-bindings/pinctrl/stm32-pinfunc.h
|
|
create mode 100644 core/include/dt-bindings/power/stm32mp1-power.h
|
|
create mode 100644 core/include/dt-bindings/reset/stm32mp1-resets.h
|
|
create mode 100644 core/secure_dt.mk
|
|
create mode 100755 scripts/bin_to_c.py
|
|
|
|
diff --git a/core/arch/arm/fdts/stm32mp15-ddr.dtsi b/core/arch/arm/fdts/stm32mp15-ddr.dtsi
|
|
new file mode 100644
|
|
index 0000000..1a5c51c
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp15-ddr.dtsi
|
|
@@ -0,0 +1,153 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+/ {
|
|
+ soc {
|
|
+ ddr: ddr@5A003000{
|
|
+
|
|
+ compatible = "st,stm32mp1-ddr";
|
|
+
|
|
+ reg = <0x5A003000 0x550
|
|
+ 0x5A004000 0x234>;
|
|
+
|
|
+ clocks = <&rcc AXIDCG>,
|
|
+ <&rcc DDRC1>,
|
|
+ <&rcc DDRC2>,
|
|
+ <&rcc DDRPHYC>,
|
|
+ <&rcc DDRCAPB>,
|
|
+ <&rcc DDRPHYCAPB>;
|
|
+
|
|
+ clock-names = "axidcg",
|
|
+ "ddrc1",
|
|
+ "ddrc2",
|
|
+ "ddrphyc",
|
|
+ "ddrcapb",
|
|
+ "ddrphycapb";
|
|
+
|
|
+ st,mem-name = DDR_MEM_NAME;
|
|
+ st,mem-speed = <DDR_MEM_SPEED>;
|
|
+ st,mem-size = <DDR_MEM_SIZE>;
|
|
+
|
|
+ st,ctl-reg = <
|
|
+ DDR_MSTR
|
|
+ DDR_MRCTRL0
|
|
+ DDR_MRCTRL1
|
|
+ DDR_DERATEEN
|
|
+ DDR_DERATEINT
|
|
+ DDR_PWRCTL
|
|
+ DDR_PWRTMG
|
|
+ DDR_HWLPCTL
|
|
+ DDR_RFSHCTL0
|
|
+ DDR_RFSHCTL3
|
|
+ DDR_CRCPARCTL0
|
|
+ DDR_ZQCTL0
|
|
+ DDR_DFITMG0
|
|
+ DDR_DFITMG1
|
|
+ DDR_DFILPCFG0
|
|
+ DDR_DFIUPD0
|
|
+ DDR_DFIUPD1
|
|
+ DDR_DFIUPD2
|
|
+ DDR_DFIPHYMSTR
|
|
+ DDR_ODTMAP
|
|
+ DDR_DBG0
|
|
+ DDR_DBG1
|
|
+ DDR_DBGCMD
|
|
+ DDR_POISONCFG
|
|
+ DDR_PCCFG
|
|
+ >;
|
|
+
|
|
+ st,ctl-timing = <
|
|
+ DDR_RFSHTMG
|
|
+ DDR_DRAMTMG0
|
|
+ DDR_DRAMTMG1
|
|
+ DDR_DRAMTMG2
|
|
+ DDR_DRAMTMG3
|
|
+ DDR_DRAMTMG4
|
|
+ DDR_DRAMTMG5
|
|
+ DDR_DRAMTMG6
|
|
+ DDR_DRAMTMG7
|
|
+ DDR_DRAMTMG8
|
|
+ DDR_DRAMTMG14
|
|
+ DDR_ODTCFG
|
|
+ >;
|
|
+
|
|
+ st,ctl-map = <
|
|
+ DDR_ADDRMAP1
|
|
+ DDR_ADDRMAP2
|
|
+ DDR_ADDRMAP3
|
|
+ DDR_ADDRMAP4
|
|
+ DDR_ADDRMAP5
|
|
+ DDR_ADDRMAP6
|
|
+ DDR_ADDRMAP9
|
|
+ DDR_ADDRMAP10
|
|
+ DDR_ADDRMAP11
|
|
+ >;
|
|
+
|
|
+ st,ctl-perf = <
|
|
+ DDR_SCHED
|
|
+ DDR_SCHED1
|
|
+ DDR_PERFHPR1
|
|
+ DDR_PERFLPR1
|
|
+ DDR_PERFWR1
|
|
+ DDR_PCFGR_0
|
|
+ DDR_PCFGW_0
|
|
+ DDR_PCFGQOS0_0
|
|
+ DDR_PCFGQOS1_0
|
|
+ DDR_PCFGWQOS0_0
|
|
+ DDR_PCFGWQOS1_0
|
|
+ DDR_PCFGR_1
|
|
+ DDR_PCFGW_1
|
|
+ DDR_PCFGQOS0_1
|
|
+ DDR_PCFGQOS1_1
|
|
+ DDR_PCFGWQOS0_1
|
|
+ DDR_PCFGWQOS1_1
|
|
+ >;
|
|
+
|
|
+ st,phy-reg = <
|
|
+ DDR_PGCR
|
|
+ DDR_ACIOCR
|
|
+ DDR_DXCCR
|
|
+ DDR_DSGCR
|
|
+ DDR_DCR
|
|
+ DDR_ODTCR
|
|
+ DDR_ZQ0CR1
|
|
+ DDR_DX0GCR
|
|
+ DDR_DX1GCR
|
|
+ DDR_DX2GCR
|
|
+ DDR_DX3GCR
|
|
+ >;
|
|
+
|
|
+ st,phy-timing = <
|
|
+ DDR_PTR0
|
|
+ DDR_PTR1
|
|
+ DDR_PTR2
|
|
+ DDR_DTPR0
|
|
+ DDR_DTPR1
|
|
+ DDR_DTPR2
|
|
+ DDR_MR0
|
|
+ DDR_MR1
|
|
+ DDR_MR2
|
|
+ DDR_MR3
|
|
+ >;
|
|
+
|
|
+ st,phy-cal = <
|
|
+ DDR_DX0DLLCR
|
|
+ DDR_DX0DQTR
|
|
+ DDR_DX0DQSTR
|
|
+ DDR_DX1DLLCR
|
|
+ DDR_DX1DQTR
|
|
+ DDR_DX1DQSTR
|
|
+ DDR_DX2DLLCR
|
|
+ DDR_DX2DQTR
|
|
+ DDR_DX2DQSTR
|
|
+ DDR_DX3DLLCR
|
|
+ DDR_DX3DQTR
|
|
+ DDR_DX3DQSTR
|
|
+ >;
|
|
+
|
|
+ status = "okay";
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi b/core/arch/arm/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi
|
|
new file mode 100644
|
|
index 0000000..16b8cf6
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi
|
|
@@ -0,0 +1,121 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+/* STM32MP157C DK1/DK2 BOARD configuration
|
|
+ * 1x DDR3L 4Gb, 16-bit, 533MHz.
|
|
+ * Reference used NT5CC256M16DP-DI from NANYA
|
|
+ *
|
|
+ * DDR type / Platform DDR3/3L
|
|
+ * freq 533MHz
|
|
+ * width 16
|
|
+ * datasheet 0 = MT41J256M16-187 / DDR3-1066 bin G
|
|
+ * DDR density 4
|
|
+ * timing mode optimized
|
|
+ * Scheduling/QoS options : type = 2
|
|
+ * address mapping : RBC
|
|
+ * Tc > + 85C : N
|
|
+ */
|
|
+
|
|
+#define DDR_MEM_NAME "DDR3-1066/888 bin G 1x4Gb 533MHz v1.41"
|
|
+#define DDR_MEM_SPEED 533000
|
|
+#define DDR_MEM_SIZE 0x20000000
|
|
+
|
|
+#define DDR_MSTR 0x00041401
|
|
+#define DDR_MRCTRL0 0x00000010
|
|
+#define DDR_MRCTRL1 0x00000000
|
|
+#define DDR_DERATEEN 0x00000000
|
|
+#define DDR_DERATEINT 0x00800000
|
|
+#define DDR_PWRCTL 0x00000000
|
|
+#define DDR_PWRTMG 0x00400010
|
|
+#define DDR_HWLPCTL 0x00000000
|
|
+#define DDR_RFSHCTL0 0x00210000
|
|
+#define DDR_RFSHCTL3 0x00000000
|
|
+#define DDR_RFSHTMG 0x0081008B
|
|
+#define DDR_CRCPARCTL0 0x00000000
|
|
+#define DDR_DRAMTMG0 0x121B2414
|
|
+#define DDR_DRAMTMG1 0x000A041C
|
|
+#define DDR_DRAMTMG2 0x0608090F
|
|
+#define DDR_DRAMTMG3 0x0050400C
|
|
+#define DDR_DRAMTMG4 0x08040608
|
|
+#define DDR_DRAMTMG5 0x06060403
|
|
+#define DDR_DRAMTMG6 0x02020002
|
|
+#define DDR_DRAMTMG7 0x00000202
|
|
+#define DDR_DRAMTMG8 0x00001005
|
|
+#define DDR_DRAMTMG14 0x000000A0
|
|
+#define DDR_ZQCTL0 0xC2000040
|
|
+#define DDR_DFITMG0 0x02060105
|
|
+#define DDR_DFITMG1 0x00000202
|
|
+#define DDR_DFILPCFG0 0x07000000
|
|
+#define DDR_DFIUPD0 0xC0400003
|
|
+#define DDR_DFIUPD1 0x00000000
|
|
+#define DDR_DFIUPD2 0x00000000
|
|
+#define DDR_DFIPHYMSTR 0x00000000
|
|
+#define DDR_ADDRMAP1 0x00070707
|
|
+#define DDR_ADDRMAP2 0x00000000
|
|
+#define DDR_ADDRMAP3 0x1F000000
|
|
+#define DDR_ADDRMAP4 0x00001F1F
|
|
+#define DDR_ADDRMAP5 0x06060606
|
|
+#define DDR_ADDRMAP6 0x0F060606
|
|
+#define DDR_ADDRMAP9 0x00000000
|
|
+#define DDR_ADDRMAP10 0x00000000
|
|
+#define DDR_ADDRMAP11 0x00000000
|
|
+#define DDR_ODTCFG 0x06000600
|
|
+#define DDR_ODTMAP 0x00000001
|
|
+#define DDR_SCHED 0x00000C01
|
|
+#define DDR_SCHED1 0x00000000
|
|
+#define DDR_PERFHPR1 0x01000001
|
|
+#define DDR_PERFLPR1 0x08000200
|
|
+#define DDR_PERFWR1 0x08000400
|
|
+#define DDR_DBG0 0x00000000
|
|
+#define DDR_DBG1 0x00000000
|
|
+#define DDR_DBGCMD 0x00000000
|
|
+#define DDR_POISONCFG 0x00000000
|
|
+#define DDR_PCCFG 0x00000010
|
|
+#define DDR_PCFGR_0 0x00010000
|
|
+#define DDR_PCFGW_0 0x00000000
|
|
+#define DDR_PCFGQOS0_0 0x02100C03
|
|
+#define DDR_PCFGQOS1_0 0x00800100
|
|
+#define DDR_PCFGWQOS0_0 0x01100C03
|
|
+#define DDR_PCFGWQOS1_0 0x01000200
|
|
+#define DDR_PCFGR_1 0x00010000
|
|
+#define DDR_PCFGW_1 0x00000000
|
|
+#define DDR_PCFGQOS0_1 0x02100C03
|
|
+#define DDR_PCFGQOS1_1 0x00800040
|
|
+#define DDR_PCFGWQOS0_1 0x01100C03
|
|
+#define DDR_PCFGWQOS1_1 0x01000200
|
|
+#define DDR_PGCR 0x01442E02
|
|
+#define DDR_PTR0 0x0022AA5B
|
|
+#define DDR_PTR1 0x04841104
|
|
+#define DDR_PTR2 0x042DA068
|
|
+#define DDR_ACIOCR 0x10400812
|
|
+#define DDR_DXCCR 0x00000C40
|
|
+#define DDR_DSGCR 0xF200001F
|
|
+#define DDR_DCR 0x0000000B
|
|
+#define DDR_DTPR0 0x38D488D0
|
|
+#define DDR_DTPR1 0x098B00D8
|
|
+#define DDR_DTPR2 0x10023600
|
|
+#define DDR_MR0 0x00000840
|
|
+#define DDR_MR1 0x00000000
|
|
+#define DDR_MR2 0x00000208
|
|
+#define DDR_MR3 0x00000000
|
|
+#define DDR_ODTCR 0x00010000
|
|
+#define DDR_ZQ0CR1 0x00000038
|
|
+#define DDR_DX0GCR 0x0000CE81
|
|
+#define DDR_DX0DLLCR 0x40000000
|
|
+#define DDR_DX0DQTR 0xFFFFFFFF
|
|
+#define DDR_DX0DQSTR 0x3DB02000
|
|
+#define DDR_DX1GCR 0x0000CE81
|
|
+#define DDR_DX1DLLCR 0x40000000
|
|
+#define DDR_DX1DQTR 0xFFFFFFFF
|
|
+#define DDR_DX1DQSTR 0x3DB02000
|
|
+#define DDR_DX2GCR 0x0000CE81
|
|
+#define DDR_DX2DLLCR 0x40000000
|
|
+#define DDR_DX2DQTR 0xFFFFFFFF
|
|
+#define DDR_DX2DQSTR 0x3DB02000
|
|
+#define DDR_DX3GCR 0x0000CE81
|
|
+#define DDR_DX3DLLCR 0x40000000
|
|
+#define DDR_DX3DQTR 0xFFFFFFFF
|
|
+#define DDR_DX3DQSTR 0x3DB02000
|
|
+
|
|
+#include "stm32mp15-ddr.dtsi"
|
|
diff --git a/core/arch/arm/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi b/core/arch/arm/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi
|
|
new file mode 100644
|
|
index 0000000..82e7104
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi
|
|
@@ -0,0 +1,122 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+/* STM32MP157C ED1 BOARD configuration
|
|
+ * 2x DDR3L 4Gb each, 16-bit, 533MHz, Single Die Package in flyby topology.
|
|
+ * Reference used NT5CC256M16DP-DI from NANYA
|
|
+ *
|
|
+ * DDR type / Platform DDR3/3L
|
|
+ * freq 533MHz
|
|
+ * width 32
|
|
+ * datasheet 0 = MT41J256M16-187 / DDR3-1066 bin G
|
|
+ * DDR density 8
|
|
+ * timing mode optimized
|
|
+ * Scheduling/QoS options : type = 2
|
|
+ * address mapping : RBC
|
|
+ * Tc > + 85C : N
|
|
+ */
|
|
+
|
|
+#define DDR_MEM_NAME "DDR3-1066/888 bin G 2x4Gb 533MHz v1.41"
|
|
+#define DDR_MEM_SPEED 533000
|
|
+#define DDR_MEM_SIZE 0x40000000
|
|
+
|
|
+#define DDR_MSTR 0x00040401
|
|
+#define DDR_MRCTRL0 0x00000010
|
|
+#define DDR_MRCTRL1 0x00000000
|
|
+#define DDR_DERATEEN 0x00000000
|
|
+#define DDR_DERATEINT 0x00800000
|
|
+#define DDR_PWRCTL 0x00000000
|
|
+#define DDR_PWRTMG 0x00400010
|
|
+#define DDR_HWLPCTL 0x00000000
|
|
+#define DDR_RFSHCTL0 0x00210000
|
|
+#define DDR_RFSHCTL3 0x00000000
|
|
+#define DDR_RFSHTMG 0x0081008B
|
|
+#define DDR_CRCPARCTL0 0x00000000
|
|
+#define DDR_DRAMTMG0 0x121B2414
|
|
+#define DDR_DRAMTMG1 0x000A041C
|
|
+#define DDR_DRAMTMG2 0x0608090F
|
|
+#define DDR_DRAMTMG3 0x0050400C
|
|
+#define DDR_DRAMTMG4 0x08040608
|
|
+#define DDR_DRAMTMG5 0x06060403
|
|
+#define DDR_DRAMTMG6 0x02020002
|
|
+#define DDR_DRAMTMG7 0x00000202
|
|
+#define DDR_DRAMTMG8 0x00001005
|
|
+#define DDR_DRAMTMG14 0x000000A0
|
|
+#define DDR_ZQCTL0 0xC2000040
|
|
+#define DDR_DFITMG0 0x02060105
|
|
+#define DDR_DFITMG1 0x00000202
|
|
+#define DDR_DFILPCFG0 0x07000000
|
|
+#define DDR_DFIUPD0 0xC0400003
|
|
+#define DDR_DFIUPD1 0x00000000
|
|
+#define DDR_DFIUPD2 0x00000000
|
|
+#define DDR_DFIPHYMSTR 0x00000000
|
|
+#define DDR_ADDRMAP1 0x00080808
|
|
+#define DDR_ADDRMAP2 0x00000000
|
|
+#define DDR_ADDRMAP3 0x00000000
|
|
+#define DDR_ADDRMAP4 0x00001F1F
|
|
+#define DDR_ADDRMAP5 0x07070707
|
|
+#define DDR_ADDRMAP6 0x0F070707
|
|
+#define DDR_ADDRMAP9 0x00000000
|
|
+#define DDR_ADDRMAP10 0x00000000
|
|
+#define DDR_ADDRMAP11 0x00000000
|
|
+#define DDR_ODTCFG 0x06000600
|
|
+#define DDR_ODTMAP 0x00000001
|
|
+#define DDR_SCHED 0x00000C01
|
|
+#define DDR_SCHED1 0x00000000
|
|
+#define DDR_PERFHPR1 0x01000001
|
|
+#define DDR_PERFLPR1 0x08000200
|
|
+#define DDR_PERFWR1 0x08000400
|
|
+#define DDR_DBG0 0x00000000
|
|
+#define DDR_DBG1 0x00000000
|
|
+#define DDR_DBGCMD 0x00000000
|
|
+#define DDR_POISONCFG 0x00000000
|
|
+#define DDR_PCCFG 0x00000010
|
|
+#define DDR_PCFGR_0 0x00010000
|
|
+#define DDR_PCFGW_0 0x00000000
|
|
+#define DDR_PCFGQOS0_0 0x02100C03
|
|
+#define DDR_PCFGQOS1_0 0x00800100
|
|
+#define DDR_PCFGWQOS0_0 0x01100C03
|
|
+#define DDR_PCFGWQOS1_0 0x01000200
|
|
+#define DDR_PCFGR_1 0x00010000
|
|
+#define DDR_PCFGW_1 0x00000000
|
|
+#define DDR_PCFGQOS0_1 0x02100C03
|
|
+#define DDR_PCFGQOS1_1 0x00800040
|
|
+#define DDR_PCFGWQOS0_1 0x01100C03
|
|
+#define DDR_PCFGWQOS1_1 0x01000200
|
|
+#define DDR_PGCR 0x01442E02
|
|
+#define DDR_PTR0 0x0022AA5B
|
|
+#define DDR_PTR1 0x04841104
|
|
+#define DDR_PTR2 0x042DA068
|
|
+#define DDR_ACIOCR 0x10400812
|
|
+#define DDR_DXCCR 0x00000C40
|
|
+#define DDR_DSGCR 0xF200001F
|
|
+#define DDR_DCR 0x0000000B
|
|
+#define DDR_DTPR0 0x38D488D0
|
|
+#define DDR_DTPR1 0x098B00D8
|
|
+#define DDR_DTPR2 0x10023600
|
|
+#define DDR_MR0 0x00000840
|
|
+#define DDR_MR1 0x00000000
|
|
+#define DDR_MR2 0x00000208
|
|
+#define DDR_MR3 0x00000000
|
|
+#define DDR_ODTCR 0x00010000
|
|
+#define DDR_ZQ0CR1 0x00000038
|
|
+#define DDR_DX0GCR 0x0000CE81
|
|
+#define DDR_DX0DLLCR 0x40000000
|
|
+#define DDR_DX0DQTR 0xFFFFFFFF
|
|
+#define DDR_DX0DQSTR 0x3DB02000
|
|
+#define DDR_DX1GCR 0x0000CE81
|
|
+#define DDR_DX1DLLCR 0x40000000
|
|
+#define DDR_DX1DQTR 0xFFFFFFFF
|
|
+#define DDR_DX1DQSTR 0x3DB02000
|
|
+#define DDR_DX2GCR 0x0000CE81
|
|
+#define DDR_DX2DLLCR 0x40000000
|
|
+#define DDR_DX2DQTR 0xFFFFFFFF
|
|
+#define DDR_DX2DQSTR 0x3DB02000
|
|
+#define DDR_DX3GCR 0x0000CE81
|
|
+#define DDR_DX3DLLCR 0x40000000
|
|
+#define DDR_DX3DQTR 0xFFFFFFFF
|
|
+#define DDR_DX3DQSTR 0x3DB02000
|
|
+
|
|
+#include "stm32mp15-ddr.dtsi"
|
|
diff --git a/core/arch/arm/fdts/stm32mp157-pinctrl.dtsi b/core/arch/arm/fdts/stm32mp157-pinctrl.dtsi
|
|
new file mode 100644
|
|
index 0000000..8037e4f
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157-pinctrl.dtsi
|
|
@@ -0,0 +1,350 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
|
|
+ * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
|
|
+ */
|
|
+#include <dt-bindings/pinctrl/stm32-pinfunc.h>
|
|
+
|
|
+/ {
|
|
+ soc {
|
|
+ pinctrl: pin-controller@50002000 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ compatible = "st,stm32mp157-pinctrl";
|
|
+ ranges = <0 0x50002000 0xa400>;
|
|
+ pins-are-numbered;
|
|
+
|
|
+ gpioa: gpio@50002000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x0 0x400>;
|
|
+ clocks = <&rcc GPIOA>;
|
|
+ st,bank-name = "GPIOA";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpiob: gpio@50003000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x1000 0x400>;
|
|
+ clocks = <&rcc GPIOB>;
|
|
+ st,bank-name = "GPIOB";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpioc: gpio@50004000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x2000 0x400>;
|
|
+ clocks = <&rcc GPIOC>;
|
|
+ st,bank-name = "GPIOC";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpiod: gpio@50005000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x3000 0x400>;
|
|
+ clocks = <&rcc GPIOD>;
|
|
+ st,bank-name = "GPIOD";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpioe: gpio@50006000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x4000 0x400>;
|
|
+ clocks = <&rcc GPIOE>;
|
|
+ st,bank-name = "GPIOE";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpiof: gpio@50007000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x5000 0x400>;
|
|
+ clocks = <&rcc GPIOF>;
|
|
+ st,bank-name = "GPIOF";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpiog: gpio@50008000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x6000 0x400>;
|
|
+ clocks = <&rcc GPIOG>;
|
|
+ st,bank-name = "GPIOG";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpioh: gpio@50009000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x7000 0x400>;
|
|
+ clocks = <&rcc GPIOH>;
|
|
+ st,bank-name = "GPIOH";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpioi: gpio@5000a000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x8000 0x400>;
|
|
+ clocks = <&rcc GPIOI>;
|
|
+ st,bank-name = "GPIOI";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpioj: gpio@5000b000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x9000 0x400>;
|
|
+ clocks = <&rcc GPIOJ>;
|
|
+ st,bank-name = "GPIOJ";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ gpiok: gpio@5000c000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0xa000 0x400>;
|
|
+ clocks = <&rcc GPIOK>;
|
|
+ st,bank-name = "GPIOK";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ qspi_bk1_pins_a: qspi-bk1-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('F', 8, AF10)>, /* QSPI_BK1_IO0 */
|
|
+ <STM32_PINMUX('F', 9, AF10)>, /* QSPI_BK1_IO1 */
|
|
+ <STM32_PINMUX('F', 7, AF9)>, /* QSPI_BK1_IO2 */
|
|
+ <STM32_PINMUX('F', 6, AF9)>; /* QSPI_BK1_IO3 */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <1>;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('B', 6, AF10)>; /* QSPI_BK1_NCS */
|
|
+ bias-pull-up;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <1>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ qspi_bk2_pins_a: qspi-bk2-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('H', 2, AF9)>, /* QSPI_BK2_IO0 */
|
|
+ <STM32_PINMUX('H', 3, AF9)>, /* QSPI_BK2_IO1 */
|
|
+ <STM32_PINMUX('G', 10, AF11)>, /* QSPI_BK2_IO2 */
|
|
+ <STM32_PINMUX('G', 7, AF11)>; /* QSPI_BK2_IO3 */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <1>;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('C', 0, AF10)>; /* QSPI_BK2_NCS */
|
|
+ bias-pull-up;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <1>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ qspi_clk_pins_a: qspi-clk-0 {
|
|
+ pins {
|
|
+ pinmux = <STM32_PINMUX('F', 10, AF9)>; /* QSPI_CLK */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <3>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ sdmmc1_b4_pins_a: sdmmc1-b4-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('C', 8, AF12)>, /* SDMMC1_D0 */
|
|
+ <STM32_PINMUX('C', 9, AF12)>, /* SDMMC1_D1 */
|
|
+ <STM32_PINMUX('C', 10, AF12)>, /* SDMMC1_D2 */
|
|
+ <STM32_PINMUX('C', 11, AF12)>, /* SDMMC1_D3 */
|
|
+ <STM32_PINMUX('D', 2, AF12)>; /* SDMMC1_CMD */
|
|
+ slew-rate = <1>;
|
|
+ drive-push-pull;
|
|
+ bias-disable;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('C', 12, AF12)>; /* SDMMC1_CK */
|
|
+ slew-rate = <2>;
|
|
+ drive-push-pull;
|
|
+ bias-disable;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ sdmmc1_dir_pins_a: sdmmc1-dir-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('F', 2, AF11)>, /* SDMMC1_D0DIR */
|
|
+ <STM32_PINMUX('C', 7, AF8)>, /* SDMMC1_D123DIR */
|
|
+ <STM32_PINMUX('B', 9, AF11)>; /* SDMMC1_CDIR */
|
|
+ slew-rate = <1>;
|
|
+ drive-push-pull;
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ pins2{
|
|
+ pinmux = <STM32_PINMUX('E', 4, AF8)>; /* SDMMC1_CKIN */
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ sdmmc1_dir_pins_b: sdmmc1-dir-1 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('E', 12, AF8)>, /* SDMMC1_D0DIR */
|
|
+ <STM32_PINMUX('E', 14, AF11)>, /* SDMMC1_D123DIR */
|
|
+ <STM32_PINMUX('B', 9, AF11)>; /* SDMMC1_CDIR */
|
|
+ slew-rate = <3>;
|
|
+ drive-push-pull;
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('E', 4, AF8)>; /* SDMMC1_CKIN */
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ sdmmc2_b4_pins_a: sdmmc2-b4-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('B', 14, AF9)>, /* SDMMC2_D0 */
|
|
+ <STM32_PINMUX('B', 15, AF9)>, /* SDMMC2_D1 */
|
|
+ <STM32_PINMUX('B', 3, AF9)>, /* SDMMC2_D2 */
|
|
+ <STM32_PINMUX('B', 4, AF9)>, /* SDMMC2_D3 */
|
|
+ <STM32_PINMUX('G', 6, AF10)>; /* SDMMC2_CMD */
|
|
+ slew-rate = <1>;
|
|
+ drive-push-pull;
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('E', 3, AF9)>; /* SDMMC2_CK */
|
|
+ slew-rate = <2>;
|
|
+ drive-push-pull;
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ sdmmc2_d47_pins_a: sdmmc2-d47-0 {
|
|
+ pins {
|
|
+ pinmux = <STM32_PINMUX('A', 8, AF9)>, /* SDMMC2_D4 */
|
|
+ <STM32_PINMUX('A', 9, AF10)>, /* SDMMC2_D5 */
|
|
+ <STM32_PINMUX('E', 5, AF9)>, /* SDMMC2_D6 */
|
|
+ <STM32_PINMUX('D', 3, AF9)>; /* SDMMC2_D7 */
|
|
+ slew-rate = <1>;
|
|
+ drive-push-pull;
|
|
+ bias-pull-up;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ uart4_pins_a: uart4-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('G', 11, AF6)>; /* UART4_TX */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <0>;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('B', 2, AF8)>; /* UART4_RX */
|
|
+ bias-disable;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ uart7_pins_a: uart7-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('E', 8, AF7)>; /* USART7_TX */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <0>;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('E', 7, AF7)>; /* USART7_RX */
|
|
+ bias-disable;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ usart3_pins_a: usart3-0 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('B', 10, AF7)>, /* USART3_TX */
|
|
+ <STM32_PINMUX('G', 8, AF8)>; /* USART3_RTS */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <0>;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('B', 12, AF8)>, /* USART3_RX */
|
|
+ <STM32_PINMUX('I', 10, AF8)>; /* USART3_CTS_NSS */
|
|
+ bias-disable;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ usart3_pins_b: usart3-1 {
|
|
+ pins1 {
|
|
+ pinmux = <STM32_PINMUX('B', 10, AF7)>, /* USART3_TX */
|
|
+ <STM32_PINMUX('G', 8, AF8)>; /* USART3_RTS */
|
|
+ bias-disable;
|
|
+ drive-push-pull;
|
|
+ slew-rate = <0>;
|
|
+ };
|
|
+ pins2 {
|
|
+ pinmux = <STM32_PINMUX('B', 12, AF8)>, /* USART3_RX */
|
|
+ <STM32_PINMUX('B', 13, AF7)>; /* USART3_CTS_NSS */
|
|
+ bias-disable;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ pinctrl_z: pin-controller-z@54004000 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ compatible = "st,stm32mp157-z-pinctrl";
|
|
+ ranges = <0 0x54004000 0x400>;
|
|
+ pins-are-numbered;
|
|
+
|
|
+ gpioz: gpio@54004000 {
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0 0x400>;
|
|
+ clocks = <&rcc GPIOZ>;
|
|
+ st,bank-name = "GPIOZ";
|
|
+ st,bank-ioport = <11>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ i2c4_pins_a: i2c4-0 {
|
|
+ pins {
|
|
+ pinmux = <STM32_PINMUX('Z', 4, AF6)>, /* I2C4_SCL */
|
|
+ <STM32_PINMUX('Z', 5, AF6)>; /* I2C4_SDA */
|
|
+ bias-disable;
|
|
+ drive-open-drain;
|
|
+ slew-rate = <0>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157a-dk1.dts b/core/arch/arm/fdts/stm32mp157a-dk1.dts
|
|
new file mode 100644
|
|
index 0000000..e42bdcd
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157a-dk1.dts
|
|
@@ -0,0 +1,380 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018-2019 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>.
|
|
+ */
|
|
+
|
|
+/dts-v1/;
|
|
+
|
|
+#include "stm32mp157c.dtsi"
|
|
+#include "stm32mp157cac-pinctrl.dtsi"
|
|
+
|
|
+/ {
|
|
+ model = "STMicroelectronics STM32MP157A-DK1 Discovery Board";
|
|
+ compatible = "st,stm32mp157a-dk1", "st,stm32mp157";
|
|
+
|
|
+ aliases {
|
|
+ serial0 = &uart4;
|
|
+ serial1 = &usart3;
|
|
+ serial2 = &uart7;
|
|
+ };
|
|
+
|
|
+ chosen {
|
|
+ stdout-path = "serial0:115200n8";
|
|
+};
|
|
+
|
|
+};
|
|
+
|
|
+&clk_hse {
|
|
+ st,digbypass;
|
|
+};
|
|
+
|
|
+&i2c4 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&i2c4_pins_a>;
|
|
+ i2c-scl-rising-time-ns = <185>;
|
|
+ i2c-scl-falling-time-ns = <20>;
|
|
+ status = "okay";
|
|
+
|
|
+ pmic: stpmic@33 {
|
|
+ compatible = "st,stpmic1";
|
|
+ reg = <0x33>;
|
|
+ interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ status = "okay";
|
|
+
|
|
+ st,main-control-register = <0x04>;
|
|
+ st,vin-control-register = <0xc0>;
|
|
+ st,usb-control-register = <0x20>;
|
|
+
|
|
+ regulators {
|
|
+ compatible = "st,stpmic1-regulators";
|
|
+
|
|
+ ldo1-supply = <&v3v3>;
|
|
+ ldo3-supply = <&vdd_ddr>;
|
|
+ ldo6-supply = <&v3v3>;
|
|
+
|
|
+ vddcore: buck1 {
|
|
+ regulator-name = "vddcore";
|
|
+ regulator-min-microvolt = <1200000>;
|
|
+ regulator-max-microvolt = <1350000>;
|
|
+ regulator-always-on;
|
|
+ regulator-initial-mode = <0>;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ vdd_ddr: buck2 {
|
|
+ regulator-name = "vdd_ddr";
|
|
+ regulator-min-microvolt = <1350000>;
|
|
+ regulator-max-microvolt = <1350000>;
|
|
+ regulator-always-on;
|
|
+ regulator-initial-mode = <0>;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ vdd: buck3 {
|
|
+ regulator-name = "vdd";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ regulator-always-on;
|
|
+ st,mask-reset;
|
|
+ regulator-initial-mode = <0>;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ v3v3: buck4 {
|
|
+ regulator-name = "v3v3";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ regulator-always-on;
|
|
+ regulator-over-current-protection;
|
|
+ regulator-initial-mode = <0>;
|
|
+ };
|
|
+
|
|
+ v1v8_audio: ldo1 {
|
|
+ regulator-name = "v1v8_audio";
|
|
+ regulator-min-microvolt = <1800000>;
|
|
+ regulator-max-microvolt = <1800000>;
|
|
+ regulator-always-on;
|
|
+ };
|
|
+
|
|
+ v3v3_hdmi: ldo2 {
|
|
+ regulator-name = "v3v3_hdmi";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ regulator-always-on;
|
|
+ };
|
|
+
|
|
+ vtt_ddr: ldo3 {
|
|
+ regulator-name = "vtt_ddr";
|
|
+ regulator-min-microvolt = <500000>;
|
|
+ regulator-max-microvolt = <750000>;
|
|
+ regulator-always-on;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ vdd_usb: ldo4 {
|
|
+ regulator-name = "vdd_usb";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ };
|
|
+
|
|
+ vdda: ldo5 {
|
|
+ regulator-name = "vdda";
|
|
+ regulator-min-microvolt = <2900000>;
|
|
+ regulator-max-microvolt = <2900000>;
|
|
+ regulator-boot-on;
|
|
+ };
|
|
+
|
|
+ v1v2_hdmi: ldo6 {
|
|
+ regulator-name = "v1v2_hdmi";
|
|
+ regulator-min-microvolt = <1200000>;
|
|
+ regulator-max-microvolt = <1200000>;
|
|
+ regulator-always-on;
|
|
+ };
|
|
+
|
|
+ vref_ddr: vref_ddr {
|
|
+ regulator-name = "vref_ddr";
|
|
+ regulator-always-on;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&iwdg2 {
|
|
+ timeout-sec = <32>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&pwr {
|
|
+ pwr-supply = <&vdd>;
|
|
+};
|
|
+
|
|
+/* RNG1 is used by non secure at run time and by secure (low power sequences) */
|
|
+&rng1 {
|
|
+ status = "okay";
|
|
+ secure-status = "okay";
|
|
+};
|
|
+
|
|
+&rtc {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&sdmmc1 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&sdmmc1_b4_pins_a>;
|
|
+ broken-cd;
|
|
+ st,neg-edge;
|
|
+ bus-width = <4>;
|
|
+ vmmc-supply = <&v3v3>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&uart4 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&uart4_pins_a>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&uart7 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&uart7_pins_a>;
|
|
+ status = "disabled";
|
|
+};
|
|
+
|
|
+&usart3 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&usart3_pins_b>;
|
|
+ status = "disabled";
|
|
+};
|
|
+
|
|
+
|
|
+/* ATF Specific */
|
|
+#include <dt-bindings/clock/stm32mp1-clksrc.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi"
|
|
+#include "stm32mp157c-security.dtsi"
|
|
+
|
|
+/ {
|
|
+ aliases {
|
|
+ gpio0 = &gpioa;
|
|
+ gpio1 = &gpiob;
|
|
+ gpio2 = &gpioc;
|
|
+ gpio3 = &gpiod;
|
|
+ gpio4 = &gpioe;
|
|
+ gpio5 = &gpiof;
|
|
+ gpio6 = &gpiog;
|
|
+ gpio7 = &gpioh;
|
|
+ gpio8 = &gpioi;
|
|
+ gpio25 = &gpioz;
|
|
+ i2c3 = &i2c4;
|
|
+ };
|
|
+};
|
|
+
|
|
+/* CLOCK presence */
|
|
+&rcc {
|
|
+ secure-status = "okay";
|
|
+ st,hsi-cal;
|
|
+ st,csi-cal;
|
|
+ st,cal-sec = <60>;
|
|
+};
|
|
+
|
|
+/* Security specific */
|
|
+&etzpc {
|
|
+ st,decprot = <
|
|
+ DECPROT(STM32MP1_ETZPC_USART1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_SPI6_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_I2C4_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_I2C6_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_RNG1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_HASH1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_CRYP1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ >;
|
|
+};
|
|
+
|
|
+&iwdg2 {
|
|
+ secure-status = "okay";
|
|
+};
|
|
+
|
|
+&pwr {
|
|
+ system_suspend_supported_soc_modes = <
|
|
+ STM32_PM_CSLEEP_RUN
|
|
+ STM32_PM_CSTOP_ALLOW_LP_STOP
|
|
+ STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR
|
|
+ >;
|
|
+
|
|
+ system_off_soc_mode = <STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF>;
|
|
+};
|
|
+
|
|
+&timers15 {
|
|
+ secure-status = "okay";
|
|
+ st,hsi-cal-input = <7>;
|
|
+ st,csi_cal-input = <8>;
|
|
+};
|
|
+
|
|
+/* Low-power states of regulators */
|
|
+&vddcore {
|
|
+ lp-stop {
|
|
+ regulator-on-in-suspend;
|
|
+ regulator-suspend-microvolt = <1200000>;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd_ddr {
|
|
+ lp-stop {
|
|
+ regulator-suspend-microvolt = <1350000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-suspend-microvolt = <1350000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd {
|
|
+ lp-stop {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v3v3 {
|
|
+ lp-stop {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v1v8_audio {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v3v3_hdmi {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vtt_ddr {
|
|
+ lp-stop {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd_usb {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdda {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v1v2_hdmi {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vref_ddr {
|
|
+ lp-stop {
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157c-dk2.dts b/core/arch/arm/fdts/stm32mp157c-dk2.dts
|
|
new file mode 100644
|
|
index 0000000..fdcf4c8
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157c-dk2.dts
|
|
@@ -0,0 +1,16 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>.
|
|
+ */
|
|
+
|
|
+/dts-v1/;
|
|
+
|
|
+#include "stm32mp157a-dk1.dts"
|
|
+
|
|
+/ {
|
|
+ model = "STMicroelectronics STM32MP157C-DK2 Discovery Board";
|
|
+ compatible = "st,stm32mp157c-dk2", "st,stm32mp157";
|
|
+
|
|
+};
|
|
+
|
|
diff --git a/core/arch/arm/fdts/stm32mp157c-ed1.dts b/core/arch/arm/fdts/stm32mp157c-ed1.dts
|
|
new file mode 100644
|
|
index 0000000..8462d23
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157c-ed1.dts
|
|
@@ -0,0 +1,381 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved
|
|
+ * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
|
|
+ */
|
|
+/dts-v1/;
|
|
+
|
|
+#include "stm32mp157c.dtsi"
|
|
+#include "stm32mp157caa-pinctrl.dtsi"
|
|
+
|
|
+/ {
|
|
+ model = "STMicroelectronics STM32MP157C eval daughter";
|
|
+ compatible = "st,stm32mp157c-ed1", "st,stm32mp157";
|
|
+
|
|
+ chosen {
|
|
+ stdout-path = "serial0:115200n8";
|
|
+ };
|
|
+
|
|
+ aliases {
|
|
+ serial0 = &uart4;
|
|
+ };
|
|
+};
|
|
+
|
|
+&clk_hse {
|
|
+ st,digbypass;
|
|
+};
|
|
+
|
|
+&i2c4 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&i2c4_pins_a>;
|
|
+ i2c-scl-rising-time-ns = <185>;
|
|
+ i2c-scl-falling-time-ns = <20>;
|
|
+ status = "okay";
|
|
+
|
|
+ pmic: stpmic@33 {
|
|
+ compatible = "st,stpmic1";
|
|
+ reg = <0x33>;
|
|
+ interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ status = "okay";
|
|
+
|
|
+ st,main-control-register = <0x04>;
|
|
+ st,vin-control-register = <0xc0>;
|
|
+ st,usb-control-register = <0x20>;
|
|
+
|
|
+ regulators {
|
|
+ compatible = "st,stpmic1-regulators";
|
|
+
|
|
+ ldo1-supply = <&v3v3>;
|
|
+ ldo2-supply = <&v3v3>;
|
|
+ ldo3-supply = <&vdd_ddr>;
|
|
+ ldo5-supply = <&v3v3>;
|
|
+ ldo6-supply = <&v3v3>;
|
|
+
|
|
+ vddcore: buck1 {
|
|
+ regulator-name = "vddcore";
|
|
+ regulator-min-microvolt = <1200000>;
|
|
+ regulator-max-microvolt = <1350000>;
|
|
+ regulator-always-on;
|
|
+ regulator-initial-mode = <0>;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ vdd_ddr: buck2 {
|
|
+ regulator-name = "vdd_ddr";
|
|
+ regulator-min-microvolt = <1350000>;
|
|
+ regulator-max-microvolt = <1350000>;
|
|
+ regulator-always-on;
|
|
+ regulator-initial-mode = <0>;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ vdd: buck3 {
|
|
+ regulator-name = "vdd";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ regulator-always-on;
|
|
+ st,mask-reset;
|
|
+ regulator-initial-mode = <0>;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ v3v3: buck4 {
|
|
+ regulator-name = "v3v3";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ regulator-always-on;
|
|
+ regulator-over-current-protection;
|
|
+ regulator-initial-mode = <0>;
|
|
+ };
|
|
+
|
|
+ vdda: ldo1 {
|
|
+ regulator-name = "vdda";
|
|
+ regulator-min-microvolt = <2900000>;
|
|
+ regulator-max-microvolt = <2900000>;
|
|
+ };
|
|
+
|
|
+ v2v8: ldo2 {
|
|
+ regulator-name = "v2v8";
|
|
+ regulator-min-microvolt = <2800000>;
|
|
+ regulator-max-microvolt = <2800000>;
|
|
+ };
|
|
+
|
|
+ vtt_ddr: ldo3 {
|
|
+ regulator-name = "vtt_ddr";
|
|
+ regulator-min-microvolt = <500000>;
|
|
+ regulator-max-microvolt = <750000>;
|
|
+ regulator-always-on;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+
|
|
+ vdd_usb: ldo4 {
|
|
+ regulator-name = "vdd_usb";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ };
|
|
+
|
|
+ vdd_sd: ldo5 {
|
|
+ regulator-name = "vdd_sd";
|
|
+ regulator-min-microvolt = <2900000>;
|
|
+ regulator-max-microvolt = <2900000>;
|
|
+ regulator-boot-on;
|
|
+ };
|
|
+
|
|
+ v1v8: ldo6 {
|
|
+ regulator-name = "v1v8";
|
|
+ regulator-min-microvolt = <1800000>;
|
|
+ regulator-max-microvolt = <1800000>;
|
|
+ };
|
|
+
|
|
+ vref_ddr: vref_ddr {
|
|
+ regulator-name = "vref_ddr";
|
|
+ regulator-always-on;
|
|
+ regulator-over-current-protection;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&iwdg2 {
|
|
+ timeout-sec = <32>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&pwr {
|
|
+ pwr-supply = <&vdd>;
|
|
+};
|
|
+
|
|
+/* RNG1 is used by non secure at run time and by secure (low power sequences) */
|
|
+&rng1 {
|
|
+ status = "okay";
|
|
+ secure-status = "okay";
|
|
+};
|
|
+
|
|
+&rtc {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&sdmmc1 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>;
|
|
+ broken-cd;
|
|
+ st,sig-dir;
|
|
+ st,neg-edge;
|
|
+ st,use-ckin;
|
|
+ bus-width = <4>;
|
|
+ vmmc-supply = <&vdd_sd>;
|
|
+ sd-uhs-sdr12;
|
|
+ sd-uhs-sdr25;
|
|
+ sd-uhs-sdr50;
|
|
+ sd-uhs-ddr50;
|
|
+ sd-uhs-sdr104;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&sdmmc2 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>;
|
|
+ non-removable;
|
|
+ no-sd;
|
|
+ no-sdio;
|
|
+ st,neg-edge;
|
|
+ bus-width = <8>;
|
|
+ vmmc-supply = <&v3v3>;
|
|
+ vqmmc-supply = <&v3v3>;
|
|
+ mmc-ddr-3_3v;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&uart4 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&uart4_pins_a>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+/* ATF Specific */
|
|
+#include <dt-bindings/clock/stm32mp1-clksrc.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi"
|
|
+#include "stm32mp157c-security.dtsi"
|
|
+
|
|
+/ {
|
|
+ aliases {
|
|
+ gpio0 = &gpioa;
|
|
+ gpio1 = &gpiob;
|
|
+ gpio2 = &gpioc;
|
|
+ gpio3 = &gpiod;
|
|
+ gpio4 = &gpioe;
|
|
+ gpio5 = &gpiof;
|
|
+ gpio6 = &gpiog;
|
|
+ gpio7 = &gpioh;
|
|
+ gpio8 = &gpioi;
|
|
+ gpio9 = &gpioj;
|
|
+ gpio10 = &gpiok;
|
|
+ gpio25 = &gpioz;
|
|
+ i2c3 = &i2c4;
|
|
+ };
|
|
+};
|
|
+
|
|
+/* CLOCK presence */
|
|
+&rcc {
|
|
+ secure-status = "okay";
|
|
+ st,hsi-cal;
|
|
+ st,csi-cal;
|
|
+ st,cal-sec = <60>;
|
|
+};
|
|
+
|
|
+/* Security specific */
|
|
+&etzpc {
|
|
+ st,decprot = <
|
|
+ DECPROT(STM32MP1_ETZPC_USART1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_SPI6_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_I2C4_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_I2C6_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_RNG1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_HASH1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ DECPROT(STM32MP1_ETZPC_CRYP1_ID, DECPROT_NS_RW, DECPROT_UNLOCK)
|
|
+ >;
|
|
+};
|
|
+
|
|
+&iwdg2 {
|
|
+ secure-status = "okay";
|
|
+};
|
|
+
|
|
+&pwr {
|
|
+ system_suspend_supported_soc_modes = <
|
|
+ STM32_PM_CSLEEP_RUN
|
|
+ STM32_PM_CSTOP_ALLOW_LP_STOP
|
|
+ STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR
|
|
+ >;
|
|
+
|
|
+ system_off_soc_mode = <STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF>;
|
|
+};
|
|
+
|
|
+&timers15 {
|
|
+ secure-status = "okay";
|
|
+ st,hsi-cal-input = <7>;
|
|
+ st,csi_cal-input = <8>;
|
|
+};
|
|
+
|
|
+/* Low-power states of regulators */
|
|
+&vddcore {
|
|
+ lp-stop {
|
|
+ regulator-on-in-suspend;
|
|
+ regulator-suspend-microvolt = <1200000>;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd_ddr {
|
|
+ lp-stop {
|
|
+ regulator-suspend-microvolt = <1350000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-suspend-microvolt = <1350000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd {
|
|
+ lp-stop {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-suspend-microvolt = <3300000>;
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v3v3 {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdda {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v2v8 {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vtt_ddr {
|
|
+ lp-stop {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd_usb {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vdd_sd {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&v1v8 {
|
|
+ standby-ddr-sr {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
+
|
|
+&vref_ddr {
|
|
+ lp-stop {
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-sr {
|
|
+ regulator-on-in-suspend;
|
|
+ };
|
|
+ standby-ddr-off {
|
|
+ regulator-off-in-suspend;
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157c-ev1.dts b/core/arch/arm/fdts/stm32mp157c-ev1.dts
|
|
new file mode 100644
|
|
index 0000000..cfde8ed
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157c-ev1.dts
|
|
@@ -0,0 +1,67 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
|
|
+ * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
|
|
+ */
|
|
+/dts-v1/;
|
|
+
|
|
+#include "stm32mp157c-ed1.dts"
|
|
+
|
|
+/ {
|
|
+ model = "STMicroelectronics STM32MP157C eval daughter on eval mother";
|
|
+ compatible = "st,stm32mp157c-ev1", "st,stm32mp157c-ed1", "st,stm32mp157";
|
|
+
|
|
+ chosen {
|
|
+ stdout-path = "serial0:115200n8";
|
|
+ };
|
|
+
|
|
+ aliases {
|
|
+ serial1 = &usart3;
|
|
+ };
|
|
+};
|
|
+
|
|
+&fmc {
|
|
+ status = "okay";
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ nand: nand@0 {
|
|
+ reg = <0>;
|
|
+ nand-on-flash-bbt;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ };
|
|
+};
|
|
+
|
|
+&qspi {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a &qspi_bk2_pins_a>;
|
|
+ reg = <0x58003000 0x1000>, <0x70000000 0x4000000>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ status = "okay";
|
|
+
|
|
+ flash0: mx66l51235l@0 {
|
|
+ compatible = "jedec,spi-nor";
|
|
+ reg = <0>;
|
|
+ spi-rx-bus-width = <4>;
|
|
+ spi-max-frequency = <108000000>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ };
|
|
+
|
|
+ flash1: mx66l51235l@1 {
|
|
+ compatible = "jedec,spi-nor";
|
|
+ reg = <1>;
|
|
+ spi-rx-bus-width = <4>;
|
|
+ spi-max-frequency = <108000000>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ };
|
|
+};
|
|
+
|
|
+&usart3 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&usart3_pins_a>;
|
|
+ status = "disabled";
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157c-security.dtsi b/core/arch/arm/fdts/stm32mp157c-security.dtsi
|
|
new file mode 100644
|
|
index 0000000..bff1043
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157c-security.dtsi
|
|
@@ -0,0 +1,71 @@
|
|
+/*
|
|
+ * Copyright : STMicroelectronics 2017
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause
|
|
+ */
|
|
+
|
|
+#include <dt-bindings/etzpc/stm32-etzpc.h>
|
|
+
|
|
+/ {
|
|
+ soc {
|
|
+ iwdg1: iwdg@5C003000 {
|
|
+ compatible = "st,stm32mp1-iwdg";
|
|
+ reg = <0x5C003000 0x400>;
|
|
+ clocks = <&rcc IWDG1>, <&rcc CK_LSI>;
|
|
+ clock-names = "pclk", "lsi";
|
|
+ interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ status = "disabled";
|
|
+ secure-status = "disabled";
|
|
+ };
|
|
+
|
|
+ etzpc: etzpc@5C007000 {
|
|
+ compatible = "st,stm32-etzpc";
|
|
+ reg = <0x5C007000 0x400>;
|
|
+ clocks = <&rcc TZPC>;
|
|
+ status = "disabled";
|
|
+ secure-status = "okay";
|
|
+ };
|
|
+
|
|
+ stgen: stgen@5C008000 {
|
|
+ compatible = "st,stm32-stgen";
|
|
+ reg = <0x5C008000 0x1000>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&bsec {
|
|
+ mac_addr: mac_addr@e4 {
|
|
+ reg = <0xe4 0x6>;
|
|
+ };
|
|
+ /* Spare field to align on 32-bit OTP granularity */
|
|
+ spare_ns_ea: spare_ns_ea@ea {
|
|
+ reg = <0xea 0x2>;
|
|
+ };
|
|
+ board_id: board_id@ec {
|
|
+ reg = <0xec 0x4>;
|
|
+ };
|
|
+};
|
|
+
|
|
+&iwdg2 {
|
|
+ secure-interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
|
|
+};
|
|
+
|
|
+&rcc {
|
|
+ secure-interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ interrupt-names = "wakeup";
|
|
+};
|
|
+
|
|
+&sdmmc1 {
|
|
+ compatible = "st,stm32-sdmmc2";
|
|
+};
|
|
+
|
|
+&sdmmc2 {
|
|
+ compatible = "st,stm32-sdmmc2";
|
|
+};
|
|
+
|
|
+&tamp {
|
|
+ compatible = "st,stm32-tamp";
|
|
+ clocks = <&rcc RTCAPB>;
|
|
+ interrupts = <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ secure-status= "disabled";
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157c.dtsi b/core/arch/arm/fdts/stm32mp157c.dtsi
|
|
new file mode 100644
|
|
index 0000000..06c2cf1
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157c.dtsi
|
|
@@ -0,0 +1,371 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved
|
|
+ * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
|
|
+ */
|
|
+#include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <dt-bindings/reset/stm32mp1-resets.h>
|
|
+
|
|
+/ {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+
|
|
+ intc: interrupt-controller@a0021000 {
|
|
+ compatible = "arm,cortex-a7-gic";
|
|
+ #interrupt-cells = <3>;
|
|
+ interrupt-controller;
|
|
+ reg = <0xa0021000 0x1000>,
|
|
+ <0xa0022000 0x2000>;
|
|
+ };
|
|
+
|
|
+ clocks {
|
|
+ clk_hse: clk-hse {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <24000000>;
|
|
+ };
|
|
+
|
|
+ clk_hsi: clk-hsi {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <64000000>;
|
|
+ };
|
|
+
|
|
+ clk_lse: clk-lse {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <32768>;
|
|
+ };
|
|
+
|
|
+ clk_lsi: clk-lsi {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <32000>;
|
|
+ };
|
|
+
|
|
+ clk_csi: clk-csi {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <4000000>;
|
|
+ };
|
|
+
|
|
+ clk_i2s_ckin: i2s_ckin {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <0>;
|
|
+ };
|
|
+
|
|
+ clk_dsi_phy: ck_dsi_phy {
|
|
+ #clock-cells = <0>;
|
|
+ compatible = "fixed-clock";
|
|
+ clock-frequency = <0>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ soc {
|
|
+ compatible = "simple-bus";
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ interrupt-parent = <&intc>;
|
|
+ ranges;
|
|
+
|
|
+ timers12: timer@40006000 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ compatible = "st,stm32-timers";
|
|
+ reg = <0x40006000 0x400>;
|
|
+ clocks = <&rcc TIM12_K>;
|
|
+ clock-names = "int";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usart2: serial@4000e000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x4000e000 0x400>;
|
|
+ clocks = <&rcc USART2_K>;
|
|
+ resets = <&rcc USART2_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usart3: serial@4000f000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x4000f000 0x400>;
|
|
+ clocks = <&rcc USART3_K>;
|
|
+ resets = <&rcc USART3_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ uart4: serial@40010000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x40010000 0x400>;
|
|
+ clocks = <&rcc UART4_K>;
|
|
+ resets = <&rcc UART4_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ uart5: serial@40011000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x40011000 0x400>;
|
|
+ clocks = <&rcc UART5_K>;
|
|
+ resets = <&rcc UART5_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+
|
|
+ uart7: serial@40018000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x40018000 0x400>;
|
|
+ clocks = <&rcc UART7_K>;
|
|
+ resets = <&rcc UART7_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ uart8: serial@40019000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x40019000 0x400>;
|
|
+ clocks = <&rcc UART8_K>;
|
|
+ resets = <&rcc UART8_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usart6: serial@44003000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x44003000 0x400>;
|
|
+ clocks = <&rcc USART6_K>;
|
|
+ resets = <&rcc USART6_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ timers15: timer@44006000 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ compatible = "st,stm32-timers";
|
|
+ reg = <0x44006000 0x400>;
|
|
+ clocks = <&rcc TIM15_K>;
|
|
+ clock-names = "int";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ sdmmc3: sdmmc@48004000 {
|
|
+ compatible = "arm,pl18x", "arm,primecell";
|
|
+ arm,primecell-periphid = <0x00253180>;
|
|
+ reg = <0x48004000 0x400>, <0x48005000 0x400>;
|
|
+ clocks = <&rcc SDMMC3_K>;
|
|
+ clock-names = "apb_pclk";
|
|
+ resets = <&rcc SDMMC3_R>;
|
|
+ cap-sd-highspeed;
|
|
+ cap-mmc-highspeed;
|
|
+ max-frequency = <120000000>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usbotg_hs: usb-otg@49000000 {
|
|
+ compatible = "st,stm32mp1-hsotg", "snps,dwc2";
|
|
+ reg = <0x49000000 0x10000>;
|
|
+ clocks = <&rcc USBO_K>;
|
|
+ clock-names = "otg";
|
|
+ resets = <&rcc USBO_R>;
|
|
+ reset-names = "dwc2";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ rcc: rcc@50000000 {
|
|
+ compatible = "st,stm32mp1-rcc", "syscon";
|
|
+ reg = <0x50000000 0x1000>;
|
|
+ #clock-cells = <1>;
|
|
+ #reset-cells = <1>;
|
|
+ interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ };
|
|
+
|
|
+ pwr: pwr@50001000 {
|
|
+ compatible = "st,stm32mp1-pwr", "syscon", "simple-mfd";
|
|
+ reg = <0x50001000 0x400>;
|
|
+ };
|
|
+
|
|
+ exti: interrupt-controller@5000d000 {
|
|
+ compatible = "st,stm32mp1-exti", "syscon";
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ reg = <0x5000d000 0x400>;
|
|
+
|
|
+ /* exti_pwr is an extra interrupt controller used for
|
|
+ * EXTI 55 to 60. It's mapped on pwr interrupt
|
|
+ * controller.
|
|
+ */
|
|
+ exti_pwr: exti-pwr {
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <2>;
|
|
+ interrupt-parent = <&pwr>;
|
|
+ st,irq-number = <6>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ syscfg: syscon@50020000 {
|
|
+ compatible = "st,stm32mp157-syscfg", "syscon";
|
|
+ reg = <0x50020000 0x400>;
|
|
+ clocks = <&rcc SYSCFG>;
|
|
+ };
|
|
+
|
|
+ cryp1: cryp@54001000 {
|
|
+ compatible = "st,stm32mp1-cryp";
|
|
+ reg = <0x54001000 0x400>;
|
|
+ interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ clocks = <&rcc CRYP1>;
|
|
+ resets = <&rcc CRYP1_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ hash1: hash@54002000 {
|
|
+ compatible = "st,stm32f756-hash";
|
|
+ reg = <0x54002000 0x400>;
|
|
+ interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ clocks = <&rcc HASH1>;
|
|
+ resets = <&rcc HASH1_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ rng1: rng@54003000 {
|
|
+ compatible = "st,stm32-rng";
|
|
+ reg = <0x54003000 0x400>;
|
|
+ clocks = <&rcc RNG1_K>;
|
|
+ resets = <&rcc RNG1_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ fmc: nand-controller@58002000 {
|
|
+ compatible = "st,stm32mp15-fmc2";
|
|
+ reg = <0x58002000 0x1000>,
|
|
+ <0x80000000 0x1000>,
|
|
+ <0x88010000 0x1000>,
|
|
+ <0x88020000 0x1000>,
|
|
+ <0x81000000 0x1000>,
|
|
+ <0x89010000 0x1000>,
|
|
+ <0x89020000 0x1000>;
|
|
+ clocks = <&rcc FMC_K>;
|
|
+ resets = <&rcc FMC_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ qspi: qspi@58003000 {
|
|
+ compatible = "st,stm32f469-qspi";
|
|
+ reg = <0x58003000 0x1000>, <0x70000000 0x10000000>;
|
|
+ reg-names = "qspi", "qspi_mm";
|
|
+ clocks = <&rcc QSPI_K>;
|
|
+ resets = <&rcc QSPI_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ sdmmc1: sdmmc@58005000 {
|
|
+ compatible = "arm,pl18x", "arm,primecell";
|
|
+ arm,primecell-periphid = <0x00253180>;
|
|
+ reg = <0x58005000 0x1000>, <0x58006000 0x1000>;
|
|
+ clocks = <&rcc SDMMC1_K>;
|
|
+ clock-names = "apb_pclk";
|
|
+ resets = <&rcc SDMMC1_R>;
|
|
+ cap-sd-highspeed;
|
|
+ cap-mmc-highspeed;
|
|
+ max-frequency = <120000000>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ sdmmc2: sdmmc@58007000 {
|
|
+ compatible = "arm,pl18x", "arm,primecell";
|
|
+ arm,primecell-periphid = <0x00253180>;
|
|
+ reg = <0x58007000 0x1000>, <0x58008000 0x1000>;
|
|
+ clocks = <&rcc SDMMC2_K>;
|
|
+ clock-names = "apb_pclk";
|
|
+ resets = <&rcc SDMMC2_R>;
|
|
+ cap-sd-highspeed;
|
|
+ cap-mmc-highspeed;
|
|
+ max-frequency = <120000000>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ iwdg2: watchdog@5a002000 {
|
|
+ compatible = "st,stm32mp1-iwdg";
|
|
+ reg = <0x5a002000 0x400>;
|
|
+ clocks = <&rcc IWDG2>, <&rcc CK_LSI>;
|
|
+ clock-names = "pclk", "lsi";
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usart1: serial@5c000000 {
|
|
+ compatible = "st,stm32h7-uart";
|
|
+ reg = <0x5c000000 0x400>;
|
|
+ interrupt-names = "event", "wakeup";
|
|
+ interrupts-extended = <&intc GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>,
|
|
+ <&exti 26 1>;
|
|
+ clocks = <&rcc USART1_K>;
|
|
+ resets = <&rcc USART1_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ spi6: spi@5c001000 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ compatible = "st,stm32h7-spi";
|
|
+ reg = <0x5c001000 0x400>;
|
|
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ clocks = <&rcc SPI6_K>;
|
|
+ resets = <&rcc SPI6_R>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ i2c4: i2c@5c002000 {
|
|
+ compatible = "st,stm32f7-i2c";
|
|
+ reg = <0x5c002000 0x400>;
|
|
+ interrupt-names = "event", "error", "wakeup";
|
|
+ interrupts-extended = <&intc GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>,
|
|
+ <&intc GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
|
|
+ <&exti 24 1>;
|
|
+ clocks = <&rcc I2C4_K>;
|
|
+ resets = <&rcc I2C4_R>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ rtc: rtc@5c004000 {
|
|
+ compatible = "st,stm32mp1-rtc";
|
|
+ reg = <0x5c004000 0x400>;
|
|
+ clocks = <&rcc RTCAPB>, <&rcc RTC>;
|
|
+ clock-names = "pclk", "rtc_ck";
|
|
+ interrupts-extended = <&intc GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
|
+ <&exti 19 1>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ bsec: nvmem@5c005000 {
|
|
+ compatible = "st,stm32mp15-bsec";
|
|
+ reg = <0x5c005000 0x400>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ ts_cal1: calib@5c {
|
|
+ reg = <0x5c 0x2>;
|
|
+ };
|
|
+ ts_cal2: calib@5e {
|
|
+ reg = <0x5e 0x2>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ i2c6: i2c@5c009000 {
|
|
+ compatible = "st,stm32f7-i2c";
|
|
+ reg = <0x5c009000 0x400>;
|
|
+ interrupt-names = "event", "error", "wakeup";
|
|
+ interrupts-extended = <&intc GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>,
|
|
+ <&intc GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>,
|
|
+ <&exti 54 1>;
|
|
+ clocks = <&rcc I2C6_K>;
|
|
+ resets = <&rcc I2C6_R>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ tamp: tamp@5c00a000 {
|
|
+ compatible = "simple-bus", "syscon", "simple-mfd";
|
|
+ reg = <0x5c00a000 0x400>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157caa-pinctrl.dtsi b/core/arch/arm/fdts/stm32mp157caa-pinctrl.dtsi
|
|
new file mode 100644
|
|
index 0000000..9b9cd08
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157caa-pinctrl.dtsi
|
|
@@ -0,0 +1,90 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>
|
|
+ */
|
|
+
|
|
+#include "stm32mp157-pinctrl.dtsi"
|
|
+/ {
|
|
+ soc {
|
|
+ pinctrl: pin-controller@50002000 {
|
|
+ st,package = <STM32MP157CAA>;
|
|
+
|
|
+ gpioa: gpio@50002000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 0 16>;
|
|
+ };
|
|
+
|
|
+ gpiob: gpio@50003000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 16 16>;
|
|
+ };
|
|
+
|
|
+ gpioc: gpio@50004000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 32 16>;
|
|
+ };
|
|
+
|
|
+ gpiod: gpio@50005000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 48 16>;
|
|
+ };
|
|
+
|
|
+ gpioe: gpio@50006000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 64 16>;
|
|
+ };
|
|
+
|
|
+ gpiof: gpio@50007000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 80 16>;
|
|
+ };
|
|
+
|
|
+ gpiog: gpio@50008000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 96 16>;
|
|
+ };
|
|
+
|
|
+ gpioh: gpio@50009000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 112 16>;
|
|
+ };
|
|
+
|
|
+ gpioi: gpio@5000a000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 128 16>;
|
|
+ };
|
|
+
|
|
+ gpioj: gpio@5000b000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 144 16>;
|
|
+ };
|
|
+
|
|
+ gpiok: gpio@5000c000 {
|
|
+ status = "okay";
|
|
+ ngpios = <8>;
|
|
+ gpio-ranges = <&pinctrl 0 160 8>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ pinctrl_z: pin-controller-z@54004000 {
|
|
+ st,package = <STM32MP157CAA>;
|
|
+
|
|
+ gpioz: gpio@54004000 {
|
|
+ status = "okay";
|
|
+ ngpios = <8>;
|
|
+ gpio-ranges = <&pinctrl_z 0 400 8>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157cab-pinctrl.dtsi b/core/arch/arm/fdts/stm32mp157cab-pinctrl.dtsi
|
|
new file mode 100644
|
|
index 0000000..c570cf9
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157cab-pinctrl.dtsi
|
|
@@ -0,0 +1,62 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>
|
|
+ */
|
|
+
|
|
+#include "stm32mp157-pinctrl.dtsi"
|
|
+/ {
|
|
+ soc {
|
|
+ pinctrl: pin-controller@50002000 {
|
|
+ st,package = <STM32MP157CAB>;
|
|
+
|
|
+ gpioa: gpio@50002000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 0 16>;
|
|
+ };
|
|
+
|
|
+ gpiob: gpio@50003000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 16 16>;
|
|
+ };
|
|
+
|
|
+ gpioc: gpio@50004000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 32 16>;
|
|
+ };
|
|
+
|
|
+ gpiod: gpio@50005000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 48 16>;
|
|
+ };
|
|
+
|
|
+ gpioe: gpio@50006000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 64 16>;
|
|
+ };
|
|
+
|
|
+ gpiof: gpio@50007000 {
|
|
+ status = "okay";
|
|
+ ngpios = <6>;
|
|
+ gpio-ranges = <&pinctrl 6 86 6>;
|
|
+ };
|
|
+
|
|
+ gpiog: gpio@50008000 {
|
|
+ status = "okay";
|
|
+ ngpios = <10>;
|
|
+ gpio-ranges = <&pinctrl 6 102 10>;
|
|
+ };
|
|
+
|
|
+ gpioh: gpio@50009000 {
|
|
+ status = "okay";
|
|
+ ngpios = <2>;
|
|
+ gpio-ranges = <&pinctrl 0 112 2>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157cac-pinctrl.dtsi b/core/arch/arm/fdts/stm32mp157cac-pinctrl.dtsi
|
|
new file mode 100644
|
|
index 0000000..777f991
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157cac-pinctrl.dtsi
|
|
@@ -0,0 +1,78 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>
|
|
+ */
|
|
+
|
|
+#include "stm32mp157-pinctrl.dtsi"
|
|
+/ {
|
|
+ soc {
|
|
+ pinctrl: pin-controller@50002000 {
|
|
+ st,package = <STM32MP157CAC>;
|
|
+
|
|
+ gpioa: gpio@50002000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 0 16>;
|
|
+ };
|
|
+
|
|
+ gpiob: gpio@50003000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 16 16>;
|
|
+ };
|
|
+
|
|
+ gpioc: gpio@50004000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 32 16>;
|
|
+ };
|
|
+
|
|
+ gpiod: gpio@50005000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 48 16>;
|
|
+ };
|
|
+
|
|
+ gpioe: gpio@50006000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 64 16>;
|
|
+ };
|
|
+
|
|
+ gpiof: gpio@50007000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 80 16>;
|
|
+ };
|
|
+
|
|
+ gpiog: gpio@50008000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 96 16>;
|
|
+ };
|
|
+
|
|
+ gpioh: gpio@50009000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 112 16>;
|
|
+ };
|
|
+
|
|
+ gpioi: gpio@5000a000 {
|
|
+ status = "okay";
|
|
+ ngpios = <12>;
|
|
+ gpio-ranges = <&pinctrl 0 128 12>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ pinctrl_z: pin-controller-z@54004000 {
|
|
+ st,package = <STM32MP157CAC>;
|
|
+
|
|
+ gpioz: gpio@54004000 {
|
|
+ status = "okay";
|
|
+ ngpios = <8>;
|
|
+ gpio-ranges = <&pinctrl_z 0 400 8>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/fdts/stm32mp157cad-pinctrl.dtsi b/core/arch/arm/fdts/stm32mp157cad-pinctrl.dtsi
|
|
new file mode 100644
|
|
index 0000000..c4c303a
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/fdts/stm32mp157cad-pinctrl.dtsi
|
|
@@ -0,0 +1,62 @@
|
|
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>
|
|
+ */
|
|
+
|
|
+#include "stm32mp157-pinctrl.dtsi"
|
|
+/ {
|
|
+ soc {
|
|
+ pinctrl: pin-controller@50002000 {
|
|
+ st,package = <STM32MP157CAD>;
|
|
+
|
|
+ gpioa: gpio@50002000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 0 16>;
|
|
+ };
|
|
+
|
|
+ gpiob: gpio@50003000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 16 16>;
|
|
+ };
|
|
+
|
|
+ gpioc: gpio@50004000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 32 16>;
|
|
+ };
|
|
+
|
|
+ gpiod: gpio@50005000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 48 16>;
|
|
+ };
|
|
+
|
|
+ gpioe: gpio@50006000 {
|
|
+ status = "okay";
|
|
+ ngpios = <16>;
|
|
+ gpio-ranges = <&pinctrl 0 64 16>;
|
|
+ };
|
|
+
|
|
+ gpiof: gpio@50007000 {
|
|
+ status = "okay";
|
|
+ ngpios = <6>;
|
|
+ gpio-ranges = <&pinctrl 6 86 6>;
|
|
+ };
|
|
+
|
|
+ gpiog: gpio@50008000 {
|
|
+ status = "okay";
|
|
+ ngpios = <10>;
|
|
+ gpio-ranges = <&pinctrl 6 102 10>;
|
|
+ };
|
|
+
|
|
+ gpioh: gpio@50009000 {
|
|
+ status = "okay";
|
|
+ ngpios = <2>;
|
|
+ gpio-ranges = <&pinctrl 0 112 2>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/core/arch/arm/include/arm32.h b/core/arch/arm/include/arm32.h
|
|
index e7c3b2d..0dbd889 100644
|
|
--- a/core/arch/arm/include/arm32.h
|
|
+++ b/core/arch/arm/include/arm32.h
|
|
@@ -1,12 +1,13 @@
|
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
/*
|
|
- * Copyright (c) 2016, Linaro Limited
|
|
+ * Copyright (c) 2016-2018, Linaro Limited
|
|
* Copyright (c) 2014, STMicroelectronics International N.V.
|
|
*/
|
|
|
|
#ifndef ARM32_H
|
|
#define ARM32_H
|
|
|
|
+#include <arm.h>
|
|
#include <sys/cdefs.h>
|
|
#include <stdint.h>
|
|
#include <util.h>
|
|
@@ -159,6 +160,17 @@
|
|
#define IDPFR1_GENTIMER_SHIFT 16
|
|
#define IDPFR1_GENTIMER_MASK (0xF << IDPFR1_GENTIMER_SHIFT)
|
|
|
|
+/* Generic timer registers and fields */
|
|
+#define CNTCR_OFFSET 0x000
|
|
+#define CNTSR_OFFSET 0x004
|
|
+#define CNTCVL_OFFSET 0x008
|
|
+#define CNTCVU_OFFSET 0x00C
|
|
+#define CNTFID_OFFSET 0x020
|
|
+
|
|
+#define CNTCR_EN BIT(0)
|
|
+#define CNTCR_HDBG BIT(1)
|
|
+#define CNTCR_FCREQ(x) ((x) << 8)
|
|
+
|
|
#ifndef ASM
|
|
#include <generated/arm32_sysreg.h>
|
|
#ifdef CFG_ARM_GICV3
|
|
diff --git a/core/arch/arm/include/kernel/delay.h b/core/arch/arm/include/kernel/delay.h
|
|
index 72ac873..2c47f0a 100644
|
|
--- a/core/arch/arm/include/kernel/delay.h
|
|
+++ b/core/arch/arm/include/kernel/delay.h
|
|
@@ -1,5 +1,6 @@
|
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
/*
|
|
+ * Copyright (c) 2018, Linaro Limited
|
|
* Copyright (C) 2017, Fuzhou Rockchip Electronics Co., Ltd.
|
|
* All rights reserved.
|
|
*
|
|
@@ -29,7 +30,14 @@
|
|
#ifndef __KERNEL_DELAY_H
|
|
#define __KERNEL_DELAY_H
|
|
|
|
+#include <stdbool.h>
|
|
+#include <stdint.h>
|
|
+
|
|
void udelay(uint32_t us);
|
|
void mdelay(uint32_t ms);
|
|
|
|
+uint64_t utimeout_init(uint32_t us);
|
|
+bool utimeout_elapsed(uint32_t us, uint64_t ref);
|
|
+unsigned int utimeout_elapsed_us(uint32_t us, uint64_t reference);
|
|
+
|
|
#endif
|
|
diff --git a/core/arch/arm/include/mm/core_mmu.h b/core/arch/arm/include/mm/core_mmu.h
|
|
index 64994d3..cec3a42 100644
|
|
--- a/core/arch/arm/include/mm/core_mmu.h
|
|
+++ b/core/arch/arm/include/mm/core_mmu.h
|
|
@@ -101,6 +101,7 @@
|
|
* MEM_AREA_NSEC_SHM: NonSecure shared RAM between NSec and TEE.
|
|
* MEM_AREA_RAM_NSEC: NonSecure RAM storing data
|
|
* MEM_AREA_RAM_SEC: Secure RAM storing some secrets
|
|
+ * MEM_AREA_ROM_SEC: Secure read only memory storing some secrets
|
|
* MEM_AREA_IO_NSEC: NonSecure HW mapped registers
|
|
* MEM_AREA_IO_SEC: Secure HW mapped registers
|
|
* MEM_AREA_RES_VASPACE: Reserved virtual memory space
|
|
@@ -121,6 +122,7 @@ enum teecore_memtypes {
|
|
MEM_AREA_NSEC_SHM,
|
|
MEM_AREA_RAM_NSEC,
|
|
MEM_AREA_RAM_SEC,
|
|
+ MEM_AREA_ROM_SEC,
|
|
MEM_AREA_IO_NSEC,
|
|
MEM_AREA_IO_SEC,
|
|
MEM_AREA_RES_VASPACE,
|
|
@@ -146,6 +148,7 @@ static inline const char *teecore_memtype_name(enum teecore_memtypes type)
|
|
[MEM_AREA_NSEC_SHM] = "NSEC_SHM",
|
|
[MEM_AREA_RAM_NSEC] = "RAM_NSEC",
|
|
[MEM_AREA_RAM_SEC] = "RAM_SEC",
|
|
+ [MEM_AREA_ROM_SEC] = "ROM_SEC",
|
|
[MEM_AREA_IO_NSEC] = "IO_NSEC",
|
|
[MEM_AREA_IO_SEC] = "IO_SEC",
|
|
[MEM_AREA_RES_VASPACE] = "RES_VASPACE",
|
|
diff --git a/core/arch/arm/include/sm/pm.h b/core/arch/arm/include/sm/pm.h
|
|
index 939f966..90f031a 100644
|
|
--- a/core/arch/arm/include/sm/pm.h
|
|
+++ b/core/arch/arm/include/sm/pm.h
|
|
@@ -34,7 +34,11 @@
|
|
struct sm_pm_ctx {
|
|
uint32_t sp;
|
|
paddr_t cpu_resume_addr;
|
|
+#ifdef CFG_WITH_LPAE
|
|
+ uint32_t suspend_regs[18];
|
|
+#else
|
|
uint32_t suspend_regs[16];
|
|
+#endif
|
|
};
|
|
|
|
/* suspend/resume core functions */
|
|
diff --git a/core/arch/arm/kernel/delay.c b/core/arch/arm/kernel/delay.c
|
|
index 2321b78..ae62187 100644
|
|
--- a/core/arch/arm/kernel/delay.c
|
|
+++ b/core/arch/arm/kernel/delay.c
|
|
@@ -1,5 +1,6 @@
|
|
// SPDX-License-Identifier: BSD-2-Clause
|
|
/*
|
|
+ * Copyright (c) 2018, Linaro Limited
|
|
* Copyright (C) 2017, Fuzhou Rockchip Electronics Co., Ltd.
|
|
* All rights reserved.
|
|
*
|
|
@@ -29,12 +30,15 @@
|
|
#include <arm.h>
|
|
#include <kernel/delay.h>
|
|
|
|
+#define US2CNT(m) (((uint64_t)m * (uint64_t)read_cntfrq()) / 1000000ULL)
|
|
+#define CNT2US(m) ((uint32_t)(((uint64_t)m * 1000000ULL) / read_cntfrq()))
|
|
+
|
|
void udelay(uint32_t us)
|
|
{
|
|
uint64_t start, target;
|
|
|
|
start = read_cntpct();
|
|
- target = ((uint64_t)read_cntfrq() * us) / 1000000ULL;
|
|
+ target = US2CNT(us);
|
|
|
|
while (read_cntpct() - start <= target)
|
|
;
|
|
@@ -44,3 +48,37 @@ void mdelay(uint32_t ms)
|
|
{
|
|
udelay(1000 * ms);
|
|
}
|
|
+
|
|
+uint64_t utimeout_init(uint32_t us)
|
|
+{
|
|
+ return read_cntpct() + US2CNT(us);
|
|
+}
|
|
+
|
|
+bool utimeout_elapsed(uint32_t us, uint64_t reference)
|
|
+{
|
|
+ uint64_t origin = reference - US2CNT(us);
|
|
+ uint64_t now = read_cntpct();
|
|
+
|
|
+ if (origin < reference)
|
|
+ return now < origin || now > reference;
|
|
+
|
|
+ return now < origin && now > reference;
|
|
+}
|
|
+
|
|
+unsigned int utimeout_elapsed_us(uint32_t us, uint64_t reference)
|
|
+{
|
|
+ uint64_t origin = reference - US2CNT(us);
|
|
+ uint64_t now = read_cntpct();
|
|
+
|
|
+ if (origin < reference) {
|
|
+ if (now < origin || now > reference)
|
|
+ return CNT2US(now - reference);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (now < origin && now > reference)
|
|
+ return CNT2US(now - reference);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/core/arch/arm/kernel/generic_boot.c b/core/arch/arm/kernel/generic_boot.c
|
|
index e5f0f3d..612eab4 100644
|
|
--- a/core/arch/arm/kernel/generic_boot.c
|
|
+++ b/core/arch/arm/kernel/generic_boot.c
|
|
@@ -73,6 +73,9 @@ uint32_t sem_cpu_sync[CFG_TEE_CORE_NB_CORE];
|
|
KEEP_PAGER(sem_cpu_sync);
|
|
#endif
|
|
|
|
+#ifdef CFG_STATIC_SECURE_DT
|
|
+extern uint8_t static_secure_dtb[];
|
|
+#endif
|
|
#ifdef CFG_DT
|
|
static void *dt_blob_addr;
|
|
#endif
|
|
@@ -471,7 +474,7 @@ static void init_runtime(unsigned long pageable_part __unused)
|
|
}
|
|
#endif
|
|
|
|
-#ifdef CFG_DT
|
|
+#if defined(CFG_DT) && !defined(CFG_STATIC_SECURE_DT)
|
|
void *get_dt_blob(void)
|
|
{
|
|
assert(cpu_mmu_enabled());
|
|
@@ -836,23 +839,42 @@ static void update_fdt(void)
|
|
panic();
|
|
}
|
|
}
|
|
+#endif /*CFG_DT && !CFG_STATIC_SECURE_DT*/
|
|
|
|
-#else
|
|
-static void init_fdt(unsigned long phys_fdt __unused)
|
|
+#if defined(CFG_DT) && defined(CFG_STATIC_SECURE_DT)
|
|
+void *get_dt_blob(void)
|
|
{
|
|
+ assert(cpu_mmu_enabled());
|
|
+
|
|
+ if (!dt_blob_addr) {
|
|
+ if (fdt_check_header((void *)&static_secure_dtb[0]))
|
|
+ panic("Invalid static DTB");
|
|
+
|
|
+ dt_blob_addr = (void *)&static_secure_dtb[0];
|
|
+ }
|
|
+
|
|
+ return dt_blob_addr;
|
|
}
|
|
+#endif
|
|
|
|
-static void update_fdt(void)
|
|
+#ifndef CFG_DT
|
|
+void *get_dt_blob(void)
|
|
{
|
|
+ return NULL;
|
|
}
|
|
+#endif
|
|
|
|
+#if !defined(CFG_DT) || defined(CFG_STATIC_SECURE_DT)
|
|
static void reset_dt_references(void)
|
|
{
|
|
}
|
|
|
|
-void *get_dt_blob(void)
|
|
+static void init_fdt(unsigned long phys_fdt __unused)
|
|
+{
|
|
+}
|
|
+
|
|
+static void update_fdt(void)
|
|
{
|
|
- return NULL;
|
|
}
|
|
|
|
static struct core_mmu_phys_mem *get_memory(void *fdt __unused,
|
|
@@ -861,7 +883,7 @@ static struct core_mmu_phys_mem *get_memory(void *fdt __unused,
|
|
return NULL;
|
|
}
|
|
|
|
-#endif /*!CFG_DT*/
|
|
+#endif /*!CFG_DT || CFG_STATIC_SECURE_DT*/
|
|
|
|
static void discover_nsec_memory(void)
|
|
{
|
|
diff --git a/core/arch/arm/mm/core_mmu.c b/core/arch/arm/mm/core_mmu.c
|
|
index f69069e..3337ca6 100644
|
|
--- a/core/arch/arm/mm/core_mmu.c
|
|
+++ b/core/arch/arm/mm/core_mmu.c
|
|
@@ -627,6 +627,8 @@ uint32_t core_mmu_type_to_attr(enum teecore_memtypes t)
|
|
return attr | TEE_MATTR_PRW | cached;
|
|
case MEM_AREA_RAM_SEC:
|
|
return attr | TEE_MATTR_SECURE | TEE_MATTR_PRW | cached;
|
|
+ case MEM_AREA_ROM_SEC:
|
|
+ return attr | TEE_MATTR_SECURE | TEE_MATTR_PR | cached;
|
|
case MEM_AREA_RES_VASPACE:
|
|
case MEM_AREA_SHM_VASPACE:
|
|
return 0;
|
|
@@ -1014,6 +1016,7 @@ void core_init_mmu_map(void)
|
|
case MEM_AREA_IO_SEC:
|
|
case MEM_AREA_IO_NSEC:
|
|
case MEM_AREA_RAM_SEC:
|
|
+ case MEM_AREA_ROM_SEC:
|
|
case MEM_AREA_RAM_NSEC:
|
|
case MEM_AREA_RES_VASPACE:
|
|
case MEM_AREA_SHM_VASPACE:
|
|
diff --git a/core/arch/arm/mm/mobj.c b/core/arch/arm/mm/mobj.c
|
|
index b2bdf6b..789a88d 100644
|
|
--- a/core/arch/arm/mm/mobj.c
|
|
+++ b/core/arch/arm/mm/mobj.c
|
|
@@ -52,6 +52,7 @@ static void *mobj_phys_get_va(struct mobj *mobj, size_t offset)
|
|
|
|
return (void *)(moph->va + offset);
|
|
}
|
|
+KEEP_PAGER(mobj_phys_get_va);
|
|
|
|
static TEE_Result mobj_phys_get_pa(struct mobj *mobj, size_t offs,
|
|
size_t granule, paddr_t *pa)
|
|
@@ -230,7 +231,7 @@ static void *mobj_mm_get_va(struct mobj *mobj, size_t offs)
|
|
return mobj_get_va(to_mobj_mm(mobj)->parent_mobj,
|
|
mobj_mm_offs(mobj, offs));
|
|
}
|
|
-
|
|
+KEEP_PAGER(mobj_mm_get_va);
|
|
|
|
static TEE_Result mobj_mm_get_pa(struct mobj *mobj, size_t offs,
|
|
size_t granule, paddr_t *pa)
|
|
diff --git a/core/arch/arm/plat-stm32mp1/boot_api.h b/core/arch/arm/plat-stm32mp1/boot_api.h
|
|
index 62e38b5..a7daffd 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/boot_api.h
|
|
+++ b/core/arch/arm/plat-stm32mp1/boot_api.h
|
|
@@ -14,7 +14,9 @@
|
|
#define BCKR_CORE1_MAGIC_NUMBER 4
|
|
|
|
/* Value for BCKR_CORE1_MAGIC_NUMBER entry */
|
|
+#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xca7face0
|
|
#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xca7face1
|
|
+#define BOOT_API_A7_RESET_MAGIC_NUMBER 0xca7dead0
|
|
|
|
/* Backup register #5: physical address of core1 entry at boot up */
|
|
#define BCKR_CORE1_BRANCH_ADDRESS 5
|
|
diff --git a/core/arch/arm/plat-stm32mp1/conf.mk b/core/arch/arm/plat-stm32mp1/conf.mk
|
|
index fb0ca5a..0940ce3 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/conf.mk
|
|
+++ b/core/arch/arm/plat-stm32mp1/conf.mk
|
|
@@ -1,9 +1,16 @@
|
|
PLATFORM_FLAVOR ?= stm32mp157c
|
|
+STM32_BOARD ?= ev1
|
|
|
|
+# 1GB and 512MB DDR target do not locate secure DDR at the same place.
|
|
+#
|
|
+flavorlist-1G = stm32mp157c-ev1 stm32mp157c-ed1
|
|
+flavorlist-512M = stm32mp157a-dk1 stm32mp157c-dk2
|
|
+
|
|
+# Generic stm32mp1 configuration directives
|
|
+#
|
|
include core/arch/arm/cpu/cortex-a7.mk
|
|
ta-targets = ta_arm32
|
|
|
|
-$(call force,CFG_TEE_CORE_NB_CORE,2)
|
|
$(call force,CFG_ARM32_core,y)
|
|
$(call force,CFG_BOOT_SECONDARY_REQUEST,y)
|
|
$(call force,CFG_GENERIC_BOOT,y)
|
|
@@ -11,23 +18,85 @@ $(call force,CFG_GIC,y)
|
|
$(call force,CFG_INIT_CNTVOFF,y)
|
|
$(call force,CFG_PM_STUBS,y)
|
|
$(call force,CFG_PSCI_ARM32,y)
|
|
+$(call force,CFG_PM_ARM32,y)
|
|
$(call force,CFG_SECONDARY_INIT_CNTFRQ,y)
|
|
$(call force,CFG_SECURE_TIME_SOURCE_CNTPCT,y)
|
|
$(call force,CFG_WITH_SOFTWARE_PRNG,y)
|
|
+$(call force,CFG_SM_PLATFORM_HANDLER,y)
|
|
+
|
|
+ifneq ($(CFG_SECURE_DT),)
|
|
+$(call force,CFG_DT,y)
|
|
+$(call force,CFG_STATIC_SECURE_DT,y)
|
|
+endif
|
|
+
|
|
+CFG_TEE_CORE_NB_CORE ?= 2
|
|
+
|
|
+ifneq (,$(filter $(CFG_SECURE_DT),$(flavorlist-512M)))
|
|
+CFG_TZDRAM_START ?= 0xde000000
|
|
+CFG_SHMEM_START ?= 0xdfe00000
|
|
+endif
|
|
|
|
CFG_TZSRAM_START ?= 0x2ffc0000
|
|
CFG_TZSRAM_SIZE ?= 0x00040000
|
|
CFG_TZDRAM_START ?= 0xfe000000
|
|
CFG_TZDRAM_SIZE ?= 0x01e00000
|
|
-CFG_SHMEM_SIZE ?= 0x00200000
|
|
CFG_SHMEM_START ?= 0xffe00000
|
|
+CFG_SHMEM_SIZE ?= 0x00200000
|
|
|
|
-CFG_WITH_PAGER ?= y
|
|
-CFG_WITH_LPAE ?= y
|
|
-CFG_WITH_STACK_CANARIES ?= y
|
|
+CFG_CORE_HEAP_SIZE ?= 49152
|
|
+CFG_WITH_PAGER ?= y
|
|
+CFG_WITH_LPAE ?= y
|
|
+CFG_WITH_STACK_CANARIES ?= y
|
|
+CFG_MMAP_REGIONS ?= 23
|
|
|
|
+ifneq ($(CFG_DT),y)
|
|
+# Some drivers mandate DT support
|
|
+$(call force,CFG_STPMIC1,n)
|
|
+$(call force,CFG_STM32_I2C,n)
|
|
+$(call force,CFG_STM32_IWDG,n)
|
|
+$(call force,CFG_STM32_RNG,n)
|
|
+$(call force,CFG_STM32_TIMER,n)
|
|
+$(call force,CFG_STM32_CLOCKSRC_CALIB,n)
|
|
+endif
|
|
+
|
|
+$(call force,CFG_STM32_BSEC,y)
|
|
+$(call force,CFG_STM32_CRYP,y)
|
|
+$(call force,CFG_STM32_ETZPC,y)
|
|
+CFG_STM32_GPIO ?= y
|
|
+CFG_STM32_I2C ?= y
|
|
+CFG_STM32_IWDG ?= y
|
|
CFG_STM32_UART ?= y
|
|
+CFG_STM32_RNG ?= y
|
|
+$(call force,CFG_STM32_RTC,y)
|
|
+CFG_STM32_TIMER ?= y
|
|
+
|
|
+CFG_STPMIC1 ?= y
|
|
+
|
|
+$(call force,CFG_STM32_BSEC_SIP,y)
|
|
+$(call force,CFG_STM32_RCC_SIP,y)
|
|
+CFG_STM32_PWR_SIP ?= y
|
|
+CFG_STM32_POWER_SERVICES ?= y
|
|
+CFG_STM32_CLOCKSRC_CALIB ?= y
|
|
+
|
|
+ifeq ($(CFG_STPMIC1),y)
|
|
+$(call force,CFG_STM32_I2C,y)
|
|
+$(call force,CFG_STM32_GPIO,y)
|
|
+endif
|
|
+
|
|
+ifeq ($(CFG_STM32_CLOCKSRC_CALIB),y)
|
|
+$(call force,CFG_STM32_TIMER,y)
|
|
+endif
|
|
+
|
|
+# Get a static mapping for the non secure low DDR (save 4kB of unpaged memory)
|
|
+CFG_STM32MP_MAP_NSEC_LOW_DDR ?= y
|
|
|
|
-# Default enable the test facitilites
|
|
+# Default enable some test facitilites
|
|
CFG_TEE_CORE_EMBED_INTERNAL_TESTS ?= y
|
|
CFG_WITH_STATS ?= y
|
|
+CFG_UNWIND ?= n
|
|
+# Non secure UART and GPIO/pinctrl for the output console
|
|
+CFG_WITH_NSEC_GPIOS ?= y
|
|
+CFG_WITH_NSEC_UARTS ?= y
|
|
+CFG_FORCE_CONSOLE_ON_SUSPEND ?= n
|
|
+# UART instance used for early console (0 disables early console)
|
|
+CFG_STM32_EARLY_CONSOLE_UART ?= 4
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32_reset.c b/core/arch/arm/plat-stm32mp1/drivers/stm32_reset.c
|
|
new file mode 100644
|
|
index 0000000..b406167
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32_reset.c
|
|
@@ -0,0 +1,60 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <drivers/stm32_reset.h>
|
|
+#include <io.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <util.h>
|
|
+
|
|
+#define RST_CLR_OFFSET 4U
|
|
+#define RESET_TIMEOUT_US 1000
|
|
+
|
|
+static size_t id2reg_offset(unsigned int reset_id)
|
|
+{
|
|
+ return ((reset_id & GENMASK_32(31, 5)) >> 5) * sizeof(uint32_t);
|
|
+}
|
|
+
|
|
+static uint8_t id2reg_bit_pos(unsigned int reset_id)
|
|
+{
|
|
+ return (uint8_t)(reset_id & GENMASK_32(4,0));
|
|
+}
|
|
+
|
|
+void stm32_reset_assert(unsigned int reset_id)
|
|
+{
|
|
+ size_t offset = id2reg_offset(reset_id);
|
|
+ uint32_t bitmsk = BIT(id2reg_bit_pos(reset_id));
|
|
+ uint64_t timeout_ref;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ write32(bitmsk, rcc_base + offset);
|
|
+
|
|
+ timeout_ref = utimeout_init(RESET_TIMEOUT_US);
|
|
+
|
|
+ while (!(read32(rcc_base + offset) & bitmsk)) {
|
|
+ if (utimeout_elapsed(RESET_TIMEOUT_US, timeout_ref)) {
|
|
+ panic("Reset timeout");
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void stm32_reset_deassert(unsigned int reset_id)
|
|
+{
|
|
+ size_t offset = id2reg_offset(reset_id) + RST_CLR_OFFSET;
|
|
+ uint32_t bitmsk = BIT(id2reg_bit_pos(reset_id));
|
|
+ uint64_t timeout_ref;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ write32(bitmsk, rcc_base + offset);
|
|
+
|
|
+ timeout_ref = utimeout_init(RESET_TIMEOUT_US);
|
|
+
|
|
+ while (read32(rcc_base + offset) & bitmsk) {
|
|
+ if (utimeout_elapsed(RESET_TIMEOUT_US, timeout_ref)) {
|
|
+ panic("Reset timeout");
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32_reset.h b/core/arch/arm/plat-stm32mp1/drivers/stm32_reset.h
|
|
new file mode 100644
|
|
index 0000000..577ba9b
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32_reset.h
|
|
@@ -0,0 +1,15 @@
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ *
|
|
+ * SPDX-License-Identifier: BSD-3-Clause
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_RESET_H__
|
|
+#define __STM32_RESET_H__
|
|
+
|
|
+#include <stdint.h>
|
|
+
|
|
+void stm32_reset_assert(uint32_t reset_id);
|
|
+void stm32_reset_deassert(uint32_t reset_id);
|
|
+
|
|
+#endif /* __STM32MP1_RESET_H__ */
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_calib.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_calib.c
|
|
new file mode 100644
|
|
index 0000000..583bf94
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_calib.c
|
|
@@ -0,0 +1,457 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <arm.h>
|
|
+#include <drivers/stm32_timer.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_clkfunc.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <initcall.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/interrupt.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <string.h>
|
|
+
|
|
+#define CAL_MAX_RETRY 20U
|
|
+
|
|
+/* List of forbiden values for HSI and CSI */
|
|
+static uint16_t fbv_hsi[] = {
|
|
+ 512, 480, 448, 416, 384, 352, 320, 288,
|
|
+ 256, 224, 192, 160, 128, 96, 64, 32, 0,
|
|
+};
|
|
+static uint16_t fbv_csi[] = {
|
|
+ 256, 240, 224, 208, 192, 176, 160, 144,
|
|
+ 128, 112, 96, 80, 64, 48, 32, 16, 0,
|
|
+};
|
|
+
|
|
+struct stm32mp1_trim_boundary_t {
|
|
+ unsigned int x1; /* Max boundary trim value around forbidden value */
|
|
+ unsigned int x2; /* Min boundary trim value around forbidden value */
|
|
+};
|
|
+
|
|
+struct stm32mp1_clk_cal {
|
|
+ uint16_t *fbv;
|
|
+ unsigned int cal_ref;
|
|
+ int trim_max;
|
|
+ int trim_min;
|
|
+ unsigned int boundary_max;
|
|
+ unsigned long ref_freq;
|
|
+ unsigned int freq_margin;
|
|
+ unsigned long (*get_freq)(void);
|
|
+ void (*set_trim)(unsigned int cal);
|
|
+ unsigned int (*get_trim)(void);
|
|
+ struct stm32mp1_trim_boundary_t boundary[16];
|
|
+};
|
|
+
|
|
+static void hsi_set_trim(unsigned int cal);
|
|
+static unsigned int hsi_get_trimed_cal(void);
|
|
+static void csi_set_trim(unsigned int cal);
|
|
+static unsigned int csi_get_trimed_cal(void);
|
|
+
|
|
+static struct stm32mp1_clk_cal *hsi_calib;
|
|
+static struct stm32mp1_clk_cal *csi_calib;
|
|
+
|
|
+static const struct stm32mp1_clk_cal hsi_calib_config = {
|
|
+ .fbv = fbv_hsi,
|
|
+ .trim_max = 63,
|
|
+ .trim_min = -64,
|
|
+ .ref_freq = 0,
|
|
+ .freq_margin = 1,
|
|
+ .set_trim = hsi_set_trim,
|
|
+ .get_trim = hsi_get_trimed_cal,
|
|
+};
|
|
+
|
|
+static const struct stm32mp1_clk_cal csi_calib_config = {
|
|
+ .fbv = fbv_csi,
|
|
+ .trim_max = 15,
|
|
+ .trim_min = -16,
|
|
+ .ref_freq = 0,
|
|
+ .freq_margin = 2,
|
|
+ .set_trim = csi_set_trim,
|
|
+ .get_trim = csi_get_trimed_cal,
|
|
+};
|
|
+
|
|
+static void hsi_set_trim(unsigned int cal)
|
|
+{
|
|
+ int clk_trim = (int)cal - (int)hsi_calib->cal_ref;
|
|
+ uint32_t trim = ((uint32_t)clk_trim << RCC_HSICFGR_HSITRIM_SHIFT) &
|
|
+ RCC_HSICFGR_HSITRIM_MASK;
|
|
+
|
|
+ mmio_clrsetbits_32(stm32_rcc_base() + RCC_HSICFGR,
|
|
+ RCC_HSICFGR_HSITRIM_MASK, trim);
|
|
+}
|
|
+KEEP_PAGER(hsi_set_trim);
|
|
+
|
|
+static unsigned int hsi_get_trimed_cal(void)
|
|
+{
|
|
+ uint32_t utrim = (mmio_read_32(stm32_rcc_base() + RCC_HSICFGR) &
|
|
+ RCC_HSICFGR_HSITRIM_MASK) >>
|
|
+ RCC_HSICFGR_HSITRIM_SHIFT;
|
|
+ int trim = (int)utrim - hsi_calib->trim_max;
|
|
+
|
|
+ if (trim + (int)hsi_calib->cal_ref < 0)
|
|
+ return 0;
|
|
+
|
|
+ return hsi_calib->cal_ref + trim;
|
|
+}
|
|
+KEEP_PAGER(hsi_get_trimed_cal);
|
|
+
|
|
+static void csi_set_trim(unsigned int cal)
|
|
+{
|
|
+ int clk_trim = (int)cal - (int)csi_calib->cal_ref +
|
|
+ csi_calib->trim_max + 1;
|
|
+ uint32_t trim = ((uint32_t)clk_trim << RCC_CSICFGR_CSITRIM_SHIFT) &
|
|
+ RCC_CSICFGR_CSITRIM_MASK;
|
|
+
|
|
+ mmio_clrsetbits_32(stm32_rcc_base() + RCC_CSICFGR,
|
|
+ RCC_CSICFGR_CSITRIM_MASK, trim);
|
|
+}
|
|
+KEEP_PAGER(csi_set_trim);
|
|
+
|
|
+static unsigned int csi_get_trimed_cal(void)
|
|
+{
|
|
+ uint32_t trim = (mmio_read_32(stm32_rcc_base() + RCC_CSICFGR) &
|
|
+ RCC_CSICFGR_CSITRIM_MASK) >>
|
|
+ RCC_CSICFGR_CSITRIM_SHIFT;
|
|
+
|
|
+ return (int)trim - csi_calib->trim_max + (int)csi_calib->cal_ref - 1;
|
|
+}
|
|
+KEEP_PAGER(csi_get_trimed_cal);
|
|
+
|
|
+static unsigned int trim_increase(struct stm32mp1_clk_cal *clk_cal,
|
|
+ unsigned int cal)
|
|
+{
|
|
+ struct stm32mp1_trim_boundary_t *boundary;
|
|
+ unsigned int new_cal;
|
|
+ int i;
|
|
+
|
|
+ /* By default: last calibration value */
|
|
+ new_cal = cal;
|
|
+
|
|
+ /* Start from Lowest cal value */
|
|
+ for (i = (int)clk_cal->boundary_max - 1; i >= 0; i--) {
|
|
+ boundary = &clk_cal->boundary[i];
|
|
+
|
|
+ if (cal < boundary->x2) {
|
|
+ new_cal = boundary->x2;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((cal >= boundary->x2) && (cal < boundary->x1)) {
|
|
+ new_cal = cal + 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return new_cal;
|
|
+}
|
|
+
|
|
+static unsigned int trim_decrease(struct stm32mp1_clk_cal *clk_cal,
|
|
+ unsigned int cal)
|
|
+{
|
|
+ struct stm32mp1_trim_boundary_t *boundary;
|
|
+ unsigned int new_cal;
|
|
+ unsigned int i;
|
|
+
|
|
+ /* By default: last calibration value */
|
|
+ new_cal = cal;
|
|
+
|
|
+ /* Start from Highest cal value */
|
|
+ for (i = 0; i < clk_cal->boundary_max; i++) {
|
|
+ boundary = &clk_cal->boundary[i];
|
|
+
|
|
+ if (cal > boundary->x1) {
|
|
+ new_cal = boundary->x1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((cal > boundary->x2) && (cal <= boundary->x1)) {
|
|
+ new_cal = cal - 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return new_cal;
|
|
+}
|
|
+
|
|
+static void rcc_calibration(struct stm32mp1_clk_cal *clk_cal)
|
|
+{
|
|
+ unsigned long margin = (clk_cal->ref_freq * clk_cal->freq_margin) / 100;
|
|
+ unsigned long min = clk_cal->ref_freq - margin;
|
|
+ unsigned long max = clk_cal->ref_freq + margin;
|
|
+ unsigned long freq = clk_cal->get_freq();
|
|
+ int cal = clk_cal->get_trim();
|
|
+ unsigned int nb_retries;
|
|
+
|
|
+ for (nb_retries = 0; nb_retries < CAL_MAX_RETRY; nb_retries++) {
|
|
+ if ((freq >= min) && (freq <= max)) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (freq < min) {
|
|
+ cal = trim_increase(clk_cal, cal);
|
|
+ } else {
|
|
+ cal = trim_decrease(clk_cal, cal);
|
|
+ }
|
|
+
|
|
+ clk_cal->set_trim(cal);
|
|
+
|
|
+ freq = clk_cal->get_freq();
|
|
+ }
|
|
+
|
|
+ if ((freq < min) || (freq > max)) {
|
|
+ DMSG("Calibration failed");
|
|
+ panic("Calibration");
|
|
+ }
|
|
+}
|
|
+
|
|
+static void save_trim(struct stm32mp1_clk_cal *clk_cal,
|
|
+ unsigned int i, unsigned int x1, unsigned int x2)
|
|
+{
|
|
+ clk_cal->boundary[i].x1 = x1;
|
|
+ clk_cal->boundary[i].x2 = x2;
|
|
+}
|
|
+
|
|
+static int trim_find_prev_boundary(struct stm32mp1_clk_cal *clk_cal,
|
|
+ unsigned int x1)
|
|
+{
|
|
+ unsigned int x = x1;
|
|
+ unsigned long freq;
|
|
+
|
|
+ clk_cal->set_trim(x1 + 1);
|
|
+ freq = clk_cal->get_freq();
|
|
+
|
|
+ while (x >= (clk_cal->cal_ref + clk_cal->trim_min)) {
|
|
+ x--;
|
|
+ clk_cal->set_trim(x);
|
|
+
|
|
+ if (clk_cal->get_freq() <= freq) {
|
|
+ break;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ return x;
|
|
+}
|
|
+
|
|
+static void trim_table_init(struct stm32mp1_clk_cal *clk_cal)
|
|
+{
|
|
+ uint16_t *trim_fbv = clk_cal->fbv;
|
|
+ unsigned int min;
|
|
+ unsigned int max;
|
|
+ int boundary = 0;
|
|
+ int i = 0;
|
|
+
|
|
+ max = clk_cal->cal_ref + clk_cal->trim_max;
|
|
+ min = clk_cal->cal_ref + clk_cal->trim_min;
|
|
+
|
|
+ while (trim_fbv[i]) {
|
|
+ unsigned int x;
|
|
+ unsigned int x1 = trim_fbv[i];
|
|
+ unsigned int x2 = trim_fbv[i + 1];
|
|
+
|
|
+ if ((max <= x2) || (min >= x1)) {
|
|
+ i++;
|
|
+ if (boundary != 0) {
|
|
+ goto out;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Take forbiden value + 1 */
|
|
+ x2 = x2 + 1;
|
|
+ if (x2 < min) {
|
|
+ x2 = min;
|
|
+ }
|
|
+
|
|
+ if (boundary == 0) {
|
|
+ /* Save first boundary */
|
|
+ save_trim(clk_cal, boundary, max, x2);
|
|
+ boundary++;
|
|
+ i++;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ x = trim_find_prev_boundary(clk_cal, x1);
|
|
+ /* Save boundary values */
|
|
+ save_trim(clk_cal, boundary, x - 1, x2);
|
|
+ boundary++;
|
|
+ i++;
|
|
+ };
|
|
+out:
|
|
+ clk_cal->boundary_max = boundary;
|
|
+}
|
|
+
|
|
+/* Timer countdown/delay argument for the target calibration periodicity */
|
|
+static uint32_t timer_val;
|
|
+
|
|
+#define CNTP_CTL_ENABLE BIT(0)
|
|
+#define CNTP_CTL_IMASK BIT(1)
|
|
+#define CNTP_CTL_ISTATUS BIT(2)
|
|
+
|
|
+static void arm_timer(void)
|
|
+{
|
|
+ if (!timer_val)
|
|
+ return;
|
|
+
|
|
+ write_cntp_ctl(read_cntp_ctl() & ~(CNTP_CTL_ENABLE | CNTP_CTL_IMASK));
|
|
+ write_cntp_tval(timer_val);
|
|
+ write_cntp_ctl(read_cntp_ctl() | CNTP_CTL_ENABLE);
|
|
+}
|
|
+
|
|
+static void arm_timer_with_period(uint32_t period_sec)
|
|
+{
|
|
+ timer_val = period_sec * read_cntfrq();
|
|
+
|
|
+ arm_timer();
|
|
+}
|
|
+
|
|
+static void calib_period(void)
|
|
+{
|
|
+ (void)stm32mp_start_clock_calib(CK_HSI);
|
|
+ (void)stm32mp_start_clock_calib(CK_CSI);
|
|
+
|
|
+ arm_timer();
|
|
+}
|
|
+
|
|
+static enum itr_return arm_cntp_it_handler(struct itr_handler *handler __unused)
|
|
+{
|
|
+ if (timer_val)
|
|
+ calib_period();
|
|
+
|
|
+ return ITRR_HANDLED;
|
|
+}
|
|
+static struct itr_handler arm_cntp_handler = {
|
|
+ .it = GIC_SPI_SEC_PHY_TIMER,
|
|
+ .handler = arm_cntp_it_handler,
|
|
+};
|
|
+KEEP_PAGER(arm_cntp_handler);
|
|
+
|
|
+static void timer_pm(enum pm_op op, void *handle __unused)
|
|
+{
|
|
+ if (op != PM_OP_RESUME || !timer_val)
|
|
+ return;
|
|
+
|
|
+ calib_period();
|
|
+}
|
|
+KEEP_PAGER(timer_pm);
|
|
+
|
|
+static TEE_Result init_arm_cntp_timer(void)
|
|
+{
|
|
+ itr_add(&arm_cntp_handler);
|
|
+ itr_enable(arm_cntp_handler.it);
|
|
+
|
|
+ stm32mp_register_pm_cb(timer_pm, NULL);
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(init_arm_cntp_timer);
|
|
+
|
|
+static void init_periodic_calibration(void *fdt, int node)
|
|
+{
|
|
+ uint32_t period = fdt_read_uint32_default(fdt, node, "st,cal-sec", 0);
|
|
+
|
|
+ DMSG("Calib period %us", period);
|
|
+ arm_timer_with_period(period);
|
|
+}
|
|
+
|
|
+int stm32mp_start_clock_calib(unsigned int clock_id)
|
|
+{
|
|
+ struct stm32mp1_clk_cal *clk_calib;
|
|
+
|
|
+ switch (clock_id) {
|
|
+ case CK_HSI:
|
|
+ clk_calib = hsi_calib;
|
|
+ break;
|
|
+ case CK_CSI:
|
|
+ clk_calib = csi_calib;
|
|
+ break;
|
|
+ default:
|
|
+ DMSG("Cannot calibrate clock %u", clock_id);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (clk_calib->ref_freq == 0U)
|
|
+ return 1;
|
|
+
|
|
+ DMSG("%s", clock_id == CK_HSI ? "HSI" : "CSI");
|
|
+ rcc_calibration(clk_calib);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void init_hsi_calibration(void *fdt, int node)
|
|
+{
|
|
+ if (!fdt_getprop(fdt, node, "st,hsi-cal", NULL))
|
|
+ return;
|
|
+
|
|
+ hsi_calib = calloc(1, sizeof(*hsi_calib));
|
|
+ assert(hsi_calib);
|
|
+ memcpy(hsi_calib, &hsi_calib_config, sizeof(*hsi_calib));
|
|
+
|
|
+ stm32_timer_freq_func(&hsi_calib->get_freq, HSI_CAL);
|
|
+ assert(hsi_calib->get_freq);
|
|
+
|
|
+ hsi_calib->ref_freq = stm32mp1_clk_get_rate(CK_HSI);
|
|
+
|
|
+ hsi_calib->cal_ref = (mmio_read_32(stm32_rcc_base() + RCC_HSICFGR) &
|
|
+ RCC_HSICFGR_HSICAL_MASK) >>
|
|
+ RCC_HSICFGR_HSICAL_SHIFT;
|
|
+
|
|
+ trim_table_init(hsi_calib);
|
|
+
|
|
+ hsi_calib->set_trim(hsi_calib->cal_ref);
|
|
+
|
|
+ stm32mp_start_clock_calib(CK_HSI);
|
|
+}
|
|
+
|
|
+static void init_csi_calibration(void *fdt, int node)
|
|
+{
|
|
+ if (!fdt_getprop(fdt, node, "st,csi-cal", NULL))
|
|
+ return;
|
|
+
|
|
+ csi_calib = calloc(1, sizeof(*csi_calib));
|
|
+ assert(csi_calib);
|
|
+ memcpy(csi_calib, &csi_calib_config, sizeof(*csi_calib));
|
|
+
|
|
+ stm32_timer_freq_func(&csi_calib->get_freq, CSI_CAL);
|
|
+ assert(csi_calib->get_freq);
|
|
+
|
|
+ csi_calib->ref_freq = stm32mp1_clk_get_rate(CK_CSI);
|
|
+
|
|
+ csi_calib->cal_ref = (mmio_read_32(stm32_rcc_base() + RCC_CSICFGR) &
|
|
+ RCC_CSICFGR_CSICAL_MASK) >>
|
|
+ RCC_CSICFGR_CSICAL_SHIFT;
|
|
+
|
|
+ trim_table_init(csi_calib);
|
|
+
|
|
+ csi_calib->set_trim(csi_calib->cal_ref);
|
|
+
|
|
+ stm32mp_start_clock_calib(CK_CSI);
|
|
+}
|
|
+
|
|
+static TEE_Result init_stm32mp1_calib(void)
|
|
+{
|
|
+ void *fdt;
|
|
+ int rcc_node = -1;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt)
|
|
+ rcc_node = fdt_get_rcc_node(fdt);
|
|
+ if (rcc_node < 0)
|
|
+ panic();
|
|
+
|
|
+ init_hsi_calibration(fdt, rcc_node);
|
|
+ init_csi_calibration(fdt, rcc_node);
|
|
+ init_periodic_calibration(fdt, rcc_node);
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(init_stm32mp1_calib);
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.c
|
|
new file mode 100644
|
|
index 0000000..acf5d60
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.c
|
|
@@ -0,0 +1,1527 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (C) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <initcall.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clksrc.h>
|
|
+#include <initcall.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <kernel/spinlock.h>
|
|
+#include <stdint.h>
|
|
+#include <stdio.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <trace.h>
|
|
+#include <util.h>
|
|
+
|
|
+#ifdef CFG_DT
|
|
+#include <drivers/stm32mp1_clkfunc.h>
|
|
+#include <libfdt.h>
|
|
+#endif
|
|
+
|
|
+enum stm32mp1_parent_id {
|
|
+/* Oscillators are defined in enum stm32mp_osc_id */
|
|
+
|
|
+/* Other parent source */
|
|
+ _HSI_KER = NB_OSC,
|
|
+ _HSE_KER,
|
|
+ _HSE_KER_DIV2,
|
|
+ _CSI_KER,
|
|
+ _PLL1_P,
|
|
+ _PLL1_Q,
|
|
+ _PLL1_R,
|
|
+ _PLL2_P,
|
|
+ _PLL2_Q,
|
|
+ _PLL2_R,
|
|
+ _PLL3_P,
|
|
+ _PLL3_Q,
|
|
+ _PLL3_R,
|
|
+ _PLL4_P,
|
|
+ _PLL4_Q,
|
|
+ _PLL4_R,
|
|
+ _ACLK,
|
|
+ _PCLK1,
|
|
+ _PCLK2,
|
|
+ _PCLK3,
|
|
+ _PCLK4,
|
|
+ _PCLK5,
|
|
+ _HCLK6,
|
|
+ _HCLK2,
|
|
+ _CK_PER,
|
|
+ _CK_MPU,
|
|
+ _CK_MCU,
|
|
+ _PARENT_NB,
|
|
+ _UNKNOWN_ID = 0xff,
|
|
+};
|
|
+
|
|
+/* Lists only the parent clock we are interested in */
|
|
+enum stm32mp1_parent_sel {
|
|
+ _STGEN_SEL,
|
|
+ _I2C46_SEL,
|
|
+ _SPI6_SEL,
|
|
+ _USART1_SEL,
|
|
+ _RNG1_SEL,
|
|
+ _UART6_SEL,
|
|
+ _UART24_SEL,
|
|
+ _UART35_SEL,
|
|
+ _UART78_SEL,
|
|
+ _ASS_SEL,
|
|
+ _MSS_SEL,
|
|
+ _USBPHY_SEL,
|
|
+ _USBO_SEL,
|
|
+ _PARENT_SEL_NB,
|
|
+ _UNKNOWN_SEL = 0xff,
|
|
+};
|
|
+
|
|
+enum stm32mp1_pll_id {
|
|
+ _PLL1,
|
|
+ _PLL2,
|
|
+ _PLL3,
|
|
+ _PLL4,
|
|
+ _PLL_NB
|
|
+};
|
|
+
|
|
+enum stm32mp1_div_id {
|
|
+ _DIV_P,
|
|
+ _DIV_Q,
|
|
+ _DIV_R,
|
|
+ _DIV_NB,
|
|
+};
|
|
+
|
|
+enum stm32mp1_clksrc_id {
|
|
+ CLKSRC_MPU,
|
|
+ CLKSRC_AXI,
|
|
+ CLKSRC_MCU,
|
|
+ CLKSRC_PLL12,
|
|
+ CLKSRC_PLL3,
|
|
+ CLKSRC_PLL4,
|
|
+ CLKSRC_RTC,
|
|
+ CLKSRC_MCO1,
|
|
+ CLKSRC_MCO2,
|
|
+ CLKSRC_NB
|
|
+};
|
|
+
|
|
+enum stm32mp1_clkdiv_id {
|
|
+ CLKDIV_MPU,
|
|
+ CLKDIV_AXI,
|
|
+ CLKDIV_MCU,
|
|
+ CLKDIV_APB1,
|
|
+ CLKDIV_APB2,
|
|
+ CLKDIV_APB3,
|
|
+ CLKDIV_APB4,
|
|
+ CLKDIV_APB5,
|
|
+ CLKDIV_RTC,
|
|
+ CLKDIV_MCO1,
|
|
+ CLKDIV_MCO2,
|
|
+ CLKDIV_NB
|
|
+};
|
|
+
|
|
+enum stm32mp1_pllcfg {
|
|
+ PLLCFG_M,
|
|
+ PLLCFG_N,
|
|
+ PLLCFG_P,
|
|
+ PLLCFG_Q,
|
|
+ PLLCFG_R,
|
|
+ PLLCFG_O,
|
|
+ PLLCFG_NB
|
|
+};
|
|
+
|
|
+enum stm32mp1_pllcsg {
|
|
+ PLLCSG_MOD_PER,
|
|
+ PLLCSG_INC_STEP,
|
|
+ PLLCSG_SSCG_MODE,
|
|
+ PLLCSG_NB
|
|
+};
|
|
+
|
|
+enum stm32mp1_plltype {
|
|
+ PLL_800,
|
|
+ PLL_1600,
|
|
+ PLL_TYPE_NB
|
|
+};
|
|
+
|
|
+struct stm32mp1_pll {
|
|
+ uint8_t refclk_min;
|
|
+ uint8_t refclk_max;
|
|
+ uint8_t divn_max;
|
|
+};
|
|
+
|
|
+struct stm32mp1_clk_gate {
|
|
+ uint16_t offset;
|
|
+ uint8_t bit;
|
|
+ uint8_t index;
|
|
+ uint8_t set_clr;
|
|
+ uint8_t sel; /* Relates to enum stm32mp1_parent_sel */
|
|
+ uint8_t fixed; /* Relates to enum stm32mp1_parent_id */
|
|
+};
|
|
+
|
|
+struct stm32mp1_clk_sel {
|
|
+ uint16_t offset;
|
|
+ uint8_t src;
|
|
+ uint8_t msk;
|
|
+ uint8_t nb_parent;
|
|
+ const uint8_t *parent;
|
|
+};
|
|
+
|
|
+#define REFCLK_SIZE 4
|
|
+struct stm32mp1_clk_pll {
|
|
+ enum stm32mp1_plltype plltype;
|
|
+ uint16_t rckxselr;
|
|
+ uint16_t pllxcfgr1;
|
|
+ uint16_t pllxcfgr2;
|
|
+ uint16_t pllxfracr;
|
|
+ uint16_t pllxcr;
|
|
+ uint16_t pllxcsgr;
|
|
+ enum stm32mp_osc_id refclk[REFCLK_SIZE];
|
|
+};
|
|
+
|
|
+/* Clocks with selectable source and not set/clr register access */
|
|
+#define _CLK_SELEC(off, b, idx, s) \
|
|
+ { \
|
|
+ .offset = (off), \
|
|
+ .bit = (b), \
|
|
+ .index = (idx), \
|
|
+ .set_clr = 0, \
|
|
+ .sel = (s), \
|
|
+ .fixed = _UNKNOWN_ID, \
|
|
+ }
|
|
+
|
|
+/* Clocks with fixed source and not set/clr register access */
|
|
+#define _CLK_FIXED(off, b, idx, f) \
|
|
+ { \
|
|
+ .offset = (off), \
|
|
+ .bit = (b), \
|
|
+ .index = (idx), \
|
|
+ .set_clr = 0, \
|
|
+ .sel = _UNKNOWN_SEL, \
|
|
+ .fixed = (f), \
|
|
+ }
|
|
+
|
|
+/* Clocks with selectable source and set/clr register access */
|
|
+#define _CLK_SC_SELEC(off, b, idx, s) \
|
|
+ { \
|
|
+ .offset = (off), \
|
|
+ .bit = (b), \
|
|
+ .index = (idx), \
|
|
+ .set_clr = 1, \
|
|
+ .sel = (s), \
|
|
+ .fixed = _UNKNOWN_ID, \
|
|
+ }
|
|
+
|
|
+/* Clocks with fixed source and set/clr register access */
|
|
+#define _CLK_SC_FIXED(off, b, idx, f) \
|
|
+ { \
|
|
+ .offset = (off), \
|
|
+ .bit = (b), \
|
|
+ .index = (idx), \
|
|
+ .set_clr = 1, \
|
|
+ .sel = _UNKNOWN_SEL, \
|
|
+ .fixed = (f), \
|
|
+ }
|
|
+
|
|
+/*
|
|
+ * Clocks with selectable source and set/clr register access
|
|
+ * and enable bit position defined by a label (argument b)
|
|
+ */
|
|
+#define _CLK_SC2_SELEC(off, b, idx, s) \
|
|
+ { \
|
|
+ .offset = (off), \
|
|
+ .index = (idx), \
|
|
+ .bit = off ## _ ## b ## _POS, \
|
|
+ .set_clr = 1, \
|
|
+ .sel = (s), \
|
|
+ .fixed = _UNKNOWN_ID, \
|
|
+ }
|
|
+#define _CLK_SC2_FIXED(off, b, idx, f) \
|
|
+ { \
|
|
+ .offset = (off), \
|
|
+ .index = (idx), \
|
|
+ .bit = off ## _ ## b ## _POS, \
|
|
+ .set_clr = 1, \
|
|
+ .sel = _UNKNOWN_SEL, \
|
|
+ .fixed = (f), \
|
|
+ }
|
|
+
|
|
+#define _CLK_PARENT(idx, off, s, m, p) \
|
|
+ [(idx)] = { \
|
|
+ .offset = (off), \
|
|
+ .src = (s), \
|
|
+ .msk = (m), \
|
|
+ .parent = (p), \
|
|
+ .nb_parent = ARRAY_SIZE(p) \
|
|
+ }
|
|
+
|
|
+#define _CLK_PLL(idx, type, off1, off2, off3, \
|
|
+ off4, off5, off6, \
|
|
+ p1, p2, p3, p4) \
|
|
+ [(idx)] = { \
|
|
+ .plltype = (type), \
|
|
+ .rckxselr = (off1), \
|
|
+ .pllxcfgr1 = (off2), \
|
|
+ .pllxcfgr2 = (off3), \
|
|
+ .pllxfracr = (off4), \
|
|
+ .pllxcr = (off5), \
|
|
+ .pllxcsgr = (off6), \
|
|
+ .refclk[0] = (p1), \
|
|
+ .refclk[1] = (p2), \
|
|
+ .refclk[2] = (p3), \
|
|
+ .refclk[3] = (p4), \
|
|
+ }
|
|
+
|
|
+static const uint8_t stm32mp1_clks[][2] = {
|
|
+ { CK_PER, _CK_PER },
|
|
+ { CK_MPU, _CK_MPU },
|
|
+ { CK_AXI, _ACLK },
|
|
+ { CK_MCU, _CK_MCU },
|
|
+ { CK_HSE, _HSE },
|
|
+ { CK_CSI, _CSI },
|
|
+ { CK_LSI, _LSI },
|
|
+ { CK_LSE, _LSE },
|
|
+ { CK_HSI, _HSI },
|
|
+ { CK_HSE_DIV2, _HSE_KER_DIV2 },
|
|
+};
|
|
+
|
|
+#define NB_GATES ARRAY_SIZE(stm32mp1_clk_gate)
|
|
+
|
|
+static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 0, DDRC1, _ACLK),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 1, DDRC1LP, _ACLK),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 2, DDRC2, _ACLK),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 3, DDRC2LP, _ACLK),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 4, DDRPHYC, _PLL2_R),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 5, DDRPHYCLP, _PLL2_R),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 6, DDRCAPB, _PCLK4),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 7, DDRCAPBLP, _PCLK4),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 8, AXIDCG, _ACLK),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 9, DDRPHYCAPB, _PCLK4),
|
|
+ _CLK_FIXED(RCC_DDRITFCR, 10, DDRPHYCAPBLP, _PCLK4),
|
|
+
|
|
+ _CLK_SC2_SELEC(RCC_MP_APB5ENSETR, SPI6EN, SPI6_K, _SPI6_SEL),
|
|
+ _CLK_SC2_SELEC(RCC_MP_APB5ENSETR, I2C4EN, I2C4_K, _I2C46_SEL),
|
|
+ _CLK_SC2_SELEC(RCC_MP_APB5ENSETR, I2C6EN, I2C6_K, _I2C46_SEL),
|
|
+ _CLK_SC2_SELEC(RCC_MP_APB5ENSETR, USART1EN, USART1_K, _USART1_SEL),
|
|
+ _CLK_SC2_FIXED(RCC_MP_APB5ENSETR, RTCAPBEN, RTCAPB, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_APB5ENSETR, TZC1EN, TZC1, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_APB5ENSETR, TZC2EN, TZC2, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_APB5ENSETR, TZPCEN, TZPC, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_APB5ENSETR, IWDG1APBEN, IWDG1, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_APB5ENSETR, BSECEN, BSEC, _PCLK5),
|
|
+ _CLK_SC2_SELEC(RCC_MP_APB5ENSETR, STGENEN, STGEN_K, _STGEN_SEL),
|
|
+
|
|
+ _CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, GPIOZEN, GPIOZ, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, CRYP1EN, CRYP1, _PCLK5),
|
|
+ _CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, HASH1EN, HASH1, _PCLK5),
|
|
+ _CLK_SC2_SELEC(RCC_MP_AHB5ENSETR, RNG1EN, RNG1_K, _RNG1_SEL),
|
|
+ _CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, BKPSRAMEN, BKPSRAM, _PCLK5),
|
|
+
|
|
+ /* Non-secure clocks */
|
|
+#ifdef CFG_WITH_NSEC_GPIOS
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 3, GPIOD, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 4, GPIOE, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 5, GPIOF, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 6, GPIOG, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 7, GPIOH, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 8, GPIOI, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 9, GPIOJ, _UNKNOWN_ID),
|
|
+ _CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_ID),
|
|
+#endif
|
|
+#ifdef CFG_WITH_NSEC_UARTS
|
|
+ _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 14, USART2_K, _UART24_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 15, USART3_K, _UART35_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 16, UART4_K, _UART24_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 17, UART5_K, _UART35_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 18, UART7_K, _UART78_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 19, UART8_K, _UART78_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL),
|
|
+#endif
|
|
+ _CLK_SC_SELEC(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL),
|
|
+ _CLK_SC_SELEC(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL),
|
|
+ _CLK_SELEC(RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL),
|
|
+ _CLK_SC_FIXED(RCC_MP_APB1ENSETR, 6, TIM12_K, _PCLK1),
|
|
+ _CLK_SC_FIXED(RCC_MP_APB2ENSETR, 2, TIM15_K, _PCLK2),
|
|
+};
|
|
+KEEP_PAGER(stm32mp1_clk_gate);
|
|
+
|
|
+/* Parents for secure aware clocks in the xxxSELR value ordering */
|
|
+static const uint8_t stgen_parents[] = {
|
|
+ _HSI_KER, _HSE_KER
|
|
+};
|
|
+
|
|
+static const uint8_t i2c46_parents[] = {
|
|
+ _PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER
|
|
+};
|
|
+
|
|
+static const uint8_t spi6_parents[] = {
|
|
+ _PCLK5, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER, _PLL3_Q
|
|
+};
|
|
+
|
|
+static const uint8_t usart1_parents[] = {
|
|
+ _PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER, _PLL4_Q, _HSE_KER
|
|
+};
|
|
+
|
|
+static const uint8_t rng1_parents[] = {
|
|
+ _CSI, _PLL4_R, _LSE, _LSI
|
|
+};
|
|
+
|
|
+/* Parents for (some) non-secure clocks */
|
|
+static const uint8_t uart6_parents[] = {
|
|
+ _PCLK2, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER
|
|
+};
|
|
+
|
|
+static const uint8_t uart234578_parents[] = {
|
|
+ _PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER
|
|
+};
|
|
+
|
|
+static const uint8_t ass_parents[] = {
|
|
+ _HSI, _HSE, _PLL2
|
|
+};
|
|
+
|
|
+static const uint8_t mss_parents[] = {
|
|
+ _HSI, _HSE, _CSI, _PLL3
|
|
+};
|
|
+
|
|
+static const uint8_t usbphy_parents[] = {
|
|
+ _HSE_KER, _PLL4_R, _HSE_KER_DIV2
|
|
+};
|
|
+
|
|
+static const uint8_t usbo_parents[] = {
|
|
+ _PLL4_R, _USB_PHY_48
|
|
+};
|
|
+
|
|
+static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
|
|
+ /* Secure aware clocks */
|
|
+ _CLK_PARENT(_STGEN_SEL, RCC_STGENCKSELR, 0, 0x3, stgen_parents),
|
|
+ _CLK_PARENT(_I2C46_SEL, RCC_I2C46CKSELR, 0, 0x7, i2c46_parents),
|
|
+ _CLK_PARENT(_SPI6_SEL, RCC_SPI6CKSELR, 0, 0x7, spi6_parents),
|
|
+ _CLK_PARENT(_USART1_SEL, RCC_UART1CKSELR, 0, 0x7, usart1_parents),
|
|
+ _CLK_PARENT(_RNG1_SEL, RCC_RNG1CKSELR, 0, 0x3, rng1_parents),
|
|
+ /* Always non-secure clocks (maybe used in some way in secure world) */
|
|
+ _CLK_PARENT(_UART6_SEL, RCC_UART6CKSELR, 0, 0x7, uart6_parents),
|
|
+ _CLK_PARENT(_UART24_SEL, RCC_UART24CKSELR, 0, 0x7, uart234578_parents),
|
|
+ _CLK_PARENT(_UART35_SEL, RCC_UART35CKSELR, 0, 0x7, uart234578_parents),
|
|
+ _CLK_PARENT(_UART78_SEL, RCC_UART78CKSELR, 0, 0x7, uart234578_parents),
|
|
+ _CLK_PARENT(_ASS_SEL, RCC_ASSCKSELR, 0, 0x3, ass_parents),
|
|
+ _CLK_PARENT(_MSS_SEL, RCC_MSSCKSELR, 0, 0x3, mss_parents),
|
|
+ _CLK_PARENT(_USBPHY_SEL, RCC_USBCKSELR, 0, 0x3, usbphy_parents),
|
|
+ _CLK_PARENT(_USBO_SEL, RCC_USBCKSELR, 4, 0x1, usbo_parents),
|
|
+};
|
|
+
|
|
+/* PLLNCFGR2 register divider by output */
|
|
+static const uint8_t pllncfgr2[_DIV_NB] = {
|
|
+ [_DIV_P] = RCC_PLLNCFGR2_DIVP_SHIFT,
|
|
+ [_DIV_Q] = RCC_PLLNCFGR2_DIVQ_SHIFT,
|
|
+ [_DIV_R] = RCC_PLLNCFGR2_DIVR_SHIFT,
|
|
+};
|
|
+
|
|
+static const struct stm32mp1_clk_pll stm32mp1_clk_pll[_PLL_NB] = {
|
|
+ _CLK_PLL(_PLL1, PLL_1600,
|
|
+ RCC_RCK12SELR, RCC_PLL1CFGR1, RCC_PLL1CFGR2,
|
|
+ RCC_PLL1FRACR, RCC_PLL1CR, RCC_PLL1CSGR,
|
|
+ _HSI, _HSE, _UNKNOWN_OSC_ID, _UNKNOWN_OSC_ID),
|
|
+ _CLK_PLL(_PLL2, PLL_1600,
|
|
+ RCC_RCK12SELR, RCC_PLL2CFGR1, RCC_PLL2CFGR2,
|
|
+ RCC_PLL2FRACR, RCC_PLL2CR, RCC_PLL2CSGR,
|
|
+ _HSI, _HSE, _UNKNOWN_OSC_ID, _UNKNOWN_OSC_ID),
|
|
+ _CLK_PLL(_PLL3, PLL_800,
|
|
+ RCC_RCK3SELR, RCC_PLL3CFGR1, RCC_PLL3CFGR2,
|
|
+ RCC_PLL3FRACR, RCC_PLL3CR, RCC_PLL3CSGR,
|
|
+ _HSI, _HSE, _CSI, _UNKNOWN_OSC_ID),
|
|
+ _CLK_PLL(_PLL4, PLL_800,
|
|
+ RCC_RCK4SELR, RCC_PLL4CFGR1, RCC_PLL4CFGR2,
|
|
+ RCC_PLL4FRACR, RCC_PLL4CR, RCC_PLL4CSGR,
|
|
+ _HSI, _HSE, _CSI, _I2S_CKIN),
|
|
+};
|
|
+
|
|
+/* Prescaler table lookups for clock computation */
|
|
+/* div = /1 /2 /4 /8 / 16 /64 /128 /512 */
|
|
+static const uint8_t stm32mp1_mcu_div[16] = {
|
|
+ 0, 1, 2, 3, 4, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9
|
|
+};
|
|
+
|
|
+/* div = /1 /2 /4 /8 /16 : same divider for PMU and APBX */
|
|
+#define stm32mp1_mpu_div stm32mp1_mpu_apbx_div
|
|
+#define stm32mp1_apbx_div stm32mp1_mpu_apbx_div
|
|
+static const uint8_t stm32mp1_mpu_apbx_div[8] = {
|
|
+ 0, 1, 2, 3, 4, 4, 4, 4
|
|
+};
|
|
+
|
|
+/* div = /1 /2 /3 /4 */
|
|
+static const uint8_t stm32mp1_axi_div[8] = {
|
|
+ 1, 2, 3, 4, 4, 4, 4, 4
|
|
+};
|
|
+
|
|
+#if TRACE_LEVEL >= TRACE_DEBUG
|
|
+static const char *const __maybe_unused stm32mp1_clk_parent_name[_PARENT_NB] = {
|
|
+ [_HSI] = "HSI",
|
|
+ [_HSE] = "HSE",
|
|
+ [_CSI] = "CSI",
|
|
+ [_LSI] = "LSI",
|
|
+ [_LSE] = "LSE",
|
|
+ [_I2S_CKIN] = "I2S_CKIN",
|
|
+ [_HSI_KER] = "HSI_KER",
|
|
+ [_HSE_KER] = "HSE_KER",
|
|
+ [_HSE_KER_DIV2] = "HSE_KER_DIV2",
|
|
+ [_CSI_KER] = "CSI_KER",
|
|
+ [_PLL1_P] = "PLL1_P",
|
|
+ [_PLL1_Q] = "PLL1_Q",
|
|
+ [_PLL1_R] = "PLL1_R",
|
|
+ [_PLL2_P] = "PLL2_P",
|
|
+ [_PLL2_Q] = "PLL2_Q",
|
|
+ [_PLL2_R] = "PLL2_R",
|
|
+ [_PLL3_P] = "PLL3_P",
|
|
+ [_PLL3_Q] = "PLL3_Q",
|
|
+ [_PLL3_R] = "PLL3_R",
|
|
+ [_PLL4_P] = "PLL4_P",
|
|
+ [_PLL4_Q] = "PLL4_Q",
|
|
+ [_PLL4_R] = "PLL4_R",
|
|
+ [_ACLK] = "ACLK",
|
|
+ [_PCLK1] = "PCLK1",
|
|
+ [_PCLK2] = "PCLK2",
|
|
+ [_PCLK3] = "PCLK3",
|
|
+ [_PCLK4] = "PCLK4",
|
|
+ [_PCLK5] = "PCLK5",
|
|
+ [_HCLK6] = "KCLK6",
|
|
+ [_HCLK2] = "HCLK2",
|
|
+ [_CK_PER] = "CK_PER",
|
|
+ [_CK_MPU] = "CK_MPU",
|
|
+ [_CK_MCU] = "CK_MCU",
|
|
+ [_USB_PHY_48] = "USB_PHY_48",
|
|
+};
|
|
+#endif
|
|
+
|
|
+/* RCC clock device driver private */
|
|
+static unsigned long stm32mp1_osc[NB_OSC];
|
|
+static unsigned int gate_refcounts[NB_GATES];
|
|
+static unsigned int refcount_lock;
|
|
+
|
|
+static const struct stm32mp1_clk_gate *gate_ref(unsigned int idx)
|
|
+{
|
|
+ return &stm32mp1_clk_gate[idx];
|
|
+}
|
|
+
|
|
+static const struct stm32mp1_clk_sel *clk_sel_ref(unsigned int idx)
|
|
+{
|
|
+ return &stm32mp1_clk_sel[idx];
|
|
+}
|
|
+
|
|
+static const struct stm32mp1_clk_pll *pll_ref(unsigned int idx)
|
|
+{
|
|
+ return &stm32mp1_clk_pll[idx];
|
|
+}
|
|
+
|
|
+static unsigned int get_id_from_rcc_bit(unsigned int offset, unsigned int bit)
|
|
+{
|
|
+ unsigned int idx;
|
|
+
|
|
+ for (idx = 0; idx < NB_GATES; idx++) {
|
|
+ const struct stm32mp1_clk_gate *gate = gate_ref(idx);
|
|
+
|
|
+ if ((offset == gate->offset) && (bit == gate->bit)) {
|
|
+ return gate->index;
|
|
+ }
|
|
+
|
|
+ if ((gate->set_clr != 0U) &&
|
|
+ (offset == (gate->offset + RCC_MP_ENCLRR_OFFSET)) &&
|
|
+ (bit == gate->bit)) {
|
|
+ return gate->index;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Currently only supported gated clocks */
|
|
+ return ~0U;
|
|
+}
|
|
+
|
|
+static unsigned long stm32mp1_clk_get_fixed(enum stm32mp_osc_id idx)
|
|
+{
|
|
+ if (idx >= NB_OSC) {
|
|
+ DMSG("clk id %d not found", idx);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return stm32mp1_osc[idx];
|
|
+}
|
|
+
|
|
+static int stm32mp1_clk_get_gated_id(unsigned long id)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < NB_GATES; i++) {
|
|
+ if (gate_ref(i)->index == id) {
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ DMSG("clk id %lu not found", id);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static enum stm32mp1_parent_sel stm32mp1_clk_get_sel(int i)
|
|
+{
|
|
+ return (enum stm32mp1_parent_sel)(gate_ref(i)->sel);
|
|
+}
|
|
+
|
|
+static enum stm32mp1_parent_id stm32mp1_clk_get_fixed_parent(int i)
|
|
+{
|
|
+ return (enum stm32mp1_parent_id)gate_ref(i)->fixed;
|
|
+}
|
|
+
|
|
+static int stm32mp1_clk_get_parent(unsigned long id)
|
|
+{
|
|
+ const struct stm32mp1_clk_sel *sel;
|
|
+ unsigned int j;
|
|
+ uint32_t p_sel;
|
|
+ int i;
|
|
+ enum stm32mp1_parent_id p;
|
|
+ enum stm32mp1_parent_sel s;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ for (j = 0U; j < ARRAY_SIZE(stm32mp1_clks); j++) {
|
|
+ if (stm32mp1_clks[j][0] == id) {
|
|
+ return (int)stm32mp1_clks[j][1];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ i = stm32mp1_clk_get_gated_id(id);
|
|
+ if (i < 0) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ p = stm32mp1_clk_get_fixed_parent(i);
|
|
+ if (p < _PARENT_NB) {
|
|
+ return (int)p;
|
|
+ }
|
|
+
|
|
+ s = stm32mp1_clk_get_sel(i);
|
|
+ if (s == _UNKNOWN_SEL) {
|
|
+ return -1;
|
|
+ }
|
|
+ if (s >= _PARENT_SEL_NB) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ sel = clk_sel_ref(s);
|
|
+ p_sel = (mmio_read_32(rcc_base + sel->offset) >> sel->src) & sel->msk;
|
|
+ if (p_sel < sel->nb_parent) {
|
|
+ return (int)sel->parent[p_sel];
|
|
+ }
|
|
+
|
|
+ DMSG("No parent selected for clk %lu", id);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static unsigned long stm32mp1_pll_get_fref(const struct stm32mp1_clk_pll *pll)
|
|
+{
|
|
+ uint32_t selr = mmio_read_32(stm32_rcc_base() + pll->rckxselr);
|
|
+ uint32_t src = selr & RCC_SELR_REFCLK_SRC_MASK;
|
|
+
|
|
+ return stm32mp1_clk_get_fixed(pll->refclk[src]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * pll_get_fvco() : return the VCO or (VCO / 2) frequency for the requested PLL
|
|
+ * - PLL1 & PLL2 => return VCO / 2 with Fpll_y_ck = FVCO / 2 * (DIVy + 1)
|
|
+ * - PLL3 & PLL4 => return VCO with Fpll_y_ck = FVCO / (DIVy + 1)
|
|
+ * => in all cases Fpll_y_ck = pll_get_fvco() / (DIVy + 1)
|
|
+ */
|
|
+static unsigned long stm32mp1_pll_get_fvco(const struct stm32mp1_clk_pll *pll)
|
|
+{
|
|
+ unsigned long refclk, fvco;
|
|
+ uint32_t cfgr1, fracr, divm, divn;
|
|
+
|
|
+ cfgr1 = mmio_read_32(stm32_rcc_base() + pll->pllxcfgr1);
|
|
+ fracr = mmio_read_32(stm32_rcc_base() + pll->pllxfracr);
|
|
+
|
|
+ divm = (cfgr1 & (RCC_PLLNCFGR1_DIVM_MASK)) >> RCC_PLLNCFGR1_DIVM_SHIFT;
|
|
+ divn = cfgr1 & RCC_PLLNCFGR1_DIVN_MASK;
|
|
+
|
|
+ refclk = stm32mp1_pll_get_fref(pll);
|
|
+
|
|
+ /*
|
|
+ * With FRACV :
|
|
+ * Fvco = Fck_ref * ((DIVN + 1) + FRACV / 2^13) / (DIVM + 1)
|
|
+ * Without FRACV
|
|
+ * Fvco = Fck_ref * ((DIVN + 1) / (DIVM + 1)
|
|
+ */
|
|
+ if ((fracr & RCC_PLLNFRACR_FRACLE) != 0U) {
|
|
+ unsigned long long numerator;
|
|
+ unsigned long long denominator;
|
|
+ uint32_t fracv = (fracr & RCC_PLLNFRACR_FRACV_MASK) >>
|
|
+ RCC_PLLNFRACR_FRACV_SHIFT;
|
|
+
|
|
+ numerator = (((unsigned long long)divn + 1U) << 13) + fracv;
|
|
+ numerator = refclk * numerator;
|
|
+ denominator = ((unsigned long long)divm + 1U) << 13;
|
|
+ fvco = (unsigned long)(numerator / denominator);
|
|
+ } else {
|
|
+ fvco = (unsigned long)(refclk * (divn + 1U) / (divm + 1U));
|
|
+ }
|
|
+
|
|
+ return fvco;
|
|
+}
|
|
+
|
|
+static unsigned long stm32mp1_read_pll_freq(enum stm32mp1_pll_id pll_id,
|
|
+ enum stm32mp1_div_id div_id)
|
|
+{
|
|
+ const struct stm32mp1_clk_pll *pll = pll_ref(pll_id);
|
|
+ unsigned long dfout;
|
|
+ uint32_t cfgr2, divy;
|
|
+
|
|
+ if (div_id >= _DIV_NB) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cfgr2 = mmio_read_32(stm32_rcc_base() + pll->pllxcfgr2);
|
|
+ divy = (cfgr2 >> pllncfgr2[div_id]) & RCC_PLLNCFGR2_DIVX_MASK;
|
|
+
|
|
+ dfout = stm32mp1_pll_get_fvco(pll) / (divy + 1U);
|
|
+
|
|
+ return dfout;
|
|
+}
|
|
+
|
|
+static unsigned long get_clock_rate(int p)
|
|
+{
|
|
+ uint32_t reg, clkdiv;
|
|
+ unsigned long clock = 0;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ switch (p) {
|
|
+ case _CK_MPU:
|
|
+ /* MPU sub system */
|
|
+ reg = mmio_read_32(rcc_base + RCC_MPCKSELR);
|
|
+ switch (reg & RCC_SELR_SRC_MASK) {
|
|
+ case RCC_MPCKSELR_HSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSI);
|
|
+ break;
|
|
+ case RCC_MPCKSELR_HSE:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSE);
|
|
+ break;
|
|
+ case RCC_MPCKSELR_PLL:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL1, _DIV_P);
|
|
+ break;
|
|
+ case RCC_MPCKSELR_PLL_MPUDIV:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL1, _DIV_P);
|
|
+
|
|
+ reg = mmio_read_32(rcc_base + RCC_MPCKDIVR);
|
|
+ clkdiv = reg & RCC_MPUDIV_MASK;
|
|
+ if (clkdiv != 0U) {
|
|
+ clock /= stm32mp1_mpu_div[clkdiv];
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ /* AXI sub system */
|
|
+ case _ACLK:
|
|
+ case _HCLK2:
|
|
+ case _HCLK6:
|
|
+ case _PCLK4:
|
|
+ case _PCLK5:
|
|
+ reg = mmio_read_32(rcc_base + RCC_ASSCKSELR);
|
|
+ switch (reg & RCC_SELR_SRC_MASK) {
|
|
+ case RCC_ASSCKSELR_HSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSI);
|
|
+ break;
|
|
+ case RCC_ASSCKSELR_HSE:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSE);
|
|
+ break;
|
|
+ case RCC_ASSCKSELR_PLL:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL2, _DIV_P);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* System clock divider */
|
|
+ reg = mmio_read_32(rcc_base + RCC_AXIDIVR);
|
|
+ clock /= stm32mp1_axi_div[reg & RCC_AXIDIV_MASK];
|
|
+
|
|
+ switch (p) {
|
|
+ case _PCLK4:
|
|
+ reg = mmio_read_32(rcc_base + RCC_APB4DIVR);
|
|
+ clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
|
|
+ break;
|
|
+ case _PCLK5:
|
|
+ reg = mmio_read_32(rcc_base + RCC_APB5DIVR);
|
|
+ clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ /* MCU sub system */
|
|
+ case _CK_MCU:
|
|
+ case _PCLK1:
|
|
+ case _PCLK2:
|
|
+ case _PCLK3:
|
|
+ reg = mmio_read_32(rcc_base + RCC_MSSCKSELR);
|
|
+ switch (reg & RCC_SELR_SRC_MASK) {
|
|
+ case RCC_MSSCKSELR_HSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSI);
|
|
+ break;
|
|
+ case RCC_MSSCKSELR_HSE:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSE);
|
|
+ break;
|
|
+ case RCC_MSSCKSELR_CSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_CSI);
|
|
+ break;
|
|
+ case RCC_MSSCKSELR_PLL:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL3, _DIV_P);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* MCU clock divider */
|
|
+ reg = mmio_read_32(rcc_base + RCC_MCUDIVR);
|
|
+ clock >>= stm32mp1_mcu_div[reg & RCC_MCUDIV_MASK];
|
|
+
|
|
+ switch (p) {
|
|
+ case _PCLK1:
|
|
+ reg = mmio_read_32(rcc_base + RCC_APB1DIVR);
|
|
+ clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
|
|
+ break;
|
|
+ case _PCLK2:
|
|
+ reg = mmio_read_32(rcc_base + RCC_APB2DIVR);
|
|
+ clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
|
|
+ break;
|
|
+ case _PCLK3:
|
|
+ reg = mmio_read_32(rcc_base + RCC_APB3DIVR);
|
|
+ clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
|
|
+ break;
|
|
+ case _CK_MCU:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case _CK_PER:
|
|
+ reg = mmio_read_32(rcc_base + RCC_CPERCKSELR);
|
|
+ switch (reg & RCC_SELR_SRC_MASK) {
|
|
+ case RCC_CPERCKSELR_HSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSI);
|
|
+ break;
|
|
+ case RCC_CPERCKSELR_HSE:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSE);
|
|
+ break;
|
|
+ case RCC_CPERCKSELR_CSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_CSI);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case _HSI:
|
|
+ case _HSI_KER:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSI);
|
|
+ break;
|
|
+ case _CSI:
|
|
+ case _CSI_KER:
|
|
+ clock = stm32mp1_clk_get_fixed(_CSI);
|
|
+ break;
|
|
+ case _HSE:
|
|
+ case _HSE_KER:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSE);
|
|
+ break;
|
|
+ case _HSE_KER_DIV2:
|
|
+ clock = stm32mp1_clk_get_fixed(_HSE) >> 1;
|
|
+ break;
|
|
+ case _LSI:
|
|
+ clock = stm32mp1_clk_get_fixed(_LSI);
|
|
+ break;
|
|
+ case _LSE:
|
|
+ clock = stm32mp1_clk_get_fixed(_LSE);
|
|
+ break;
|
|
+ /* PLL */
|
|
+ case _PLL1_P:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL1, _DIV_P);
|
|
+ break;
|
|
+ case _PLL1_Q:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL1, _DIV_Q);
|
|
+ break;
|
|
+ case _PLL1_R:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL1, _DIV_R);
|
|
+ break;
|
|
+ case _PLL2_P:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL2, _DIV_P);
|
|
+ break;
|
|
+ case _PLL2_Q:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL2, _DIV_Q);
|
|
+ break;
|
|
+ case _PLL2_R:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL2, _DIV_R);
|
|
+ break;
|
|
+ case _PLL3_P:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL3, _DIV_P);
|
|
+ break;
|
|
+ case _PLL3_Q:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL3, _DIV_Q);
|
|
+ break;
|
|
+ case _PLL3_R:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL3, _DIV_R);
|
|
+ break;
|
|
+ case _PLL4_P:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL4, _DIV_P);
|
|
+ break;
|
|
+ case _PLL4_Q:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL4, _DIV_Q);
|
|
+ break;
|
|
+ case _PLL4_R:
|
|
+ clock = stm32mp1_read_pll_freq(_PLL4, _DIV_R);
|
|
+ break;
|
|
+ /* Other */
|
|
+ case _USB_PHY_48:
|
|
+ clock = stm32mp1_clk_get_fixed(_USB_PHY_48);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return clock;
|
|
+}
|
|
+
|
|
+static void __clk_enable(struct stm32mp1_clk_gate const *gate)
|
|
+{
|
|
+ uintptr_t base = stm32_rcc_base();
|
|
+ uint32_t bit = BIT(gate->bit);
|
|
+
|
|
+ if (gate->set_clr != 0U) {
|
|
+ mmio_write_32(base + gate->offset, bit);
|
|
+ } else {
|
|
+ io_mask32_stm32shregs(base + gate->offset, bit, bit);
|
|
+ }
|
|
+
|
|
+ FMSG("Clock %u has been enabled", gate->index);
|
|
+}
|
|
+
|
|
+static void __clk_disable(struct stm32mp1_clk_gate const *gate)
|
|
+{
|
|
+ uintptr_t base = stm32_rcc_base();
|
|
+ uint32_t bit = BIT(gate->bit);
|
|
+
|
|
+ if (gate->set_clr != 0U) {
|
|
+ mmio_write_32(base + gate->offset + RCC_MP_ENCLRR_OFFSET, bit);
|
|
+ } else {
|
|
+ io_mask32_stm32shregs(base + gate->offset, 0, bit);
|
|
+ }
|
|
+
|
|
+ FMSG("Clock %u has been disabled", gate->index);
|
|
+}
|
|
+
|
|
+static bool __clk_is_enabled(struct stm32mp1_clk_gate const *gate)
|
|
+{
|
|
+ uintptr_t base = stm32_rcc_base();
|
|
+
|
|
+ return mmio_read_32(base + gate->offset) & BIT(gate->bit);
|
|
+}
|
|
+
|
|
+bool stm32mp1_clk_is_enabled(unsigned long id)
|
|
+{
|
|
+ int i = stm32mp1_clk_get_gated_id(id);
|
|
+
|
|
+ if (i < 0) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return __clk_is_enabled(gate_ref(i));
|
|
+}
|
|
+
|
|
+unsigned int stm32mp1_clk_get_refcount(unsigned long id)
|
|
+{
|
|
+ int i = stm32mp1_clk_get_gated_id(id);
|
|
+
|
|
+ return gate_refcounts[i];
|
|
+}
|
|
+
|
|
+void __stm32mp1_clk_enable(unsigned long id, bool secure)
|
|
+{
|
|
+ int i = stm32mp1_clk_get_gated_id(id);
|
|
+ uint32_t exceptions;
|
|
+
|
|
+ if (i < 0) {
|
|
+ DMSG("Invalid clock %lu: %d", id, i);
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ exceptions = may_spin_lock(&refcount_lock);
|
|
+
|
|
+ if (incr_shrefcnt(&gate_refcounts[i], secure) != 0) {
|
|
+ __clk_enable(gate_ref(i));
|
|
+ }
|
|
+
|
|
+ may_spin_unlock(&refcount_lock, exceptions);
|
|
+}
|
|
+
|
|
+void __stm32mp1_clk_disable(unsigned long id, bool secure)
|
|
+{
|
|
+ int i = stm32mp1_clk_get_gated_id(id);
|
|
+ uint32_t exceptions;
|
|
+
|
|
+ if (i < 0) {
|
|
+ DMSG("Invalid clock %lu: %d", id, i);
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ exceptions = may_spin_lock(&refcount_lock);
|
|
+
|
|
+ if (decr_shrefcnt(&gate_refcounts[i], secure) != 0) {
|
|
+ __clk_disable(gate_ref(i));
|
|
+ }
|
|
+
|
|
+ may_spin_unlock(&refcount_lock, exceptions);
|
|
+}
|
|
+
|
|
+static long get_timer_rate(long parent_rate, unsigned int apb_bus)
|
|
+{
|
|
+ uint32_t timgxpre;
|
|
+ uint32_t apbxdiv;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ switch (apb_bus) {
|
|
+ case 1:
|
|
+ apbxdiv = mmio_read_32(rcc_base + RCC_APB1DIVR) &
|
|
+ RCC_APBXDIV_MASK;
|
|
+ timgxpre = mmio_read_32(rcc_base + RCC_TIMG1PRER) &
|
|
+ RCC_TIMGXPRER_TIMGXPRE;
|
|
+ break;
|
|
+ case 2:
|
|
+ apbxdiv = mmio_read_32(rcc_base + RCC_APB2DIVR) &
|
|
+ RCC_APBXDIV_MASK;
|
|
+ timgxpre = mmio_read_32(rcc_base + RCC_TIMG2PRER) &
|
|
+ RCC_TIMGXPRER_TIMGXPRE;
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (apbxdiv == 0) {
|
|
+ return parent_rate;
|
|
+ }
|
|
+
|
|
+ return parent_rate * (timgxpre + 1) * 2;
|
|
+}
|
|
+
|
|
+unsigned long stm32mp1_clk_get_rate(unsigned long id)
|
|
+{
|
|
+ int p;
|
|
+ unsigned long rate;
|
|
+
|
|
+ p = stm32mp1_clk_get_parent(id);
|
|
+ if (p < 0) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ rate = get_clock_rate(p);
|
|
+
|
|
+ if ((id >= TIM2_K) && (id <= TIM14_K)) {
|
|
+ rate = get_timer_rate(rate, 1);
|
|
+ }
|
|
+ if ((id >= TIM1_K) && (id <= TIM17_K)) {
|
|
+ rate = get_timer_rate(rate, 2);
|
|
+ }
|
|
+
|
|
+ return rate;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static void stm32mp1_osc_clk_init(const char *name,
|
|
+ enum stm32mp_osc_id index)
|
|
+{
|
|
+ uint32_t frequency;
|
|
+ void *fdt;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ stm32mp1_osc[index] = 0;
|
|
+
|
|
+ if (fdt_osc_read_freq(fdt, name, &frequency) == 0) {
|
|
+ stm32mp1_osc[index] = frequency;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void stm32mp1_osc_init(void)
|
|
+{
|
|
+ enum stm32mp_osc_id i;
|
|
+ char **name __maybe_unused = (char **)&stm32mp_osc_node_label[0];
|
|
+
|
|
+ for (i = (enum stm32mp_osc_id)0 ; i < NB_OSC; i++) {
|
|
+ stm32mp1_osc_clk_init(stm32mp_osc_node_label[i], i);
|
|
+ DMSG("Osc %s frequency: %lu", name[i], stm32mp1_osc[i]);
|
|
+ }
|
|
+}
|
|
+#else
|
|
+static void stm32mp1_osc_init(void)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Lookup platform clock from enable bit location in RCC registers.
|
|
+ * Return a valid clock ID on success, return ~0 on error.
|
|
+ */
|
|
+unsigned long stm32mp1_clk_rcc2id(size_t offset, size_t bit)
|
|
+{
|
|
+ return get_id_from_rcc_bit(offset, bit);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get the parent ID of the target parent clock, for tagging as secure
|
|
+ * shared clock dependencies.
|
|
+ */
|
|
+static int get_parent_id_parent(unsigned int parent_id)
|
|
+{
|
|
+ enum stm32mp1_parent_sel s = _UNKNOWN_SEL;
|
|
+ enum stm32mp1_pll_id pll_id;
|
|
+ uint32_t p_sel;
|
|
+
|
|
+ switch (parent_id) {
|
|
+ case _ACLK:
|
|
+ case _PCLK4:
|
|
+ case _PCLK5:
|
|
+ s = _ASS_SEL;
|
|
+ break;
|
|
+ case _PLL1_P:
|
|
+ case _PLL1_Q:
|
|
+ case _PLL1_R:
|
|
+ pll_id = _PLL1;
|
|
+ break;
|
|
+ case _PLL2_P:
|
|
+ case _PLL2_Q:
|
|
+ case _PLL2_R:
|
|
+ pll_id = _PLL2;
|
|
+ break;
|
|
+ case _PLL3_P:
|
|
+ case _PLL3_Q:
|
|
+ case _PLL3_R:
|
|
+ pll_id = _PLL3;
|
|
+ break;
|
|
+ case _PLL4_P:
|
|
+ case _PLL4_Q:
|
|
+ case _PLL4_R:
|
|
+ pll_id = _PLL4;
|
|
+ break;
|
|
+ case _PCLK1:
|
|
+ case _PCLK2:
|
|
+ case _HCLK2:
|
|
+ case _HCLK6:
|
|
+ case _CK_PER:
|
|
+ case _CK_MPU:
|
|
+ case _CK_MCU:
|
|
+ case _USB_PHY_48:
|
|
+ /* We do not expected to access these */
|
|
+ panic();
|
|
+ break;
|
|
+ default:
|
|
+ /* Other parents have no parent */
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (s != _UNKNOWN_SEL) {
|
|
+ const struct stm32mp1_clk_sel *sel = clk_sel_ref(s);
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ p_sel = (mmio_read_32(rcc_base + sel->offset) >> sel->src) &
|
|
+ sel->msk;
|
|
+
|
|
+ if (p_sel < sel->nb_parent) {
|
|
+ return (int)sel->parent[p_sel];
|
|
+ }
|
|
+ } else {
|
|
+ const struct stm32mp1_clk_pll *pll = pll_ref(pll_id);
|
|
+
|
|
+ p_sel = mmio_read_32(stm32_rcc_base() + pll->rckxselr) &
|
|
+ RCC_SELR_REFCLK_SRC_MASK;
|
|
+
|
|
+ if (pll->refclk[p_sel] != _UNKNOWN_OSC_ID) {
|
|
+ return (int)pll->refclk[p_sel];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ FMSG("No parent selected for %s", stm32mp1_clk_parent_name[parent_id]);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void secure_parent_clocks(unsigned long parent_id)
|
|
+{
|
|
+ int grandparent_id;
|
|
+
|
|
+ switch (parent_id) {
|
|
+ /* Secure only the parents for these clocks */
|
|
+ case _ACLK:
|
|
+ case _HCLK2:
|
|
+ case _HCLK6:
|
|
+ case _PCLK4:
|
|
+ case _PCLK5:
|
|
+ break;
|
|
+ /* PLLs */
|
|
+ case _PLL1_P:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL1_P);
|
|
+ break;
|
|
+ case _PLL1_Q:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL1_Q);
|
|
+ break;
|
|
+ case _PLL1_R:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL1_R);
|
|
+ break;
|
|
+
|
|
+ case _PLL2_P:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL2_P);
|
|
+ break;
|
|
+ case _PLL2_Q:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL2_Q);
|
|
+ break;
|
|
+ case _PLL2_R:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL2_R);
|
|
+ break;
|
|
+
|
|
+ case _PLL3_P:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3_P);
|
|
+ break;
|
|
+ case _PLL3_Q:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3_Q);
|
|
+ break;
|
|
+ case _PLL3_R:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3_R);
|
|
+ break;
|
|
+
|
|
+ /* Source clocks */
|
|
+ case _HSI:
|
|
+ case _HSI_KER:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_HSI);
|
|
+ break;
|
|
+ case _LSI:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_LSI);
|
|
+ break;
|
|
+ case _CSI:
|
|
+ case _CSI_KER:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_CSI);
|
|
+ break;
|
|
+ case _HSE:
|
|
+ case _HSE_KER:
|
|
+ case _HSE_KER_DIV2:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_HSE);
|
|
+ break;
|
|
+ case _LSE:
|
|
+ stm32mp_register_secure_periph(STM32MP1_SHRES_LSE);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ grandparent_id = get_parent_id_parent(parent_id);
|
|
+ if (grandparent_id >= 0) {
|
|
+ secure_parent_clocks(grandparent_id);
|
|
+ }
|
|
+}
|
|
+
|
|
+void stm32mp_register_clock_parents_secure(unsigned long clock_id)
|
|
+{
|
|
+ int parent_id;
|
|
+
|
|
+ switch (clock_id) {
|
|
+ case PLL1:
|
|
+ parent_id = get_parent_id_parent(_PLL1_P);
|
|
+ break;
|
|
+ case PLL2:
|
|
+ parent_id = get_parent_id_parent(_PLL2_P);
|
|
+ break;
|
|
+ case PLL3:
|
|
+ parent_id = get_parent_id_parent(_PLL3_P);
|
|
+ break;
|
|
+ case PLL4:
|
|
+ EMSG("PLL4 cannot be secure");
|
|
+ panic();
|
|
+ default:
|
|
+ /* Others are expected gateable clock */
|
|
+ parent_id = stm32mp1_clk_get_parent(clock_id);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (parent_id < 0) {
|
|
+ DMSG("No parent for clock %lu", clock_id);
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ secure_parent_clocks(parent_id);
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+/*
|
|
+ * Check that the device tree does not provide clock tree configuration
|
|
+ * information. Such configuration would not be applied since the early boot
|
|
+ * loader is in charge of configuring the clock tree and enabling the PLLs.
|
|
+ */
|
|
+static void init_clock_tree_from_dt(void)
|
|
+{
|
|
+ void *fdt;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+ int node = -1;
|
|
+ unsigned int i;
|
|
+ int len;
|
|
+ int ignored = 0;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt != NULL) {
|
|
+ node = fdt_get_rcc_node(fdt);
|
|
+ }
|
|
+
|
|
+ if ((fdt == NULL) || (node < 0)) {
|
|
+ panic("RCC DT");
|
|
+ }
|
|
+
|
|
+ if ((_fdt_get_status(fdt, node) & DT_STATUS_OK_SEC) == 0) {
|
|
+ panic("RCC disabled");
|
|
+ }
|
|
+
|
|
+ assert(virt_to_phys((void *)stm32_rcc_base()) ==
|
|
+ fdt_rcc_read_addr(fdt));
|
|
+
|
|
+ /* Expect booting from a secure setup */
|
|
+ if ((mmio_read_32(rcc_base + RCC_TZCR) & RCC_TZCR_TZEN) == 0) {
|
|
+ panic("RCC TZC[TZEN]");
|
|
+ }
|
|
+
|
|
+ /* Get oscillator frequency to handle freq get/set operations */
|
|
+ stm32mp1_osc_init();
|
|
+
|
|
+ node = fdt_get_rcc_node(fdt);
|
|
+ assert(node >= 0);
|
|
+
|
|
+ /*
|
|
+ * OP-TEE core is not in charge of the clock tree configuration.
|
|
+ * This is expected from an earlier boot stage. Modifying the clock
|
|
+ * tree here may jeopardize the already configured clock tree.
|
|
+ * The sequence below ignores such DT directives with a friendly
|
|
+ * debug trace.
|
|
+ */
|
|
+ if (fdt_getprop(fdt, node, "st,clksrc", &len)) {
|
|
+ DMSG("Ignore source clocks configuration from DT");
|
|
+ ignored++;
|
|
+ }
|
|
+ if (fdt_getprop(fdt, node, "st,clkdiv", &len)) {
|
|
+ DMSG("Ignore clock divisors configuration from DT");
|
|
+ ignored++;
|
|
+ }
|
|
+ if (fdt_getprop(fdt, node, "st,pkcs", &len)) {
|
|
+ DMSG("Ignore peripheral clocks tree configuration from DT");
|
|
+ ignored++;
|
|
+ }
|
|
+ for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) {
|
|
+ char name[12];
|
|
+
|
|
+ snprintf(name, sizeof(name), "st,pll@%d", i);
|
|
+ node = fdt_rcc_subnode_offset(fdt, name);
|
|
+
|
|
+ if (node <= 0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, node, "cfg", &len) ||
|
|
+ fdt_getprop(fdt, node, "frac", &len)) {
|
|
+ DMSG("Ignore PLL%u configurations from DT", i);
|
|
+ ignored++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ignored != 0) {
|
|
+ IMSG("DT clock tree configurations were ignored");
|
|
+ }
|
|
+}
|
|
+#else
|
|
+static void init_clock_tree_from_dt(void)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ /* Expect booting from a secure setup */
|
|
+ if ((mmio_read_32(rcc_base + RCC_TZCR) & RCC_TZCR_TZEN) == 0) {
|
|
+ panic("RCC TZC[TZEN]");
|
|
+ }
|
|
+}
|
|
+#endif /*CFG_DT*/
|
|
+
|
|
+/* Sync secure clock refcount after all drivers probe/inits, */
|
|
+void stm32mp_update_earlyboot_clocks_state(void)
|
|
+{
|
|
+ unsigned int idx;
|
|
+
|
|
+ for (idx = 0; idx < NB_GATES; idx++) {
|
|
+ unsigned long clock_id = gate_ref(idx)->index;
|
|
+
|
|
+ /*
|
|
+ * Drop non-secure refcount set on shareable clocks that are
|
|
+ * not shared. Secure clock should not hold a non-secure
|
|
+ * refcount. Non-secure clock cannot hold any refcount.
|
|
+ */
|
|
+ if (__clk_is_enabled(gate_ref(idx)) &&
|
|
+ stm32mp_clock_is_shareable(clock_id) &&
|
|
+ !stm32mp_clock_is_shared(clock_id)) {
|
|
+ stm32mp1_clk_disable_non_secure(clock_id);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Disable secure clocks enabled from early boot but not explicitly
|
|
+ * enabled from the secure world.
|
|
+ */
|
|
+ if (__clk_is_enabled(gate_ref(idx)) &&
|
|
+ !stm32mp_clock_is_non_secure(clock_id) &&
|
|
+ !gate_refcounts[idx]) {
|
|
+ __clk_disable(gate_ref(idx));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Dump clocks state */
|
|
+ for (idx = 0; idx < NB_GATES; idx++) {
|
|
+ unsigned long __maybe_unused clock_id = gate_ref(idx)->index;
|
|
+ int __maybe_unused p = stm32mp1_clk_get_parent(clock_id);
|
|
+
|
|
+ FMSG("stm32mp clock %3lu is %sabled (refcnt %d) (parent %d %s)",
|
|
+ clock_id,
|
|
+ __clk_is_enabled(gate_ref(idx)) ? "en" : "dis",
|
|
+ gate_refcounts[idx],
|
|
+ p, p < 0 ? "n.a" : stm32mp1_clk_parent_name[p]);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Set a non-secure refcount on shareable clock that were enabled from boot */
|
|
+static void sync_earlyboot_clocks_state(void)
|
|
+{
|
|
+ unsigned int idx;
|
|
+
|
|
+ for (idx = 0; idx < NB_GATES; idx++) {
|
|
+ assert(!gate_refcounts[idx]);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Set a non-secure refcount for shareable clocks enabled from boot.
|
|
+ * It will be dropped after core inits for secure-only clocks.
|
|
+ */
|
|
+ for (idx = 0; idx < NB_GATES; idx++) {
|
|
+ struct stm32mp1_clk_gate const *gate = gate_ref(idx);
|
|
+
|
|
+ if (__clk_is_enabled(gate) &&
|
|
+ stm32mp_clock_is_shareable(gate->index)) {
|
|
+ gate_refcounts[idx] = SHREFCNT_NONSECURE_FLAG;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Register secure clock parents and init a refcount for
|
|
+ * secure only resources that are not registered from a driver probe.
|
|
+ * - DDR controller and phy clocks.
|
|
+ * - TZC400, ETZPC and STGEN clocks.
|
|
+ * - RTCAPB clocks on multi-core
|
|
+ */
|
|
+ stm32mp_register_clock_parents_secure(DDRC1);
|
|
+ stm32mp1_clk_enable_secure(DDRC1);
|
|
+ stm32mp_register_clock_parents_secure(DDRC1LP);
|
|
+ stm32mp1_clk_enable_secure(DDRC1LP);
|
|
+ stm32mp_register_clock_parents_secure(DDRC2);
|
|
+ stm32mp1_clk_enable_secure(DDRC2);
|
|
+ stm32mp_register_clock_parents_secure(DDRC2LP);
|
|
+ stm32mp1_clk_enable_secure(DDRC2LP);
|
|
+ stm32mp_register_clock_parents_secure(DDRPHYC);
|
|
+ stm32mp1_clk_enable_secure(DDRPHYC);
|
|
+ stm32mp_register_clock_parents_secure(DDRPHYCLP);
|
|
+ stm32mp1_clk_enable_secure(DDRPHYCLP);
|
|
+ stm32mp_register_clock_parents_secure(DDRCAPB);
|
|
+ stm32mp1_clk_enable_secure(DDRCAPB);
|
|
+ stm32mp_register_clock_parents_secure(AXIDCG);
|
|
+ stm32mp1_clk_enable_secure(AXIDCG);
|
|
+ stm32mp_register_clock_parents_secure(DDRPHYCAPB);
|
|
+ stm32mp1_clk_enable_secure(DDRPHYCAPB);
|
|
+ stm32mp_register_clock_parents_secure(DDRPHYCAPBLP);
|
|
+ stm32mp1_clk_enable_secure(DDRPHYCAPBLP);
|
|
+
|
|
+ stm32mp_register_clock_parents_secure(TZPC);
|
|
+ stm32mp1_clk_enable_secure(TZPC);
|
|
+ stm32mp_register_clock_parents_secure(TZC1);
|
|
+ stm32mp1_clk_enable_secure(TZC1);
|
|
+ stm32mp_register_clock_parents_secure(TZC2);
|
|
+ stm32mp1_clk_enable_secure(TZC2);
|
|
+ stm32mp_register_clock_parents_secure(STGEN_K);
|
|
+ stm32mp1_clk_enable_secure(STGEN_K);
|
|
+
|
|
+ stm32mp_register_clock_parents_secure(BSEC);
|
|
+ stm32mp1_clk_enable_secure(BSEC);
|
|
+
|
|
+ stm32mp_register_clock_parents_secure(BKPSRAM);
|
|
+
|
|
+ stm32mp_register_clock_parents_secure(RTCAPB);
|
|
+
|
|
+#if CFG_TEE_CORE_NB_CORE > 1
|
|
+ stm32mp1_clk_enable_secure(RTCAPB);
|
|
+#endif
|
|
+
|
|
+ /* The low power sequences mandates RNG1 and CRYP1 support */
|
|
+ stm32mp_register_clock_parents_secure(RNG1_K);
|
|
+ stm32mp_register_clock_parents_secure(CRYP1);
|
|
+}
|
|
+
|
|
+static void _clock_resume(void)
|
|
+{
|
|
+ unsigned int idx;
|
|
+
|
|
+ /* Sync secure and shared clocks physical state on functional state */
|
|
+ for (idx = 0; idx < NB_GATES; idx++) {
|
|
+ struct stm32mp1_clk_gate const *gate = gate_ref(idx);
|
|
+
|
|
+ if (stm32mp_clock_is_non_secure(gate->index)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (gate_refcounts[idx]) {
|
|
+ DMSG("Force clock %d enable", gate->index);
|
|
+ __clk_enable(gate);
|
|
+ } else {
|
|
+ DMSG("Force clock %d disable", gate->index);
|
|
+ __clk_disable(gate);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void stm32mp_clock_suspend_resume(enum pm_op op)
|
|
+{
|
|
+ switch (op) {
|
|
+ case PM_OP_SUSPEND:
|
|
+ /* Nothing to do */
|
|
+ break;
|
|
+ case PM_OP_RESUME:
|
|
+ _clock_resume();
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+static TEE_Result stm32mp1_clk_probe(void)
|
|
+{
|
|
+ init_clock_tree_from_dt();
|
|
+
|
|
+ sync_earlyboot_clocks_state();
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+/* Setup clock support before driver initialization */
|
|
+service_init(stm32mp1_clk_probe);
|
|
+
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.h b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.h
|
|
new file mode 100644
|
|
index 0000000..783d81d
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clk.h
|
|
@@ -0,0 +1,62 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_CLK_H__
|
|
+#define __STM32MP1_CLK_H__
|
|
+
|
|
+#include <stdbool.h>
|
|
+#include <stddef.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+enum stm32mp_osc_id {
|
|
+ _HSI,
|
|
+ _HSE,
|
|
+ _CSI,
|
|
+ _LSI,
|
|
+ _LSE,
|
|
+ _I2S_CKIN,
|
|
+ _USB_PHY_48,
|
|
+ NB_OSC,
|
|
+ _UNKNOWN_OSC_ID = 0xFF
|
|
+};
|
|
+
|
|
+void __stm32mp1_clk_enable(unsigned long id, bool caller_is_secure);
|
|
+void __stm32mp1_clk_disable(unsigned long id, bool caller_is_secure);
|
|
+bool stm32mp1_clk_is_enabled(unsigned long id);
|
|
+
|
|
+static inline void stm32mp1_clk_enable_non_secure(unsigned long id)
|
|
+{
|
|
+ __stm32mp1_clk_enable(id, false);
|
|
+}
|
|
+
|
|
+static inline void stm32mp1_clk_enable_secure(unsigned long id)
|
|
+{
|
|
+ __stm32mp1_clk_enable(id, true);
|
|
+}
|
|
+
|
|
+static inline void stm32mp1_clk_disable_non_secure(unsigned long id)
|
|
+{
|
|
+ __stm32mp1_clk_disable(id, false);
|
|
+}
|
|
+
|
|
+static inline void stm32mp1_clk_disable_secure(unsigned long id)
|
|
+{
|
|
+ __stm32mp1_clk_disable(id, true);
|
|
+}
|
|
+
|
|
+unsigned int stm32mp1_clk_get_refcount(unsigned long id);
|
|
+
|
|
+unsigned long stm32mp1_clk_get_rate(unsigned long id);
|
|
+
|
|
+unsigned long stm32mp1_clk_rcc2id(size_t offset, size_t bit);
|
|
+
|
|
+void stm32mp_register_clock_parents_secure(unsigned long id);
|
|
+
|
|
+void stm32mp_update_earlyboot_clocks_state(void);
|
|
+
|
|
+void stm32mp1_clock_suspend(void);
|
|
+void stm32mp1_clock_resume(void);
|
|
+
|
|
+#endif /* __STM32MP1_CLK_H__ */
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.c
|
|
new file mode 100644
|
|
index 0000000..c83d561
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.c
|
|
@@ -0,0 +1,340 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <dt-bindings/clock/stm32mp1-clksrc.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <libfdt.h>
|
|
+#include <stm32_util.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_clkfunc.h>
|
|
+#include <stm32mp_dt.h>
|
|
+
|
|
+#define DT_RCC_NODE_NAME "rcc@50000000"
|
|
+#define DT_RCC_CLK_COMPAT "st,stm32mp1-rcc"
|
|
+#define DT_RCC_COMPAT "syscon"
|
|
+#define DT_STGEN_COMPAT "st,stm32-stgen"
|
|
+#define DT_UART_COMPAT "st,stm32h7-uart"
|
|
+
|
|
+const char *stm32mp_osc_node_label[NB_OSC] = {
|
|
+ [_LSI] = "clk-lsi",
|
|
+ [_LSE] = "clk-lse",
|
|
+ [_HSI] = "clk-hsi",
|
|
+ [_HSE] = "clk-hse",
|
|
+ [_CSI] = "clk-csi",
|
|
+ [_I2S_CKIN] = "i2s_ckin",
|
|
+ [_USB_PHY_48] = "ck_usbo_48m"
|
|
+};
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads the frequency of an oscillator from its name.
|
|
+ * It reads the value indicated inside the device tree.
|
|
+ * Returns 0 on success, and a negative FDT/ERRNO error code on failure.
|
|
+ * On success, value is stored in the second parameter.
|
|
+ ******************************************************************************/
|
|
+int fdt_osc_read_freq(void *fdt, const char *name, uint32_t *freq)
|
|
+{
|
|
+ int node, subnode;
|
|
+
|
|
+ node = fdt_path_offset(fdt, "/clocks");
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ fdt_for_each_subnode(subnode, fdt, node) {
|
|
+ const char *cchar;
|
|
+ int ret;
|
|
+
|
|
+ cchar = fdt_get_name(fdt, subnode, &ret);
|
|
+ if (cchar == NULL) {
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (strncmp(cchar, name, (size_t)ret) == 0) {
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, subnode, "clock-frequency",
|
|
+ &ret);
|
|
+ if (cuint == NULL) {
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ *freq = fdt32_to_cpu(*cuint);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Oscillator not found, freq=0 */
|
|
+ *freq = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function checks the presence of an oscillator property from its id.
|
|
+ * The search is done inside the device tree.
|
|
+ * Returns true/false regarding search result.
|
|
+ ******************************************************************************/
|
|
+bool fdt_osc_read_bool(void *fdt, enum stm32mp_osc_id osc_id,
|
|
+ const char *prop_name)
|
|
+{
|
|
+ int node, subnode;
|
|
+
|
|
+ if (osc_id >= NB_OSC) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ node = fdt_path_offset(fdt, "/clocks");
|
|
+ if (node < 0) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ fdt_for_each_subnode(subnode, fdt, node) {
|
|
+ const char *cchar;
|
|
+ int ret;
|
|
+
|
|
+ cchar = fdt_get_name(fdt, subnode, &ret);
|
|
+ if (cchar == NULL) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (strncmp(cchar, stm32mp_osc_node_label[osc_id],
|
|
+ (size_t)ret) != 0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads a value of a oscillator property from its id.
|
|
+ * Returns value on success, and a default value if property not found.
|
|
+ * Default value is passed as parameter.
|
|
+ ******************************************************************************/
|
|
+uint32_t fdt_osc_read_uint32_default(void *fdt, enum stm32mp_osc_id osc_id,
|
|
+ const char *prop_name, uint32_t dflt_value)
|
|
+{
|
|
+ int node, subnode;
|
|
+
|
|
+ if (osc_id >= NB_OSC) {
|
|
+ return dflt_value;
|
|
+ }
|
|
+
|
|
+ node = fdt_path_offset(fdt, "/clocks");
|
|
+ if (node < 0) {
|
|
+ return dflt_value;
|
|
+ }
|
|
+
|
|
+ fdt_for_each_subnode(subnode, fdt, node) {
|
|
+ const char *cchar;
|
|
+ int ret;
|
|
+
|
|
+ cchar = fdt_get_name(fdt, subnode, &ret);
|
|
+ if (cchar == NULL) {
|
|
+ return dflt_value;
|
|
+ }
|
|
+
|
|
+ if (strncmp(cchar, stm32mp_osc_node_label[osc_id],
|
|
+ (size_t)ret) != 0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ return fdt_read_uint32_default(fdt, subnode, prop_name,
|
|
+ dflt_value);
|
|
+ }
|
|
+
|
|
+ return dflt_value;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads the rcc base address.
|
|
+ * It reads the value indicated inside the device tree.
|
|
+ * Returns address on success, and 0 on failure.
|
|
+ ******************************************************************************/
|
|
+uint32_t fdt_rcc_read_addr(void *fdt)
|
|
+{
|
|
+ int node, subnode;
|
|
+
|
|
+ node = fdt_path_offset(fdt, "/soc");
|
|
+ if (node < 0) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ fdt_for_each_subnode(subnode, fdt, node) {
|
|
+ const char *cchar;
|
|
+ int ret;
|
|
+
|
|
+ cchar = fdt_get_name(fdt, subnode, &ret);
|
|
+ if (cchar == NULL) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (strncmp(cchar, DT_RCC_NODE_NAME, (size_t)ret) == 0) {
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, subnode, "reg", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return fdt32_to_cpu(*cuint);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function returns the RCC node in the device tree.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_rcc_node(void *fdt)
|
|
+{
|
|
+ return fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads a series of parameters in rcc-clk section.
|
|
+ * It reads the values indicated inside the device tree, from property name.
|
|
+ * The number of parameters is also indicated as entry parameter.
|
|
+ * Returns 0 on success, and a negative FDT/ERRNO error code on failure.
|
|
+ * On success, values are stored at the second parameter address.
|
|
+ ******************************************************************************/
|
|
+int fdt_rcc_read_uint32_array(void *fdt, const char *prop_name,
|
|
+ uint32_t *array, uint32_t count)
|
|
+{
|
|
+ int node = fdt_get_rcc_node(fdt);
|
|
+
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return fdt_read_uint32_array(fdt, node, prop_name, array, count);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the subnode offset in rcc-clk section from its name.
|
|
+ * It reads the values indicated inside the device tree.
|
|
+ * Returns offset on success, and a negative FDT/ERRNO error code on failure.
|
|
+ ******************************************************************************/
|
|
+int fdt_rcc_subnode_offset(void *fdt, const char *name)
|
|
+{
|
|
+ int node, subnode;
|
|
+
|
|
+ node = fdt_get_rcc_node(fdt);
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ subnode = fdt_subnode_offset(fdt, node, name);
|
|
+ if (subnode <= 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return subnode;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the pointer to a rcc-clk property from its name.
|
|
+ * It reads the values indicated inside the device tree.
|
|
+ * Length of the property is stored in the second parameter.
|
|
+ * Returns pointer on success, and NULL value on failure.
|
|
+ ******************************************************************************/
|
|
+const fdt32_t *fdt_rcc_read_prop(void *fdt, const char *prop_name, int *lenp)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+ int node, len;
|
|
+
|
|
+ node = fdt_get_rcc_node(fdt);
|
|
+ if (node < 0) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, prop_name, &len);
|
|
+ if (cuint == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ *lenp = len;
|
|
+ return cuint;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads the stgen base address.
|
|
+ * It reads the value indicated inside the device tree.
|
|
+ * Returns address on success, and NULL value on failure.
|
|
+ ******************************************************************************/
|
|
+uintptr_t get_stgen_base(void)
|
|
+{
|
|
+ int node;
|
|
+ const fdt32_t *cuint;
|
|
+ void *fdt;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ node = fdt_node_offset_by_compatible(fdt, -1, DT_STGEN_COMPAT);
|
|
+ if (node < 0) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "reg", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return fdt32_to_cpu(*cuint);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the frequency of the specified uart instance.
|
|
+ * From this instance, all the uarts nodes in DT are parsed, and the register
|
|
+ * base is compared to the instance. If match between these two values, then
|
|
+ * the clock source is read from the DT and we deduce the frequency.
|
|
+ * Returns clock frequency on success, 0 value on failure.
|
|
+ ******************************************************************************/
|
|
+unsigned long get_uart_clock_freq(uint32_t instance)
|
|
+{
|
|
+ int node;
|
|
+ void *fdt;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Check for UART nodes */
|
|
+ node = fdt_node_offset_by_compatible(fdt, -1, DT_UART_COMPAT);
|
|
+ while (node != -FDT_ERR_NOTFOUND) {
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "reg", NULL);
|
|
+ if (cuint == NULL)
|
|
+ goto next;
|
|
+
|
|
+ if ((uint32_t)fdt32_to_cpu(*cuint) == instance) {
|
|
+ unsigned long clk_id;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "clocks", NULL);
|
|
+ if (cuint == NULL)
|
|
+ goto next;
|
|
+
|
|
+ cuint++;
|
|
+ clk_id = (unsigned long)(fdt32_to_cpu(*cuint));
|
|
+
|
|
+ return stm32mp1_clk_get_rate(clk_id);
|
|
+ }
|
|
+next:
|
|
+ node = fdt_node_offset_by_compatible(fdt, node, DT_UART_COMPAT);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.h b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.h
|
|
new file mode 100644
|
|
index 0000000..cbb489b
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_clkfunc.h
|
|
@@ -0,0 +1,33 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_CLKFUNC_H__
|
|
+#define __STM32MP1_CLKFUNC_H__
|
|
+
|
|
+#include <libfdt.h>
|
|
+#include <stdbool.h>
|
|
+
|
|
+extern const char *stm32mp_osc_node_label[NB_OSC];
|
|
+
|
|
+int fdt_osc_read_freq(void *fdt, const char *name, uint32_t *freq);
|
|
+bool fdt_osc_read_bool(void *fdt, enum stm32mp_osc_id osc_id,
|
|
+ const char *prop_name);
|
|
+uint32_t fdt_osc_read_uint32_default(void *fdt, enum stm32mp_osc_id osc_id,
|
|
+ const char *prop_name,
|
|
+ uint32_t dflt_value);
|
|
+
|
|
+int fdt_get_rcc_node(void *fdt);
|
|
+uint32_t fdt_rcc_read_addr(void *fdt);
|
|
+int fdt_rcc_read_uint32_array(void *fdt, const char *prop_name,
|
|
+ uint32_t *array, uint32_t count);
|
|
+int fdt_rcc_subnode_offset(void *fdt, const char *name);
|
|
+const fdt32_t *fdt_rcc_read_prop(void *fdt, const char *prop_name, int *lenp);
|
|
+bool fdt_get_rcc_secure_status(void *fdt);
|
|
+
|
|
+uintptr_t get_stgen_base(void);
|
|
+
|
|
+unsigned long get_uart_clock_freq(uint32_t instance);
|
|
+
|
|
+#endif /* __STM32MP1_CLKFUNC_H__ */
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.c
|
|
new file mode 100644
|
|
index 0000000..03ded28
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.c
|
|
@@ -0,0 +1,513 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <arm32.h>
|
|
+#include <boot_api.h>
|
|
+#include <drivers/stm32_rtc.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_ddrc.h>
|
|
+#include <drivers/stm32mp1_pwr.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <io.h>
|
|
+#include <mm/core_mmu.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <stm32_util.h>
|
|
+#include <string.h>
|
|
+#include <trace.h>
|
|
+
|
|
+#define TIMEOUT_500US (500 * 1000)
|
|
+
|
|
+static uintptr_t get_ddrctrl_base(void)
|
|
+{
|
|
+ static void *va;
|
|
+
|
|
+ if (!cpu_mmu_enabled()) {
|
|
+ return DDRCTRL_BASE;
|
|
+ }
|
|
+
|
|
+ if (!va) {
|
|
+ va = phys_to_virt(DDRCTRL_BASE, MEM_AREA_IO_SEC);
|
|
+ }
|
|
+
|
|
+ return (uintptr_t)va;
|
|
+}
|
|
+
|
|
+static uintptr_t get_ddrphy_base(void)
|
|
+{
|
|
+ static void *va;
|
|
+
|
|
+ if (!cpu_mmu_enabled()) {
|
|
+ return DDRPHYC_BASE;
|
|
+ }
|
|
+
|
|
+ if (!va) {
|
|
+ va = phys_to_virt(DDRPHYC_BASE, MEM_AREA_IO_SEC);
|
|
+ }
|
|
+
|
|
+ return (uintptr_t)va;
|
|
+}
|
|
+
|
|
+static void ddr_disable_clock(void)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ /* Disable all clocks */
|
|
+ mmio_clrbits_32(rcc_base + RCC_DDRITFCR,
|
|
+ RCC_DDRITFCR_DDRC1EN |
|
|
+ RCC_DDRITFCR_DDRC2EN |
|
|
+ RCC_DDRITFCR_DDRPHYCAPBEN |
|
|
+ RCC_DDRITFCR_DDRCAPBEN);
|
|
+}
|
|
+
|
|
+static void ddr_enable_clock(void)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ /* Enable all clocks */
|
|
+ mmio_setbits_32(rcc_base + RCC_DDRITFCR,
|
|
+ RCC_DDRITFCR_DDRC1EN |
|
|
+ RCC_DDRITFCR_DDRC2EN |
|
|
+ RCC_DDRITFCR_DDRPHYCEN |
|
|
+ RCC_DDRITFCR_DDRPHYCAPBEN |
|
|
+ RCC_DDRITFCR_DDRCAPBEN);
|
|
+}
|
|
+
|
|
+static void do_sw_handshake(void)
|
|
+{
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE);
|
|
+}
|
|
+
|
|
+static void do_sw_ack(void)
|
|
+{
|
|
+ uint64_t to_ref;
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE);
|
|
+
|
|
+ to_ref = utimeout_init(TIMEOUT_500US);
|
|
+ while ((mmio_read_32(ddrctrl_base + DDRCTRL_SWSTAT) &
|
|
+ DDRCTRL_SWSTAT_SW_DONE_ACK) == 0U) {
|
|
+ if (utimeout_elapsed(TIMEOUT_500US, to_ref)) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ddr_sw_self_refresh_in(void)
|
|
+{
|
|
+ uint64_t to_ref;
|
|
+ uint32_t operating_mode;
|
|
+ uint32_t selref_type;
|
|
+ uint8_t op_mode_changed = 0;
|
|
+ uintptr_t pwr_base = stm32_pwr_base();
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+ uintptr_t ddrphy_base = get_ddrphy_base();
|
|
+
|
|
+ mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN);
|
|
+
|
|
+ /* Blocks AXI ports from taking anymore transactions */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_PCTRL_0,
|
|
+ DDRCTRL_PCTRL_N_PORT_EN);
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_PCTRL_1,
|
|
+ DDRCTRL_PCTRL_N_PORT_EN);
|
|
+
|
|
+ /*
|
|
+ * Waits unit all AXI ports are idle
|
|
+ * Poll PSTAT.rd_port_busy_n = 0
|
|
+ * Poll PSTAT.wr_port_busy_n = 0
|
|
+ */
|
|
+ to_ref = utimeout_init(TIMEOUT_500US);
|
|
+ while (mmio_read_32(ddrctrl_base + DDRCTRL_PSTAT)) {
|
|
+ if (utimeout_elapsed(TIMEOUT_500US, to_ref)) {
|
|
+ goto pstat_failed;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* SW Self-Refresh entry */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_SELFREF_SW);
|
|
+
|
|
+ /*
|
|
+ * Wait operating mode change in self-refresh mode
|
|
+ * with STAT.operating_mode[1:0]==11.
|
|
+ * Ensure transition to self-refresh was due to software
|
|
+ * by checking also that STAT.selfref_type[1:0]=2.
|
|
+ */
|
|
+ to_ref = utimeout_init(TIMEOUT_500US);
|
|
+ while (!utimeout_elapsed(TIMEOUT_500US, to_ref)) {
|
|
+ uint32_t stat = mmio_read_32(ddrctrl_base + DDRCTRL_STAT);
|
|
+
|
|
+ operating_mode = stat & DDRCTRL_STAT_OPERATING_MODE_MASK;
|
|
+ selref_type = stat & DDRCTRL_STAT_SELFREF_TYPE_MASK;
|
|
+
|
|
+ if ((operating_mode == DDRCTRL_STAT_OPERATING_MODE_SR) &&
|
|
+ (selref_type == DDRCTRL_STAT_SELFREF_TYPE_SR)) {
|
|
+ op_mode_changed = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (op_mode_changed == 0U)
|
|
+ goto selfref_sw_failed;
|
|
+
|
|
+ /* IOs powering down (PUBL registers) */
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDR);
|
|
+
|
|
+ mmio_clrsetbits_32(ddrphy_base + DDRPHYC_ACIOCR,
|
|
+ DDRPHYC_ACIOCR_CKPDD_MASK,
|
|
+ DDRPHYC_ACIOCR_CKPDD_0);
|
|
+
|
|
+ mmio_clrsetbits_32(ddrphy_base + DDRPHYC_ACIOCR,
|
|
+ DDRPHYC_ACIOCR_CKPDR_MASK,
|
|
+ DDRPHYC_ACIOCR_CKPDR_0);
|
|
+
|
|
+ mmio_clrsetbits_32(ddrphy_base + DDRPHYC_ACIOCR,
|
|
+ DDRPHYC_ACIOCR_CSPDD_MASK,
|
|
+ DDRPHYC_ACIOCR_CSPDD_0);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR);
|
|
+
|
|
+ mmio_clrsetbits_32(ddrphy_base + DDRPHYC_DSGCR,
|
|
+ DDRPHYC_DSGCR_ODTPDD_MASK,
|
|
+ DDRPHYC_DSGCR_ODTPDD_0);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD);
|
|
+
|
|
+ mmio_clrsetbits_32(ddrphy_base + DDRPHYC_DSGCR,
|
|
+ DDRPHYC_DSGCR_CKEPDD_MASK,
|
|
+ DDRPHYC_DSGCR_CKEPDD_0);
|
|
+
|
|
+ /* Disable PZQ cell (PUBL register) */
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD);
|
|
+
|
|
+ /* Activate sw retention in PWRCTRL */
|
|
+ mmio_setbits_32(pwr_base + PWR_CR3_OFF, PWR_CR3_DDRRETEN);
|
|
+
|
|
+ /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */
|
|
+ mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL);
|
|
+
|
|
+ /* Disable all DLLs: GLITCH window */
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_ACDLLCR,
|
|
+ DDRPHYC_ACDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DX0DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DX1DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DX2DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_DX3DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ /* Switch controller clocks (uMCTL2/PUBL) to DLL output clock */
|
|
+ mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL);
|
|
+
|
|
+ /* Disable all clocks */
|
|
+ ddr_disable_clock();
|
|
+
|
|
+ return 0;
|
|
+
|
|
+selfref_sw_failed:
|
|
+ /* This bit should be cleared to restore DDR in its previous state */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_SELFREF_SW);
|
|
+
|
|
+pstat_failed:
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_0,
|
|
+ DDRCTRL_PCTRL_N_PORT_EN);
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1,
|
|
+ DDRCTRL_PCTRL_N_PORT_EN);
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int ddr_sw_self_refresh_exit(void)
|
|
+{
|
|
+ uint64_t to_ref;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+ uintptr_t pwr_base = stm32_pwr_base();
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+ uintptr_t ddrphy_base = get_ddrphy_base();
|
|
+
|
|
+ /* Enable all clocks */
|
|
+ ddr_enable_clock();
|
|
+
|
|
+ do_sw_handshake();
|
|
+
|
|
+ /* Mask dfi_init_complete_en */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_DFIMISC,
|
|
+ DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN);
|
|
+
|
|
+ do_sw_ack();
|
|
+
|
|
+ /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */
|
|
+ mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL);
|
|
+
|
|
+ /* Enable all DLLs: GLITCH window */
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_ACDLLCR,
|
|
+ DDRPHYC_ACDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DX0DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DX1DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DX2DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DX3DLLCR,
|
|
+ DDRPHYC_DXNDLLCR_DLLDIS);
|
|
+
|
|
+ /* Additional delay to avoid early DLL clock switch */
|
|
+ udelay(10);
|
|
+
|
|
+ /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */
|
|
+ mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL);
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_ACDLLCR, DDRPHYC_ACDLLCR_DLLSRST);
|
|
+
|
|
+ udelay(10);
|
|
+
|
|
+ mmio_setbits_32(ddrphy_base + DDRPHYC_ACDLLCR,
|
|
+ DDRPHYC_ACDLLCR_DLLSRST);
|
|
+
|
|
+ /* PHY partial init: (DLL lock and ITM reset) */
|
|
+ mmio_write_32(ddrphy_base + DDRPHYC_PIR,
|
|
+ DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK |
|
|
+ DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_INIT);
|
|
+
|
|
+ /* Need to wait at least 10 clock cycles before accessing PGSR */
|
|
+ udelay(1);
|
|
+
|
|
+ to_ref = utimeout_init(TIMEOUT_500US);
|
|
+ while ((mmio_read_32(ddrphy_base + DDRPHYC_PGSR) &
|
|
+ DDRPHYC_PGSR_IDONE) == 0U) {
|
|
+ if (utimeout_elapsed(TIMEOUT_500US, to_ref)) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ do_sw_handshake();
|
|
+
|
|
+ /* Unmask dfi_init_complete_en to uMCTL2 */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_DFIMISC,
|
|
+ DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN);
|
|
+
|
|
+ do_sw_ack();
|
|
+
|
|
+ /* Deactivate sw retention in PWR */
|
|
+ mmio_clrbits_32(pwr_base + PWR_CR3_OFF, PWR_CR3_DDRRETEN);
|
|
+
|
|
+ /* Enable PZQ cell (PUBL register) */
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD);
|
|
+
|
|
+ /* Enable pad drivers */
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_ACIOCR,
|
|
+ DDRPHYC_ACIOCR_CKPDD_MASK);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_ACIOCR,
|
|
+ DDRPHYC_ACIOCR_CSPDD_MASK);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_ODTPDD_MASK);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD);
|
|
+
|
|
+ mmio_clrbits_32(ddrphy_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKEPDD_MASK);
|
|
+
|
|
+ /* Remove selfrefresh */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_SELFREF_SW);
|
|
+
|
|
+ /* Wait operating_mode == normal */
|
|
+ to_ref = utimeout_init(TIMEOUT_500US);
|
|
+
|
|
+ while ((mmio_read_32(ddrctrl_base + DDRCTRL_STAT) &
|
|
+ DDRCTRL_STAT_OPERATING_MODE_MASK) !=
|
|
+ DDRCTRL_STAT_OPERATING_MODE_NORMAL) {
|
|
+ if (utimeout_elapsed(TIMEOUT_500US, to_ref)) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* AXI ports are no longer blocked from taking transactions */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_0,
|
|
+ DDRCTRL_PCTRL_N_PORT_EN);
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1,
|
|
+ DDRCTRL_PCTRL_N_PORT_EN);
|
|
+
|
|
+ mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+uint32_t get_ddrphy_calibration(void)
|
|
+{
|
|
+ uintptr_t ddrphy_base = get_ddrphy_base();
|
|
+ uint32_t zcal = mmio_read_32(ddrphy_base + DDRPHYC_ZQ0CR0);
|
|
+
|
|
+ return (zcal & DDRPHYC_ZQ0CRN_ZDATA_MASK) >> DDRPHYC_ZQ0CRN_ZDATA_SHIFT;
|
|
+}
|
|
+
|
|
+int ddr_standby_sr_entry(uint32_t *zq0cr0_zdata)
|
|
+{
|
|
+ uintptr_t pwr_base = stm32_pwr_base();
|
|
+ uintptr_t ddrphy_base = get_ddrphy_base();
|
|
+
|
|
+ /* Save IOs calibration values */
|
|
+ if (zq0cr0_zdata != NULL) {
|
|
+ *zq0cr0_zdata = mmio_read_32(ddrphy_base + DDRPHYC_ZQ0CR0) &
|
|
+ DDRPHYC_ZQ0CRN_ZDATA_MASK;
|
|
+ }
|
|
+
|
|
+ /* Put DDR in Self-Refresh */
|
|
+ if (ddr_sw_self_refresh_in() != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Enable I/O retention mode in standby */
|
|
+ mmio_setbits_32(pwr_base + PWR_CR3_OFF, PWR_CR3_DDRSREN);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ddr_sr_mode_ssr(void)
|
|
+{
|
|
+ uintptr_t rcc_ddritfcr = stm32_rcc_base() + RCC_DDRITFCR;
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1EN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2EN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCAPBLPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCAPBLPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCAPBEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCAPBEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCEN);
|
|
+
|
|
+ mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN);
|
|
+
|
|
+ mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK);
|
|
+
|
|
+ /* Disable HW LP interface of uMCTL2 */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_HWLPCTL,
|
|
+ DDRCTRL_HWLPCTL_HW_LP_EN);
|
|
+
|
|
+ /* Configure Automatic LP modes of uMCTL2 */
|
|
+ mmio_clrsetbits_32(ddrctrl_base + DDRCTRL_PWRTMG,
|
|
+ DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK,
|
|
+ DDRCTRL_PWRTMG_SELFREF_TO_X32_0);
|
|
+
|
|
+ /*
|
|
+ * Disable Clock disable with LP modes
|
|
+ * (used in RUN mode for LPDDR2 with specific timing).
|
|
+ */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE);
|
|
+
|
|
+ /* Disable automatic Self-Refresh mode */
|
|
+ mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_SELFREF_EN);
|
|
+}
|
|
+
|
|
+void ddr_sr_mode_asr(void)
|
|
+{
|
|
+ uintptr_t rcc_ddritfcr = stm32_rcc_base() + RCC_DDRITFCR;
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN);
|
|
+
|
|
+ mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK,
|
|
+ RCC_DDRITFCR_DDRCKMOD_ASR1);
|
|
+
|
|
+ /* Enable HW LP interface of uMCTL2 */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_HWLPCTL,
|
|
+ DDRCTRL_HWLPCTL_HW_LP_EN);
|
|
+
|
|
+ /* Configure Automatic LP modes of uMCTL2 */
|
|
+ mmio_clrsetbits_32(ddrctrl_base + DDRCTRL_PWRTMG,
|
|
+ DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK,
|
|
+ DDRCTRL_PWRTMG_SELFREF_TO_X32_0);
|
|
+
|
|
+ /*
|
|
+ * Enable Clock disable with LP modes
|
|
+ * (used in RUN mode for LPDDR2 with specific timing).
|
|
+ */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE);
|
|
+
|
|
+ /* Enable automatic Self-Refresh for ASR mode */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_SELFREF_EN);
|
|
+}
|
|
+
|
|
+void ddr_sr_mode_hsr(void)
|
|
+{
|
|
+ uintptr_t rcc_ddritfcr = stm32_rcc_base() + RCC_DDRITFCR;
|
|
+ uintptr_t ddrctrl_base = get_ddrctrl_base();
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN);
|
|
+
|
|
+ mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN);
|
|
+
|
|
+ mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN);
|
|
+
|
|
+ mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN);
|
|
+
|
|
+ mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK,
|
|
+ RCC_DDRITFCR_DDRCKMOD_HSR1);
|
|
+
|
|
+ /* Enable HW LP interface of uMCTL2 */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_HWLPCTL,
|
|
+ DDRCTRL_HWLPCTL_HW_LP_EN);
|
|
+
|
|
+ /* Configure Automatic LP modes of uMCTL2 */
|
|
+ mmio_clrsetbits_32(ddrctrl_base + DDRCTRL_PWRTMG,
|
|
+ DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK,
|
|
+ DDRCTRL_PWRTMG_SELFREF_TO_X32_0);
|
|
+
|
|
+ /*
|
|
+ * Enable Clock disable with LP modes
|
|
+ * (used in RUN mode for LPDDR2 with specific timing).
|
|
+ */
|
|
+ mmio_setbits_32(ddrctrl_base + DDRCTRL_PWRCTL,
|
|
+ DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE);
|
|
+}
|
|
+
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.h b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.h
|
|
new file mode 100644
|
|
index 0000000..59014b4
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_ddrc.h
|
|
@@ -0,0 +1,205 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_DDRC_H__
|
|
+#define __STM32MP1_DDRC_H__
|
|
+
|
|
+#include <util.h>
|
|
+
|
|
+/* DDR Controller */
|
|
+/* DDR Controller registers offsets */
|
|
+#define DDRCTRL_MSTR 0x000
|
|
+#define DDRCTRL_STAT 0x004
|
|
+#define DDRCTRL_MRCTRL0 0x010
|
|
+#define DDRCTRL_MRSTAT 0x018
|
|
+#define DDRCTRL_PWRCTL 0x030
|
|
+#define DDRCTRL_PWRTMG 0x034
|
|
+#define DDRCTRL_HWLPCTL 0x038
|
|
+#define DDRCTRL_RFSHCTL3 0x060
|
|
+#define DDRCTRL_RFSHTMG 0x064
|
|
+#define DDRCTRL_INIT0 0x0D0
|
|
+#define DDRCTRL_DFIMISC 0x1B0
|
|
+#define DDRCTRL_DBG1 0x304
|
|
+#define DDRCTRL_DBGCAM 0x308
|
|
+#define DDRCTRL_DBGCMD 0x30C
|
|
+#define DDRCTRL_DBGSTAT 0x310
|
|
+#define DDRCTRL_SWCTL 0x320
|
|
+#define DDRCTRL_SWSTAT 0x324
|
|
+#define DDRCTRL_PSTAT 0x3FC
|
|
+#define DDRCTRL_PCTRL_0 0x490
|
|
+#define DDRCTRL_PCTRL_1 0x540
|
|
+
|
|
+/* DDR Controller Register fields */
|
|
+#define DDRCTRL_MSTR_DDR3 BIT(0)
|
|
+#define DDRCTRL_MSTR_LPDDR2 BIT(2)
|
|
+#define DDRCTRL_MSTR_LPDDR3 BIT(3)
|
|
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK_32(13, 12)
|
|
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_FULL 0
|
|
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF BIT(12)
|
|
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER BIT(13)
|
|
+#define DDRCTRL_MSTR_DLL_OFF_MODE BIT(15)
|
|
+
|
|
+#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK_32(2, 0)
|
|
+#define DDRCTRL_STAT_OPERATING_MODE_NORMAL BIT(0)
|
|
+#define DDRCTRL_STAT_OPERATING_MODE_SR (BIT(0) | BIT(1))
|
|
+#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK_32(5, 4)
|
|
+#define DDRCTRL_STAT_SELFREF_TYPE_ASR (BIT(4) | BIT(5))
|
|
+#define DDRCTRL_STAT_SELFREF_TYPE_SR BIT(5)
|
|
+
|
|
+#define DDRCTRL_MRCTRL0_MR_TYPE_WRITE 0
|
|
+/* only one rank supported */
|
|
+#define DDRCTRL_MRCTRL0_MR_RANK_SHIFT 4
|
|
+#define DDRCTRL_MRCTRL0_MR_RANK_ALL \
|
|
+ BIT(DDRCTRL_MRCTRL0_MR_RANK_SHIFT)
|
|
+#define DDRCTRL_MRCTRL0_MR_ADDR_SHIFT 12
|
|
+#define DDRCTRL_MRCTRL0_MR_ADDR_MASK GENMASK_32(15, 12)
|
|
+#define DDRCTRL_MRCTRL0_MR_WR BIT(31)
|
|
+
|
|
+#define DDRCTRL_MRSTAT_MR_WR_BUSY BIT(0)
|
|
+
|
|
+#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0)
|
|
+#define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1)
|
|
+#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3)
|
|
+#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5)
|
|
+
|
|
+#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK_32(19, 12)
|
|
+#define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16)
|
|
+
|
|
+#define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0)
|
|
+
|
|
+#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0)
|
|
+
|
|
+#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK_32(27, 16)
|
|
+#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT 16
|
|
+
|
|
+#define DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK GENMASK_32(31, 30)
|
|
+#define DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL BIT(30)
|
|
+
|
|
+#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0)
|
|
+
|
|
+#define DDRCTRL_DBG1_DIS_HIF BIT(1)
|
|
+
|
|
+#define DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY BIT(29)
|
|
+#define DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY BIT(28)
|
|
+#define DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY BIT(26)
|
|
+#define DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH GENMASK_32(12, 8)
|
|
+#define DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH GENMASK_32(4, 0)
|
|
+
|
|
+#define DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY \
|
|
+ (DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY | \
|
|
+ DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY)
|
|
+
|
|
+#define DDRCTRL_DBGCAM_DBG_Q_DEPTH \
|
|
+ (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \
|
|
+ DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH | \
|
|
+ DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH)
|
|
+
|
|
+#define DDRCTRL_DBGCMD_RANK0_REFRESH BIT(0)
|
|
+
|
|
+#define DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY BIT(0)
|
|
+
|
|
+#define DDRCTRL_SWCTL_SW_DONE BIT(0)
|
|
+
|
|
+#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0)
|
|
+
|
|
+#define DDRCTRL_PCTRL_N_PORT_EN BIT(0)
|
|
+
|
|
+/* DDR PHY registers offsets */
|
|
+#define DDRPHYC_PIR 0x004
|
|
+#define DDRPHYC_PGCR 0x008
|
|
+#define DDRPHYC_PGSR 0x00C
|
|
+#define DDRPHYC_DLLGCR 0x010
|
|
+#define DDRPHYC_ACDLLCR 0x014
|
|
+#define DDRPHYC_PTR0 0x018
|
|
+#define DDRPHYC_ACIOCR 0x024
|
|
+#define DDRPHYC_DXCCR 0x028
|
|
+#define DDRPHYC_DSGCR 0x02C
|
|
+#define DDRPHYC_ZQ0CR0 0x180
|
|
+#define DDRPHYC_DX0GCR 0x1C0
|
|
+#define DDRPHYC_DX0DLLCR 0x1CC
|
|
+#define DDRPHYC_DX1GCR 0x200
|
|
+#define DDRPHYC_DX1DLLCR 0x20C
|
|
+#define DDRPHYC_DX2GCR 0x240
|
|
+#define DDRPHYC_DX2DLLCR 0x24C
|
|
+#define DDRPHYC_DX3GCR 0x280
|
|
+#define DDRPHYC_DX3DLLCR 0x28C
|
|
+
|
|
+/* DDR PHY Register fields */
|
|
+#define DDRPHYC_PIR_INIT BIT(0)
|
|
+#define DDRPHYC_PIR_DLLSRST BIT(1)
|
|
+#define DDRPHYC_PIR_DLLLOCK BIT(2)
|
|
+#define DDRPHYC_PIR_ZCAL BIT(3)
|
|
+#define DDRPHYC_PIR_ITMSRST BIT(4)
|
|
+#define DDRPHYC_PIR_DRAMRST BIT(5)
|
|
+#define DDRPHYC_PIR_DRAMINIT BIT(6)
|
|
+#define DDRPHYC_PIR_QSTRN BIT(7)
|
|
+#define DDRPHYC_PIR_ICPC BIT(16)
|
|
+#define DDRPHYC_PIR_ZCALBYP BIT(30)
|
|
+#define DDRPHYC_PIR_INITSTEPS_MASK GENMASK(31, 7)
|
|
+
|
|
+#define DDRPHYC_PGCR_DFTCMP BIT(2)
|
|
+#define DDRPHYC_PGCR_PDDISDX BIT(24)
|
|
+#define DDRPHYC_PGCR_RFSHDT_MASK GENMASK_32(28, 25)
|
|
+
|
|
+#define DDRPHYC_PGSR_IDONE BIT(0)
|
|
+#define DDRPHYC_PGSR_DTERR BIT(5)
|
|
+#define DDRPHYC_PGSR_DTIERR BIT(6)
|
|
+#define DDRPHYC_PGSR_DFTERR BIT(7)
|
|
+#define DDRPHYC_PGSR_RVERR BIT(8)
|
|
+#define DDRPHYC_PGSR_RVEIRR BIT(9)
|
|
+
|
|
+#define DDRPHYC_DLLGCR_BPS200 BIT(23)
|
|
+
|
|
+#define DDRPHYC_ACDLLCR_DLLSRST BIT(30)
|
|
+#define DDRPHYC_ACDLLCR_DLLDIS BIT(31)
|
|
+
|
|
+#define DDRPHYC_PTR0_TDLLSRST_OFFSET 0
|
|
+#define DDRPHYC_PTR0_TDLLSRST_MASK GENMASK_32(5, 0)
|
|
+#define DDRPHYC_PTR0_TDLLLOCK_OFFSET 6
|
|
+#define DDRPHYC_PTR0_TDLLLOCK_MASK GENMASK_32(17, 6)
|
|
+#define DDRPHYC_PTR0_TITMSRST_OFFSET 18
|
|
+#define DDRPHYC_PTR0_TITMSRST_MASK GENMASK_32(21, 18)
|
|
+
|
|
+#define DDRPHYC_ACIOCR_ACPDD BIT(3)
|
|
+#define DDRPHYC_ACIOCR_ACPDR BIT(4)
|
|
+#define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK_32(10, 8)
|
|
+#define DDRPHYC_ACIOCR_CKPDD_0 BIT(8)
|
|
+#define DDRPHYC_ACIOCR_CKPDR_MASK GENMASK_32(13, 11)
|
|
+#define DDRPHYC_ACIOCR_CKPDR_0 BIT(11)
|
|
+#define DDRPHYC_ACIOCR_CSPDD_MASK GENMASK_32(21, 18)
|
|
+#define DDRPHYC_ACIOCR_CSPDD_0 BIT(18)
|
|
+#define DDRPHYC_ACIOCR_RSTPDD BIT(27)
|
|
+#define DDRPHYC_ACIOCR_RSTPDR BIT(28)
|
|
+
|
|
+#define DDRPHYC_DXCCR_DXPDD BIT(2)
|
|
+#define DDRPHYC_DXCCR_DXPDR BIT(3)
|
|
+
|
|
+#define DDRPHYC_DSGCR_CKEPDD_MASK GENMASK_32(19, 16)
|
|
+#define DDRPHYC_DSGCR_CKEPDD_0 BIT(16)
|
|
+#define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK_32(23, 20)
|
|
+#define DDRPHYC_DSGCR_ODTPDD_0 BIT(20)
|
|
+#define DDRPHYC_DSGCR_NL2PD BIT(24)
|
|
+
|
|
+#define DDRPHYC_ZQ0CRN_ZDATA_MASK GENMASK_32(27, 0)
|
|
+#define DDRPHYC_ZQ0CRN_ZDATA_SHIFT 0
|
|
+#define DDRPHYC_ZQ0CRN_ZDEN BIT(28)
|
|
+#define DDRPHYC_ZQ0CRN_ZQPD BIT(31)
|
|
+
|
|
+#define DDRPHYC_DXNGCR_DXEN BIT(0)
|
|
+
|
|
+#define DDRPHYC_DXNDLLCR_DLLSRST BIT(30)
|
|
+#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31)
|
|
+#define DDRPHYC_DXNDLLCR_SDPHASE_MASK GENMASK(17, 14)
|
|
+#define DDRPHYC_DXNDLLCR_SDPHASE_SHIFT 14
|
|
+
|
|
+uint32_t get_ddrphy_calibration(void);
|
|
+
|
|
+int ddr_sw_self_refresh_exit(void);
|
|
+int ddr_standby_sr_entry(uint32_t *zq0cr0_zdata);
|
|
+void ddr_sr_mode_ssr(void);
|
|
+void ddr_sr_mode_asr(void);
|
|
+void ddr_sr_mode_hsr(void);
|
|
+
|
|
+#endif /*__STM32MP1_DDRC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.c
|
|
new file mode 100644
|
|
index 0000000..ed38575
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.c
|
|
@@ -0,0 +1,614 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <kernel/delay.h>
|
|
+#include <drivers/stm32_i2c.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_pmic.h>
|
|
+#include <drivers/stpmic1.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <libfdt.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <stdbool.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <trace.h>
|
|
+#include <util.h>
|
|
+
|
|
+#define STPMIC1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK_32(6, 2))
|
|
+#define STPMIC1_LDO12356_OUTPUT_SHIFT 2
|
|
+#define STPMIC1_LDO3_MODE (uint8_t)(BIT(7))
|
|
+#define STPMIC1_LDO3_DDR_SEL 31U
|
|
+#define STPMIC1_LDO3_1800000 (9U << STPMIC1_LDO12356_OUTPUT_SHIFT)
|
|
+
|
|
+#define STPMIC1_BUCK_OUTPUT_SHIFT 2
|
|
+#define STPMIC1_BUCK3_1V8 (39U << STPMIC1_BUCK_OUTPUT_SHIFT)
|
|
+
|
|
+#define MODE_STANDBY 8U
|
|
+
|
|
+#define STPMIC1_DEFAULT_START_UP_DELAY_MS 1
|
|
+
|
|
+static struct i2c_handle_s i2c_handle;
|
|
+static uint32_t pmic_i2c_addr;
|
|
+
|
|
+bool stm32mp_with_pmic(void)
|
|
+{
|
|
+ return (i2c_handle.dt_status & DT_STATUS_OK_SEC) != 0;
|
|
+}
|
|
+
|
|
+static int dt_get_pmic_node(void *fdt)
|
|
+{
|
|
+ return fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1");
|
|
+}
|
|
+
|
|
+static int dt_pmic_status(void)
|
|
+{
|
|
+ void *fdt = get_dt_blob();
|
|
+
|
|
+ if (fdt) {
|
|
+ int node = dt_get_pmic_node(fdt);
|
|
+
|
|
+ if (node > 0) {
|
|
+ return _fdt_get_status(fdt, node);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static bool dt_pmic_is_secure(void)
|
|
+{
|
|
+ int status = dt_pmic_status();
|
|
+
|
|
+ return ((unsigned)status == DT_STATUS_OK_SEC) &&
|
|
+ (i2c_handle.dt_status == DT_STATUS_OK_SEC);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @idx: Private identifier provided by the target PMIC driver
|
|
+ * @flags: Operations expected when entering a low power sequence
|
|
+ * @voltage: Target voltage to apply during low power sequences
|
|
+ */
|
|
+struct regu_bo_config {
|
|
+ uint8_t flags;
|
|
+ struct stpmic1_bo_cfg cfg;
|
|
+};
|
|
+
|
|
+#define REGU_BO_FLAG_ENABLE_REGU BIT(0)
|
|
+#define REGU_BO_FLAG_SET_VOLTAGE BIT(1)
|
|
+#define REGU_BO_FLAG_PULL_DOWN BIT(2)
|
|
+#define REGU_BO_FLAG_MASK_RESET BIT(3)
|
|
+
|
|
+static struct regu_bo_config *regu_bo_config;
|
|
+static size_t regu_bo_count;
|
|
+
|
|
+static int save_boot_on_config(void)
|
|
+{
|
|
+ int pmic_node, regulators_node, regulator_node;
|
|
+ void *fdt;
|
|
+
|
|
+ assert(!regu_bo_config && !regu_bo_count);
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ pmic_node = dt_get_pmic_node(fdt);
|
|
+ if (pmic_node < 0) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators");
|
|
+
|
|
+ fdt_for_each_subnode(regulator_node, fdt, regulators_node) {
|
|
+ const fdt32_t *cuint;
|
|
+ const char *name;
|
|
+ struct regu_bo_config regu_cfg;
|
|
+ uint16_t mv;
|
|
+
|
|
+ if (fdt_getprop(fdt, regulator_node, "regulator-boot-on",
|
|
+ NULL) == NULL) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ memset(®u_cfg, 0, sizeof(regu_cfg));
|
|
+ name = fdt_get_name(fdt, regulator_node, NULL);
|
|
+
|
|
+ regu_cfg.flags |= REGU_BO_FLAG_ENABLE_REGU;
|
|
+
|
|
+ if (fdt_getprop(fdt, regulator_node, "regulator-pull-down",
|
|
+ NULL) != NULL) {
|
|
+ stpmic1_bo_pull_down_cfg(name, ®u_cfg.cfg);
|
|
+ regu_cfg.flags |= REGU_BO_FLAG_PULL_DOWN;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, regulator_node, "st,mask-reset",
|
|
+ NULL) != NULL) {
|
|
+ stpmic1_bo_mask_reset_cfg(name, ®u_cfg.cfg);
|
|
+ regu_cfg.flags |= REGU_BO_FLAG_MASK_RESET;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, regulator_node,
|
|
+ "regulator-min-microvolt", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ /* DT uses microvolts, whereas driver awaits millivolts */
|
|
+ mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U);
|
|
+
|
|
+ if (!stpmic1_bo_voltage_cfg(name, mv, ®u_cfg.cfg)) {
|
|
+ regu_cfg.flags |= REGU_BO_FLAG_SET_VOLTAGE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Save config in the Boot On configuration list */
|
|
+ regu_bo_count++;
|
|
+ regu_bo_config = realloc(regu_bo_config,
|
|
+ regu_bo_count * sizeof(regu_cfg));
|
|
+ if (regu_bo_config == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ memcpy(®u_bo_config[regu_bo_count - 1], ®u_cfg,
|
|
+ sizeof(regu_cfg));
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void stm32mp_pmic_apply_boot_on_config(void)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < regu_bo_count; i++) {
|
|
+ struct regu_bo_config *regu_cfg = ®u_bo_config[i];
|
|
+
|
|
+ if (regu_cfg->flags & REGU_BO_FLAG_SET_VOLTAGE) {
|
|
+ if (stpmic1_bo_voltage_unpg(®u_cfg->cfg)) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (regu_cfg->flags & REGU_BO_FLAG_ENABLE_REGU) {
|
|
+ if (stpmic1_bo_enable_unpg(®u_cfg->cfg)) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (regu_cfg->flags & REGU_BO_FLAG_PULL_DOWN) {
|
|
+ if (stpmic1_bo_pull_down_unpg(®u_cfg->cfg)) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (regu_cfg->flags & REGU_BO_FLAG_MASK_RESET) {
|
|
+ if (stpmic1_bo_mask_reset_unpg(®u_cfg->cfg)) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @idx: Private identifier provided by the target PMIC driver
|
|
+ * @flags: Operations expected when entering a low power sequence
|
|
+ * @voltage: Target voltage to apply during low power sequences
|
|
+ */
|
|
+struct regu_lp_config {
|
|
+ uint8_t flags;
|
|
+ struct stpmic1_lp_cfg cfg;
|
|
+};
|
|
+
|
|
+#define REGU_LP_FLAG_LOAD_PWRCTRL BIT(0)
|
|
+#define REGU_LP_FLAG_ON_IN_SUSPEND BIT(1)
|
|
+#define REGU_LP_FLAG_OFF_IN_SUSPEND BIT(2)
|
|
+#define REGU_LP_FLAG_SET_VOLTAGE BIT(3)
|
|
+#define REGU_LP_FLAG_MODE_STANDBY BIT(4)
|
|
+
|
|
+struct regu_lp_state {
|
|
+ const char *name;
|
|
+ size_t cfg_count;
|
|
+ struct regu_lp_config *cfg;
|
|
+};
|
|
+
|
|
+#define REGU_LP_STATE_DISK 0
|
|
+#define REGU_LP_STATE_STANDBY 1
|
|
+#define REGU_LP_STATE_MEM 2
|
|
+#define REGU_LP_STATE_COUNT 3
|
|
+
|
|
+static struct regu_lp_state regu_lp_state[REGU_LP_STATE_COUNT] = {
|
|
+ [REGU_LP_STATE_DISK] = { .name = "standby-ddr-off", },
|
|
+ [REGU_LP_STATE_STANDBY] = { .name = "standby-ddr-sr", },
|
|
+ [REGU_LP_STATE_MEM] = { .name = "lp-stop", },
|
|
+};
|
|
+
|
|
+static unsigned int regu_lp_state2idx(const char *name)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(regu_lp_state); i++) {
|
|
+ struct regu_lp_state *state = ®u_lp_state[i];
|
|
+
|
|
+ if (!strncmp(name, state->name, strlen(state->name))) {
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ panic();
|
|
+}
|
|
+
|
|
+static int save_low_power_config(const char *lp_state)
|
|
+{
|
|
+ int pmic_node, regulators_node, regulator_node;
|
|
+ void *fdt;
|
|
+ unsigned int state_idx = regu_lp_state2idx(lp_state);
|
|
+ struct regu_lp_state *state = ®u_lp_state[state_idx];
|
|
+
|
|
+ assert(!state->cfg && !state->cfg_count);
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ pmic_node = dt_get_pmic_node(fdt);
|
|
+ if (pmic_node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators");
|
|
+
|
|
+ fdt_for_each_subnode(regulator_node, fdt, regulators_node) {
|
|
+ const fdt32_t *cuint;
|
|
+ const char *reg_name;
|
|
+ int regulator_state_node;
|
|
+ struct regu_lp_config *regu_cfg;
|
|
+
|
|
+ state->cfg_count++;
|
|
+ state->cfg = realloc(state->cfg,
|
|
+ state->cfg_count * sizeof(*state->cfg));
|
|
+ if (state->cfg == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ regu_cfg = &state->cfg[state->cfg_count - 1];
|
|
+
|
|
+ memset(regu_cfg, 0, sizeof(*regu_cfg));
|
|
+
|
|
+ reg_name = fdt_get_name(fdt, regulator_node, NULL);
|
|
+
|
|
+ if (stpmic1_lp_cfg(reg_name, ®u_cfg->cfg) != 0) {
|
|
+ EMSG("Invalid regu name %s", reg_name);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Always copy active configuration (Control register) to
|
|
+ * PWRCTRL Control register, even if regulator_state_node
|
|
+ * does not exist.
|
|
+ */
|
|
+ regu_cfg->flags |= REGU_LP_FLAG_LOAD_PWRCTRL;
|
|
+
|
|
+ /* Then apply configs from regulator_state_node */
|
|
+ regulator_state_node = fdt_subnode_offset(fdt,
|
|
+ regulator_node,
|
|
+ lp_state);
|
|
+ if (regulator_state_node <= 0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, regulator_state_node,
|
|
+ "regulator-on-in-suspend", NULL) != NULL) {
|
|
+ regu_cfg->flags |= REGU_LP_FLAG_ON_IN_SUSPEND;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, regulator_state_node,
|
|
+ "regulator-off-in-suspend", NULL) != NULL) {
|
|
+ regu_cfg->flags |= REGU_LP_FLAG_OFF_IN_SUSPEND;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, regulator_state_node,
|
|
+ "regulator-suspend-microvolt", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ uint32_t mv = fdt32_to_cpu(*cuint) / 1000U;
|
|
+
|
|
+ if (stpmic1_lp_voltage_cfg(reg_name, mv,
|
|
+ ®u_cfg->cfg) == 0) {
|
|
+ regu_cfg->flags |= REGU_LP_FLAG_SET_VOLTAGE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, regulator_state_node,
|
|
+ "regulator-mode", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ if (fdt32_to_cpu(*cuint) == MODE_STANDBY) {
|
|
+ regu_cfg->flags |= REGU_LP_FLAG_MODE_STANDBY;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * int stm32mp_pmic_set_lp_config(char *lp_state)
|
|
+ *
|
|
+ * Load the low power configuration stored in regu_lp_state[].
|
|
+ */
|
|
+void stm32mp_pmic_apply_lp_config(const char *lp_state)
|
|
+{
|
|
+ unsigned int state_idx = regu_lp_state2idx(lp_state);
|
|
+ struct regu_lp_state *state = ®u_lp_state[state_idx];
|
|
+ size_t i;
|
|
+
|
|
+ if (stpmic1_powerctrl_on() != 0) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < state->cfg_count; i++) {
|
|
+ struct stpmic1_lp_cfg *cfg = &state->cfg[i].cfg;
|
|
+
|
|
+ if ((state->cfg[i].flags & REGU_LP_FLAG_LOAD_PWRCTRL) != 0) {
|
|
+ if (stpmic1_lp_load_unpg(cfg) != 0) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((state->cfg[i].flags & REGU_LP_FLAG_ON_IN_SUSPEND) != 0) {
|
|
+ if (stpmic1_lp_on_off_unpg(cfg, 1) != 0) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((state->cfg[i].flags & REGU_LP_FLAG_OFF_IN_SUSPEND) != 0) {
|
|
+ if (stpmic1_lp_on_off_unpg(cfg, 0) != 0) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((state->cfg[i].flags & REGU_LP_FLAG_SET_VOLTAGE) != 0) {
|
|
+ if (stpmic1_lp_voltage_unpg(cfg) != 0) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((state->cfg[i].flags & REGU_LP_FLAG_MODE_STANDBY) != 0) {
|
|
+ if (stpmic1_lp_mode_unpg(cfg, 1) != 0) {
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int save_power_configurations(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ if (save_boot_on_config() != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(regu_lp_state); i++) {
|
|
+ if (save_low_power_config(regu_lp_state[i].name) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get PMIC and its I2C bus configuration from the device tree.
|
|
+ * Return 0 on success, negative on error, 1 if no PMIC node is defined.
|
|
+ */
|
|
+static int dt_pmic_i2c_config(struct dt_node_info *i2c_info,
|
|
+ struct stm32_pinctrl **pinctrl,
|
|
+ size_t *pinctrl_count,
|
|
+ struct stm32_i2c_init_s *init)
|
|
+{
|
|
+ int pmic_node;
|
|
+ int i2c_node;
|
|
+ void *fdt;
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (!fdt) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ pmic_node = dt_get_pmic_node(fdt);
|
|
+ if (pmic_node < 0) {
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, pmic_node, "reg", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1;
|
|
+ if (pmic_i2c_addr > UINT16_MAX) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ i2c_node = fdt_parent_offset(fdt, pmic_node);
|
|
+ if (i2c_node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ fdt_fill_device_info(fdt, i2c_info, i2c_node);
|
|
+ if (i2c_info->base == 0U) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return stm32_i2c_get_setup_from_fdt(fdt, i2c_node, init,
|
|
+ pinctrl, pinctrl_count);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * PMIC and resource initialization
|
|
+ */
|
|
+
|
|
+/* Return true if PMIC is available, false if not found, panics on errors */
|
|
+static bool initialize_pmic_i2c(void)
|
|
+{
|
|
+ int ret;
|
|
+ struct dt_node_info i2c_info;
|
|
+ struct i2c_handle_s *i2c = &i2c_handle;
|
|
+ struct stm32_pinctrl *pinctrl;
|
|
+ size_t pinctrl_count;
|
|
+ struct stm32_i2c_init_s i2c_init;
|
|
+
|
|
+ ret = dt_pmic_i2c_config(&i2c_info, &pinctrl, &pinctrl_count,
|
|
+ &i2c_init);
|
|
+ if (ret < 0) {
|
|
+ EMSG("I2C configuration failed %d\n", ret);
|
|
+ panic();
|
|
+ }
|
|
+ if (ret != 0) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* Initialize PMIC I2C */
|
|
+ i2c->pbase = i2c_info.base;
|
|
+ i2c->vbase = (uintptr_t)phys_to_virt(i2c_info.base, MEM_AREA_IO_SEC);
|
|
+ assert(i2c->vbase);
|
|
+ i2c->dt_status = i2c_info.status;
|
|
+ i2c->clock = i2c_info.clock;
|
|
+ i2c_init.own_address1 = pmic_i2c_addr;
|
|
+ i2c_init.addressing_mode = I2C_ADDRESSINGMODE_7BIT;
|
|
+ i2c_init.dual_address_mode = I2C_DUALADDRESS_DISABLE;
|
|
+ i2c_init.own_address2 = 0;
|
|
+ i2c_init.own_address2_masks = I2C_OAR2_OA2NOMASK;
|
|
+ i2c_init.general_call_mode = I2C_GENERALCALL_DISABLE;
|
|
+ i2c_init.no_stretch_mode = I2C_NOSTRETCH_DISABLE;
|
|
+ i2c_init.analog_filter = 1;
|
|
+ i2c_init.digital_filter_coef = 0;
|
|
+
|
|
+ i2c->pinctrl = pinctrl;
|
|
+ i2c->pinctrl_count = pinctrl_count;
|
|
+
|
|
+ stm32mp_get_pmic();
|
|
+
|
|
+ ret = stm32_i2c_init(i2c, &i2c_init);
|
|
+ if (ret != 0) {
|
|
+ EMSG("Cannot initialize I2C %x (%d)\n", i2c->pbase, ret);
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ if (!stm32_i2c_is_device_ready(i2c, pmic_i2c_addr, 1,
|
|
+ I2C_TIMEOUT_BUSY_MS)) {
|
|
+ EMSG("I2C device not ready\n");
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ stpmic1_bind_i2c(i2c, (uint16_t)pmic_i2c_addr);
|
|
+
|
|
+ stm32mp_put_pmic();
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Automated suspend/resume at system suspend/resume is expected
|
|
+ * only when the PMIC is secure. If it is non secure, only atomic
|
|
+ * execution context acn get/put the PMIC resources.
|
|
+ */
|
|
+static void pmic_suspend_resume(enum pm_op op, void __unused *handle)
|
|
+{
|
|
+ switch (op) {
|
|
+ case PM_OP_SUSPEND:
|
|
+ stm32_i2c_suspend(&i2c_handle);
|
|
+ break;
|
|
+ case PM_OP_RESUME:
|
|
+ stm32_i2c_resume(&i2c_handle);
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+KEEP_PAGER(pmic_suspend_resume);
|
|
+
|
|
+/* stm32mp_get/put_pmic allows secure atomic sequences to use non secure PMIC */
|
|
+void stm32mp_get_pmic(void)
|
|
+{
|
|
+ stm32_i2c_resume(&i2c_handle);
|
|
+}
|
|
+
|
|
+void stm32mp_put_pmic(void)
|
|
+{
|
|
+ stm32_i2c_suspend(&i2c_handle);
|
|
+}
|
|
+
|
|
+static void register_non_secure_pmic(void)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ if (!i2c_handle.pbase) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ stm32mp_register_non_secure_periph_iomem(i2c_handle.pbase);
|
|
+ stm32mp_register_clock_parents_secure(i2c_handle.clock);
|
|
+
|
|
+ for (n = 0; n < i2c_handle.pinctrl_count; n++) {
|
|
+ stm32mp_register_non_secure_gpio(i2c_handle.pinctrl[n].bank,
|
|
+ i2c_handle.pinctrl[n].pin);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void register_secure_pmic(void)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ stm32mp_register_pm_cb(pmic_suspend_resume, NULL);
|
|
+ stm32mp_register_secure_periph_iomem(i2c_handle.pbase);
|
|
+
|
|
+ for (n = 0; n < i2c_handle.pinctrl_count; n++) {
|
|
+ stm32mp_register_secure_gpio(i2c_handle.pinctrl[n].bank,
|
|
+ i2c_handle.pinctrl[n].pin);
|
|
+ }
|
|
+}
|
|
+
|
|
+static TEE_Result initialize_pmic(void)
|
|
+{
|
|
+ unsigned long pmic_version;
|
|
+
|
|
+ if (!initialize_pmic_i2c()) {
|
|
+ DMSG("No PMIC");
|
|
+ register_non_secure_pmic();
|
|
+ return TEE_SUCCESS;
|
|
+ }
|
|
+
|
|
+ stm32mp_get_pmic();
|
|
+
|
|
+ if (stpmic1_get_version(&pmic_version)) {
|
|
+ panic("Failed to access PMIC");
|
|
+ }
|
|
+
|
|
+ DMSG("PMIC version = 0x%02lx", pmic_version);
|
|
+ stpmic1_dump_regulators();
|
|
+
|
|
+ if (save_power_configurations() != 0) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ if (dt_pmic_is_secure()) {
|
|
+ register_secure_pmic();
|
|
+ } else {
|
|
+ register_non_secure_pmic();
|
|
+ }
|
|
+
|
|
+ stm32mp_put_pmic();
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(initialize_pmic);
|
|
+
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.h b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.h
|
|
new file mode 100644
|
|
index 0000000..f50943d
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.h
|
|
@@ -0,0 +1,33 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_PMIC_H__
|
|
+#define __STM32MP1_PMIC_H__
|
|
+
|
|
+#include <kernel/panic.h>
|
|
+
|
|
+#ifdef CFG_STPMIC1
|
|
+void stm32mp_pmic_apply_boot_on_config(void);
|
|
+void stm32mp_pmic_apply_lp_config(const char *lp_state);
|
|
+void stm32mp_get_pmic(void);
|
|
+void stm32mp_put_pmic(void);
|
|
+#else
|
|
+void stm32mp_pmic_apply_boot_on_config(void)
|
|
+{
|
|
+}
|
|
+void stm32mp_pmic_apply_lp_config(const char *lp_state)
|
|
+{
|
|
+}
|
|
+void stm32mp_get_pmic(void)
|
|
+{
|
|
+ panic();
|
|
+}
|
|
+void stm32mp_put_pmic(void)
|
|
+{
|
|
+ panic();
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif /*__STM32MP1_PMIC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.c
|
|
new file mode 100644
|
|
index 0000000..ce60f50
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.c
|
|
@@ -0,0 +1,21 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <drivers/stm32mp1_pwr.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+
|
|
+uintptr_t stm32_pwr_base(void)
|
|
+{
|
|
+ static void *va;
|
|
+
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return PWR_BASE;
|
|
+
|
|
+ if (!va)
|
|
+ va = phys_to_virt(PWR_BASE, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return (uintptr_t)va;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.h b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.h
|
|
new file mode 100644
|
|
index 0000000..1d44e8b
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr.h
|
|
@@ -0,0 +1,48 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_PWR_H__
|
|
+#define __STM32MP1_PWR_H__
|
|
+
|
|
+#include <util.h>
|
|
+
|
|
+#define PWR_CR1_OFF 0x00
|
|
+#define PWR_CR2_OFF 0x08
|
|
+#define PWR_CR3_OFF 0x0c
|
|
+#define PWR_MPUCR_OFF 0x10
|
|
+#define PWR_WKUPCR_OFF 0x20
|
|
+#define PWR_MPUWKUPENR_OFF 0x28
|
|
+
|
|
+#define PWR_OFFSET_MASK GENMASK_32(5, 0)
|
|
+
|
|
+#define PWR_CR1_LPDS BIT(0)
|
|
+#define PWR_CR1_LPCFG BIT(1)
|
|
+#define PWR_CR1_LVDS BIT(2)
|
|
+#define PWR_CR1_DBP BIT(8)
|
|
+
|
|
+#define PWR_CR2_BREN BIT(0)
|
|
+#define PWR_CR2_RREN BIT(1)
|
|
+
|
|
+#define PWR_CR3_VBE BIT(8)
|
|
+#define PWR_CR3_VBRS BIT(9)
|
|
+#define PWR_CR3_DDRSREN BIT(10)
|
|
+#define PWR_CR3_DDRSRDIS BIT(11)
|
|
+#define PWR_CR3_DDRRETEN BIT(12)
|
|
+#define PWR_CR3_USB33DEN BIT(24)
|
|
+#define PWR_CR3_REG18EN BIT(28)
|
|
+#define PWR_CR3_REG11EN BIT(30)
|
|
+
|
|
+#define PWR_MPUCR_PDDS BIT(0)
|
|
+#define PWR_MPUCR_CSTDBYDIS BIT(3)
|
|
+#define PWR_MPUCR_CSSF BIT(9)
|
|
+
|
|
+#define PWR_WKUPCR_MASK (GENMASK_32(27, 16) | \
|
|
+ GENMASK_32(13, 8) | GENMASK_32(5, 0))
|
|
+
|
|
+#define PWR_MPUWKUPENR_MASK GENMASK_32(5, 0)
|
|
+
|
|
+uintptr_t stm32_pwr_base(void);
|
|
+
|
|
+#endif /*__STM32MP1_PWR_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.c b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.c
|
|
new file mode 100644
|
|
index 0000000..a9df116
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.c
|
|
@@ -0,0 +1,51 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <io.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <stm32_util.h>
|
|
+
|
|
+uintptr_t stm32_rcc_base(void)
|
|
+{
|
|
+ static void *va;
|
|
+
|
|
+ if (!cpu_mmu_enabled()) {
|
|
+ return RCC_BASE;
|
|
+ }
|
|
+
|
|
+ if (!va) {
|
|
+ va = phys_to_virt(RCC_BASE, MEM_AREA_IO_SEC);
|
|
+ }
|
|
+
|
|
+ return (uintptr_t)va;
|
|
+}
|
|
+
|
|
+void stm32_rcc_secure(int enable)
|
|
+{
|
|
+ uintptr_t base = stm32_rcc_base();
|
|
+
|
|
+ if (enable != 0) {
|
|
+ io_mask32_stm32shregs(base + RCC_TZCR, RCC_TZCR_TZEN,
|
|
+ RCC_TZCR_TZEN);
|
|
+ } else {
|
|
+ io_mask32_stm32shregs(base + RCC_TZCR, 0, RCC_TZCR_TZEN);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool stm32_rcc_is_secure(void)
|
|
+{
|
|
+ uintptr_t base = stm32_rcc_base();
|
|
+
|
|
+ return read32(base + RCC_TZCR) & RCC_TZCR_TZEN;
|
|
+}
|
|
+
|
|
+bool stm32_rcc_is_mckprot(void)
|
|
+{
|
|
+ uintptr_t base = stm32_rcc_base();
|
|
+
|
|
+ return read32(base + RCC_TZCR) & RCC_TZCR_MCKPROT;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.h b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.h
|
|
new file mode 100644
|
|
index 0000000..6a7a665
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_rcc.h
|
|
@@ -0,0 +1,539 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_RCC_H__
|
|
+#define __STM32MP1_RCC_H__
|
|
+
|
|
+#include <stdbool.h>
|
|
+#include <util.h>
|
|
+
|
|
+#define RCC_TZCR 0x00
|
|
+#define RCC_OCENSETR 0x0C
|
|
+#define RCC_OCENCLRR 0x10
|
|
+#define RCC_HSICFGR 0x18
|
|
+#define RCC_CSICFGR 0x1C
|
|
+#define RCC_MPCKSELR 0x20
|
|
+#define RCC_ASSCKSELR 0x24
|
|
+#define RCC_RCK12SELR 0x28
|
|
+#define RCC_MPCKDIVR 0x2C
|
|
+#define RCC_AXIDIVR 0x30
|
|
+#define RCC_APB4DIVR 0x3C
|
|
+#define RCC_APB5DIVR 0x40
|
|
+#define RCC_RTCDIVR 0x44
|
|
+#define RCC_MSSCKSELR 0x48
|
|
+#define RCC_PLL1CR 0x80
|
|
+#define RCC_PLL1CFGR1 0x84
|
|
+#define RCC_PLL1CFGR2 0x88
|
|
+#define RCC_PLL1FRACR 0x8C
|
|
+#define RCC_PLL1CSGR 0x90
|
|
+#define RCC_PLL2CR 0x94
|
|
+#define RCC_PLL2CFGR1 0x98
|
|
+#define RCC_PLL2CFGR2 0x9C
|
|
+#define RCC_PLL2FRACR 0xA0
|
|
+#define RCC_PLL2CSGR 0xA4
|
|
+#define RCC_I2C46CKSELR 0xC0
|
|
+#define RCC_SPI6CKSELR 0xC4
|
|
+#define RCC_UART1CKSELR 0xC8
|
|
+#define RCC_RNG1CKSELR 0xCC
|
|
+#define RCC_CPERCKSELR 0xD0
|
|
+#define RCC_STGENCKSELR 0xD4
|
|
+#define RCC_DDRITFCR 0xD8
|
|
+#define RCC_MP_BOOTCR 0x100
|
|
+#define RCC_MP_SREQSETR 0x104
|
|
+#define RCC_MP_SREQCLRR 0x108
|
|
+#define RCC_MP_GCR 0x10C
|
|
+#define RCC_MP_APRSTCR 0x110
|
|
+#define RCC_MP_APRSTSR 0x114
|
|
+#define RCC_BDCR 0x140
|
|
+#define RCC_RDLSICR 0x144
|
|
+#define RCC_APB4RSTSETR 0x180
|
|
+#define RCC_APB4RSTCLRR 0x184
|
|
+#define RCC_APB5RSTSETR 0x188
|
|
+#define RCC_APB5RSTCLRR 0x18C
|
|
+#define RCC_AHB5RSTSETR 0x190
|
|
+#define RCC_AHB5RSTCLRR 0x194
|
|
+#define RCC_AHB6RSTSETR 0x198
|
|
+#define RCC_AHB6RSTCLRR 0x19C
|
|
+#define RCC_TZAHB6RSTSETR 0x1A0
|
|
+#define RCC_TZAHB6RSTCLRR 0x1A4
|
|
+#define RCC_MP_APB4ENSETR 0x200
|
|
+#define RCC_MP_APB4ENCLRR 0x204
|
|
+#define RCC_MP_APB5ENSETR 0x208
|
|
+#define RCC_MP_APB5ENCLRR 0x20C
|
|
+#define RCC_MP_AHB5ENSETR 0x210
|
|
+#define RCC_MP_AHB5ENCLRR 0x214
|
|
+#define RCC_MP_AHB6ENSETR 0x218
|
|
+#define RCC_MP_AHB6ENCLRR 0x21C
|
|
+#define RCC_MP_TZAHB6ENSETR 0x220
|
|
+#define RCC_MP_TZAHB6ENCLRR 0x224
|
|
+#define RCC_MC_APB4ENSETR 0x280
|
|
+#define RCC_MC_APB4ENCLRR 0x284
|
|
+#define RCC_MC_APB5ENSETR 0x288
|
|
+#define RCC_MC_APB5ENCLRR 0x28C
|
|
+#define RCC_MC_AHB5ENSETR 0x290
|
|
+#define RCC_MC_AHB5ENCLRR 0x294
|
|
+#define RCC_MC_AHB6ENSETR 0x298
|
|
+#define RCC_MC_AHB6ENCLRR 0x29C
|
|
+#define RCC_MP_APB4LPENSETR 0x300
|
|
+#define RCC_MP_APB4LPENCLRR 0x304
|
|
+#define RCC_MP_APB5LPENSETR 0x308
|
|
+#define RCC_MP_APB5LPENCLRR 0x30C
|
|
+#define RCC_MP_AHB5LPENSETR 0x310
|
|
+#define RCC_MP_AHB5LPENCLRR 0x314
|
|
+#define RCC_MP_AHB6LPENSETR 0x318
|
|
+#define RCC_MP_AHB6LPENCLRR 0x31C
|
|
+#define RCC_MP_TZAHB6LPENSETR 0x320
|
|
+#define RCC_MP_TZAHB6LPENCLRR 0x324
|
|
+#define RCC_MC_APB4LPENSETR 0x380
|
|
+#define RCC_MC_APB4LPENCLRR 0x384
|
|
+#define RCC_MC_APB5LPENSETR 0x388
|
|
+#define RCC_MC_APB5LPENCLRR 0x38C
|
|
+#define RCC_MC_AHB5LPENSETR 0x390
|
|
+#define RCC_MC_AHB5LPENCLRR 0x394
|
|
+#define RCC_MC_AHB6LPENSETR 0x398
|
|
+#define RCC_MC_AHB6LPENCLRR 0x39C
|
|
+#define RCC_BR_RSTSCLRR 0x400
|
|
+#define RCC_MP_GRSTCSETR 0x404
|
|
+#define RCC_MP_RSTSCLRR 0x408
|
|
+#define RCC_MP_IWDGFZSETR 0x40C
|
|
+#define RCC_MP_IWDGFZCLRR 0x410
|
|
+#define RCC_MP_CIER 0x414
|
|
+#define RCC_MP_CIFR 0x418
|
|
+#define RCC_PWRLPDLYCR 0x41C
|
|
+#define RCC_MP_RSTSSETR 0x420
|
|
+#define RCC_MCO1CFGR 0x800
|
|
+#define RCC_MCO2CFGR 0x804
|
|
+#define RCC_OCRDYR 0x808
|
|
+#define RCC_DBGCFGR 0x80C
|
|
+#define RCC_RCK3SELR 0x820
|
|
+#define RCC_RCK4SELR 0x824
|
|
+#define RCC_TIMG1PRER 0x828
|
|
+#define RCC_TIMG2PRER 0x82C
|
|
+#define RCC_MCUDIVR 0x830
|
|
+#define RCC_APB1DIVR 0x834
|
|
+#define RCC_APB2DIVR 0x838
|
|
+#define RCC_APB3DIVR 0x83C
|
|
+#define RCC_PLL3CR 0x880
|
|
+#define RCC_PLL3CFGR1 0x884
|
|
+#define RCC_PLL3CFGR2 0x888
|
|
+#define RCC_PLL3FRACR 0x88C
|
|
+#define RCC_PLL3CSGR 0x890
|
|
+#define RCC_PLL4CR 0x894
|
|
+#define RCC_PLL4CFGR1 0x898
|
|
+#define RCC_PLL4CFGR2 0x89C
|
|
+#define RCC_PLL4FRACR 0x8A0
|
|
+#define RCC_PLL4CSGR 0x8A4
|
|
+#define RCC_I2C12CKSELR 0x8C0
|
|
+#define RCC_I2C35CKSELR 0x8C4
|
|
+#define RCC_SAI1CKSELR 0x8C8
|
|
+#define RCC_SAI2CKSELR 0x8CC
|
|
+#define RCC_SAI3CKSELR 0x8D0
|
|
+#define RCC_SAI4CKSELR 0x8D4
|
|
+#define RCC_SPI2S1CKSELR 0x8D8
|
|
+#define RCC_SPI2S23CKSELR 0x8DC
|
|
+#define RCC_SPI45CKSELR 0x8E0
|
|
+#define RCC_UART6CKSELR 0x8E4
|
|
+#define RCC_UART24CKSELR 0x8E8
|
|
+#define RCC_UART35CKSELR 0x8EC
|
|
+#define RCC_UART78CKSELR 0x8F0
|
|
+#define RCC_SDMMC12CKSELR 0x8F4
|
|
+#define RCC_SDMMC3CKSELR 0x8F8
|
|
+#define RCC_ETHCKSELR 0x8FC
|
|
+#define RCC_QSPICKSELR 0x900
|
|
+#define RCC_FMCCKSELR 0x904
|
|
+#define RCC_FDCANCKSELR 0x90C
|
|
+#define RCC_SPDIFCKSELR 0x914
|
|
+#define RCC_CECCKSELR 0x918
|
|
+#define RCC_USBCKSELR 0x91C
|
|
+#define RCC_RNG2CKSELR 0x920
|
|
+#define RCC_DSICKSELR 0x924
|
|
+#define RCC_ADCCKSELR 0x928
|
|
+#define RCC_LPTIM45CKSELR 0x92C
|
|
+#define RCC_LPTIM23CKSELR 0x930
|
|
+#define RCC_LPTIM1CKSELR 0x934
|
|
+#define RCC_APB1RSTSETR 0x980
|
|
+#define RCC_APB1RSTCLRR 0x984
|
|
+#define RCC_APB2RSTSETR 0x988
|
|
+#define RCC_APB2RSTCLRR 0x98C
|
|
+#define RCC_APB3RSTSETR 0x990
|
|
+#define RCC_APB3RSTCLRR 0x994
|
|
+#define RCC_AHB2RSTSETR 0x998
|
|
+#define RCC_AHB2RSTCLRR 0x99C
|
|
+#define RCC_AHB3RSTSETR 0x9A0
|
|
+#define RCC_AHB3RSTCLRR 0x9A4
|
|
+#define RCC_AHB4RSTSETR 0x9A8
|
|
+#define RCC_AHB4RSTCLRR 0x9AC
|
|
+#define RCC_MP_APB1ENSETR 0xA00
|
|
+#define RCC_MP_APB1ENCLRR 0xA04
|
|
+#define RCC_MP_APB2ENSETR 0xA08
|
|
+#define RCC_MP_APB2ENCLRR 0xA0C
|
|
+#define RCC_MP_APB3ENSETR 0xA10
|
|
+#define RCC_MP_APB3ENCLRR 0xA14
|
|
+#define RCC_MP_AHB2ENSETR 0xA18
|
|
+#define RCC_MP_AHB2ENCLRR 0xA1C
|
|
+#define RCC_MP_AHB3ENSETR 0xA20
|
|
+#define RCC_MP_AHB3ENCLRR 0xA24
|
|
+#define RCC_MP_AHB4ENSETR 0xA28
|
|
+#define RCC_MP_AHB4ENCLRR 0xA2C
|
|
+#define RCC_MP_MLAHBENSETR 0xA38
|
|
+#define RCC_MP_MLAHBENCLRR 0xA3C
|
|
+#define RCC_MC_APB1ENSETR 0xA80
|
|
+#define RCC_MC_APB1ENCLRR 0xA84
|
|
+#define RCC_MC_APB2ENSETR 0xA88
|
|
+#define RCC_MC_APB2ENCLRR 0xA8C
|
|
+#define RCC_MC_APB3ENSETR 0xA90
|
|
+#define RCC_MC_APB3ENCLRR 0xA94
|
|
+#define RCC_MC_AHB2ENSETR 0xA98
|
|
+#define RCC_MC_AHB2ENCLRR 0xA9C
|
|
+#define RCC_MC_AHB3ENSETR 0xAA0
|
|
+#define RCC_MC_AHB3ENCLRR 0xAA4
|
|
+#define RCC_MC_AHB4ENSETR 0xAA8
|
|
+#define RCC_MC_AHB4ENCLRR 0xAAC
|
|
+#define RCC_MC_AXIMENSETR 0xAB0
|
|
+#define RCC_MC_AXIMENCLRR 0xAB4
|
|
+#define RCC_MC_MLAHBENSETR 0xAB8
|
|
+#define RCC_MC_MLAHBENCLRR 0xABC
|
|
+#define RCC_MP_APB1LPENSETR 0xB00
|
|
+#define RCC_MP_APB1LPENCLRR 0xB04
|
|
+#define RCC_MP_APB2LPENSETR 0xB08
|
|
+#define RCC_MP_APB2LPENCLRR 0xB0C
|
|
+#define RCC_MP_APB3LPENSETR 0xB10
|
|
+#define RCC_MP_APB3LPENCLRR 0xB14
|
|
+#define RCC_MP_AHB2LPENSETR 0xB18
|
|
+#define RCC_MP_AHB2LPENCLRR 0xB1C
|
|
+#define RCC_MP_AHB3LPENSETR 0xB20
|
|
+#define RCC_MP_AHB3LPENCLRR 0xB24
|
|
+#define RCC_MP_AHB4LPENSETR 0xB28
|
|
+#define RCC_MP_AHB4LPENCLRR 0xB2C
|
|
+#define RCC_MP_AXIMLPENSETR 0xB30
|
|
+#define RCC_MP_AXIMLPENCLRR 0xB34
|
|
+#define RCC_MP_MLAHBLPENSETR 0xB38
|
|
+#define RCC_MP_MLAHBLPENCLRR 0xB3C
|
|
+#define RCC_MC_APB1LPENSETR 0xB80
|
|
+#define RCC_MC_APB1LPENCLRR 0xB84
|
|
+#define RCC_MC_APB2LPENSETR 0xB88
|
|
+#define RCC_MC_APB2LPENCLRR 0xB8C
|
|
+#define RCC_MC_APB3LPENSETR 0xB90
|
|
+#define RCC_MC_APB3LPENCLRR 0xB94
|
|
+#define RCC_MC_AHB2LPENSETR 0xB98
|
|
+#define RCC_MC_AHB2LPENCLRR 0xB9C
|
|
+#define RCC_MC_AHB3LPENSETR 0xBA0
|
|
+#define RCC_MC_AHB3LPENCLRR 0xBA4
|
|
+#define RCC_MC_AHB4LPENSETR 0xBA8
|
|
+#define RCC_MC_AHB4LPENCLRR 0xBAC
|
|
+#define RCC_MC_AXIMLPENSETR 0xBB0
|
|
+#define RCC_MC_AXIMLPENCLRR 0xBB4
|
|
+#define RCC_MC_MLAHBLPENSETR 0xBB8
|
|
+#define RCC_MC_MLAHBLPENCLRR 0xBBC
|
|
+#define RCC_MC_RSTSCLRR 0xC00
|
|
+#define RCC_MC_CIER 0xC14
|
|
+#define RCC_MC_CIFR 0xC18
|
|
+#define RCC_VERR 0xFF4
|
|
+#define RCC_IDR 0xFF8
|
|
+#define RCC_SIDR 0xFFC
|
|
+
|
|
+#define RCC_OFFSET_MASK GENMASK_32(11, 0)
|
|
+
|
|
+/* Values for RCC_TZCR register */
|
|
+#define RCC_TZCR_TZEN BIT(0)
|
|
+#define RCC_TZCR_MCKPROT BIT(1)
|
|
+
|
|
+/* Used for most of RCC_<x>SELR registers */
|
|
+#define RCC_SELR_SRC_MASK GENMASK_32(2, 0)
|
|
+#define RCC_SELR_REFCLK_SRC_MASK GENMASK_32(1, 0)
|
|
+#define RCC_SELR_SRCRDY BIT(31)
|
|
+
|
|
+/* Values of RCC_MPCKSELR register */
|
|
+#define RCC_MPCKSELR_HSI 0x00000000
|
|
+#define RCC_MPCKSELR_HSE 0x00000001
|
|
+#define RCC_MPCKSELR_PLL 0x00000002
|
|
+#define RCC_MPCKSELR_PLL_MPUDIV 0x00000003
|
|
+
|
|
+/* Values of RCC_ASSCKSELR register */
|
|
+#define RCC_ASSCKSELR_HSI 0x00000000
|
|
+#define RCC_ASSCKSELR_HSE 0x00000001
|
|
+#define RCC_ASSCKSELR_PLL 0x00000002
|
|
+
|
|
+/* Values of RCC_MSSCKSELR register */
|
|
+#define RCC_MSSCKSELR_HSI 0x00000000
|
|
+#define RCC_MSSCKSELR_HSE 0x00000001
|
|
+#define RCC_MSSCKSELR_CSI 0x00000002
|
|
+#define RCC_MSSCKSELR_PLL 0x00000003
|
|
+
|
|
+/* Values of RCC_CPERCKSELR register */
|
|
+#define RCC_CPERCKSELR_HSI 0x00000000
|
|
+#define RCC_CPERCKSELR_CSI 0x00000001
|
|
+#define RCC_CPERCKSELR_HSE 0x00000002
|
|
+
|
|
+/* Used for most of DIVR register: max div for RTC */
|
|
+#define RCC_DIVR_DIV_MASK GENMASK_32(5, 0)
|
|
+#define RCC_DIVR_DIVRDY BIT(31)
|
|
+
|
|
+/* Masks for specific DIVR registers */
|
|
+#define RCC_APBXDIV_MASK GENMASK_32(2, 0)
|
|
+#define RCC_MPUDIV_MASK GENMASK_32(2, 0)
|
|
+#define RCC_AXIDIV_MASK GENMASK_32(2, 0)
|
|
+#define RCC_MCUDIV_MASK GENMASK_32(3, 0)
|
|
+
|
|
+/* Used for TIMER Prescaler */
|
|
+#define RCC_TIMGXPRER_TIMGXPRE BIT(0)
|
|
+
|
|
+/* Offset between RCC_MP_xxxENSETR and RCC_MP_xxxENCLRR registers */
|
|
+#define RCC_MP_ENCLRR_OFFSET 4u
|
|
+
|
|
+/* Fields of RCC_BDCR register */
|
|
+#define RCC_BDCR_LSEON BIT(0)
|
|
+#define RCC_BDCR_LSEBYP BIT(1)
|
|
+#define RCC_BDCR_LSERDY BIT(2)
|
|
+#define RCC_BDCR_DIGBYP BIT(3)
|
|
+#define RCC_BDCR_LSEDRV_MASK GENMASK_32(5, 4)
|
|
+#define RCC_BDCR_LSEDRV_SHIFT 4
|
|
+#define RCC_BDCR_LSECSSON BIT(8)
|
|
+#define RCC_BDCR_RTCCKEN BIT(20)
|
|
+#define RCC_BDCR_RTCSRC_MASK GENMASK_32(17, 16)
|
|
+#define RCC_BDCR_RTCSRC_SHIFT 16
|
|
+#define RCC_BDCR_VSWRST BIT(31)
|
|
+
|
|
+/* Fields of RCC_RDLSICR register */
|
|
+#define RCC_RDLSICR_LSION BIT(0)
|
|
+#define RCC_RDLSICR_LSIRDY BIT(1)
|
|
+
|
|
+/* Used for all RCC_PLL<n>CR registers */
|
|
+#define RCC_PLLNCR_PLLON BIT(0)
|
|
+#define RCC_PLLNCR_PLLRDY BIT(1)
|
|
+#define RCC_PLLNCR_SSCG_CTRL BIT(2)
|
|
+#define RCC_PLLNCR_DIVPEN BIT(4)
|
|
+#define RCC_PLLNCR_DIVQEN BIT(5)
|
|
+#define RCC_PLLNCR_DIVREN BIT(6)
|
|
+#define RCC_PLLNCR_DIVEN_SHIFT 4
|
|
+
|
|
+/* Used for all RCC_PLL<n>CFGR1 registers */
|
|
+#define RCC_PLLNCFGR1_DIVM_SHIFT 16
|
|
+#define RCC_PLLNCFGR1_DIVM_MASK GENMASK_32(21, 16)
|
|
+#define RCC_PLLNCFGR1_DIVN_SHIFT 0
|
|
+#define RCC_PLLNCFGR1_DIVN_MASK GENMASK_32(8, 0)
|
|
+/* Only for PLL3 and PLL4 */
|
|
+#define RCC_PLLNCFGR1_IFRGE_SHIFT 24
|
|
+#define RCC_PLLNCFGR1_IFRGE_MASK GENMASK_32(25, 24)
|
|
+
|
|
+/* Used for all RCC_PLL<n>CFGR2 registers */
|
|
+#define RCC_PLLNCFGR2_DIVX_MASK GENMASK_32(6, 0)
|
|
+#define RCC_PLLNCFGR2_DIVP_SHIFT 0
|
|
+#define RCC_PLLNCFGR2_DIVP_MASK GENMASK_32(6, 0)
|
|
+#define RCC_PLLNCFGR2_DIVQ_SHIFT 8
|
|
+#define RCC_PLLNCFGR2_DIVQ_MASK GENMASK_32(14, 8)
|
|
+#define RCC_PLLNCFGR2_DIVR_SHIFT 16
|
|
+#define RCC_PLLNCFGR2_DIVR_MASK GENMASK_32(22, 16)
|
|
+
|
|
+/* Used for all RCC_PLL<n>FRACR registers */
|
|
+#define RCC_PLLNFRACR_FRACV_SHIFT 3
|
|
+#define RCC_PLLNFRACR_FRACV_MASK GENMASK_32(15, 3)
|
|
+#define RCC_PLLNFRACR_FRACLE BIT(16)
|
|
+
|
|
+/* Used for all RCC_PLL<n>CSGR registers */
|
|
+#define RCC_PLLNCSGR_INC_STEP_SHIFT 16
|
|
+#define RCC_PLLNCSGR_INC_STEP_MASK GENMASK_32(30, 16)
|
|
+#define RCC_PLLNCSGR_MOD_PER_SHIFT 0
|
|
+#define RCC_PLLNCSGR_MOD_PER_MASK GENMASK_32(12, 0)
|
|
+#define RCC_PLLNCSGR_SSCG_MODE_SHIFT 15
|
|
+#define RCC_PLLNCSGR_SSCG_MODE_MASK BIT(15)
|
|
+
|
|
+/* Used for RCC_OCENSETR and RCC_OCENCLRR registers */
|
|
+#define RCC_OCENR_HSION BIT(0)
|
|
+#define RCC_OCENR_HSIKERON BIT(1)
|
|
+#define RCC_OCENR_CSION BIT(4)
|
|
+#define RCC_OCENR_CSIKERON BIT(5)
|
|
+#define RCC_OCENR_DIGBYP BIT(7)
|
|
+#define RCC_OCENR_HSEON BIT(8)
|
|
+#define RCC_OCENR_HSEKERON BIT(9)
|
|
+#define RCC_OCENR_HSEBYP BIT(10)
|
|
+#define RCC_OCENR_HSECSSON BIT(11)
|
|
+
|
|
+/* Fields of RCC_OCRDYR register */
|
|
+#define RCC_OCRDYR_HSIRDY BIT(0)
|
|
+#define RCC_OCRDYR_HSIDIVRDY BIT(2)
|
|
+#define RCC_OCRDYR_CSIRDY BIT(4)
|
|
+#define RCC_OCRDYR_HSERDY BIT(8)
|
|
+
|
|
+/* Fields of RCC_DDRITFCR register */
|
|
+#define RCC_DDRITFCR_DDRC1EN BIT(0)
|
|
+#define RCC_DDRITFCR_DDRC1LPEN BIT(1)
|
|
+#define RCC_DDRITFCR_DDRC2EN BIT(2)
|
|
+#define RCC_DDRITFCR_DDRC2LPEN BIT(3)
|
|
+#define RCC_DDRITFCR_DDRPHYCEN BIT(4)
|
|
+#define RCC_DDRITFCR_DDRPHYCLPEN BIT(5)
|
|
+#define RCC_DDRITFCR_DDRCAPBEN BIT(6)
|
|
+#define RCC_DDRITFCR_DDRCAPBLPEN BIT(7)
|
|
+#define RCC_DDRITFCR_AXIDCGEN BIT(8)
|
|
+#define RCC_DDRITFCR_DDRPHYCAPBEN BIT(9)
|
|
+#define RCC_DDRITFCR_DDRPHYCAPBLPEN BIT(10)
|
|
+#define RCC_DDRITFCR_DDRCAPBRST BIT(14)
|
|
+#define RCC_DDRITFCR_DDRCAXIRST BIT(15)
|
|
+#define RCC_DDRITFCR_DDRCORERST BIT(16)
|
|
+#define RCC_DDRITFCR_DPHYAPBRST BIT(17)
|
|
+#define RCC_DDRITFCR_DPHYRST BIT(18)
|
|
+#define RCC_DDRITFCR_DPHYCTLRST BIT(19)
|
|
+#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK_32(22, 20)
|
|
+#define RCC_DDRITFCR_DDRCKMOD_SHIFT 20
|
|
+#define RCC_DDRITFCR_DDRCKMOD_SSR 0
|
|
+#define RCC_DDRITFCR_DDRCKMOD_ASR1 BIT(20)
|
|
+#define RCC_DDRITFCR_DDRCKMOD_HSR1 BIT(21)
|
|
+#define RCC_DDRITFCR_GSKPCTRL BIT(24)
|
|
+
|
|
+/* Fields of RCC_HSICFGR register */
|
|
+#define RCC_HSICFGR_HSIDIV_MASK GENMASK_32(1, 0)
|
|
+#define RCC_HSICFGR_HSITRIM_SHIFT 8
|
|
+#define RCC_HSICFGR_HSITRIM_MASK GENMASK_32(14, 8)
|
|
+#define RCC_HSICFGR_HSICAL_SHIFT 16
|
|
+#define RCC_HSICFGR_HSICAL_MASK GENMASK_32(27, 16)
|
|
+
|
|
+/* Fields of RCC_CSICFGR register */
|
|
+#define RCC_CSICFGR_CSITRIM_SHIFT 8
|
|
+#define RCC_CSICFGR_CSITRIM_MASK GENMASK_32(12, 8)
|
|
+#define RCC_CSICFGR_CSICAL_SHIFT 16
|
|
+#define RCC_CSICFGR_CSICAL_MASK GENMASK_32(23, 16)
|
|
+
|
|
+/* Used for RCC_MCO related operations */
|
|
+#define RCC_MCOCFG_MCOON BIT(12)
|
|
+#define RCC_MCOCFG_MCODIV_MASK GENMASK_32(7, 4)
|
|
+#define RCC_MCOCFG_MCODIV_SHIFT 4
|
|
+#define RCC_MCOCFG_MCOSRC_MASK GENMASK_32(2, 0)
|
|
+
|
|
+/* Fields of RCC_DBGCFGR register */
|
|
+#define RCC_DBGCFGR_DBGCKEN BIT(8)
|
|
+
|
|
+/* RCC register fields for reset reasons */
|
|
+#define RCC_MP_RSTSCLRR_PORRSTF BIT(0)
|
|
+#define RCC_MP_RSTSCLRR_BORRSTF BIT(1)
|
|
+#define RCC_MP_RSTSCLRR_PADRSTF BIT(2)
|
|
+#define RCC_MP_RSTSCLRR_HCSSRSTF BIT(3)
|
|
+#define RCC_MP_RSTSCLRR_VCORERSTF BIT(4)
|
|
+#define RCC_MP_RSTSCLRR_MPSYSRSTF BIT(6)
|
|
+#define RCC_MP_RSTSCLRR_MCSYSRSTF BIT(7)
|
|
+#define RCC_MP_RSTSCLRR_IWDG1RSTF BIT(8)
|
|
+#define RCC_MP_RSTSCLRR_IWDG2RSTF BIT(9)
|
|
+#define RCC_MP_RSTSCLRR_STDBYRSTF BIT(11)
|
|
+#define RCC_MP_RSTSCLRR_CSTDBYRSTF BIT(12)
|
|
+
|
|
+/* Global Reset Register */
|
|
+#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0)
|
|
+#define RCC_MP_GRSTCSETR_MCURST BIT(1)
|
|
+#define RCC_MP_GRSTCSETR_MPUP0RST BIT(4)
|
|
+#define RCC_MP_GRSTCSETR_MPUP1RST BIT(5)
|
|
+
|
|
+/* Clock Source Interrupt Flag Register */
|
|
+#define RCC_MP_CIFR_LSIRDYF BIT(0)
|
|
+#define RCC_MP_CIFR_LSERDYF BIT(1)
|
|
+#define RCC_MP_CIFR_HSIRDYF BIT(2)
|
|
+#define RCC_MP_CIFR_HSERDYF BIT(3)
|
|
+#define RCC_MP_CIFR_CSIRDYF BIT(4)
|
|
+#define RCC_MP_CIFR_PLL1DYF BIT(8)
|
|
+#define RCC_MP_CIFR_PLL2DYF BIT(9)
|
|
+#define RCC_MP_CIFR_PLL3DYF BIT(10)
|
|
+#define RCC_MP_CIFR_PLL4DYF BIT(11)
|
|
+#define RCC_MP_CIFR_LSECSSF BIT(16)
|
|
+#define RCC_MP_CIFR_WKUPF BIT(20)
|
|
+#define RCC_MP_CIFR_MASK (RCC_MP_CIFR_LSIRDYF | RCC_MP_CIFR_LSERDYF | \
|
|
+ RCC_MP_CIFR_HSIRDYF | RCC_MP_CIFR_HSERDYF | \
|
|
+ RCC_MP_CIFR_CSIRDYF | RCC_MP_CIFR_PLL1DYF | \
|
|
+ RCC_MP_CIFR_PLL2DYF | RCC_MP_CIFR_PLL3DYF | \
|
|
+ RCC_MP_CIFR_PLL4DYF | RCC_MP_CIFR_LSECSSF | \
|
|
+ RCC_MP_CIFR_WKUPF)
|
|
+
|
|
+/* Stop Request Set Register */
|
|
+#define RCC_MP_SREQSETR_STPREQ_P0 BIT(0)
|
|
+#define RCC_MP_SREQSETR_STPREQ_P1 BIT(1)
|
|
+
|
|
+/* Stop Request Clear Register */
|
|
+#define RCC_MP_SREQCLRR_STPREQ_P0 BIT(0)
|
|
+#define RCC_MP_SREQCLRR_STPREQ_P1 BIT(1)
|
|
+
|
|
+/* RCC_MP_APB5RST(SET|CLR)R bit fields */
|
|
+#define RCC_APB5RSTSETR_SPI6RST BIT(0)
|
|
+#define RCC_APB5RSTSETR_I2C4RST BIT(2)
|
|
+#define RCC_APB5RSTSETR_I2C6RST BIT(3)
|
|
+#define RCC_APB5RSTSETR_USART1RST BIT(4)
|
|
+#define RCC_APB5RSTSETR_STGENRST BIT(20)
|
|
+
|
|
+/* RCC_MP_APB5EN(SET|CLR)R bit fields */
|
|
+#define RCC_MP_APB5ENSETR_SPI6EN_POS 0
|
|
+#define RCC_MP_APB5ENSETR_I2C4EN_POS 2
|
|
+#define RCC_MP_APB5ENSETR_I2C6EN_POS 3
|
|
+#define RCC_MP_APB5ENSETR_USART1EN_POS 4
|
|
+#define RCC_MP_APB5ENSETR_RTCAPBEN_POS 8
|
|
+#define RCC_MP_APB5ENSETR_TZC1EN_POS 11
|
|
+#define RCC_MP_APB5ENSETR_TZC2EN_POS 12
|
|
+#define RCC_MP_APB5ENSETR_TZPCEN_POS 13
|
|
+#define RCC_MP_APB5ENSETR_IWDG1APBEN_POS 15
|
|
+#define RCC_MP_APB5ENSETR_BSECEN_POS 16
|
|
+#define RCC_MP_APB5ENSETR_STGENEN_POS 20
|
|
+
|
|
+#define RCC_MP_APB5ENSETR_SPI6EN BIT(RCC_MP_APB5ENSETR_SPI6EN_POS)
|
|
+#define RCC_MP_APB5ENSETR_I2C4EN BIT(RCC_MP_APB5ENSETR_I2C4EN_POS)
|
|
+#define RCC_MP_APB5ENSETR_I2C6EN BIT(RCC_MP_APB5ENSETR_I2C6EN_POS)
|
|
+#define RCC_MP_APB5ENSETR_USART1EN BIT(RCC_MP_APB5ENSETR_USART1EN_POS)
|
|
+#define RCC_MP_APB5ENSETR_RTCAPBEN BIT(RCC_MP_APB5ENSETR_RTCAPBEN_POS)
|
|
+#define RCC_MP_APB5ENSETR_TZC1EN BIT(RCC_MP_APB5ENSETR_TZC1EN_POS)
|
|
+#define RCC_MP_APB5ENSETR_TZC2EN BIT(RCC_MP_APB5ENSETR_TZC2EN_POS)
|
|
+#define RCC_MP_APB5ENSETR_TZPCEN BIT(RCC_MP_APB5ENSETR_TZPCEN_POS)
|
|
+#define RCC_MP_APB5ENSETR_IWDG1APBEN BIT(RCC_MP_APB5ENSETR_IWDG1APBEN_POS)
|
|
+#define RCC_MP_APB5ENSETR_BSECEN BIT(RCC_MP_APB5ENSETR_BSECEN_POS)
|
|
+#define RCC_MP_APB5ENSETR_STGENEN BIT(RCC_MP_APB5ENSETR_STGENEN_POS)
|
|
+
|
|
+/* RCC_MP_APB5LPEN(SET|CLR)R bit fields */
|
|
+#define RCC_MP_APB5LPENSETR_SPI6LPEN BIT(0)
|
|
+#define RCC_MP_APB5LPENSETR_I2C4LPEN BIT(2)
|
|
+#define RCC_MP_APB5LPENSETR_I2C6LPEN BIT(3)
|
|
+#define RCC_MP_APB5LPENSETR_USART1LPEN BIT(4)
|
|
+#define RCC_MP_APB5LPENSETR_RTCAPBLPEN BIT(8)
|
|
+#define RCC_MP_APB5LPENSETR_TZC1LPEN BIT(11)
|
|
+#define RCC_MP_APB5LPENSETR_TZC2LPEN BIT(12)
|
|
+#define RCC_MP_APB5LPENSETR_TZPCLPEN BIT(13)
|
|
+#define RCC_MP_APB5LPENSETR_IWDG1APBLPEN BIT(15)
|
|
+#define RCC_MP_APB5LPENSETR_BSECLPEN BIT(16)
|
|
+#define RCC_MP_APB5LPENSETR_STGENLPEN BIT(20)
|
|
+#define RCC_MP_APB5LPENSETR_STGENSTPEN BIT(21)
|
|
+
|
|
+/* RCC_MP_AHB5RST(SET|CLR)R bit fields */
|
|
+#define RCC_AHB5RSTSETR_GPIOZRST BIT(0)
|
|
+#define RCC_AHB5RSTSETR_CRYP1RST BIT(4)
|
|
+#define RCC_AHB5RSTSETR_HASH1RST BIT(5)
|
|
+#define RCC_AHB5RSTSETR_RNG1RST BIT(6)
|
|
+#define RCC_AHB5RSTSETR_AXIMCRST BIT(16)
|
|
+
|
|
+/* RCC_MP_AHB5EN(SET|CLR)R bit fields */
|
|
+#define RCC_MP_AHB5ENSETR_GPIOZEN_POS 0
|
|
+#define RCC_MP_AHB5ENSETR_CRYP1EN_POS 4
|
|
+#define RCC_MP_AHB5ENSETR_HASH1EN_POS 5
|
|
+#define RCC_MP_AHB5ENSETR_RNG1EN_POS 6
|
|
+#define RCC_MP_AHB5ENSETR_BKPSRAMEN_POS 8
|
|
+#define RCC_MP_AHB5ENSETR_AXIMCEN_POS 16
|
|
+
|
|
+#define RCC_MP_AHB5ENSETR_GPIOZEN BIT(RCC_MP_AHB5ENSETR_GPIOZEN_POS)
|
|
+#define RCC_MP_AHB5ENSETR_CRYP1EN BIT(RCC_MP_AHB5ENSETR_CRYP1EN_POS)
|
|
+#define RCC_MP_AHB5ENSETR_HASH1EN BIT(RCC_MP_AHB5ENSETR_HASH1EN_POS)
|
|
+#define RCC_MP_AHB5ENSETR_RNG1EN BIT(RCC_MP_AHB5ENSETR_RNG1EN_POS)
|
|
+#define RCC_MP_AHB5ENSETR_BKPSRAMEN BIT(RCC_MP_AHB5ENSETR_BKPSRAMEN_POS)
|
|
+#define RCC_MP_AHB5ENSETR_AXIMCEN BIT(RCC_MP_AHB5ENSETR_AXIMCEN_POS)
|
|
+
|
|
+/* RCC_MP_AHB5LPEN(SET|CLR)R bit fields */
|
|
+#define RCC_MP_AHB5LPENSETR_GPIOZLPEN BIT(0)
|
|
+#define RCC_MP_AHB5LPENSETR_CRYP1LPEN BIT(4)
|
|
+#define RCC_MP_AHB5LPENSETR_HASH1LPEN BIT(5)
|
|
+#define RCC_MP_AHB5LPENSETR_RNG1LPEN BIT(6)
|
|
+#define RCC_MP_AHB5LPENSETR_BKPSRAMLPEN BIT(8)
|
|
+
|
|
+/* RCC_MP_IWDGFZ(SET|CLR)R bit fields */
|
|
+#define RCC_MP_IWDGFZSETR_IWDG1 BIT(0)
|
|
+#define RCC_MP_IWDGFZSETR_IWDG2 BIT(1)
|
|
+
|
|
+#ifndef ASM
|
|
+uintptr_t stm32_rcc_base(void);
|
|
+void stm32_rcc_secure(int enable);
|
|
+bool stm32_rcc_is_secure(void);
|
|
+bool stm32_rcc_is_mckprot(void);
|
|
+#endif
|
|
+
|
|
+#endif /*__STM32MP1_RCC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/drivers/sub.mk b/core/arch/arm/plat-stm32mp1/drivers/sub.mk
|
|
new file mode 100644
|
|
index 0000000..c668efa
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/drivers/sub.mk
|
|
@@ -0,0 +1,8 @@
|
|
+srcs-y += stm32mp1_pwr.c
|
|
+srcs-y += stm32mp1_rcc.c
|
|
+srcs-$(CFG_STM32_CLOCKSRC_CALIB) += stm32mp1_calib.c
|
|
+srcs-y += stm32_reset.c
|
|
+srcs-$(CFG_STPMIC1) += stm32mp1_pmic.c
|
|
+srcs-y += stm32mp1_clk.c
|
|
+srcs-$(CFG_DT) += stm32mp1_clkfunc.c
|
|
+srcs-y += stm32mp1_ddrc.c
|
|
diff --git a/core/arch/arm/plat-stm32mp1/link.mk b/core/arch/arm/plat-stm32mp1/link.mk
|
|
index 97e22ea..01a9b8e 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/link.mk
|
|
+++ b/core/arch/arm/plat-stm32mp1/link.mk
|
|
@@ -11,14 +11,14 @@ endef
|
|
all: $(link-out-dir)/tee-header_v2.stm32
|
|
cleanfiles += $(link-out-dir)/tee-header_v2.stm32
|
|
$(link-out-dir)/tee-header_v2.stm32: $(link-out-dir)/tee-header_v2.bin
|
|
- $(stm32image_cmd) --source $< --dest $@
|
|
+ $(stm32image_cmd) --source $< --dest $@ --bintype 0x20
|
|
|
|
all: $(link-out-dir)/tee-pager_v2.stm32
|
|
cleanfiles += $(link-out-dir)/tee-pager_v2.stm32
|
|
$(link-out-dir)/tee-pager_v2.stm32: $(link-out-dir)/tee-pager_v2.bin
|
|
- $(stm32image_cmd) --source $< --dest $@
|
|
+ $(stm32image_cmd) --source $< --dest $@ --bintype 0x21
|
|
|
|
all: $(link-out-dir)/tee-pageable_v2.stm32
|
|
cleanfiles += $(link-out-dir)/tee-pageable_v2.stm32
|
|
$(link-out-dir)/tee-pageable_v2.stm32: $(link-out-dir)/tee-pageable_v2.bin
|
|
- $(stm32image_cmd) --source $< --dest $@
|
|
+ $(stm32image_cmd) --source $< --dest $@ --bintype 0x22
|
|
diff --git a/core/arch/arm/plat-stm32mp1/main.c b/core/arch/arm/plat-stm32mp1/main.c
|
|
index f45e3cd..1921678 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/main.c
|
|
+++ b/core/arch/arm/plat-stm32mp1/main.c
|
|
@@ -4,32 +4,105 @@
|
|
* Copyright (c) 2016-2018, Linaro Limited
|
|
*/
|
|
|
|
+#include <arm.h>
|
|
#include <boot_api.h>
|
|
#include <console.h>
|
|
#include <drivers/gic.h>
|
|
+#include <drivers/stm32_bsec.h>
|
|
+#include <drivers/stm32_etzpc.h>
|
|
+#include <drivers/stm32_iwdg.h>
|
|
#include <drivers/stm32_uart.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <dt-bindings/etzpc/stm32-etzpc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
#include <kernel/generic_boot.h>
|
|
+#include <kernel/interrupt.h>
|
|
#include <kernel/misc.h>
|
|
#include <kernel/panic.h>
|
|
#include <kernel/pm_stubs.h>
|
|
+#include <kernel/spinlock.h>
|
|
+#include <mm/core_mmu.h>
|
|
#include <mm/core_memprot.h>
|
|
#include <platform_config.h>
|
|
+#include <sm/optee_smc.h>
|
|
#include <sm/psci.h>
|
|
+#include <tee/arch_svc.h>
|
|
#include <tee/entry_std.h>
|
|
#include <tee/entry_fast.h>
|
|
+#include <stdint.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <string.h>
|
|
+#include <trace.h>
|
|
+#include <util.h>
|
|
|
|
-register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, CONSOLE_UART_SIZE);
|
|
+#ifdef CFG_WITH_NSEC_GPIOS
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, GPIOS_NSEC_BASE, GPIOS_NSEC_SIZE);
|
|
+#endif
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, RNG1_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, IWDG1_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, IWDG2_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, RTC_BASE, SMALL_PAGE_SIZE);
|
|
+#ifdef CFG_WITH_NSEC_UARTS
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, USART1_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, USART2_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, USART3_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, UART4_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, UART5_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, USART6_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, UART7_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_NSEC, UART8_BASE, SMALL_PAGE_SIZE);
|
|
+#endif
|
|
|
|
-register_phys_mem(MEM_AREA_IO_SEC, GIC_BASE, GIC_SIZE);
|
|
-register_phys_mem(MEM_AREA_IO_SEC, BKP_REGS_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, GIC_IOMEM_BASE, GIC_IOMEM_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, TAMP_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, RCC_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, PWR_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, ETZPC_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, BSEC_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, I2C4_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, STGEN_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, GPIOZ_BASE,
|
|
+ STM32MP1_GPIOZ_MAX_COUNT * SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, RNG1_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, IWDG1_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, RTC_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, DDRCTRL_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, DDRPHYC_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, BKPSRAM_BASE, SMALL_PAGE_SIZE);
|
|
+register_phys_mem(MEM_AREA_IO_SEC, USART1_BASE, SMALL_PAGE_SIZE);
|
|
|
|
-static struct gic_data gic_data;
|
|
-static struct console_pdata console_data;
|
|
+register_phys_mem(MEM_AREA_ROM_SEC, TEE_RAM_START, TEE_RAM_PH_SIZE);
|
|
|
|
-static void main_fiq(void)
|
|
+register_ddr(DDR_BASE, STM32MP1_DDR_SIZE_DFLT);
|
|
+
|
|
+#ifdef CFG_STM32MP_MAP_NSEC_LOW_DDR
|
|
+register_phys_mem(MEM_AREA_RAM_NSEC, DDR_BASE, SMALL_PAGE_SIZE);
|
|
+#endif
|
|
+
|
|
+#define _ID2STR(id) (#id)
|
|
+#define ID2STR(id) _ID2STR(id)
|
|
+
|
|
+static TEE_Result platform_banner(void)
|
|
{
|
|
- gic_it_handle(&gic_data);
|
|
+#ifdef CFG_DT
|
|
+ IMSG("Platform stm32mp1: flavor %s - device tree %s",
|
|
+ ID2STR(PLATFORM_FLAVOR), ID2STR(CFG_SECURE_DT));
|
|
+ IMSG("Model: %s", fdt_get_board_model(get_dt_blob()));
|
|
+#else
|
|
+ IMSG("Platform stm32mp1: flavor %s - no device tree",
|
|
+ ID2STR(PLATFORM_FLAVOR));
|
|
+#endif
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
}
|
|
+service_init(platform_banner);
|
|
+
|
|
+static void main_fiq(void);
|
|
|
|
static const struct thread_handlers handlers = {
|
|
.std_smc = tee_entry_std,
|
|
@@ -48,10 +121,132 @@ const struct thread_handlers *generic_boot_get_handlers(void)
|
|
return &handlers;
|
|
}
|
|
|
|
+/*
|
|
+ * Console
|
|
+ *
|
|
+ * We cannot use the generic serial_console support since probing
|
|
+ * the console requires the platform clock driver to be already
|
|
+ * up and ready which is done only once service_init are completed.
|
|
+ *
|
|
+ * If the console uses a non secure UART, its clock might have been
|
|
+ * disabled by the non secure world, in which case secure traces must
|
|
+ * be dropped unless what the driver may stall since the UART FIFO is
|
|
+ * never emptied.
|
|
+ */
|
|
+#define CONSOLE_WITHOUT_CLOCK_MAGIC ~0UL
|
|
+
|
|
+static struct stm32_uart_pdata console_data;
|
|
+static struct serial_chip *serial_console;
|
|
+
|
|
void console_init(void)
|
|
{
|
|
- stm32_uart_init(&console_data, CONSOLE_UART_BASE);
|
|
+ /* Early console initialization before MMU setup */
|
|
+ struct uart {
|
|
+ uintptr_t pa;
|
|
+ bool secure;
|
|
+ } uarts[] = {
|
|
+ [0] = { .pa = 0 },
|
|
+ [1] = { .pa = USART1_BASE, .secure = true, },
|
|
+ [2] = { .pa = USART2_BASE, .secure = false, },
|
|
+ [3] = { .pa = USART3_BASE, .secure = false, },
|
|
+ [4] = { .pa = UART4_BASE, .secure = false, },
|
|
+ [5] = { .pa = UART5_BASE, .secure = false, },
|
|
+ [6] = { .pa = USART6_BASE, .secure = false, },
|
|
+ [7] = { .pa = UART7_BASE, .secure = false, },
|
|
+ [8] = { .pa = UART8_BASE, .secure = false, },
|
|
+ };
|
|
+
|
|
+ COMPILE_TIME_ASSERT(CFG_STM32_EARLY_CONSOLE_UART < ARRAY_SIZE(uarts));
|
|
+ assert(!cpu_mmu_enabled());
|
|
+
|
|
+ if (!uarts[CFG_STM32_EARLY_CONSOLE_UART].pa) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* No clock/PINCTRL yet bound to the UART console */
|
|
+ console_data.clock = CONSOLE_WITHOUT_CLOCK_MAGIC;
|
|
+
|
|
+ console_data.secure = uarts[CFG_STM32_EARLY_CONSOLE_UART].secure;
|
|
+ stm32_uart_init(&console_data, uarts[CFG_STM32_EARLY_CONSOLE_UART].pa);
|
|
+
|
|
+#ifdef CFG_FORCE_CONSOLE_ON_SUSPEND
|
|
+ serial_console = &console_data.chip;
|
|
+#else
|
|
register_serial_console(&console_data.chip);
|
|
+#endif
|
|
+
|
|
+ IMSG("Early console on UART#%u", CFG_STM32_EARLY_CONSOLE_UART);
|
|
+}
|
|
+
|
|
+#ifdef CFG_FORCE_CONSOLE_ON_SUSPEND
|
|
+void console_putc(int ch)
|
|
+{
|
|
+ if (!serial_console)
|
|
+ return;
|
|
+
|
|
+ stm32_pinctrl_load_active_cfg(console_data.pinctrl,
|
|
+ console_data.pinctrl_count);
|
|
+
|
|
+ if (ch == '\n')
|
|
+ serial_console->ops->putc(serial_console, '\r');
|
|
+
|
|
+ serial_console->ops->putc(serial_console, ch);
|
|
+}
|
|
+
|
|
+void console_flush(void)
|
|
+{
|
|
+ if (!serial_console)
|
|
+ return;
|
|
+
|
|
+ serial_console->ops->flush(serial_console);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if CFG_DT
|
|
+/* Probe console once clocks inits (service_init level) are completed */
|
|
+static TEE_Result stm32_uart_console_probe(void)
|
|
+{
|
|
+ void *fdt;
|
|
+ int node;
|
|
+ struct stm32_uart_pdata *pd = NULL;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ node = fdt_get_stdout_node_offset(fdt);
|
|
+ if (node >= 0) {
|
|
+ pd = probe_uart_from_dt_node(fdt, node);
|
|
+ }
|
|
+
|
|
+ if (pd) {
|
|
+ serial_console = NULL;
|
|
+ dmb();
|
|
+ memcpy(&console_data, pd, sizeof(*pd));
|
|
+ dmb();
|
|
+ serial_console = &console_data.chip;
|
|
+ free(pd);
|
|
+ IMSG("UART console probed from DT (%ssecure)",
|
|
+ pd->secure ? "" : "non ");
|
|
+ } else {
|
|
+ IMSG("No UART console probed from DT");
|
|
+ serial_console = NULL;
|
|
+ }
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+service_init_late(stm32_uart_console_probe);
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * GIC init, used also for primary/secondary boot core wake completion
|
|
+ */
|
|
+static struct gic_data gic_data;
|
|
+
|
|
+static void main_fiq(void)
|
|
+{
|
|
+ gic_it_handle(&gic_data);
|
|
}
|
|
|
|
void main_init_gic(void)
|
|
@@ -59,75 +254,378 @@ void main_init_gic(void)
|
|
void *gicc_base;
|
|
void *gicd_base;
|
|
|
|
- gicc_base = phys_to_virt(GIC_BASE + GICC_OFFSET, MEM_AREA_IO_SEC);
|
|
- gicd_base = phys_to_virt(GIC_BASE + GICD_OFFSET, MEM_AREA_IO_SEC);
|
|
+ gicc_base = phys_to_virt(GIC_IOMEM_BASE + GICC_OFFSET, MEM_AREA_IO_SEC);
|
|
+ gicd_base = phys_to_virt(GIC_IOMEM_BASE + GICD_OFFSET, MEM_AREA_IO_SEC);
|
|
if (!gicc_base || !gicd_base)
|
|
panic();
|
|
|
|
gic_init(&gic_data, (vaddr_t)gicc_base, (vaddr_t)gicd_base);
|
|
itr_init(&gic_data.chip);
|
|
+
|
|
+#ifdef CFG_PSCI_ARM32
|
|
+ stm32mp_register_online_cpu();
|
|
+#endif
|
|
}
|
|
|
|
void main_secondary_init_gic(void)
|
|
{
|
|
gic_cpu_init(&gic_data);
|
|
+
|
|
+#if CFG_TEE_CORE_NB_CORE == 2
|
|
+ /* Secondary core release constraint on APB5 clock */
|
|
+ write32(BOOT_API_A7_RESET_MAGIC_NUMBER,
|
|
+ stm32mp_bkpreg(BCKR_CORE1_MAGIC_NUMBER));
|
|
+ stm32mp1_clk_disable_secure(RTCAPB);
|
|
+#endif
|
|
+
|
|
+#ifdef CFG_PSCI_ARM32
|
|
+ stm32mp_register_online_cpu();
|
|
+#endif
|
|
+}
|
|
+
|
|
+/* Specific GIC suspend/resume function called aside registered PM handlers */
|
|
+void stm32mp_gic_suspend_resume(enum pm_op op)
|
|
+{
|
|
+ switch (op) {
|
|
+ case PM_OP_SUSPEND:
|
|
+ gic_suspend(&gic_data);
|
|
+ break;
|
|
+ case PM_OP_RESUME:
|
|
+ gic_resume(&gic_data);
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+KEEP_PAGER(stm32mp_gic_suspend_resume);
|
|
+
|
|
+/* stm32mp1 low power sequence needs straight access to GIC */
|
|
+uintptr_t get_gicc_base(void)
|
|
+{
|
|
+ uintptr_t pbase = GIC_IOMEM_BASE + GICC_OFFSET;
|
|
+
|
|
+ if (cpu_mmu_enabled())
|
|
+ return (uintptr_t)phys_to_virt(pbase, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return pbase;
|
|
+}
|
|
+
|
|
+uintptr_t get_gicd_base(void)
|
|
+{
|
|
+ uintptr_t pbase = GIC_IOMEM_BASE + GICD_OFFSET;
|
|
+
|
|
+ if (cpu_mmu_enabled())
|
|
+ return (uintptr_t)phys_to_virt(pbase, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return pbase;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Various platform specific functions
|
|
+ */
|
|
+unsigned int stm32mp_get_otp_max(void)
|
|
+{
|
|
+ return STM32MP1_OTP_MAX_ID;
|
|
+}
|
|
+unsigned int stm32mp_get_otp_upper_start(void)
|
|
+{
|
|
+ return STM32MP1_UPPER_OTP_START;
|
|
+}
|
|
+
|
|
+bool __weak stm32mp_with_pmic(void)
|
|
+{
|
|
+ return false;
|
|
}
|
|
|
|
/*
|
|
- * SMP boot support and access to the mailbox
|
|
+ * SIP and other platform specific services
|
|
*/
|
|
-#define GIC_SEC_SGI_0 8
|
|
+bool sm_platform_handler(struct sm_ctx *ctx)
|
|
+{
|
|
+ uint32_t *a0 = (uint32_t *)(&ctx->nsec.r0);
|
|
+ uint32_t *a1 = (uint32_t *)(&ctx->nsec.r1);
|
|
+ uint32_t *a2 = (uint32_t *)(&ctx->nsec.r2);
|
|
+ uint32_t *a3 = (uint32_t *)(&ctx->nsec.r3);
|
|
+
|
|
+ if (!OPTEE_SMC_IS_FAST_CALL(*a0))
|
|
+ return true;
|
|
+
|
|
+ switch (OPTEE_SMC_OWNER_NUM(*a0)) {
|
|
+ case OPTEE_SMC_OWNER_SIP:
|
|
+ return stm32_sip_service(ctx, a0, a1, a2, a3);
|
|
+ case OPTEE_SMC_OWNER_OEM:
|
|
+ return stm32_oem_service(ctx, a0, a1, a2, a3);
|
|
+ default:
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
|
|
-static vaddr_t bckreg_base(void)
|
|
+/* SoC versioning util */
|
|
+uint32_t stm32mp1_dbgmcu_get_chip_version(void)
|
|
+{
|
|
+ uintptr_t base = DBGMCU_BASE;
|
|
+
|
|
+ if (cpu_mmu_enabled())
|
|
+ base = (uintptr_t)phys_to_virt(DBGMCU_BASE, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return read32(base + DBGMCU_IDC) >> 16;
|
|
+}
|
|
+
|
|
+static uintptr_t stm32_tamp_base(void)
|
|
{
|
|
static void *va;
|
|
|
|
if (!cpu_mmu_enabled())
|
|
- return BKP_REGS_BASE + BKP_REGISTER_OFF;
|
|
+ return TAMP_BASE;
|
|
|
|
if (!va)
|
|
- va = phys_to_virt(BKP_REGS_BASE + BKP_REGISTER_OFF,
|
|
- MEM_AREA_IO_SEC);
|
|
+ va = phys_to_virt(TAMP_BASE, MEM_AREA_IO_SEC);
|
|
|
|
- return (vaddr_t)va;
|
|
+ return (uintptr_t)va;
|
|
}
|
|
|
|
-static uint32_t *bckreg_address(unsigned int idx)
|
|
+static uintptr_t bkpreg_base(void)
|
|
{
|
|
- return (uint32_t *)bckreg_base() + idx;
|
|
+ return stm32_tamp_base() + TAMP_BKP_REGISTER_OFF;
|
|
}
|
|
|
|
-static void release_secondary_early_hpen(size_t pos)
|
|
+uintptr_t stm32mp_bkpreg(unsigned int idx)
|
|
{
|
|
- uint32_t *p_entry = bckreg_address(BCKR_CORE1_BRANCH_ADDRESS);
|
|
- uint32_t *p_magic = bckreg_address(BCKR_CORE1_MAGIC_NUMBER);
|
|
+ return bkpreg_base() + (idx * sizeof(uint32_t));
|
|
+}
|
|
|
|
- *p_entry = TEE_LOAD_ADDR;
|
|
- *p_magic = BOOT_API_A7_CORE1_MAGIC_NUMBER;
|
|
+uintptr_t stm32mp1_bkpsram_base(void)
|
|
+{
|
|
+ static void *va;
|
|
|
|
- dmb();
|
|
- isb();
|
|
- itr_raise_sgi(GIC_SEC_SGI_0, BIT(pos));
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return BKPSRAM_BASE;
|
|
+
|
|
+ if (!va)
|
|
+ va = phys_to_virt(BKPSRAM_BASE, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return (uintptr_t)va;
|
|
+}
|
|
+
|
|
+uintptr_t stm32_get_stgen_base(void)
|
|
+{
|
|
+ static uintptr_t va;
|
|
+
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return STGEN_BASE;
|
|
+
|
|
+ if (!va)
|
|
+ va = (uintptr_t)phys_to_virt(STGEN_BASE, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return va;
|
|
}
|
|
|
|
-int psci_cpu_on(uint32_t core_id, uint32_t entry, uint32_t context_id)
|
|
+uintptr_t stm32_get_gpio_bank_base(unsigned int bank)
|
|
{
|
|
- size_t pos = get_core_pos_mpidr(core_id);
|
|
- static bool core_is_released[CFG_TEE_CORE_NB_CORE];
|
|
+ /* Non secure banks and mapped together, same for secure banks */
|
|
+ static uintptr_t gpiox_va;
|
|
+ static uintptr_t gpioz_va;
|
|
+
|
|
+ switch (bank) {
|
|
+ case GPIO_BANK_A:
|
|
+ case GPIO_BANK_B:
|
|
+ case GPIO_BANK_C:
|
|
+ case GPIO_BANK_D:
|
|
+ case GPIO_BANK_E:
|
|
+ case GPIO_BANK_F:
|
|
+ case GPIO_BANK_G:
|
|
+ case GPIO_BANK_H:
|
|
+ case GPIO_BANK_I:
|
|
+ case GPIO_BANK_J:
|
|
+ case GPIO_BANK_K:
|
|
+ if (!gpiox_va && cpu_mmu_enabled())
|
|
+ gpiox_va = (uintptr_t)phys_to_virt(GPIOS_NSEC_BASE,
|
|
+ MEM_AREA_IO_NSEC);
|
|
|
|
- if (!pos || pos >= CFG_TEE_CORE_NB_CORE)
|
|
- return PSCI_RET_INVALID_PARAMETERS;
|
|
+ if (cpu_mmu_enabled())
|
|
+ return gpiox_va + (bank * GPIO_BANK_OFFSET);
|
|
|
|
- DMSG("core pos: %zu: ns_entry %#" PRIx32, pos, entry);
|
|
+ return GPIOS_NSEC_BASE + (bank * GPIO_BANK_OFFSET);
|
|
|
|
- if (core_is_released[pos]) {
|
|
- DMSG("core %zu already released", pos);
|
|
- return PSCI_RET_DENIED;
|
|
+ case GPIO_BANK_Z:
|
|
+ if (!gpioz_va && cpu_mmu_enabled())
|
|
+ gpioz_va = (uintptr_t)phys_to_virt(GPIOZ_BASE,
|
|
+ MEM_AREA_IO_SEC);
|
|
+
|
|
+ if (cpu_mmu_enabled())
|
|
+ return gpioz_va;
|
|
+
|
|
+ return GPIOZ_BASE;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+uint32_t stm32_get_gpio_bank_offset(unsigned int bank)
|
|
+{
|
|
+ if (bank == GPIO_BANK_Z) {
|
|
+ return 0;
|
|
+ } else {
|
|
+ return bank * GPIO_BANK_OFFSET;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Return clock ID on success, negative value on error */
|
|
+int stm32_get_gpio_bank_clock(unsigned int bank)
|
|
+{
|
|
+ switch (bank) {
|
|
+ case GPIO_BANK_A:
|
|
+ case GPIO_BANK_B:
|
|
+ case GPIO_BANK_C:
|
|
+ case GPIO_BANK_D:
|
|
+ case GPIO_BANK_E:
|
|
+ case GPIO_BANK_F:
|
|
+ case GPIO_BANK_G:
|
|
+ case GPIO_BANK_H:
|
|
+ case GPIO_BANK_I:
|
|
+ case GPIO_BANK_J:
|
|
+ case GPIO_BANK_K:
|
|
+ return (int)GPIOA + (bank - GPIO_BANK_A);
|
|
+ case GPIO_BANK_Z:
|
|
+ return (int)GPIOZ;
|
|
+ default:
|
|
+ panic();
|
|
}
|
|
- core_is_released[pos] = true;
|
|
+}
|
|
+
|
|
+int stm32mp_iwdg_irq2instance(size_t irq)
|
|
+{
|
|
+ int instance = (int)irq - STM32MP1_IRQ_IWDG1;
|
|
+
|
|
+ assert((instance >= IWDG1_INST) && (instance <= IWDG2_INST));
|
|
+ return instance;
|
|
+}
|
|
+
|
|
+size_t stm32mp_iwdg_instance2irq(int instance)
|
|
+{
|
|
+ return (size_t)(instance + STM32MP1_IRQ_IWDG1);
|
|
+}
|
|
|
|
- generic_boot_set_core_ns_entry(pos, entry, context_id);
|
|
- release_secondary_early_hpen(pos);
|
|
+unsigned int stm32mp_iwdg_iomem2instance(uintptr_t pbase)
|
|
+{
|
|
+ switch (pbase) {
|
|
+ case IWDG1_BASE:
|
|
+ return IWDG1_INST;
|
|
+ case IWDG2_BASE:
|
|
+ return IWDG2_INST;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+unsigned long stm32_get_iwdg_otp_config(uintptr_t pbase)
|
|
+{
|
|
+ unsigned int idx;
|
|
+ unsigned long iwdg_cfg = 0;
|
|
+ uint32_t otp_value;
|
|
+
|
|
+ idx = stm32mp_iwdg_iomem2instance(pbase);
|
|
+
|
|
+ if (bsec_read_otp(&otp_value, HW2_OTP))
|
|
+ panic();
|
|
+
|
|
+ if (otp_value & BIT(idx + HW2_OTP_IWDG_HW_ENABLE_SHIFT))
|
|
+ iwdg_cfg |= IWDG_HW_ENABLED;
|
|
+
|
|
+ if (!(otp_value & BIT(idx + HW2_OTP_IWDG_FZ_STOP_SHIFT)))
|
|
+ iwdg_cfg |= IWDG_ENABLE_ON_STOP;
|
|
+
|
|
+ if (!(otp_value & BIT(idx + HW2_OTP_IWDG_FZ_STANDBY_SHIFT)))
|
|
+ iwdg_cfg |= IWDG_ENABLE_ON_STANDBY;
|
|
+
|
|
+ return iwdg_cfg;
|
|
+}
|
|
+
|
|
+uintptr_t stm32mp_get_etzpc_base(void)
|
|
+{
|
|
+ return ETZPC_BASE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function allows to split bindings between platform and ETZPC
|
|
+ * HW mapping. If this conversion was done at driver level, the driver
|
|
+ * should include all supported platform bindings. ETZPC may be used on
|
|
+ * other platforms.
|
|
+ */
|
|
+enum etzpc_decprot_attributes stm32mp_etzpc_binding2decprot(uint32_t mode)
|
|
+{
|
|
+ switch (mode) {
|
|
+ case DECPROT_S_RW:
|
|
+ return TZPC_DECPROT_S_RW;
|
|
+ case DECPROT_NS_R_S_W:
|
|
+ return TZPC_DECPROT_NS_R_S_W;
|
|
+ case DECPROT_MCU_ISOLATION:
|
|
+ return TZPC_DECPROT_MCU_ISOLATION;
|
|
+ case DECPROT_NS_RW:
|
|
+ return TZPC_DECPROT_NS_RW;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef CFG_STM32_RTC
|
|
+/*******************************************************************************
|
|
+ * This function determines the number of needed RTC calendar read operations
|
|
+ * to get consistent values (1 or 2 depending of clock frequencies).
|
|
+ * If APB1 frequency is less than 7 times the RTC one, the software has to
|
|
+ * read the calendar time and date register twice.
|
|
+ * This function computes each of them and does the comparison.
|
|
+ * Returns true if read twice is needed, false else.
|
|
+ ******************************************************************************/
|
|
+bool stm32_rtc_get_read_twice(void)
|
|
+{
|
|
+ unsigned long apb1_freq;
|
|
+ unsigned long rtc_freq = 0;
|
|
+ uint32_t apb1_div;
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ /* Compute RTC frequency */
|
|
+ switch ((mmio_read_32(rcc_base + RCC_BDCR) &
|
|
+ RCC_BDCR_RTCSRC_MASK) >> RCC_BDCR_RTCSRC_SHIFT) {
|
|
+ case 1:
|
|
+ rtc_freq = stm32mp1_clk_get_rate(CK_LSE);
|
|
+ break;
|
|
+ case 2:
|
|
+ rtc_freq = stm32mp1_clk_get_rate(CK_LSI);
|
|
+ break;
|
|
+ case 3:
|
|
+ rtc_freq = stm32mp1_clk_get_rate(CK_HSE);
|
|
+ rtc_freq /= (mmio_read_32(rcc_base + RCC_RTCDIVR) &
|
|
+ RCC_DIVR_DIV_MASK) + 1U;
|
|
+ break;
|
|
+ default:
|
|
+ /* Forbidden values: consider only one needed read here */
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ /* Compute APB1 frequency */
|
|
+ apb1_div = mmio_read_32(rcc_base + RCC_APB1DIVR) & RCC_APBXDIV_MASK;
|
|
+ apb1_freq = stm32mp1_clk_get_rate(CK_MCU) >> apb1_div;
|
|
+
|
|
+ /* Compare APB1 and 7*RTC frequencies */
|
|
+ return apb1_freq < (rtc_freq * 7U);
|
|
+}
|
|
+#endif
|
|
+
|
|
+uint32_t may_spin_lock(unsigned int *lock)
|
|
+{
|
|
+ if (!lock || !cpu_mmu_enabled()) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return cpu_spin_lock_xsave(lock);
|
|
+}
|
|
+
|
|
+void may_spin_unlock(unsigned int *lock, uint32_t exceptions)
|
|
+{
|
|
+ if (!lock || !cpu_mmu_enabled()) {
|
|
+ return;
|
|
+ }
|
|
|
|
- return PSCI_RET_SUCCESS;
|
|
+ cpu_spin_unlock_xrestore(lock, exceptions);
|
|
}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/platform_config.h b/core/arch/arm/plat-stm32mp1/platform_config.h
|
|
index 6f5dd97..e8d1660 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/platform_config.h
|
|
+++ b/core/arch/arm/plat-stm32mp1/platform_config.h
|
|
@@ -8,22 +8,185 @@
|
|
|
|
#include <mm/generic_ram_layout.h>
|
|
|
|
+/* Enable/disable use of the core0 reset control from RCC */
|
|
+#undef STM32MP1_USE_MPU0_RESET
|
|
+
|
|
/* Make stacks aligned to data cache line length */
|
|
#define STACK_ALIGNMENT 32
|
|
|
|
-#define GIC_BASE 0xA0021000ul
|
|
-#define GIC_SIZE 0x2000
|
|
-#define GICC_OFFSET 0x1000
|
|
-#define GICD_OFFSET 0x0000
|
|
+#if defined(CFG_WITH_PAGER)
|
|
+#if defined(CFG_WITH_LPAE)
|
|
+/*
|
|
+ * Optimize unpaged memory size:
|
|
+ * - one table for the level2 table for overall vmem range
|
|
+ * - two tables for TEE RAM fine grain mapping [2ffc.0000 301f.ffff]
|
|
+ * - one table for internal RAMs (PM: ROMed core TEE RAM & DDR first page)
|
|
+ * - one table for a 2MByte dynamiq shared virtual memory (SHM_VASPACE)
|
|
+ */
|
|
+#define MAX_XLAT_TABLES 5
|
|
+#else
|
|
+/*
|
|
+ * Optimize unpaged memory size:
|
|
+ * - two tables for TEE RAM mapping [2ffc.0000 300f.ffff]
|
|
+ * - one table for secure internal RAMs (PM: ROMed core TEE RAM)
|
|
+ * - one table for non-secure internal RAMs (PM: DDR first page)
|
|
+ * - two tables for a 2MByte dynamiq shared virtual memory (SHM_VASPACE)
|
|
+ */
|
|
+#define MAX_XLAT_TABLES 6
|
|
+#endif /*CFG_WITH_LPAE*/
|
|
+#else
|
|
+/* Be generous, there is plenty of secure DDR */
|
|
+#define MAX_XLAT_TABLES 10
|
|
+#endif /*CFG_WITH_PAGER*/
|
|
+
|
|
+/* Expected platform default size, if not found in device tree */
|
|
+#define STM32MP1_DDR_SIZE_DFLT (1 * 1024 * 1024 * 1024)
|
|
|
|
-#define BKP_REGS_BASE 0x5C00A000
|
|
-#define BKP_REGISTER_OFF 0x100
|
|
+#define GIC_IOMEM_BASE 0xa0021000ul
|
|
+#define GIC_IOMEM_SIZE 0x00007000
|
|
+#define DDR_BASE 0xc0000000ul
|
|
+#define SYSRAM_BASE 0x2ffc0000
|
|
+#define BKPSRAM_BASE 0x54000000
|
|
|
|
+#define BSEC_BASE 0x5c005000
|
|
+#define CRYP1_BASE 0x54001000
|
|
+#define DBGMCU_BASE 0x50081000
|
|
+#define DDRCTRL_BASE 0x5a003000
|
|
+#define DDRPHYC_BASE 0x5a004000
|
|
+#define ETZPC_BASE 0x5c007000
|
|
+#define EXTI_BASE 0x5000D000
|
|
+#define GPIOA_BASE 0x50002000
|
|
+#define GPIOB_BASE 0x50003000
|
|
+#define GPIOC_BASE 0x50004000
|
|
+#define GPIOD_BASE 0x50005000
|
|
+#define GPIOE_BASE 0x50006000
|
|
+#define GPIOF_BASE 0x50007000
|
|
+#define GPIOG_BASE 0x50008000
|
|
+#define GPIOH_BASE 0x50009000
|
|
+#define GPIOI_BASE 0x5000a000
|
|
+#define GPIOJ_BASE 0x5000b000
|
|
+#define GPIOK_BASE 0x5000c000
|
|
+#define GPIOZ_BASE 0x54004000
|
|
+#define HASH1_BASE 0x54002000
|
|
+#define I2C4_BASE 0x5c002000
|
|
+#define I2C6_BASE 0x5c009000
|
|
+#define IWDG1_BASE 0x5c003000
|
|
+#define IWDG2_BASE 0x5a002000
|
|
+#define PWR_BASE 0x50001000
|
|
+#define RCC_BASE 0x50000000
|
|
+#define RNG1_BASE 0x54003000
|
|
+#define RTC_BASE 0x5c004000
|
|
+#define SPI6_BASE 0x5c001000
|
|
+#define STGEN_BASE 0x5C008000
|
|
+#define TAMP_BASE 0x5c00a000
|
|
+#define USART1_BASE 0x5c000000
|
|
+#define USART2_BASE 0x4000e000
|
|
+#define USART3_BASE 0x4000f000
|
|
#define UART4_BASE 0x40010000
|
|
-#define STM32MP1_DEBUG_USART_BASE UART4_BASE
|
|
-#define GIC_SPI_UART4 84
|
|
+#define UART5_BASE 0x40011000
|
|
+#define USART6_BASE 0x44003000
|
|
+#define UART7_BASE 0x40018000
|
|
+#define UART8_BASE 0x40019000
|
|
+#define TZC_BASE 0x5c006000
|
|
+
|
|
+/* BSEC OTP resources */
|
|
+#define STM32MP1_OTP_MAX_ID 0x5FU
|
|
+#define STM32MP1_UPPER_OTP_START 0x20U
|
|
+
|
|
+#define OTP_MAX_SIZE (STM32MP1_OTP_MAX_ID + 1U)
|
|
+
|
|
+#define DATA0_OTP 0
|
|
+#define PART_NUMBER_OTP 1
|
|
+#define MONOTONIC_OTP 4
|
|
+#define NAND_OTP 9
|
|
+#define UID0_OTP 13
|
|
+#define UID1_OTP 14
|
|
+#define UID2_OTP 15
|
|
+#define HW2_OTP 18
|
|
+
|
|
+#define DATA0_OTP_SECURED BIT(6)
|
|
+
|
|
+#define HW2_OTP_IWDG_HW_ENABLE_SHIFT 3
|
|
+#define HW2_OTP_IWDG_FZ_STOP_SHIFT 5
|
|
+#define HW2_OTP_IWDG_FZ_STANDBY_SHIFT 7
|
|
+
|
|
+/*
|
|
+ * GPIO banks: 11 non secure banks (A to K) and 1 secure bank (Z)
|
|
+ * Bank register's base address is computed from the bank ID listed here.
|
|
+ */
|
|
+#define GPIOS_NSEC_COUNT 11
|
|
+#define GPIOS_NSEC_BASE GPIOA_BASE
|
|
+#define GPIOS_NSEC_SIZE (GPIOS_NSEC_COUNT * SMALL_PAGE_SIZE)
|
|
+
|
|
+#define STM32MP1_GPIOZ_MAX_COUNT 1
|
|
+#define STM32MP1_GPIOZ_PIN_MAX_COUNT 8
|
|
+
|
|
+#define GPIO_BANK_OFFSET 0x1000U
|
|
+
|
|
+/* Bank IDs used in GPIO driver API */
|
|
+#define GPIO_BANK_A 0U
|
|
+#define GPIO_BANK_B 1U
|
|
+#define GPIO_BANK_C 2U
|
|
+#define GPIO_BANK_D 3U
|
|
+#define GPIO_BANK_E 4U
|
|
+#define GPIO_BANK_F 5U
|
|
+#define GPIO_BANK_G 6U
|
|
+#define GPIO_BANK_H 7U
|
|
+#define GPIO_BANK_I 8U
|
|
+#define GPIO_BANK_J 9U
|
|
+#define GPIO_BANK_K 10U
|
|
+#define GPIO_BANK_Z 25U
|
|
+
|
|
+/* IWDG resources */
|
|
+#define IWDG1_INST 0
|
|
+#define IWDG2_INST 1
|
|
+
|
|
+#define STM32MP1_IRQ_IWDG1 182U
|
|
+#define STM32MP1_IRQ_IWDG2 183U
|
|
+
|
|
+/* TAMP resources */
|
|
+#define TAMP_BKP_REGISTER_OFF 0x100
|
|
+
|
|
+/* RCC platform resources */
|
|
+#define RCC_WAKEUP_IT 177
|
|
+
|
|
+/* SoC revision */
|
|
+#define STM32MP1_REV_A 0x00001000
|
|
+#define STM32MP1_REV_B 0x00002000
|
|
+
|
|
+/* DBGMCU resources */
|
|
+#define DBGMCU_IDC 0x0
|
|
+
|
|
+/* BKPSRAM layout */
|
|
+#define BKPSRAM_SIZE SMALL_PAGE_SIZE
|
|
+#define BKPSRAM_PM_OFFSET 0x000
|
|
+#define BKPSRAM_PM_SIZE (BKPSRAM_PM_MAILBOX_SIZE + \
|
|
+ BKPSRAM_PM_CONTEXT_SIZE)
|
|
+
|
|
+#define BKPSRAM_PM_MAILBOX_OFFSET BKPSRAM_PM_OFFSET
|
|
+#define BKPSRAM_PM_MAILBOX_SIZE 0x100
|
|
+#define BKPSRAM_PM_CONTEXT_OFFSET (BKPSRAM_PM_MAILBOX_OFFSET + \
|
|
+ BKPSRAM_PM_MAILBOX_SIZE)
|
|
+#define BKPSRAM_PM_CONTEXT_SIZE 0x700
|
|
+
|
|
+/* SYSRAM */
|
|
+#define SYSRAM_SIZE 0x40000
|
|
+
|
|
+/* GIC resources */
|
|
+#define GICD_OFFSET 0x00000000
|
|
+#define GICC_OFFSET 0x00001000
|
|
+#define GICH_OFFSET 0x00003000
|
|
+#define GICV_OFFSET 0x00005000
|
|
+
|
|
+#define GIC_NON_SEC_SGI_0 0
|
|
+#define GIC_SEC_SGI_0 8
|
|
+#define GIC_SEC_SGI_1 9
|
|
+#define GIC_SPI_SEC_PHY_TIMER 29
|
|
+
|
|
+#define TARGET_CPU0_GIC_MASK BIT(0)
|
|
+#define TARGET_CPU1_GIC_MASK BIT(1)
|
|
+#define TARGET_CPUS_GIC_MASK GENMASK_32(CFG_TEE_CORE_NB_CORE - 1, 0)
|
|
|
|
-#define CONSOLE_UART_BASE STM32MP1_DEBUG_USART_BASE
|
|
-#define CONSOLE_UART_SIZE 1024
|
|
+#define STM32MP_GIC_PRIORITY_CSTOP 0xc0
|
|
|
|
#endif /*PLATFORM_CONFIG_H*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/context.c b/core/arch/arm/plat-stm32mp1/pm/context.c
|
|
new file mode 100644
|
|
index 0000000..2ce72b3
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/context.c
|
|
@@ -0,0 +1,510 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <arm32.h>
|
|
+#include <boot_api.h>
|
|
+#include <drivers/gic.h>
|
|
+#include <drivers/stm32_rng.h>
|
|
+#include <drivers/stm32_rtc.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_ddrc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include <generated/context_asm_defines.h>
|
|
+#include <initcall.h>
|
|
+#include <kernel/cache_helpers.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <mm/mobj.h>
|
|
+#include <platform_config.h>
|
|
+#include <stdlib.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <string.h>
|
|
+
|
|
+#include "context.h"
|
|
+#include "power.h"
|
|
+
|
|
+#define TRAINING_AREA_SIZE 64
|
|
+
|
|
+#define STANDBY_CONTEXT_MAGIC (0x00010000 + TRAINING_AREA_SIZE)
|
|
+
|
|
+/*
|
|
+ * Context saved in TEE RAM during lower power sequence.
|
|
+ * Can be allocated if to big for static allocation.
|
|
+ *
|
|
+ * @stgen_cnt_h: Upper 32bit of the STGEN counter
|
|
+ * @stgen_cnt_l: Lower 32bit of the STGEN counter
|
|
+ * @rtc: RTC time read at suspend
|
|
+ */
|
|
+struct pm_context {
|
|
+ uint32_t stgen_cnt_h;
|
|
+ uint32_t stgen_cnt_l;
|
|
+ struct stm32_rtc_calendar rtc;
|
|
+};
|
|
+
|
|
+static struct pm_context plat_ctx;
|
|
+
|
|
+/*
|
|
+ * BKPSRAM contains a mailbox used with early boot stages for resume sequence.
|
|
+ * The mailbox content data that must be restored before OP-TEE is resumed.
|
|
+ *
|
|
+ * @magic: magic value read by early boot stage for consistency
|
|
+ * @zq0cr0_zdata: DDRPHY configuration to be restored.
|
|
+ * @ddr_training_backup: DDR area saved at suspend and backed up at resume
|
|
+ */
|
|
+struct pm_mailbox {
|
|
+ uint32_t magic;
|
|
+ uint32_t core0_resume_ep;
|
|
+ uint32_t zq0cr0_zdata;
|
|
+ uint8_t ddr_training_backup[TRAINING_AREA_SIZE];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * BKPSRAM contains OP-TEE resume instruction sequence which restores
|
|
+ * TEE RAM content. The BKPSRAM contains restoration materials
|
|
+ * (key, tag) and the resume entry point in restored TEE RAM.
|
|
+ */
|
|
+static struct retram_resume_ctx *get_retram_resume_ctx(void)
|
|
+{
|
|
+ uintptr_t bkpsram_base = stm32mp1_bkpsram_base();
|
|
+ uintptr_t context_base = bkpsram_base + BKPSRAM_PM_CONTEXT_OFFSET;
|
|
+
|
|
+ return (struct retram_resume_ctx *)context_base;
|
|
+}
|
|
+
|
|
+static struct pm_mailbox *get_pm_mailbox(void)
|
|
+{
|
|
+ uintptr_t bkpsram_base = stm32mp1_bkpsram_base();
|
|
+ uintptr_t mailbox_base = bkpsram_base + BKPSRAM_PM_MAILBOX_OFFSET;
|
|
+
|
|
+ return (struct pm_mailbox *)mailbox_base;
|
|
+}
|
|
+
|
|
+#if TRACE_LEVEL >= TRACE_DEBUG
|
|
+static void __maybe_unused dump_context(void)
|
|
+{
|
|
+ struct pm_mailbox *mailbox = get_pm_mailbox();
|
|
+ struct retram_resume_ctx *ctx = get_retram_resume_ctx();
|
|
+
|
|
+ stm32_clock_enable(RTCAPB);
|
|
+
|
|
+ DMSG("Backup registers: address 0x%" PRIx32 ", magic 0x%" PRIx32,
|
|
+ *(uint32_t *)stm32mp_bkpreg(BCKR_CORE1_BRANCH_ADDRESS),
|
|
+ *(uint32_t *)stm32mp_bkpreg(BCKR_CORE1_MAGIC_NUMBER));
|
|
+
|
|
+ stm32_clock_disable(RTCAPB);
|
|
+
|
|
+ stm32_clock_enable(BKPSRAM);
|
|
+
|
|
+ DMSG("BKPSRAM mailbox: 0x%" PRIx32 ", zd 0x%" PRIx32 ", ep 0x%" PRIx32,
|
|
+ mailbox->magic, mailbox->zq0cr0_zdata,
|
|
+ mailbox->core0_resume_ep);
|
|
+
|
|
+ DMSG("BKPSRAM context: teeram backup @%" PRIx32 ", resume @0x%" PRIx32,
|
|
+ ctx->teeram_bkp_pa, ctx->resume_pa);
|
|
+
|
|
+ stm32_clock_disable(BKPSRAM);
|
|
+}
|
|
+#else
|
|
+static void __maybe_unused dump_context(void)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Save and restore functions
|
|
+ */
|
|
+static void save_time(void)
|
|
+{
|
|
+ uintptr_t stgen = stm32_get_stgen_base();
|
|
+
|
|
+ plat_ctx.stgen_cnt_h = read32(stgen + CNTCVU_OFFSET);
|
|
+ plat_ctx.stgen_cnt_l = read32(stgen + CNTCVL_OFFSET);
|
|
+ if (plat_ctx.stgen_cnt_l < 10) {
|
|
+ plat_ctx.stgen_cnt_h = read32(stgen + CNTCVU_OFFSET);
|
|
+ }
|
|
+
|
|
+ stm32_rtc_get_calendar(&plat_ctx.rtc);
|
|
+}
|
|
+
|
|
+#if TRACE_LEVEL >= TRACE_DEBUG
|
|
+static void print_ccm_decryption_duration(void)
|
|
+{
|
|
+ uintptr_t stgen = stm32_get_stgen_base();
|
|
+ struct retram_resume_ctx *ctx = get_retram_resume_ctx();
|
|
+
|
|
+
|
|
+ stm32_clock_enable(BKPSRAM);
|
|
+
|
|
+ DMSG("CCM decryption duration %llums",
|
|
+ ((unsigned long long)ctx->stgen_cnt * 1000) /
|
|
+ mmio_read_32(stgen + CNTFID_OFFSET));
|
|
+
|
|
+ stm32_clock_enable(BKPSRAM);
|
|
+}
|
|
+#else
|
|
+static void print_ccm_decryption_duration(void)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void restore_time(void)
|
|
+{
|
|
+ struct stm32_rtc_calendar current_calendar;
|
|
+ unsigned long long stdby_time_in_ms;
|
|
+ unsigned long long cnt;
|
|
+ uintptr_t stgen = stm32_get_stgen_base();
|
|
+ struct retram_resume_ctx __maybe_unused *ctx = get_retram_resume_ctx();
|
|
+
|
|
+ stm32_rtc_get_calendar(¤t_calendar);
|
|
+ stdby_time_in_ms = stm32_rtc_diff_calendar(¤t_calendar,
|
|
+ &plat_ctx.rtc);
|
|
+
|
|
+ cnt = ((uint64_t)plat_ctx.stgen_cnt_h << 32) | plat_ctx.stgen_cnt_l;
|
|
+ cnt += (stdby_time_in_ms * mmio_read_32(stgen + CNTFID_OFFSET)) / 1000U;
|
|
+
|
|
+ mmio_clrbits_32(stgen + CNTCR_OFFSET, CNTCR_EN);
|
|
+ mmio_write_32(stgen + CNTCVL_OFFSET, (uint32_t)cnt);
|
|
+ mmio_write_32(stgen + CNTCVU_OFFSET, (uint32_t)(cnt >> 32));
|
|
+ mmio_setbits_32(stgen + CNTCR_OFFSET, CNTCR_EN);
|
|
+
|
|
+ print_ccm_decryption_duration();
|
|
+}
|
|
+
|
|
+static bool __maybe_unused pm_cb_is_valid(void (*cb)(enum pm_op op, void *hdl),
|
|
+ void *hdl)
|
|
+{
|
|
+ void *cb_voidp = (void *)(uintptr_t)cb;
|
|
+ paddr_t cb_phy = virt_to_phys(cb_voidp);
|
|
+ paddr_t hdl_phy = virt_to_phys(hdl);
|
|
+ bool __maybe_unused valid;
|
|
+
|
|
+ valid = (phys_to_virt(cb_phy, MEM_AREA_TEE_RAM_RX) == cb_voidp) &&
|
|
+ ((phys_to_virt(hdl_phy, MEM_AREA_TEE_RAM_RX) == hdl) ||
|
|
+ (phys_to_virt(hdl_phy, MEM_AREA_TEE_RAM_RO) == hdl) ||
|
|
+ (phys_to_virt(hdl_phy, MEM_AREA_TEE_RAM_RW) == hdl));
|
|
+
|
|
+ if (!valid) {
|
|
+ EMSG("pm_cb mandates unpaged arguments %p %p", cb_voidp, hdl);
|
|
+ }
|
|
+
|
|
+ return valid;
|
|
+}
|
|
+
|
|
+struct pm_cb {
|
|
+ void (*callback)(enum pm_op op, void *handle);
|
|
+ void *handle;
|
|
+};
|
|
+static struct pm_cb *pm_cb;
|
|
+static size_t pm_cb_count;
|
|
+
|
|
+void stm32mp_register_pm_cb(void (*callback)(enum pm_op op, void *handle),
|
|
+ void *handle)
|
|
+{
|
|
+ assert(pm_cb_is_valid(callback, handle));
|
|
+
|
|
+ pm_cb_count++;
|
|
+ pm_cb = realloc(pm_cb, sizeof(struct pm_cb) * pm_cb_count);
|
|
+ if (!pm_cb){
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ pm_cb[pm_cb_count - 1].callback = callback;
|
|
+ pm_cb[pm_cb_count - 1].handle = handle;
|
|
+}
|
|
+
|
|
+static void save_soc_context(void)
|
|
+{
|
|
+ const enum pm_op suspend = PM_OP_SUSPEND;
|
|
+ size_t n;
|
|
+
|
|
+ for (n = 0; n < pm_cb_count; n++) {
|
|
+ pm_cb[n].callback(suspend, pm_cb[n].handle);
|
|
+ }
|
|
+
|
|
+ /* Suspend core services */
|
|
+ stm32mp_gic_suspend_resume(suspend);
|
|
+ stm32mp_clock_suspend_resume(suspend);
|
|
+}
|
|
+
|
|
+static void restore_soc_context(void)
|
|
+{
|
|
+ const enum pm_op resume = PM_OP_RESUME;
|
|
+ size_t n;
|
|
+
|
|
+ /* Resume core services */
|
|
+ stm32mp_gic_suspend_resume(resume);
|
|
+ stm32mp_clock_suspend_resume(resume);
|
|
+
|
|
+ for (n = 0; n < pm_cb_count; n++) {
|
|
+ pm_cb[n].callback(resume, pm_cb[n].handle);
|
|
+ }
|
|
+}
|
|
+
|
|
+uintptr_t stm32mp_pm_retram_resume_ep(void)
|
|
+{
|
|
+ struct retram_resume_ctx *ctx = get_retram_resume_ctx();
|
|
+
|
|
+ return (uintptr_t)&ctx->resume_sequence;
|
|
+}
|
|
+
|
|
+/* Clear the content of the PM mailbox */
|
|
+void stm32mp_pm_wipe_context(void)
|
|
+{
|
|
+ struct retram_resume_ctx *ctx = get_retram_resume_ctx();
|
|
+ struct pm_mailbox *mailbox = get_pm_mailbox();
|
|
+
|
|
+ stm32_clock_enable(BKPSRAM);
|
|
+
|
|
+ memset(ctx, 0xa5, sizeof(*ctx));
|
|
+ memset(mailbox, 0xa5, sizeof(*mailbox));
|
|
+
|
|
+ stm32_clock_disable(BKPSRAM);
|
|
+}
|
|
+
|
|
+static struct mobj *teeram_bkp_mobj;
|
|
+
|
|
+static void init_retram_resume_resources(void)
|
|
+{
|
|
+ struct retram_resume_ctx *ctx = get_retram_resume_ctx();
|
|
+ const size_t size = (uintptr_t)stm32mp_bkpsram_image_end -
|
|
+ (uintptr_t)stm32mp_bkpsram_resume;
|
|
+ paddr_t __maybe_unused pa;
|
|
+
|
|
+ COMPILE_TIME_ASSERT(sizeof(struct pm_mailbox) <
|
|
+ BKPSRAM_PM_MAILBOX_SIZE);
|
|
+ COMPILE_TIME_ASSERT(sizeof(struct retram_resume_ctx) <
|
|
+ BKPSRAM_PM_CONTEXT_SIZE);
|
|
+ assert((sizeof(*ctx) + size) < BKPSRAM_PM_CONTEXT_SIZE);
|
|
+
|
|
+ teeram_bkp_mobj = mobj_mm_alloc(mobj_sec_ddr, TEE_RAM_PH_SIZE,
|
|
+ &tee_mm_sec_ddr);
|
|
+ if (teeram_bkp_mobj == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+ assert((mobj_get_va(teeram_bkp_mobj, 0) != NULL) &&
|
|
+ (mobj_get_pa(teeram_bkp_mobj, 0, 0, &pa) == 0));
|
|
+
|
|
+ stm32_clock_enable(BKPSRAM);
|
|
+ memset(ctx, 0, sizeof(*ctx));
|
|
+ stm32_clock_disable(BKPSRAM);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * When returning from STANDBY, the 64 first bytes of DDR will be overwritten
|
|
+ * during DDR DQS training. This area must then be saved before going to
|
|
+ * standby in the PM mailbox with the earlier boot stages.
|
|
+ */
|
|
+static void save_ddr_training_area(void)
|
|
+{
|
|
+ struct pm_mailbox *mailbox = get_pm_mailbox();
|
|
+ size_t size = sizeof(mailbox->ddr_training_backup);
|
|
+ struct mobj __maybe_unused *mobj = NULL;
|
|
+ paddr_t pa = DDR_BASE;
|
|
+ void *va = phys_to_virt(pa, MEM_AREA_RAM_NSEC);
|
|
+
|
|
+#if !defined(CFG_STM32MP_MAP_NSEC_LOW_DDR)
|
|
+ /* Config switch helps not requesting mobj_mapped_shm_alloc() unpaged */
|
|
+ if (!va) {
|
|
+ mobj = mobj_mapped_shm_alloc(&pa, SMALL_PAGE_SIZE, 0, 0);
|
|
+ va = mobj_get_va(mobj, 0);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ memcpy(&mailbox->ddr_training_backup[0], va, size);
|
|
+
|
|
+ mobj_free(mobj);
|
|
+}
|
|
+
|
|
+static void load_earlyboot_pm_mailbox(void)
|
|
+{
|
|
+ struct pm_mailbox *mailbox = get_pm_mailbox();
|
|
+
|
|
+ COMPILE_TIME_ASSERT(sizeof(struct pm_mailbox) <
|
|
+ BKPSRAM_PM_MAILBOX_SIZE);
|
|
+
|
|
+ assert(stm32_clock_is_enabled(BKPSRAM));
|
|
+
|
|
+ memset(mailbox, 0, sizeof(*mailbox));
|
|
+
|
|
+ mailbox->zq0cr0_zdata = get_ddrphy_calibration();
|
|
+
|
|
+ save_ddr_training_area();
|
|
+}
|
|
+
|
|
+#ifdef CFG_STM32_RNG
|
|
+/*
|
|
+ * CRYP relies on standard format for CCM IV/B0/CRT0 data. Our sequence uses
|
|
+ * no AAD, 4 bytes to encode the payload byte size and a 11 byte nonce.
|
|
+ */
|
|
+#define PM_CCM_Q 4
|
|
+#define PM_CCM_Q_FLAGS (PM_CCM_Q - 1)
|
|
+#define PM_CCM_TAG_LEN 16
|
|
+#define PM_CCM_TAG_FLAGS (((PM_CCM_TAG_LEN - 2) / 2) << 3)
|
|
+
|
|
+static void save_teeram_in_ddr(void)
|
|
+{
|
|
+ struct retram_resume_ctx *ctx = get_retram_resume_ctx();
|
|
+ size_t __maybe_unused size = (uintptr_t)stm32mp_bkpsram_image_end -
|
|
+ (uintptr_t)stm32mp_bkpsram_resume;
|
|
+ paddr_t pa;
|
|
+ struct ccm_unpg_ctx *ccm = &ctx->ccm_ctx;
|
|
+ void *teeram = phys_to_virt(TEE_RAM_START, MEM_AREA_ROM_SEC);
|
|
+ void *teeram_bkp = mobj_get_va(teeram_bkp_mobj, 0);
|
|
+
|
|
+ COMPILE_TIME_ASSERT(PM_CTX_CCM_KEY_SIZE == sizeof(ccm->key));
|
|
+ COMPILE_TIME_ASSERT(PM_CTX_CCM_CTR1_SIZE == sizeof(ccm->ctr1));
|
|
+ COMPILE_TIME_ASSERT(PM_CTX_CCM_B0_SIZE == sizeof(ccm->b0));
|
|
+ COMPILE_TIME_ASSERT(PM_CTX_CCM_CTR0_SIZE == sizeof(ccm->ctr0));
|
|
+ COMPILE_TIME_ASSERT(PM_CTX_CCM_TAG_SIZE == sizeof(ccm->tag));
|
|
+
|
|
+ assert(stm32_clock_is_enabled(BKPSRAM) &&
|
|
+ stm32_clock_is_enabled(CRYP1));
|
|
+
|
|
+ memcpy(ctx->resume_sequence,
|
|
+ (void *)(uintptr_t)stm32mp_bkpsram_resume, size);
|
|
+
|
|
+ memset(ctx, 0, sizeof(*ctx));
|
|
+ ctx->resume_pa = virt_to_phys((void *)(uintptr_t)stm32mp_sysram_resume);
|
|
+ if (mobj_get_pa(teeram_bkp_mobj, 0, 0, &pa)) {
|
|
+ panic();
|
|
+ }
|
|
+ ctx->teeram_bkp_pa = (uint32_t)pa;
|
|
+ ctx->cryp1_base = (uint32_t)phys_to_virt(CRYP1_BASE, MEM_AREA_IO_SEC);
|
|
+ ctx->rcc_base = (uint32_t)phys_to_virt(RCC_BASE, MEM_AREA_IO_SEC);
|
|
+ ctx->stgen_base = (uint32_t)phys_to_virt(STGEN_BASE, MEM_AREA_IO_SEC);
|
|
+
|
|
+ if (stm32_rng_read((uint8_t *)ccm->key, sizeof(ccm->key))) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ assert(((PM_CCM_TAG_FLAGS & ~0x38U) | (PM_CCM_Q_FLAGS & ~0x07U)) == 0);
|
|
+ COMPILE_TIME_ASSERT(PM_CCM_Q <= 4);
|
|
+ COMPILE_TIME_ASSERT(TEE_RAM_PH_SIZE > UINT16_MAX);
|
|
+ COMPILE_TIME_ASSERT(TEE_RAM_PH_SIZE < UINT32_MAX);
|
|
+
|
|
+ if (stm32_rng_read((uint8_t *)ccm->ctr1, sizeof(ccm->ctr1))) {
|
|
+ panic();
|
|
+ }
|
|
+ ccm->ctr1[0] &= GENMASK_32(24, 0);
|
|
+ memcpy(ccm->b0, ccm->ctr1, sizeof(ccm->b0));
|
|
+ memcpy(ccm->ctr0, ccm->ctr1, sizeof(ccm->ctr0));
|
|
+
|
|
+ ccm->ctr0[0] |= PM_CCM_Q_FLAGS << 24;
|
|
+ ccm->ctr0[3] = 0;
|
|
+ ccm->ctr1[0] |= PM_CCM_Q_FLAGS << 24;
|
|
+ ccm->ctr1[3] = 1;
|
|
+ ccm->b0[0] |= (PM_CCM_Q_FLAGS | PM_CCM_TAG_FLAGS) << 24;
|
|
+ ccm->b0[3] = TEE_RAM_PH_SIZE;
|
|
+
|
|
+ stm32mp_ccm_encrypt_teeram(ctx, teeram_bkp, teeram, TEE_RAM_PH_SIZE);
|
|
+ dcache_clean_range(teeram_bkp, TEE_RAM_PH_SIZE);
|
|
+
|
|
+ memcpy(ctx->ccm_ref_tag, ccm->tag, sizeof(ctx->ccm_ref_tag));
|
|
+
|
|
+ DMSG("CCM encryption duration %llums",
|
|
+ ((unsigned long long)ctx->stgen_cnt * 1000) /
|
|
+ mmio_read_32(ctx->stgen_base + CNTFID_OFFSET));
|
|
+ ctx->stgen_cnt = 0;
|
|
+}
|
|
+#else
|
|
+static void save_teeram_in_ddr(void)
|
|
+{
|
|
+ panic("Mandates RNG support");
|
|
+}
|
|
+#endif /*CFG_STM32_RNG*/
|
|
+
|
|
+/* Finalize the PM mailbox now that everything is loaded */
|
|
+static void enable_pm_mailbox(unsigned int suspend)
|
|
+{
|
|
+ struct pm_mailbox *mailbox = get_pm_mailbox();
|
|
+ uint32_t magic = BOOT_API_A7_CORE0_MAGIC_NUMBER;
|
|
+ uint32_t hint = 0;
|
|
+
|
|
+ assert(stm32_clock_is_enabled(BKPSRAM) &&
|
|
+ stm32_clock_is_enabled(RTCAPB));
|
|
+
|
|
+ if (suspend) {
|
|
+ hint = virt_to_phys(&get_retram_resume_ctx()->resume_sequence);
|
|
+ }
|
|
+
|
|
+ write32(magic, stm32mp_bkpreg(BCKR_CORE1_MAGIC_NUMBER));
|
|
+ write32(hint, stm32mp_bkpreg(BCKR_CORE1_BRANCH_ADDRESS));
|
|
+
|
|
+ mailbox->core0_resume_ep = hint;
|
|
+ mailbox->magic = STANDBY_CONTEXT_MAGIC;
|
|
+}
|
|
+
|
|
+static void gate_pm_context_clocks(bool enable)
|
|
+{
|
|
+ static bool clocks_enabled;
|
|
+
|
|
+ if (enable) {
|
|
+ assert(!clocks_enabled);
|
|
+ stm32_clock_enable(BKPSRAM);
|
|
+ stm32_clock_enable(RTCAPB);
|
|
+ stm32_clock_enable(CRYP1);
|
|
+ clocks_enabled = true;
|
|
+ } else {
|
|
+ /* Suspended TEE RAM state left the clocks enabled */
|
|
+ if (clocks_enabled) {
|
|
+ stm32_clock_disable(BKPSRAM);
|
|
+ stm32_clock_disable(RTCAPB);
|
|
+ stm32_clock_disable(CRYP1);
|
|
+ clocks_enabled = false;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Context (TEE RAM content + peripherals) must be restored
|
|
+ * only if system may reach STANDBY state.
|
|
+ */
|
|
+void stm32mp_pm_save_context(unsigned int soc_mode)
|
|
+{
|
|
+ save_time();
|
|
+
|
|
+ if (!need_to_backup_cpu_context(soc_mode)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ gate_pm_context_clocks(true);
|
|
+ load_earlyboot_pm_mailbox();
|
|
+ save_soc_context();
|
|
+ save_teeram_in_ddr();
|
|
+ enable_pm_mailbox(1);
|
|
+}
|
|
+
|
|
+void stm32mp_pm_restore_context(unsigned int soc_mode)
|
|
+{
|
|
+ if (need_to_backup_cpu_context(soc_mode)) {
|
|
+ restore_soc_context();
|
|
+ gate_pm_context_clocks(false);
|
|
+ }
|
|
+
|
|
+ restore_time();
|
|
+}
|
|
+
|
|
+void stm32mp_pm_shutdown_context(void)
|
|
+{
|
|
+ gate_pm_context_clocks(true);
|
|
+ load_earlyboot_pm_mailbox();
|
|
+ enable_pm_mailbox(0);
|
|
+ gate_pm_context_clocks(false);
|
|
+}
|
|
+
|
|
+static TEE_Result init_pm_support(void)
|
|
+{
|
|
+ init_retram_resume_resources();
|
|
+
|
|
+ stm32mp_pm_wipe_context();
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(init_pm_support);
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/context.h b/core/arch/arm/plat-stm32mp1/pm/context.h
|
|
new file mode 100644
|
|
index 0000000..9070fac
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/context.h
|
|
@@ -0,0 +1,100 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP_PM_CONTEXT_H__
|
|
+#define __STM32MP_PM_CONTEXT_H__
|
|
+
|
|
+#ifndef ASM
|
|
+#include <compiler.h>
|
|
+#include <stdint.h>
|
|
+#include <stddef.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#endif
|
|
+
|
|
+#define PM_CTX_CCM_KEY_SIZE 32
|
|
+#define PM_CTX_CCM_CTR1_SIZE 16
|
|
+#define PM_CTX_CCM_B0_SIZE 16
|
|
+#define PM_CTX_CCM_CTR0_SIZE 16
|
|
+#define PM_CTX_CCM_TAG_SIZE 16
|
|
+
|
|
+#ifndef ASM
|
|
+/*
|
|
+ * All materials for the CCM sequence using CRYP support are preloaded
|
|
+ * in this specific structure. Note that the sequence does not use AAD.
|
|
+ *
|
|
+ * @key: AES key material buffer
|
|
+ * @ctr1: Preformatted 128bit CTR1 block
|
|
+ * @ctr1: Preformatted 128bit B0 block
|
|
+ * @ctr1: Preformatted 128bit CTR0 block
|
|
+ * @tag: Buffer where the generated CCM tag is stored
|
|
+ */
|
|
+struct ccm_unpg_ctx {
|
|
+ uint32_t key[PM_CTX_CCM_KEY_SIZE / sizeof(uint32_t)];
|
|
+ uint32_t ctr1[PM_CTX_CCM_CTR1_SIZE / sizeof(uint32_t)];
|
|
+ uint32_t b0[PM_CTX_CCM_B0_SIZE / sizeof(uint32_t)];
|
|
+ uint32_t ctr0[PM_CTX_CCM_CTR0_SIZE / sizeof(uint32_t)];
|
|
+ uint32_t tag[PM_CTX_CCM_TAG_SIZE / sizeof(uint32_t)];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * This structure is used by pm_helpers.S at early resume from retention RAM.
|
|
+ * It is defined here and used by context_asm_defines.c to generate offset
|
|
+ * macros for the assembly implementation in pm_helpers.S.
|
|
+ *
|
|
+ * To lower the memory footprint of suspend sequence, The same function is
|
|
+ * used for encryption (executed from TEE RAM with MMU enabled) and for
|
|
+ * decryption (executed from BKPSRAM with MMU disabled). Therefore some
|
|
+ * required addresses are provided by the caller through this structure
|
|
+ * especially some SoC interface registers that are likely to have different
|
|
+ * physical and virtual addresses.
|
|
+ *
|
|
+ * @resume_pa: OP-TEE resume physical entry in TEE RAM (once restored)
|
|
+ * @teeram_bkp_pa: Physical base address in TEE RAM backup in DDR
|
|
+ * @cryp1_base: Base address of the CRYP1 registers (physical or virtual)
|
|
+ * @rcc_base: Base address of the RCC registers (physical or virtual)
|
|
+ * @stgen_base: Base address of the STGEN registers (physical or virtual)
|
|
+ * @stgen_cnt: STGEN cycle counter backup cell and measure of cycles spent
|
|
+ * @ccm_ref_tag: 128bit arrays storing tag generated during encryption
|
|
+ * @ccm_ctx: Structure storing CCM configuration and generated tag
|
|
+ * @resume_sequence: Code/data array for the BKPSRAM resume sequence
|
|
+ */
|
|
+struct retram_resume_ctx {
|
|
+ uint32_t resume_pa;
|
|
+ uint32_t teeram_bkp_pa;
|
|
+ uint32_t cryp1_base;
|
|
+ uint32_t rcc_base;
|
|
+ uint32_t stgen_base;
|
|
+ uint32_t stgen_cnt;
|
|
+ uint8_t ccm_ref_tag[PM_CTX_CCM_TAG_SIZE];
|
|
+ struct ccm_unpg_ctx ccm_ctx;
|
|
+ /* Last start the resume routine ARM (32bit) instructions sequence */
|
|
+ uint32_t resume_sequence[];
|
|
+};
|
|
+
|
|
+extern const uint8_t stm32mp_bkpsram_image_end[];
|
|
+void stm32mp_bkpsram_resume(void);
|
|
+void stm32mp_sysram_resume(void);
|
|
+
|
|
+void stm32mp_cpu_reset_state(void);
|
|
+
|
|
+void stm32mp_pm_save_context(unsigned int soc_mode);
|
|
+void stm32mp_pm_restore_context(unsigned int soc_mode);
|
|
+void stm32mp_pm_shutdown_context(void);
|
|
+void stm32mp_pm_wipe_context(void);
|
|
+
|
|
+int stm32mp1_set_pm_domain_state(enum stm32mp1_pm_domain domain, bool status);
|
|
+
|
|
+uint32_t stm32mp1_get_lp_soc_mode(uint32_t psci_mode);
|
|
+int stm32mp1_set_lp_deepest_soc_mode(uint32_t psci_mode, uint32_t soc_mode);
|
|
+
|
|
+uintptr_t stm32mp_pm_retram_resume_ep(void);
|
|
+
|
|
+int stm32mp_ccm_encrypt_teeram(struct retram_resume_ctx *ctx,
|
|
+ void *dst, void *src, size_t size);
|
|
+int stm32mp_ccm_decrypt_teeram(struct retram_resume_ctx *ctx,
|
|
+ void *dst, void *src, size_t size);
|
|
+#endif /*ASM*/
|
|
+
|
|
+#endif /*__STM32MP_PM_CONTEXT_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/context_asm_defines.c b/core/arch/arm/plat-stm32mp1/pm/context_asm_defines.c
|
|
new file mode 100644
|
|
index 0000000..34c797f
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/context_asm_defines.c
|
|
@@ -0,0 +1,28 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics
|
|
+ * Copyright (c) 2018, Linaro Limited
|
|
+ */
|
|
+
|
|
+#include <gen-asm-defines.h>
|
|
+
|
|
+#include "context.h"
|
|
+
|
|
+#define OFFSET_OF_CTX_STRUCT(_f) offsetof(struct retram_resume_ctx, _f)
|
|
+#define OFFSET_OF_CMM_CTX_STRUCT(_f) (OFFSET_OF_CTX_STRUCT(ccm_ctx) + \
|
|
+ offsetof(struct ccm_unpg_ctx, _f))
|
|
+DEFINES
|
|
+{
|
|
+ DEFINE(PM_CTX_RESUME_PA, OFFSET_OF_CTX_STRUCT(resume_pa));
|
|
+ DEFINE(PM_CTX_TEERAM_BKP_PA, OFFSET_OF_CTX_STRUCT(teeram_bkp_pa));
|
|
+ DEFINE(PM_CTX_CRYP1_BASE, OFFSET_OF_CTX_STRUCT(cryp1_base));
|
|
+ DEFINE(PM_CTX_RCC_BASE, OFFSET_OF_CTX_STRUCT(rcc_base));
|
|
+ DEFINE(PM_CTX_STGEN_BASE, OFFSET_OF_CTX_STRUCT(stgen_base));
|
|
+ DEFINE(PM_CTX_STGEN_CNT, OFFSET_OF_CTX_STRUCT(stgen_cnt));
|
|
+ DEFINE(PM_CTX_CCM_KEY, OFFSET_OF_CMM_CTX_STRUCT(key));
|
|
+ DEFINE(PM_CTX_CCM_CTR1, OFFSET_OF_CMM_CTX_STRUCT(ctr1));
|
|
+ DEFINE(PM_CTX_CCM_B0, OFFSET_OF_CMM_CTX_STRUCT(b0));
|
|
+ DEFINE(PM_CTX_CCM_CTR0, OFFSET_OF_CMM_CTX_STRUCT(ctr0));
|
|
+ DEFINE(PM_CTX_CCM_TAG, OFFSET_OF_CMM_CTX_STRUCT(tag));
|
|
+ DEFINE(PM_CTX_CCM_REF_TAG, OFFSET_OF_CTX_STRUCT(ccm_ref_tag));
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/low_power.c b/core/arch/arm/plat-stm32mp1/pm/low_power.c
|
|
new file mode 100644
|
|
index 0000000..bd28014
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/low_power.c
|
|
@@ -0,0 +1,431 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <arm.h>
|
|
+#include <assert.h>
|
|
+#include <boot_api.h>
|
|
+#include <console.h>
|
|
+#include <drivers/gic.h>
|
|
+#include <drivers/stm32_iwdg.h>
|
|
+#include <drivers/stm32_rtc.h>
|
|
+#include <drivers/stm32mp1_ddrc.h>
|
|
+#include <drivers/stm32mp1_pmic.h>
|
|
+#include <drivers/stm32mp1_pwr.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <drivers/stpmic1.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include <initcall.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/cache_helpers.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/misc.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <stdbool.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <stm32_util.h>
|
|
+#include <trace.h>
|
|
+
|
|
+#include "context.h"
|
|
+#include "power.h"
|
|
+
|
|
+static uint8_t gicd_rcc_wakeup;
|
|
+static uint8_t gicc_pmr;
|
|
+static bool ddr_in_selfrefresh;
|
|
+
|
|
+struct pwr_lp_config {
|
|
+ uint32_t pwr_cr1;
|
|
+ uint32_t pwr_mpucr;
|
|
+ const char *regul_suspend_node_name;
|
|
+};
|
|
+
|
|
+#define PWR_CR1_MASK (PWR_CR1_LPDS | PWR_CR1_LPCFG | PWR_CR1_LVDS)
|
|
+#define PWR_MPUCR_MASK (PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF | PWR_MPUCR_PDDS)
|
|
+
|
|
+static const struct pwr_lp_config config_pwr[STM32_PM_MAX_SOC_MODE] = {
|
|
+ [STM32_PM_CSLEEP_RUN] = {
|
|
+ .pwr_cr1 = 0U,
|
|
+ .pwr_mpucr = 0U,
|
|
+ .regul_suspend_node_name = NULL,
|
|
+ },
|
|
+ [STM32_PM_CSTOP_ALLOW_STOP] = {
|
|
+ .pwr_cr1 = 0U,
|
|
+ .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF,
|
|
+ .regul_suspend_node_name = NULL,
|
|
+ },
|
|
+ [STM32_PM_CSTOP_ALLOW_LP_STOP] = {
|
|
+ .pwr_cr1 = PWR_CR1_LPDS,
|
|
+ .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF,
|
|
+ .regul_suspend_node_name = "lp-stop",
|
|
+ },
|
|
+ [STM32_PM_CSTOP_ALLOW_LPLV_STOP] = {
|
|
+ .pwr_cr1 = PWR_CR1_LVDS | PWR_CR1_LPDS | PWR_CR1_LPCFG,
|
|
+ .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF,
|
|
+ .regul_suspend_node_name = "lplv-stop",
|
|
+ },
|
|
+ [STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR] = {
|
|
+ .pwr_cr1 = 0U,
|
|
+ .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF |
|
|
+ PWR_MPUCR_PDDS,
|
|
+ .regul_suspend_node_name = "standby-ddr-sr",
|
|
+ },
|
|
+ [STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF] = {
|
|
+ .pwr_cr1 = 0U,
|
|
+ .pwr_mpucr = PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF |
|
|
+ PWR_MPUCR_PDDS,
|
|
+ .regul_suspend_node_name = "standby-ddr-off",
|
|
+ },
|
|
+ [STM32_PM_SHUTDOWN] = {
|
|
+ .pwr_cr1 = 0U,
|
|
+ .pwr_mpucr = 0U,
|
|
+ .regul_suspend_node_name = "standby-ddr-off",
|
|
+ },
|
|
+};
|
|
+
|
|
+static void set_rcc_it_priority(uint8_t *it_prio, uint8_t *pmr)
|
|
+{
|
|
+ *it_prio = itr_set_ipriority(RCC_WAKEUP_IT, GIC_HIGHEST_SEC_PRIORITY);
|
|
+ *pmr = itr_set_pmr(STM32MP_GIC_PRIORITY_CSTOP);
|
|
+}
|
|
+
|
|
+static void restore_rcc_it_priority(uint8_t it_prio, uint8_t pmr)
|
|
+{
|
|
+ (void)itr_set_ipriority(RCC_WAKEUP_IT, it_prio);
|
|
+ (void)itr_set_pmr(pmr);
|
|
+}
|
|
+
|
|
+static void stm32_apply_pmic_suspend_config(uint32_t mode)
|
|
+{
|
|
+ if (stm32mp_with_pmic()) {
|
|
+ const char *name = config_pwr[mode].regul_suspend_node_name;
|
|
+
|
|
+ assert(mode < ARRAY_SIZE(config_pwr));
|
|
+ stm32mp_get_pmic();
|
|
+ stm32mp_pmic_apply_lp_config(name);
|
|
+ stm32mp_pmic_apply_boot_on_config(); // ??? should be done at wakeup only?
|
|
+ stm32mp_put_pmic();
|
|
+ }
|
|
+}
|
|
+
|
|
+#define CONSOLE_FLUSH_DELAY_MS 10
|
|
+
|
|
+#if TRACE_LEVEL >= TRACE_DEBUG
|
|
+static void wait_console_flushed(void)
|
|
+{
|
|
+ console_flush();
|
|
+ mdelay(CONSOLE_FLUSH_DELAY_MS);
|
|
+}
|
|
+#else
|
|
+static void wait_console_flushed(void)
|
|
+{
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void cpu_wfi(void)
|
|
+{
|
|
+ dsb();
|
|
+ isb();
|
|
+ wfi();
|
|
+}
|
|
+
|
|
+void stm32_pm_cpu_wfi(void)
|
|
+{
|
|
+ wait_console_flushed();
|
|
+ cpu_wfi();
|
|
+}
|
|
+
|
|
+/*If IWDG is not supported, provide a stubbed weak watchdog kicker */
|
|
+void __weak stm32_iwdg_refresh(uint32_t __unused instance)
|
|
+{
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_enter_cstop - Prepare CSTOP mode
|
|
+ *
|
|
+ * @mode - Target low power mode
|
|
+ * Return 0 if succeed to suspend, non 0 else.
|
|
+ */
|
|
+int stm32_enter_cstop(uint32_t mode)
|
|
+{
|
|
+ uint32_t pwr_cr1 = config_pwr[mode].pwr_cr1;
|
|
+ uintptr_t pwr_base = stm32_pwr_base();
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+ int rc;
|
|
+
|
|
+ stm32_apply_pmic_suspend_config(mode);
|
|
+
|
|
+ if (stm32mp_with_pmic() && (mode == STM32_PM_CSTOP_ALLOW_LP_STOP)) {
|
|
+ pwr_cr1 |= PWR_CR1_LPCFG;
|
|
+ }
|
|
+
|
|
+ /* Workaround for non secure cache issue: this should not be needed */
|
|
+ dcache_op_all(DCACHE_OP_CLEAN_INV);
|
|
+
|
|
+ /* Clear RCC interrupt before enabling it */
|
|
+ mmio_setbits_32(rcc_base + RCC_MP_CIFR, RCC_MP_CIFR_WKUPF);
|
|
+
|
|
+ /* Enable RCC Wake-up */
|
|
+ mmio_setbits_32(rcc_base + RCC_MP_CIER, RCC_MP_CIFR_WKUPF);
|
|
+
|
|
+ /* Configure low power mode */
|
|
+ mmio_clrsetbits_32(pwr_base + PWR_MPUCR_OFF, PWR_MPUCR_MASK,
|
|
+ config_pwr[mode].pwr_mpucr);
|
|
+ mmio_clrsetbits_32(pwr_base + PWR_CR1_OFF, PWR_CR1_MASK, pwr_cr1);
|
|
+
|
|
+ /* Clear RCC pending interrupt flags */
|
|
+ mmio_write_32(rcc_base + RCC_MP_CIFR, RCC_MP_CIFR_MASK);
|
|
+
|
|
+ /* Request CSTOP mode to RCC */
|
|
+ mmio_setbits_32(rcc_base + RCC_MP_SREQSETR,
|
|
+ RCC_MP_SREQSETR_STPREQ_P0 | RCC_MP_SREQSETR_STPREQ_P1);
|
|
+
|
|
+ stm32_iwdg_refresh(IWDG2_INST);
|
|
+
|
|
+ set_rcc_it_priority(&gicd_rcc_wakeup, &gicc_pmr);
|
|
+
|
|
+ rc = ddr_standby_sr_entry(NULL);
|
|
+
|
|
+ ddr_in_selfrefresh = (rc == 0);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_exit_cstop - Exit from CSTOP mode
|
|
+ */
|
|
+void stm32_exit_cstop(void)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ if (ddr_in_selfrefresh) {
|
|
+ if (ddr_sw_self_refresh_exit() != 0) {
|
|
+ panic();
|
|
+ }
|
|
+ ddr_in_selfrefresh = false;
|
|
+ }
|
|
+
|
|
+ restore_rcc_it_priority(gicd_rcc_wakeup, gicc_pmr);
|
|
+
|
|
+ /* Disable STOP request */
|
|
+ mmio_setbits_32(rcc_base + RCC_MP_SREQCLRR,
|
|
+ RCC_MP_SREQSETR_STPREQ_P0 | RCC_MP_SREQSETR_STPREQ_P1);
|
|
+
|
|
+ /* Disable RCC Wake-up */
|
|
+ mmio_clrbits_32(rcc_base + RCC_MP_CIER, RCC_MP_CIFR_WKUPF);
|
|
+
|
|
+ dsb();
|
|
+ isb();
|
|
+}
|
|
+
|
|
+static void __noreturn reset_cores(void)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+ uint32_t reset_mask;
|
|
+ uint32_t target_mask;
|
|
+
|
|
+ if (get_core_pos() == 0) {
|
|
+ reset_mask = RCC_MP_GRSTCSETR_MPUP0RST;
|
|
+ target_mask = TARGET_CPU1_GIC_MASK;
|
|
+ } else {
|
|
+ reset_mask = RCC_MP_GRSTCSETR_MPUP1RST;
|
|
+ target_mask = TARGET_CPU0_GIC_MASK;
|
|
+ }
|
|
+
|
|
+ itr_raise_sgi(GIC_SEC_SGI_1, target_mask);
|
|
+ dcache_op_all(DCACHE_OP_CLEAN_INV);
|
|
+ write32(reset_mask, rcc_base + RCC_MP_GRSTCSETR);
|
|
+ cpu_wfi();
|
|
+ panic("Cores reset");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_pm_cpus_reset - Reset only cpus
|
|
+ */
|
|
+void __noreturn stm32_cores_reset(void)
|
|
+{
|
|
+ reset_cores();
|
|
+}
|
|
+KEEP_PAGER(stm32_cores_reset);
|
|
+
|
|
+/*
|
|
+ * stm32_enter_cstop_shutdown - Shutdown CPUs to target low power mode
|
|
+ * @mode - Target low power mode
|
|
+ */
|
|
+void __noreturn stm32_enter_cstop_shutdown(uint32_t mode)
|
|
+{
|
|
+ switch (mode) {
|
|
+ case STM32_PM_SHUTDOWN:
|
|
+ if (stm32mp_with_pmic()) {
|
|
+ wait_console_flushed();
|
|
+ stm32mp_get_pmic();
|
|
+ stpmic1_switch_off();
|
|
+ udelay(100);
|
|
+ }
|
|
+ break;
|
|
+ case STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR:
|
|
+ case STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF:
|
|
+#ifdef STM32MP1_USE_MPU0_RESET
|
|
+ stm32mp_pm_shutdown_context();
|
|
+ stm32_enter_cstop(mode);
|
|
+ cpu_wfi();
|
|
+ reset_cores();
|
|
+#else
|
|
+ if (stm32mp_with_pmic()) {
|
|
+ wait_console_flushed();
|
|
+ stm32mp_get_pmic();
|
|
+ stpmic1_switch_off();
|
|
+ udelay(100);
|
|
+ }
|
|
+#endif
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ panic();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_enter_cstop_reset - Reset CPUs to target low power mode
|
|
+ * @mode - Target low power mode
|
|
+ */
|
|
+void __noreturn stm32_enter_cstop_reset(uint32_t mode)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+
|
|
+ switch (mode) {
|
|
+ case STM32_PM_SHUTDOWN:
|
|
+ write32(RCC_MP_GRSTCSETR_MPSYSRST, rcc_base + RCC_MP_GRSTCSETR);
|
|
+ udelay(100);
|
|
+ break;
|
|
+ default:
|
|
+#ifdef STM32MP1_USE_MPU0_RESET
|
|
+ reset_cores();
|
|
+#else
|
|
+ IMSG("Forced system reset");
|
|
+ wait_console_flushed();
|
|
+ write32(RCC_MP_GRSTCSETR_MPSYSRST, rcc_base + RCC_MP_GRSTCSETR);
|
|
+ udelay(100);
|
|
+#endif
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ panic();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_enter_csleep - enter CSLEEP state while WFI and exit in CRUN
|
|
+ *
|
|
+ * Configure PWR for CSLEEP state. CPU shall execute a WFI and return
|
|
+ * once a interrupt is pending.
|
|
+ */
|
|
+void stm32_enter_csleep(void)
|
|
+{
|
|
+ uintptr_t pwr_base = stm32_pwr_base();
|
|
+
|
|
+ mmio_clrsetbits_32(pwr_base + PWR_MPUCR_OFF, PWR_MPUCR_MASK,
|
|
+ config_pwr[STM32_PM_CSLEEP_RUN].pwr_mpucr);
|
|
+ mmio_clrsetbits_32(pwr_base + PWR_CR1_OFF, PWR_CR1_MASK,
|
|
+ config_pwr[STM32_PM_CSLEEP_RUN].pwr_cr1);
|
|
+
|
|
+ stm32_pm_cpu_wfi();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Secure interrupts used in the low power sequences
|
|
+ */
|
|
+#define GICC_IAR 0x00C
|
|
+#define GICC_IAR_IT_ID_MASK 0x3ff
|
|
+#define GICC_EOIR 0x010
|
|
+
|
|
+/* RCC Wakeup interrupt is used to wake from suspeneded mode */
|
|
+static enum itr_return rcc_wakeup_it_handler(struct itr_handler *hdl __unused)
|
|
+{
|
|
+ /* This interrupt is not expected to be handled */
|
|
+ panic("RCC wakeup interrupt");
|
|
+ return ITRR_HANDLED;
|
|
+}
|
|
+
|
|
+static struct itr_handler rcc_wakeup_handler = {
|
|
+ .it = RCC_WAKEUP_IT,
|
|
+ .handler = rcc_wakeup_it_handler,
|
|
+};
|
|
+KEEP_PAGER(rcc_wakeup_handler);
|
|
+
|
|
+/* SGI9 (secure SGI 1) informs targeted CPU it shall reset */
|
|
+static enum itr_return sgi9_it_handler(struct itr_handler *handler)
|
|
+{
|
|
+ uintptr_t rcc_base = stm32_rcc_base();
|
|
+ uint32_t reset_mask;
|
|
+ uintptr_t gicc_base = get_gicc_base();
|
|
+
|
|
+ write32(handler->it, gicc_base + GICC_EOIR);
|
|
+
|
|
+ if (get_core_pos() == 0) {
|
|
+ reset_mask = RCC_MP_GRSTCSETR_MPUP0RST;
|
|
+ } else {
|
|
+ reset_mask = RCC_MP_GRSTCSETR_MPUP1RST;
|
|
+ }
|
|
+
|
|
+ dcache_op_all(DCACHE_OP_CLEAN_INV);
|
|
+ write32(reset_mask, rcc_base + RCC_MP_GRSTCSETR);
|
|
+ cpu_wfi();
|
|
+ panic("Core reset");
|
|
+
|
|
+ return ITRR_HANDLED;
|
|
+}
|
|
+
|
|
+static struct itr_handler sgi9_reset_handler = {
|
|
+ .it = GIC_SEC_SGI_1,
|
|
+ .handler = sgi9_it_handler,
|
|
+};
|
|
+KEEP_PAGER(sgi9_reset_handler);
|
|
+
|
|
+static TEE_Result init_low_power(void)
|
|
+{
|
|
+ uintptr_t pwr_base = stm32_pwr_base();
|
|
+
|
|
+ itr_add(&rcc_wakeup_handler);
|
|
+ itr_enable(rcc_wakeup_handler.it);
|
|
+
|
|
+ itr_add(&sgi9_reset_handler);
|
|
+ itr_enable(sgi9_reset_handler.it);
|
|
+
|
|
+ /* Enable retention for BKPSRAM and BKPREG */
|
|
+ io_mask32(pwr_base + PWR_CR2_OFF,
|
|
+ PWR_CR2_BREN | PWR_CR2_RREN, PWR_CR2_BREN | PWR_CR2_RREN);
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+service_init(init_low_power);
|
|
+
|
|
+/*
|
|
+ * CPU low power sequences
|
|
+ */
|
|
+void __noreturn stm32_pm_cpu_power_down_wfi(void)
|
|
+{
|
|
+ if (get_core_pos() == 0) {
|
|
+ void (*reset_ep)(void) = stm32mp_sysram_resume;
|
|
+
|
|
+ wait_console_flushed();
|
|
+
|
|
+ dsb();
|
|
+ isb();
|
|
+ wfi();
|
|
+ /* STANDBY not reached: resume from retained SYSRAM */
|
|
+ stm32_exit_cstop();
|
|
+ stm32mp_cpu_reset_state();
|
|
+ reset_ep();
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ dcache_op_level1(DCACHE_OP_CLEAN);
|
|
+ write32(RCC_MP_GRSTCSETR_MPUP1RST, stm32_rcc_base() + RCC_MP_GRSTCSETR);
|
|
+ cpu_wfi();
|
|
+ panic();
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/pm_helpers.S b/core/arch/arm/plat-stm32mp1/pm/pm_helpers.S
|
|
new file mode 100644
|
|
index 0000000..91c7580
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/pm_helpers.S
|
|
@@ -0,0 +1,635 @@
|
|
+/* SPDX-License-Identifier: BSD-2-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics
|
|
+ * Copyright (c) 2017 NXP
|
|
+ * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <arm.h>
|
|
+#include <arm32_macros.S>
|
|
+#include <asm.S>
|
|
+#include <boot_api.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <generated/context_asm_defines.h>
|
|
+#include <kernel/cache_helpers.h>
|
|
+#include <kernel/tz_proc_def.h>
|
|
+#include <kernel/tz_ssvce_def.h>
|
|
+#include <kernel/unwind.h>
|
|
+#include <platform_config.h>
|
|
+#include <mm/core_mmu.h>
|
|
+#include <util.h>
|
|
+
|
|
+#include "context.h"
|
|
+
|
|
+/*
|
|
+ * Right bit shift distance to reach timeout from a 1s STGEN freq count
|
|
+ * Value N relates to 1000ms / 2^N, i.e 7 relates to 7.8125ms=~8ms
|
|
+ */
|
|
+#define CCM_TIMEOUT_128MS 2
|
|
+#define CCM_TIMEOUT_8MS 7
|
|
+#define CCM_TIMEOUT_1MS 10
|
|
+#define CCM_TIMEOUT_16US 16
|
|
+#define CCM_TIMEOUT CCM_TIMEOUT_8MS
|
|
+
|
|
+/*
|
|
+ * CRYP interface register used for AES CCM
|
|
+ */
|
|
+#define CRYP_CR 0x000
|
|
+#define CRYP_SR 0x004
|
|
+#define CRYP_DIN 0x008
|
|
+#define CRYP_DOUT 0x00c
|
|
+#define CRYP_KEYR_BASE 0x020
|
|
+#define CRYP_IVR_BASE 0x040
|
|
+
|
|
+#define CRYP_CR_ALGODIR_DECRYPT BIT(2)
|
|
+#define CRYP_CR_ALGOMODE_MASK (BIT(19) | GENMASK_32(5, 3))
|
|
+#define CRYP_CR_ALGOMODE(m) (((m & BIT(3)) << 16) | (m & 0x7) << 3)
|
|
+#define ALGOMODE_AES_CCM 0x9
|
|
+#define CRYP_CR_DATATYPE_SHIFT 6
|
|
+#define CRYP_CR_DATATYPE_8BIT (2 << CRYP_CR_DATATYPE_SHIFT)
|
|
+#define CRYP_CR_KEYSIZE_SHIFT 8
|
|
+#define CRYP_CR_KEYSIZE_256BIT (2U << CRYP_CR_KEYSIZE_SHIFT)
|
|
+#define CRYP_CR_CRYPEN BIT(15)
|
|
+#define CRYP_CR_FFLUSH BIT(14)
|
|
+#define CRYP_CR_GCM_CCMPH_SHIFT 16
|
|
+#define CRYP_CR_PHASE_MASK (0x3 << CRYP_CR_GCM_CCMPH_SHIFT)
|
|
+#define CRYP_CR_INIT_PHASE (0 << CRYP_CR_GCM_CCMPH_SHIFT)
|
|
+#define CRYP_CR_HEADER_PHASE (1 << CRYP_CR_GCM_CCMPH_SHIFT)
|
|
+#define CRYP_CR_PAYLOAD_PHASE (2 << CRYP_CR_GCM_CCMPH_SHIFT)
|
|
+#define CRYP_CR_FINAL_PHASE (3 << CRYP_CR_GCM_CCMPH_SHIFT)
|
|
+
|
|
+#define CRYP_SR_BUSY BIT(4)
|
|
+#define CRYP_SR_OFFU BIT(3)
|
|
+#define CRYP_SR_OFNE BIT(2)
|
|
+#define CRYP_SR_IFNF BIT(1)
|
|
+#define CRYP_SR_IFEM BIT(0)
|
|
+
|
|
+ /* Bound of the binary image loaded in retained memory */
|
|
+ .global stm32mp_bkpsram_image_end
|
|
+
|
|
+/*
|
|
+ * stm32mp_bkpsram_resume - Restore TEE RAM from backup memory and resume into
|
|
+ *
|
|
+ * This function executes at early resume from suspend state. It is the
|
|
+ * entrypoint of the OP-TEE provided to early boot stage when SoC wakes.
|
|
+ * This code is located in a retained memory, MMU disabled. This function
|
|
+ * shall restore TEE RAM content for OP-TEE to resume execution. Once
|
|
+ * TEE RAM is restored, this function branches to the resident resume entry
|
|
+ * point in TEE_RAM. This function and its resources shall execute in place.
|
|
+ */
|
|
+FUNC stm32mp_bkpsram_resume , :
|
|
+UNWIND( .fnstart)
|
|
+UNWIND( .cantunwind)
|
|
+ /*
|
|
+ * Almost all sequences here expect PM context structure base address
|
|
+ * from CPU register r11.
|
|
+ */
|
|
+ mov_imm r11, (BKPSRAM_BASE + BKPSRAM_PM_CONTEXT_OFFSET)
|
|
+
|
|
+ /* stm32mp_ccm_teeram needs some HW interface base addresss */
|
|
+ mov_imm r0, CRYP1_BASE
|
|
+ str r0, [r11, #PM_CTX_CRYP1_BASE]
|
|
+ mov_imm r0, RCC_BASE
|
|
+ str r0, [r11, #PM_CTX_RCC_BASE]
|
|
+ mov_imm r0, STGEN_BASE
|
|
+ str r0, [r11, #PM_CTX_STGEN_BASE]
|
|
+
|
|
+ bl _clear_early_mailbox
|
|
+ bl _prepare_time
|
|
+
|
|
+ mov_imm r0, TEE_RAM_START
|
|
+ ldr r1, [r11, #PM_CTX_TEERAM_BKP_PA]
|
|
+ mov_imm r2, TEE_RAM_PH_SIZE
|
|
+ mov_imm r3, 1
|
|
+ bl stm32mp_ccm_teeram
|
|
+ cmp r0, #0
|
|
+ bne _failed
|
|
+
|
|
+ /* Compare the generated and reference tags */
|
|
+ add r8, r11, #PM_CTX_CCM_TAG
|
|
+ add r9, r11, #PM_CTX_CCM_REF_TAG
|
|
+ ldm r8, {r2-r5}
|
|
+ ldm r9, {r6-r9}
|
|
+ mov r0, #0
|
|
+ cmp r2, r6
|
|
+ addeq r0, #1
|
|
+ cmp r3, r7
|
|
+ addeq r0, #1
|
|
+ cmp r4, r8
|
|
+ addeq r0, #1
|
|
+ cmp r5, r9
|
|
+ addeq r0, #1
|
|
+ cmp r0, #4
|
|
+ bne _failed
|
|
+ bl _save_resume_time
|
|
+
|
|
+ /* Resume into the restored TEE RAM */
|
|
+ ldr r1, [r11, #PM_CTX_RESUME_PA]
|
|
+ bx r1
|
|
+
|
|
+_failed:
|
|
+ /* Clear context including key and reference tag */
|
|
+ mov r0, #0xa5
|
|
+ mov_imm r12, BKPSRAM_PM_CONTEXT_SIZE
|
|
+ add r12, r11, r12
|
|
+1: str r0, [r11], #4
|
|
+ cmp r11, r12
|
|
+ blt 1b
|
|
+ b .
|
|
+
|
|
+ /*
|
|
+ * _clear_early_mailbox - Wipe mailbox in case of reset
|
|
+ *
|
|
+ * Sratches r0-r4.
|
|
+ * All other CPU registers are preserved.
|
|
+ */
|
|
+_clear_early_mailbox:
|
|
+ /* Clear the backup registers (first enable RTCAPB clock) */
|
|
+ mov_imm r0, (RCC_BASE + RCC_MP_APB5ENSETR)
|
|
+ mov_imm r2, RCC_MP_APB5ENSETR_RTCAPBEN
|
|
+ ldr r1, [r0]
|
|
+ ands r1, r1, r2
|
|
+ moveq r1, r2
|
|
+ movne r1, #0
|
|
+ str r2, [r0]
|
|
+ mov_imm r2, (TAMP_BASE + TAMP_BKP_REGISTER_OFF)
|
|
+ mov_imm r3, (BCKR_CORE1_MAGIC_NUMBER * 4)
|
|
+ mov_imm r4, BOOT_API_A7_RESET_MAGIC_NUMBER
|
|
+ str r4, [r2, r3]
|
|
+ mov_imm r3, (BCKR_CORE1_BRANCH_ADDRESS * 4)
|
|
+ mov r4, #0
|
|
+ str r4, [r2, r3]
|
|
+ /* Restore RTCAPB clock initial state */
|
|
+ str r1, [r0, #RCC_MP_ENCLRR_OFFSET]
|
|
+ bx lr
|
|
+
|
|
+ /*
|
|
+ * prepare_time - save/reset cycle counter to prevent later overflow
|
|
+ *
|
|
+ * Save current 32bit lower counter and reset to 0 so that later
|
|
+ * timeout test do not need to care about overflow.
|
|
+ *
|
|
+ * Expects r11 is context base and lr is return address.
|
|
+ * Scrathes r0-r2.
|
|
+ * All other CPU registers are preserved.
|
|
+ */
|
|
+_prepare_time:
|
|
+ ldr r2, [r11, #PM_CTX_STGEN_BASE]
|
|
+ /* Disable STGEN counter */
|
|
+ ldr r1, [r2, #CNTCR_OFFSET]
|
|
+ bic r1, r1, #CNTCR_EN
|
|
+ str r1, [r2, #CNTCR_OFFSET]
|
|
+1: ldr r1, [r2, #CNTSR_OFFSET]
|
|
+ tst r1, #CNTCR_EN
|
|
+ bne 1b
|
|
+ /* Save and reset STGEN counter */
|
|
+ ldr r0, [r2, #CNTCVL_OFFSET]
|
|
+ str r0, [r11, #PM_CTX_STGEN_CNT]
|
|
+ mov r0, #0
|
|
+ str r0, [r2, #CNTCVL_OFFSET]
|
|
+ ldr r0, [r2, #CNTCVU_OFFSET]
|
|
+ str r0, [r2, #CNTCVU_OFFSET]
|
|
+ /* Enable STGEN counter */
|
|
+ ldr r1, [r2, #CNTCR_OFFSET]
|
|
+ orr r1, r1, #CNTCR_EN
|
|
+ str r1, [r2, #CNTCR_OFFSET]
|
|
+ bx lr
|
|
+
|
|
+ /*
|
|
+ * save_resume_time - save time spent and restore STGEN cycle counter
|
|
+ *
|
|
+ * Restore STGEN counter to initial value incremented by the current
|
|
+ * count. Note 32bit upper may need to be incremented.
|
|
+ *
|
|
+ * Expects r11 is context base and lr is return address.
|
|
+ * Scrathes r0-r3.
|
|
+ * All other CPU registers are preserved.
|
|
+ */
|
|
+_save_resume_time:
|
|
+ /* Compute update STGEN counter 32bit LSB value */
|
|
+ ldr r2, [r11, #PM_CTX_STGEN_BASE]
|
|
+ ldr r0, [r11, #PM_CTX_STGEN_CNT]
|
|
+ ldr r3, [r2, #CNTCVL_OFFSET]
|
|
+ str r3, [r11, #PM_CTX_STGEN_CNT]
|
|
+ adds r0, r0, r3
|
|
+ /* Disable STGEN */
|
|
+ ldr r1, [r2, #CNTCR_OFFSET]
|
|
+ bic r1, r1, #CNTCR_EN
|
|
+ str r1, [r2, #CNTCR_OFFSET]
|
|
+1: ldr r1, [r2, #CNTSR_OFFSET]
|
|
+ tst r1, #CNTCR_EN
|
|
+ bne 1b
|
|
+ /* Update counter (increment 32bit MSB if requried) */
|
|
+ str r0, [r2, #CNTCVL_OFFSET]
|
|
+ ldr r0, [r2, #CNTCVU_OFFSET]
|
|
+ addcs r0, r0, #1
|
|
+ str r0, [r2, #CNTCVU_OFFSET] /* Write CNTCVU value ... */
|
|
+ ldr r0, [r2, #CNTCVU_OFFSET] /* ... and wait it is set */
|
|
+ /* Enable STGEN */
|
|
+ ldr r0, [r2, #CNTCR_OFFSET]
|
|
+ orr r0, r0, #CNTCR_EN
|
|
+ str r0, [r2, #CNTCR_OFFSET]
|
|
+ bx lr
|
|
+
|
|
+ /*
|
|
+ * _setup_cryp1 - Enable CRYP1 hardware: reset & clock
|
|
+ * _reset_cryp1 - Reset CRYP1 hardware
|
|
+ *
|
|
+ * Function call before and after CCM sequence. Note that the CRYP1
|
|
+ * clock remain enabled. It is disabled later by the resume sequence.
|
|
+ *
|
|
+ * Expects r11 is context base and lr is return address.
|
|
+ * Scratches r0-r3.
|
|
+ */
|
|
+_setup_cryp1:
|
|
+ ldr r1, [r11, #PM_CTX_RCC_BASE]
|
|
+ mov_imm r0, RCC_MP_AHB5ENSETR_CRYP1EN
|
|
+ str r0, [r1, #RCC_MP_AHB5ENSETR]
|
|
+ /* Intentionnally fall through reset_cryp1 */
|
|
+_reset_cryp1:
|
|
+ ldr r3, [r11, #PM_CTX_RCC_BASE]
|
|
+ mov_imm r0, RCC_AHB5RSTSETR_CRYP1RST
|
|
+ str r0, [r3, #RCC_AHB5RSTSETR]
|
|
+1: ldr r1, [r3, #RCC_AHB5RSTSETR]
|
|
+ ands r1, r1, r0
|
|
+ beq 1b
|
|
+ mov_imm r0, RCC_AHB5RSTSETR_CRYP1RST
|
|
+ str r0, [r3, #RCC_AHB5RSTCLRR]
|
|
+1: ldr r1, [r3, #RCC_AHB5RSTSETR]
|
|
+ ands r1, r1, r0
|
|
+ bne 1b
|
|
+ bx lr
|
|
+
|
|
+ /*
|
|
+ * _ccm_arm_8ms_timeout - Init 8ms threshold for _ccm_failed_on_timeout
|
|
+ * _ccm_fail_on_timeout - Check STGEN counter against timeout threshold
|
|
+ *
|
|
+ * These function are used by the macro wait_flag_timeout_8ms. The
|
|
+ * former loads the timeout in CPU register r0 while the later get the
|
|
+ * timeout counter threshold from CPU register r0.
|
|
+ *
|
|
+ * Expect r11 is context base and lr is return address.
|
|
+ * Scratch r0-r1.
|
|
+ * All other CPU registers are preserved.
|
|
+ */
|
|
+_ccm_arm_8ms_timeout:
|
|
+ ldr r1, [r11, #PM_CTX_STGEN_BASE]
|
|
+ ldr r0, [r1, #CNTFID_OFFSET]
|
|
+ lsrs r0, r0, #CCM_TIMEOUT
|
|
+ moveq r0, #1
|
|
+ ldr r1, [r1, #CNTCVL_OFFSET]
|
|
+ adds r0, r0, r1
|
|
+ bcs _ccm_failed
|
|
+ bx lr
|
|
+
|
|
+_ccm_fail_on_timeout:
|
|
+ ldr r1, [r11, #PM_CTX_STGEN_BASE]
|
|
+ ldr r1, [r1, #CNTCVL_OFFSET]
|
|
+ cmp r1, r0
|
|
+ bge _ccm_failed
|
|
+ bx lr
|
|
+
|
|
+ /*
|
|
+ * Macro WAIT_FLAG_TIMEOUT compares timeout threshold (r0) with
|
|
+ * current time and branches the CCM failure entry on timeout.
|
|
+ * It is assumed the 32bit timestamps cannot overflow.
|
|
+ */
|
|
+ .macro WAIT_FLAG_TIMEOUT register_offset, bit_mask, awaited_mask
|
|
+ bl _ccm_arm_8ms_timeout
|
|
+ 1:
|
|
+ bl _ccm_fail_on_timeout
|
|
+ ldr r1, [r10, #(\register_offset)]
|
|
+ and r1, r1, #(\bit_mask)
|
|
+ cmp r1, #(\awaited_mask)
|
|
+ bne 1b
|
|
+ .endm
|
|
+
|
|
+/*
|
|
+ * stm32mp_ccm_teeram - Size optimzed unpaged CCM encryption/decryption
|
|
+ *
|
|
+ * This sequence encrypts or decrypts a input block using AES CCM with a
|
|
+ * 256bit key and no AAD and generates the CCM tag. The key, CTR0, CTR1
|
|
+ * and B0 block are read from PM context structure. The generated tag is
|
|
+ * stored in the PM context structure.
|
|
+ *
|
|
+ * This function is executed from TEE RAM during suspend sequence to generate
|
|
+ * the encrypted data and the tag. This function is also executed from BKPSRAM
|
|
+ * called with MMU disabled. Therefore this sequence shall be comply with
|
|
+ * position independent code constraints.
|
|
+ *
|
|
+ * Expects at entry:
|
|
+ * lr = caller return address
|
|
+ * r11 = retram_resume_ctx structure base address
|
|
+ * r0 = Destination buffer for the output data (ciphertext or plaintext)
|
|
+ * r1 = Source buffer for the input data (plaintext or ciphertext)
|
|
+ * r2 = Input (and output) data size in bytes
|
|
+ * r3 = 1 if decrypting, 0 if encrypting
|
|
+ */
|
|
+stm32mp_ccm_teeram:
|
|
+ /*
|
|
+ * Use of the CPU registers in the whole stm32mp_ccm_teeram sequence
|
|
+ *
|
|
+ * sp: preserved, not used
|
|
+ * lr: scratch register used to call subroutines.
|
|
+ * r12: saves the caller link register for final return
|
|
+ * r11: context from BKPSRAM
|
|
+ * r10: CRYP1 base address
|
|
+ * r9: destination buffer
|
|
+ * r8: source buffer to cipher
|
|
+ * r7: data byte counter
|
|
+ * r0-r6 are scratch registers
|
|
+ */
|
|
+ mov r12, lr
|
|
+ ldr r10, [r11, #PM_CTX_CRYP1_BASE]
|
|
+ mov r9, r0
|
|
+ mov r8, r1
|
|
+ mov r7, r2
|
|
+ mov r6, r3
|
|
+
|
|
+ bl _setup_cryp1
|
|
+
|
|
+ mov_imm r0, (CRYP_CR_ALGOMODE(ALGOMODE_AES_CCM) | \
|
|
+ CRYP_CR_DATATYPE_8BIT | CRYP_CR_FFLUSH | \
|
|
+ CRYP_CR_KEYSIZE_256BIT)
|
|
+ cmp r6, #0
|
|
+ orrne r0, r0, #CRYP_CR_ALGODIR_DECRYPT
|
|
+ str r0, [r10, #CRYP_CR]
|
|
+
|
|
+ /* Check data alignment (addresses and size) */
|
|
+ ands r0, r7, #0x0F
|
|
+ bne _ccm_failed
|
|
+ ands r0, r8, #0x03
|
|
+ bne _ccm_failed
|
|
+ ands r0, r9, #0x03
|
|
+ bne _ccm_failed
|
|
+
|
|
+ ldr r0, [r11, #PM_CTX_CCM_KEY]
|
|
+ str r0, [r10, #CRYP_KEYR_BASE]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 4)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 4)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 8)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 8)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 12)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 12)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 16)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 16)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 20)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 20)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 24)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 24)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_KEY + 28)]
|
|
+ str r0, [r10, #(CRYP_KEYR_BASE + 28)]
|
|
+
|
|
+ ldr r0, [r11, #PM_CTX_CCM_CTR1]
|
|
+ str r0, [r10, #CRYP_IVR_BASE]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_CTR1 + 4)]
|
|
+ str r0, [r10, #(CRYP_IVR_BASE + 4)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_CTR1 + 8)]
|
|
+ str r0, [r10, #(CRYP_IVR_BASE + 8)]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_CTR1 + 12)]
|
|
+ str r0, [r10, #(CRYP_IVR_BASE + 12)]
|
|
+
|
|
+ /* Setup CRYP for the CCM Init Phase */
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+ orr r0, r0, #(CRYP_CR_CRYPEN | CRYP_CR_INIT_PHASE)
|
|
+ str r0, [r10, #CRYP_CR]
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+
|
|
+ ldr r0, [r11, #PM_CTX_CCM_B0]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_B0 + 4)]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_B0 + 8)]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_B0 + 12)]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+
|
|
+ WAIT_FLAG_TIMEOUT CRYP_CR, CRYP_CR_CRYPEN, 0
|
|
+
|
|
+ /* Setup CRYP for the CCM Payload phase */
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+ bic r0, r0, #CRYP_CR_PHASE_MASK
|
|
+ orr r0, r0, #CRYP_CR_PAYLOAD_PHASE
|
|
+ orr r0, r0, #CRYP_CR_CRYPEN
|
|
+ str r0, [r10, #CRYP_CR]
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+
|
|
+_next_block:
|
|
+ WAIT_FLAG_TIMEOUT CRYP_SR, CRYP_SR_IFEM, CRYP_SR_IFEM
|
|
+
|
|
+ /* Feed input data, r8 stores the current source buffer */
|
|
+ ldr r0, [r8], #4
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r8], #4
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r8], #4
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r8], #4
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+
|
|
+ WAIT_FLAG_TIMEOUT CRYP_SR, CRYP_SR_OFNE, CRYP_SR_OFNE
|
|
+
|
|
+ /* Store output data, r9 stores the current source buffer */
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r9], #4
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r9], #4
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r9], #4
|
|
+ /* Before last 32bit word, the output FIFO shall not be empty */
|
|
+ ldr r0, [r10, #CRYP_SR]
|
|
+ ands r0, r0, #CRYP_SR_OFNE
|
|
+ beq _ccm_failed
|
|
+ /* After last 32bit word for this 128block, FIFO shall be empty */
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r9], #4
|
|
+ ldr r0, [r10, #CRYP_SR]
|
|
+ ands r0, r0, #CRYP_SR_OFNE
|
|
+ bne _ccm_failed
|
|
+
|
|
+ /* Another round if remaining data */
|
|
+ subs r7, r7, #16
|
|
+ bne _next_block;
|
|
+
|
|
+ WAIT_FLAG_TIMEOUT CRYP_SR, CRYP_SR_BUSY, 0
|
|
+
|
|
+ /*
|
|
+ * Data processing completed, now remains the tag generation.
|
|
+ * Here expect SR[IFNF]=SR[OFNE]=1 and all others bits are 0.
|
|
+ */
|
|
+ ldr r0, [r10, #CRYP_SR]
|
|
+ cmp r0, #(CRYP_SR_IFEM | CRYP_SR_IFNF)
|
|
+ bne _ccm_failed
|
|
+
|
|
+ /* Setup CRYP1 for the CCM Final Phase */
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+ bic r0, r0, #CRYP_CR_CRYPEN
|
|
+ str r0, [r10, #CRYP_CR]
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+ bic r0, r0, #CRYP_CR_PHASE_MASK
|
|
+ bic r0, r0, #CRYP_CR_ALGODIR_DECRYPT
|
|
+ orr r0, r0, #CRYP_CR_FINAL_PHASE
|
|
+ orr r0, r0, #CRYP_CR_CRYPEN
|
|
+ str r0, [r10, #CRYP_CR]
|
|
+ ldr r0, [r10, #CRYP_CR]
|
|
+
|
|
+ /* Load CTR0 to generate the tag */
|
|
+ ldr r0, [r11, #PM_CTX_CCM_CTR0]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_CTR0 + 4)]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_CTR0 + 8)]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+ ldr r0, [r11, #(PM_CTX_CCM_CTR0 + 12)]
|
|
+ str r0, [r10, #CRYP_DIN]
|
|
+
|
|
+ WAIT_FLAG_TIMEOUT CRYP_SR, CRYP_SR_OFNE, CRYP_SR_OFNE
|
|
+
|
|
+ /* Store generated tag in the PM_CTX structure */
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r11, #PM_CTX_CCM_TAG]
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r11, #(PM_CTX_CCM_TAG + 4)]
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r11, #(PM_CTX_CCM_TAG + 8)]
|
|
+ /* Before last 32bit word, the output FIFO shall not be empty */
|
|
+ ldr r0, [r10, #CRYP_SR]
|
|
+ ands r0, r0, #CRYP_SR_OFNE
|
|
+ beq _ccm_failed
|
|
+ /* After last 32bit word for this 128block, FIFO shall be empty */
|
|
+ ldr r0, [r10, #CRYP_DOUT]
|
|
+ str r0, [r11, #(PM_CTX_CCM_TAG + 12)]
|
|
+ ldr r0, [r10, #CRYP_SR]
|
|
+ ands r0, r0, #CRYP_SR_OFNE
|
|
+ bne _ccm_failed
|
|
+
|
|
+ /* Successful return */
|
|
+ bl _reset_cryp1
|
|
+ mov r0, #0
|
|
+ bx r12
|
|
+
|
|
+_ccm_failed:
|
|
+ bl _reset_cryp1
|
|
+ mov r0, #1
|
|
+ bx r12
|
|
+
|
|
+/* End address of the PIC resume sequence copy in retained RAM */
|
|
+stm32mp_bkpsram_image_end:
|
|
+ nop
|
|
+
|
|
+UNWIND( .fnend)
|
|
+END_FUNC stm32mp_bkpsram_resume
|
|
+
|
|
+/*
|
|
+ * int stm32mp_ccm_encrypt_teeram(ctx, dst, src, len)
|
|
+ */
|
|
+FUNC stm32mp_ccm_encrypt_teeram , :
|
|
+UNWIND( .fnstart)
|
|
+ push {r4-r12, lr}
|
|
+UNWIND( .save {r4-r12, lr})
|
|
+ mov r11, r0
|
|
+ mov r0, r1
|
|
+ mov r1, r2
|
|
+ mov r2, r3
|
|
+ mov r3, #0
|
|
+ push {r0-r3}
|
|
+ bl _prepare_time
|
|
+ pop {r0-r3}
|
|
+ bl stm32mp_ccm_teeram
|
|
+ bl _save_resume_time
|
|
+ pop {r4-r12, pc}
|
|
+UNWIND( .fnend)
|
|
+END_FUNC stm32mp_ccm_encrypt_teeram
|
|
+
|
|
+/*
|
|
+ * int stm32mp_ccm_decrypt_teeram(ctx, cryp_base, dst, src)
|
|
+ */
|
|
+FUNC stm32mp_ccm_decrypt_teeram , :
|
|
+UNWIND( .fnstart)
|
|
+ push {r4-r12, lr}
|
|
+UNWIND( .save {r4-r12, lr})
|
|
+ mov r11, r0
|
|
+ mov r0, r1
|
|
+ mov r1, r2
|
|
+ mov r2, r3
|
|
+ mov r3, #1
|
|
+ push {r0-r3}
|
|
+ bl _prepare_time
|
|
+ pop {r0-r3}
|
|
+ bl stm32mp_ccm_teeram
|
|
+ bl _save_resume_time
|
|
+ pop {r4-r12, pc}
|
|
+UNWIND( .fnend)
|
|
+END_FUNC stm32mp_ccm_decrypt_teeram
|
|
+
|
|
+/*
|
|
+ * stm32mp_sysram_resume - Resume OP-TEE execution
|
|
+ *
|
|
+ * This function is the entry point of OP-TEE core resume sequence in the TEE
|
|
+ * RAM. When TEE RAM is lost during a power cycle, stm32mp_bkpsram_resume() is
|
|
+ * called to restore TEE RAM content and branch to this stm32mp_sysram_resume()
|
|
+ * routine.
|
|
+ *
|
|
+ * This function calls the OP-TEE core generic PM resume API
|
|
+ * sm_pm_cpu_resume().
|
|
+ */
|
|
+FUNC stm32mp_sysram_resume, :
|
|
+UNWIND( .fnstart)
|
|
+UNWIND( .cantunwind)
|
|
+ /* Invalidate the data cache */
|
|
+ mov r0, #0 @ ; write the cache size selection register to be
|
|
+ write_csselr r0 @ ; sure we address the data cache
|
|
+ isb @ ; isb to sync the change to the cachesizeid reg
|
|
+
|
|
+ mov r0, #0 @ ; set way number to 0
|
|
+_inv_nextway:
|
|
+ mov r1, #0 @ ; set line number (=index) to 0
|
|
+_inv_nextline:
|
|
+ orr r2, r0, r1 @ ; construct way/index value
|
|
+ write_dcisw r2 @ ; inval data or unified cache line by set/way
|
|
+ add r1, r1, #1 << LINE_FIELD_OFFSET @ ; increment the index
|
|
+ cmp r1, #1 << LINE_FIELD_OVERFLOW @ ; overflow out of set field?
|
|
+ bne _inv_nextline
|
|
+ add r0, r0, #1 << WAY_FIELD_OFFSET @ ; increment the way number
|
|
+ cmp r0, #0 @ ; overflow out of way field?
|
|
+ bne _inv_nextway
|
|
+
|
|
+ dsb
|
|
+ isb
|
|
+
|
|
+ /* Resume sequence executes in Monitor mode */
|
|
+ cps #CPSR_MODE_MON
|
|
+
|
|
+ blx plat_cpu_reset_early
|
|
+ b sm_pm_cpu_resume
|
|
+UNWIND( .fnend)
|
|
+END_FUNC stm32mp_sysram_resume
|
|
+
|
|
+/*
|
|
+ * stm32mp_cpu_reset_state - set CPU in a reset like state
|
|
+ *
|
|
+ * Disable CPU env (interrupts, cache, SMP, MMU) and return.
|
|
+ * Preserve the execution mode in CPSR.
|
|
+ */
|
|
+FUNC stm32mp_cpu_reset_state, :
|
|
+UNWIND( .fnstart)
|
|
+ push {r12, lr}
|
|
+UNWIND( .save {r12, lr})
|
|
+
|
|
+ cpsid aif
|
|
+
|
|
+ bl psci_armv7_cpu_off
|
|
+
|
|
+ write_bpiall
|
|
+ isb
|
|
+ dsb
|
|
+ read_sctlr r0
|
|
+ bic r0, r0, #SCTLR_M
|
|
+ bic r0, r0, #SCTLR_I
|
|
+ write_sctlr r0
|
|
+ isb
|
|
+ dsb sy
|
|
+
|
|
+ pop {r12, pc}
|
|
+UNWIND( .fnend)
|
|
+END_FUNC stm32mp_cpu_reset_state
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/power.h b/core/arch/arm/plat-stm32mp1/pm/power.h
|
|
new file mode 100644
|
|
index 0000000..21ca7be
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/power.h
|
|
@@ -0,0 +1,26 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP_PM_POWER_H__
|
|
+#define __STM32MP_PM_POWER_H__
|
|
+
|
|
+#include <compiler.h>
|
|
+#include <stdint.h>
|
|
+#include <stddef.h>
|
|
+
|
|
+bool need_to_backup_cpu_context(unsigned int soc_mode);
|
|
+
|
|
+void stm32_enter_csleep(void);
|
|
+
|
|
+int stm32_enter_cstop(uint32_t mode);
|
|
+void stm32_exit_cstop(void);
|
|
+
|
|
+void stm32_enter_cstop_shutdown(uint32_t mode) __noreturn;
|
|
+void stm32_enter_cstop_reset(uint32_t mode) __noreturn;
|
|
+
|
|
+void stm32_pm_cpu_power_down_wfi(void) __noreturn;
|
|
+void stm32_pm_cpu_wfi(void);
|
|
+
|
|
+#endif /*__STM32MP_PM_POWER_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/power_config.c b/core/arch/arm/plat-stm32mp1/pm/power_config.c
|
|
new file mode 100644
|
|
index 0000000..7845ede
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/power_config.c
|
|
@@ -0,0 +1,212 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <trace.h>
|
|
+#include <util.h>
|
|
+
|
|
+#ifdef CFG_DT
|
|
+#include <libfdt.h>
|
|
+#endif
|
|
+
|
|
+#include "context.h"
|
|
+#include "power.h"
|
|
+
|
|
+#define DT_PWR_COMPAT "st,stm32mp1-pwr"
|
|
+#define SYSTEM_SUSPEND_SUPPORTED_MODES "system_suspend_supported_soc_modes"
|
|
+#define SYSTEM_OFF_MODE "system_off_soc_mode"
|
|
+
|
|
+static uint32_t deepest_suspend_mode = STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR;
|
|
+static uint32_t system_off_mode = STM32_PM_SHUTDOWN;
|
|
+
|
|
+/* Default initialized to NOT supported until set from DT directives */
|
|
+static uint8_t stm32mp1_supported_soc_modes[STM32_PM_MAX_SOC_MODE];
|
|
+
|
|
+/* Init with all domains ON */
|
|
+static bool stm32mp1_pm_dom[STM32MP1_PD_MAX_PM_DOMAIN] = {
|
|
+ [STM32MP1_PD_VSW] = false,
|
|
+ [STM32MP1_PD_CORE_RET] = false,
|
|
+ [STM32MP1_PD_CORE] = false
|
|
+};
|
|
+
|
|
+bool need_to_backup_cpu_context(unsigned int soc_mode)
|
|
+{
|
|
+ switch (soc_mode) {
|
|
+ case STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR:
|
|
+ return true;
|
|
+ case STM32_PM_CSLEEP_RUN:
|
|
+ case STM32_PM_CSTOP_ALLOW_STOP:
|
|
+ case STM32_PM_CSTOP_ALLOW_LP_STOP:
|
|
+ case STM32_PM_CSTOP_ALLOW_LPLV_STOP:
|
|
+ case STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF:
|
|
+ case STM32_PM_SHUTDOWN:
|
|
+ return false;
|
|
+ default:
|
|
+ EMSG("Invalid mode 0x%x", soc_mode);
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static int dt_get_pwr_node(void *fdt)
|
|
+{
|
|
+ return fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static bool get_pm_domain_state(uint8_t mode)
|
|
+{
|
|
+ bool res = true;
|
|
+ enum stm32mp1_pm_domain id = STM32MP1_PD_MAX_PM_DOMAIN;
|
|
+
|
|
+ while (res && (id > mode)) {
|
|
+ id--;
|
|
+ res &= stm32mp1_pm_dom[id];
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+int stm32mp1_set_pm_domain_state(enum stm32mp1_pm_domain domain, bool status)
|
|
+{
|
|
+ if (domain >= STM32MP1_PD_MAX_PM_DOMAIN) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ stm32mp1_pm_dom[domain] = status;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static void save_supported_mode(void *fdt, int pwr_node)
|
|
+{
|
|
+ int len;
|
|
+ uint32_t count;
|
|
+ unsigned int i;
|
|
+ uint32_t supported[ARRAY_SIZE(stm32mp1_supported_soc_modes)];
|
|
+ const void *prop;
|
|
+
|
|
+ prop = fdt_getprop(fdt, pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES, &len);
|
|
+ if (prop == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ count = (uint32_t)len / sizeof(uint32_t);
|
|
+ if (count > STM32_PM_MAX_SOC_MODE) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ if (fdt_read_uint32_array(fdt, pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES,
|
|
+ &supported[0], count) < 0) {
|
|
+ panic("PWR DT");
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ if (supported[i] >= STM32_PM_MAX_SOC_MODE) {
|
|
+ panic("Invalid mode");
|
|
+ }
|
|
+ stm32mp1_supported_soc_modes[supported[i]] = true;
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+static bool is_supported_mode(uint32_t soc_mode)
|
|
+{
|
|
+ assert(soc_mode < ARRAY_SIZE(stm32mp1_supported_soc_modes));
|
|
+ return stm32mp1_supported_soc_modes[soc_mode] == 1;
|
|
+}
|
|
+
|
|
+uint32_t stm32mp1_get_lp_soc_mode(uint32_t psci_mode)
|
|
+{
|
|
+ uint32_t mode;
|
|
+
|
|
+ if (psci_mode == PSCI_MODE_SYSTEM_OFF) {
|
|
+ return system_off_mode;
|
|
+ }
|
|
+
|
|
+ mode = deepest_suspend_mode;
|
|
+
|
|
+ if ((mode == STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR) &&
|
|
+ ((!get_pm_domain_state(STM32MP1_PD_CORE_RET)) ||
|
|
+ (!is_supported_mode(mode)))) {
|
|
+ mode = STM32_PM_CSTOP_ALLOW_LPLV_STOP;
|
|
+ }
|
|
+
|
|
+ if ((mode == STM32_PM_CSTOP_ALLOW_LPLV_STOP) &&
|
|
+ ((!get_pm_domain_state(STM32MP1_PD_CORE)) ||
|
|
+ (!is_supported_mode(mode)))) {
|
|
+ mode = STM32_PM_CSTOP_ALLOW_LP_STOP;
|
|
+ }
|
|
+
|
|
+ if ((mode == STM32_PM_CSTOP_ALLOW_LP_STOP) &&
|
|
+ (!is_supported_mode(mode))) {
|
|
+ mode = STM32_PM_CSTOP_ALLOW_STOP;
|
|
+ }
|
|
+
|
|
+ if ((mode == STM32_PM_CSTOP_ALLOW_STOP) &&
|
|
+ (!is_supported_mode(mode))) {
|
|
+ mode = STM32_PM_CSLEEP_RUN;
|
|
+ }
|
|
+
|
|
+ return mode;
|
|
+}
|
|
+
|
|
+int stm32mp1_set_lp_deepest_soc_mode(uint32_t psci_mode, uint32_t soc_mode)
|
|
+{
|
|
+ if (soc_mode >= STM32_PM_MAX_SOC_MODE) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (psci_mode == PSCI_MODE_SYSTEM_SUSPEND) {
|
|
+ deepest_suspend_mode = soc_mode;
|
|
+ }
|
|
+
|
|
+ if (psci_mode == PSCI_MODE_SYSTEM_OFF) {
|
|
+ system_off_mode = soc_mode;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static TEE_Result stm32mp1_init_lp_states(void)
|
|
+{
|
|
+ void *fdt;
|
|
+ int pwr_node = -1;
|
|
+ const fdt32_t *cuint = NULL;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt != NULL) {
|
|
+ pwr_node = dt_get_pwr_node(fdt);
|
|
+ }
|
|
+ if (pwr_node >= 0) {
|
|
+ cuint = fdt_getprop(fdt, pwr_node, SYSTEM_OFF_MODE, NULL);
|
|
+ }
|
|
+ if ((fdt == NULL) || (pwr_node < 0) || (cuint == NULL)) {
|
|
+ IMSG("No power configuration found in DT");
|
|
+ return TEE_SUCCESS;
|
|
+ }
|
|
+
|
|
+ system_off_mode = fdt32_to_cpu(*cuint);
|
|
+
|
|
+ /* Initialize suspend support to the deepest possible mode */
|
|
+ deepest_suspend_mode = STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR;
|
|
+
|
|
+ save_supported_mode(fdt, pwr_node);
|
|
+
|
|
+ DMSG("Power configuration: shutdown to %u, suspend to %u",
|
|
+ stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_OFF),
|
|
+ stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_SUSPEND));
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+service_init(stm32mp1_init_lp_states);
|
|
+#endif
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/psci.c b/core/arch/arm/plat-stm32mp1/pm/psci.c
|
|
new file mode 100644
|
|
index 0000000..6e8219e
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/psci.c
|
|
@@ -0,0 +1,427 @@
|
|
+// SPDX-License-Identifier: BSD-2-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <arm.h>
|
|
+#include <boot_api.h>
|
|
+#include <drivers/stm32_etzpc.h>
|
|
+#include <drivers/stm32_rng.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include <initcall.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/cache_helpers.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/interrupt.h>
|
|
+#include <kernel/misc.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <kernel/spinlock.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <sm/pm.h>
|
|
+#include <sm/psci.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <string.h>
|
|
+#include <trace.h>
|
|
+#include <util.h>
|
|
+
|
|
+#include "context.h"
|
|
+#include "power.h"
|
|
+
|
|
+/*
|
|
+ * SMP boot support and access to the mailbox
|
|
+ */
|
|
+#define CORE_OFF 0
|
|
+#define CORE_AWAKE 1
|
|
+#define CORE_RET 2
|
|
+#define CORE_ON 3
|
|
+
|
|
+static int core_state[CFG_TEE_CORE_NB_CORE];
|
|
+static unsigned int __maybe_unused state_lock = SPINLOCK_UNLOCK;
|
|
+
|
|
+static uint32_t __maybe_unused lock_state_access(void)
|
|
+{
|
|
+ return may_spin_lock(&state_lock);
|
|
+}
|
|
+
|
|
+static void __maybe_unused unlock_state_access(uint32_t exceptions)
|
|
+{
|
|
+ may_spin_unlock(&state_lock, exceptions);
|
|
+}
|
|
+
|
|
+int psci_affinity_info(uint32_t affinity, uint32_t lowest_affinity_level)
|
|
+{
|
|
+ unsigned int pos = get_core_pos_mpidr(affinity);
|
|
+
|
|
+ DMSG("core %zu, state %u", pos, core_state[pos]);
|
|
+
|
|
+ if ((pos >= CFG_TEE_CORE_NB_CORE) ||
|
|
+ (lowest_affinity_level > PSCI_AFFINITY_LEVEL_ON)) {
|
|
+ return PSCI_RET_INVALID_PARAMETERS;
|
|
+ }
|
|
+
|
|
+ switch (core_state[pos]) {
|
|
+ case CORE_OFF:
|
|
+ case CORE_RET:
|
|
+ return PSCI_AFFINITY_LEVEL_OFF;
|
|
+ case CORE_AWAKE:
|
|
+ return PSCI_AFFINITY_LEVEL_ON_PENDING;
|
|
+ case CORE_ON:
|
|
+ return PSCI_AFFINITY_LEVEL_ON;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+#if CFG_TEE_CORE_NB_CORE == 1
|
|
+/*
|
|
+ * Function called when a CPU is booted through the OP-TEE.
|
|
+ * All cores shall register when online.
|
|
+ */
|
|
+void stm32mp_register_online_cpu(void)
|
|
+{
|
|
+ assert((core_state[0] == CORE_OFF) ||
|
|
+ (core_state[0] == CORE_RET));
|
|
+ core_state[0] = CORE_ON;
|
|
+}
|
|
+#else
|
|
+void stm32mp_register_online_cpu(void)
|
|
+{
|
|
+ size_t pos = get_core_pos();
|
|
+ uint32_t excep = lock_state_access();
|
|
+
|
|
+ if (pos == 0) {
|
|
+ assert((core_state[pos] == CORE_OFF) ||
|
|
+ (core_state[pos] == CORE_RET));
|
|
+ } else {
|
|
+ if (core_state[pos] != CORE_AWAKE) {
|
|
+ core_state[pos] = CORE_OFF;
|
|
+ unlock_state_access(excep);
|
|
+ stm32_pm_cpu_power_down_wfi();
|
|
+ /* No return */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ core_state[pos] = CORE_ON;
|
|
+ unlock_state_access(excep);
|
|
+}
|
|
+
|
|
+#define GICD_SGIR 0xF00
|
|
+static void raise_sgi0_as_secure(void)
|
|
+{
|
|
+ dsb_ishst();
|
|
+ write32(GIC_NON_SEC_SGI_0 | SHIFT_U32(TARGET_CPU1_GIC_MASK, 16),
|
|
+ get_gicd_base() + GICD_SGIR);
|
|
+}
|
|
+
|
|
+static void release_secondary_early_hpen(size_t pos __unused)
|
|
+{
|
|
+ /* Need to send SIG#0 over Group0 after individual core 1 reset */
|
|
+ raise_sgi0_as_secure();
|
|
+ udelay(20);
|
|
+
|
|
+ stm32_clock_enable(RTCAPB);
|
|
+
|
|
+ write32(TEE_LOAD_ADDR,
|
|
+ stm32mp_bkpreg(BCKR_CORE1_BRANCH_ADDRESS));
|
|
+ write32(BOOT_API_A7_CORE1_MAGIC_NUMBER,
|
|
+ stm32mp_bkpreg(BCKR_CORE1_MAGIC_NUMBER));
|
|
+
|
|
+ stm32_clock_disable(RTCAPB);
|
|
+
|
|
+ dsb_ishst();
|
|
+ itr_raise_sgi(GIC_SEC_SGI_0, TARGET_CPU1_GIC_MASK);
|
|
+}
|
|
+
|
|
+/* Override default psci_cpu_on() with platform specific sequence */
|
|
+int psci_cpu_on(uint32_t core_id, uint32_t entry, uint32_t context_id)
|
|
+{
|
|
+ size_t pos = get_core_pos_mpidr(core_id);
|
|
+ uint32_t excep;
|
|
+ int rc;
|
|
+
|
|
+ if (!pos || pos >= CFG_TEE_CORE_NB_CORE)
|
|
+ return PSCI_RET_INVALID_PARAMETERS;
|
|
+
|
|
+ DMSG("core %zu, ns_entry 0x%" PRIx32 ", state %u",
|
|
+ pos, entry, core_state[pos]);
|
|
+
|
|
+ excep = lock_state_access();
|
|
+
|
|
+ switch (core_state[pos]) {
|
|
+ case CORE_ON:
|
|
+ rc = PSCI_RET_ALREADY_ON;
|
|
+ break;
|
|
+ case CORE_AWAKE:
|
|
+ rc = PSCI_RET_ON_PENDING;
|
|
+ break;
|
|
+ case CORE_RET:
|
|
+ rc = PSCI_RET_DENIED;
|
|
+ break;
|
|
+ case CORE_OFF:
|
|
+ core_state[pos] = CORE_AWAKE;
|
|
+ rc = PSCI_RET_SUCCESS;
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ unlock_state_access(excep);
|
|
+
|
|
+ if (rc == PSCI_RET_SUCCESS) {
|
|
+ generic_boot_set_core_ns_entry(pos, entry, context_id);
|
|
+ release_secondary_early_hpen(pos);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Override default psci_cpu_off() with platform specific sequence */
|
|
+int psci_cpu_off(void)
|
|
+{
|
|
+ unsigned int pos = get_core_pos();
|
|
+ uint32_t excep;
|
|
+
|
|
+ if (pos == 0) {
|
|
+ EMSG("PSCI_CPU_OFF not supported for core0, use system_off");
|
|
+ return PSCI_RET_INTERNAL_FAILURE;
|
|
+ }
|
|
+
|
|
+ DMSG("core %u", pos);
|
|
+
|
|
+ excep = lock_state_access();
|
|
+
|
|
+ assert(core_state[pos] == CORE_ON);
|
|
+ core_state[pos] = CORE_OFF;
|
|
+
|
|
+ unlock_state_access(excep);
|
|
+
|
|
+ /* Enable BKPREG access for the disabled CPU */
|
|
+ stm32_clock_enable(RTCAPB);
|
|
+
|
|
+ thread_mask_exceptions(THREAD_EXCP_ALL);
|
|
+ stm32_pm_cpu_power_down_wfi();
|
|
+ panic();
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int enter_cstop_suspend(unsigned int soc_mode)
|
|
+{
|
|
+ int rc = 1;
|
|
+
|
|
+ if (read_isr() != 0) {
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ if (stm32_enter_cstop(soc_mode)) {
|
|
+ goto resume;
|
|
+ }
|
|
+
|
|
+ if (need_to_backup_cpu_context(soc_mode)) {
|
|
+ stm32_pm_cpu_power_down_wfi();
|
|
+ } else {
|
|
+ stm32_pm_cpu_wfi();
|
|
+ rc = 0;
|
|
+ }
|
|
+
|
|
+resume:
|
|
+ stm32_exit_cstop();
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int plat_suspend(uint32_t arg)
|
|
+{
|
|
+ unsigned int soc_mode = arg;
|
|
+ size_t pos = get_core_pos();
|
|
+ int rc = 1;
|
|
+
|
|
+ if (read_isr() != 0) {
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* No need to lock state access as CPU is alone when here */
|
|
+ assert(core_state[pos] == CORE_ON);
|
|
+ core_state[pos] = CORE_RET;
|
|
+
|
|
+ stm32mp_pm_save_context(soc_mode);
|
|
+
|
|
+ rc = enter_cstop_suspend(soc_mode);
|
|
+
|
|
+ stm32mp_pm_restore_context(soc_mode);
|
|
+ stm32mp_pm_wipe_context();
|
|
+
|
|
+ assert(core_state[pos] == CORE_RET);
|
|
+ core_state[pos] = CORE_ON;
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void plat_resume(uint32_t arg)
|
|
+{
|
|
+ unsigned int soc_mode = arg;
|
|
+
|
|
+ plat_cpu_reset_late();
|
|
+ main_init_gic();
|
|
+
|
|
+ assert(core_state[get_core_pos()] == CORE_ON);
|
|
+
|
|
+ stm32mp_pm_restore_context(soc_mode);
|
|
+}
|
|
+
|
|
+#if !defined(CFG_STM32_RNG)
|
|
+static bool plat_can_suspend(void)
|
|
+{
|
|
+ /* RNG is mandated for suspending TEE RAM content */
|
|
+ return false;
|
|
+}
|
|
+#else
|
|
+#if CFG_TEE_CORE_NB_CORE == 1
|
|
+static bool plat_can_suspend(void)
|
|
+{
|
|
+ return true;
|
|
+}
|
|
+#else /*CFG_TEE_CORE_NB_CORE==1*/
|
|
+static bool plat_can_suspend(void)
|
|
+{
|
|
+ size_t pos = get_core_pos();
|
|
+ size_t n;
|
|
+ uint32_t excep;
|
|
+ bool rc = true;
|
|
+
|
|
+ excep = lock_state_access();
|
|
+
|
|
+ for (n = 0; n < ARRAY_SIZE(core_state); n++) {
|
|
+ if (n == pos) {
|
|
+ continue;
|
|
+ }
|
|
+ if (core_state[n] == CORE_AWAKE) {
|
|
+ /* State core as lost and proceed suspend */
|
|
+ core_state[n] = CORE_OFF;
|
|
+ }
|
|
+ if (core_state[n] != CORE_OFF) {
|
|
+ rc = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ unlock_state_access(excep);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+#endif /*CFG_TEE_CORE_NB_CORE==1*/
|
|
+#endif /*CFG_STM32_RNG*/
|
|
+
|
|
+/* Override default psci_system_suspend() with platform specific sequence */
|
|
+int psci_system_suspend(uintptr_t entry, uint32_t context_id __unused,
|
|
+ struct sm_nsec_ctx *nsec)
|
|
+{
|
|
+ int ret = PSCI_RET_INVALID_PARAMETERS;
|
|
+ uint32_t soc_mode;
|
|
+ int pos = get_core_pos();
|
|
+
|
|
+ DMSG("core %u", pos);
|
|
+
|
|
+ if (!plat_can_suspend()) {
|
|
+ return PSCI_RET_DENIED;
|
|
+ }
|
|
+
|
|
+ soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_SUSPEND);
|
|
+
|
|
+ switch (soc_mode) {
|
|
+ case STM32_PM_CSLEEP_RUN:
|
|
+ stm32_enter_csleep();
|
|
+ nsec->mon_lr = (uint32_t)entry;
|
|
+ return PSCI_RET_SUCCESS;
|
|
+ case STM32_PM_SHUTDOWN:
|
|
+ stm32_enter_cstop_shutdown(soc_mode);
|
|
+ panic();
|
|
+ default:
|
|
+ /* Others are suspended mode: at least some context to backup */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ assert(cpu_mmu_enabled() && core_state[pos] == CORE_ON);
|
|
+
|
|
+ if (need_to_backup_cpu_context(soc_mode)) {
|
|
+ sm_save_unbanked_regs(&nsec->ub_regs);
|
|
+ /*
|
|
+ * sm_pm_cpu_suspend(arg, func) saves the CPU core context in TEE RAM
|
|
+ * then calls func(arg) to run the platform lower power sequence.
|
|
+ *
|
|
+ * If platform fails to suspend, sm_pm_cpu_suspend() returns with a
|
|
+ * non null return code. When sm_pm_cpu_suspend() returns 0 platform
|
|
+ * context must be restored.
|
|
+ */
|
|
+ ret = sm_pm_cpu_suspend((uint32_t)soc_mode, plat_suspend);
|
|
+ if (ret == 0) {
|
|
+ plat_resume((uint32_t)soc_mode);
|
|
+ sm_restore_unbanked_regs(&nsec->ub_regs);
|
|
+ }
|
|
+ } else {
|
|
+ ret = plat_suspend((uint32_t)soc_mode);
|
|
+ }
|
|
+
|
|
+ if (ret == 0) {
|
|
+ nsec->mon_lr = (uint32_t)entry;
|
|
+ IMSG("Resumed");
|
|
+ return PSCI_RET_SUCCESS;
|
|
+ }
|
|
+
|
|
+ return PSCI_RET_INTERNAL_FAILURE;
|
|
+}
|
|
+
|
|
+/* Override default psci_system_off() with platform specific sequence */
|
|
+void __noreturn psci_system_off(void)
|
|
+{
|
|
+ uint32_t soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_OFF);
|
|
+
|
|
+ DMSG("core %u", get_core_pos());
|
|
+
|
|
+ stm32_enter_cstop_shutdown(soc_mode);
|
|
+}
|
|
+
|
|
+/* Override default psci_system_reset() with platform specific sequence */
|
|
+void __noreturn psci_system_reset(void)
|
|
+{
|
|
+ uint32_t soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_OFF);
|
|
+
|
|
+ DMSG("core %u", get_core_pos());
|
|
+
|
|
+ stm32_enter_cstop_reset(soc_mode);
|
|
+}
|
|
+
|
|
+void __noreturn stm32mp_platform_reset(int __unused cpu)
|
|
+{
|
|
+ psci_system_reset();
|
|
+}
|
|
+
|
|
+/* Override default psci_cpu_on() with platform supported features */
|
|
+int psci_features(uint32_t psci_fid)
|
|
+{
|
|
+ switch (psci_fid) {
|
|
+ case PSCI_PSCI_FEATURES:
|
|
+ case PSCI_VERSION:
|
|
+#if CFG_TEE_CORE_NB_CORE > 1
|
|
+ case PSCI_CPU_ON:
|
|
+ case PSCI_CPU_OFF:
|
|
+#endif
|
|
+ case PSCI_SYSTEM_SUSPEND:
|
|
+ case PSCI_SYSTEM_RESET:
|
|
+ case PSCI_SYSTEM_OFF:
|
|
+ return PSCI_RET_SUCCESS;
|
|
+ default:
|
|
+ return PSCI_RET_NOT_SUPPORTED;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Override default psci_version() to enable PSCI_VERSION_1_0 API */
|
|
+uint32_t psci_version(void)
|
|
+{
|
|
+ return PSCI_VERSION_1_0;
|
|
+}
|
|
+
|
|
diff --git a/core/arch/arm/plat-stm32mp1/pm/sub.mk b/core/arch/arm/plat-stm32mp1/pm/sub.mk
|
|
new file mode 100644
|
|
index 0000000..d8930a4
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/pm/sub.mk
|
|
@@ -0,0 +1,7 @@
|
|
+asm-defines-y += context_asm_defines.c
|
|
+
|
|
+srcs-y += context.c
|
|
+srcs-y += low_power.c
|
|
+srcs-y += pm_helpers.S
|
|
+srcs-y += power_config.c
|
|
+srcs-$(CFG_PSCI_ARM32) += psci.c
|
|
diff --git a/core/arch/arm/plat-stm32mp1/reset.S b/core/arch/arm/plat-stm32mp1/reset.S
|
|
index 69ab151..abf8b9a 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/reset.S
|
|
+++ b/core/arch/arm/plat-stm32mp1/reset.S
|
|
@@ -23,8 +23,34 @@ UNWIND( .fnstart)
|
|
mov_imm r1, STM32MP1_NSACR_PRESERVE_MASK
|
|
and r0, r0, r1
|
|
write_nsacr r0
|
|
+ isb
|
|
+
|
|
+ read_actlr r0
|
|
+ orr r0, r0, #ACTLR_SMP
|
|
+ write_actlr r0
|
|
+
|
|
+ /*
|
|
+ * Always reset CNTVOFF for the dear non secure world.
|
|
+ * This operation requires being in Monitor mode and
|
|
+ * non secure state.
|
|
+ */
|
|
+ mrs r1, cpsr
|
|
+ cps #CPSR_MODE_MON
|
|
+ isb
|
|
+
|
|
+ read_scr r2
|
|
+ orr r0, r2, #SCR_NS
|
|
+ write_scr r0
|
|
+ isb
|
|
+
|
|
+ mov r0, #0
|
|
+ write_cntvoff r0, r0
|
|
|
|
+ write_scr r2
|
|
isb
|
|
- bx lr
|
|
+ msr cpsr, r1
|
|
+ isb
|
|
+
|
|
+ bx lr
|
|
UNWIND( .fnend)
|
|
END_FUNC plat_cpu_reset_early
|
|
diff --git a/core/arch/arm/plat-stm32mp1/scripts/stm32image.py b/core/arch/arm/plat-stm32mp1/scripts/stm32image.py
|
|
index 55363af..2def5b8 100755
|
|
--- a/core/arch/arm/plat-stm32mp1/scripts/stm32image.py
|
|
+++ b/core/arch/arm/plat-stm32mp1/scripts/stm32image.py
|
|
@@ -1,4 +1,4 @@
|
|
-#!/usr/bin/env python3
|
|
+#!/usr/bin/env python
|
|
# SPDX-License-Identifier: BSD-2-Clause
|
|
#
|
|
# Copyright (c) 2017-2018, STMicroelectronics
|
|
@@ -35,7 +35,7 @@ def stm32image_checksum(dest_fd, sizedest):
|
|
return csum
|
|
|
|
|
|
-def stm32image_set_header(dest_fd, load, entry):
|
|
+def stm32image_set_header(dest_fd, load, entry, bintype):
|
|
sizedest = get_size(dest_fd)
|
|
|
|
checksum = stm32image_checksum(dest_fd, sizedest)
|
|
@@ -68,11 +68,12 @@ def stm32image_set_header(dest_fd, load, entry):
|
|
dest_fd.write(b'\x00' * 64)
|
|
|
|
# Padding
|
|
- dest_fd.write(b'\x00' * 84)
|
|
+ dest_fd.write(b'\x00' * 83)
|
|
+ dest_fd.write(struct.pack('<B', bintype))
|
|
dest_fd.close()
|
|
|
|
|
|
-def stm32image_create_header_file(source, dest, load, entry):
|
|
+def stm32image_create_header_file(source, dest, load, entry, bintype):
|
|
dest_fd = open(dest, 'w+b')
|
|
src_fd = open(source, 'rb')
|
|
|
|
@@ -86,7 +87,7 @@ def stm32image_create_header_file(source, dest, load, entry):
|
|
|
|
src_fd.close()
|
|
|
|
- stm32image_set_header(dest_fd, load, entry)
|
|
+ stm32image_set_header(dest_fd, load, entry, bintype)
|
|
|
|
dest_fd.close()
|
|
|
|
@@ -113,6 +114,10 @@ def get_args():
|
|
required=True, type=int_parse,
|
|
help='Entry point')
|
|
|
|
+ parser.add_argument('--bintype',
|
|
+ required=True, type=int_parse,
|
|
+ help='Binary identification')
|
|
+
|
|
return parser.parse_args()
|
|
|
|
|
|
@@ -122,11 +127,13 @@ def main():
|
|
destination_file = args.dest
|
|
load_address = args.load
|
|
entry_point = args.entry
|
|
+ binary_type = args.bintype
|
|
|
|
stm32image_create_header_file(source_file,
|
|
destination_file,
|
|
load_address,
|
|
- entry_point)
|
|
+ entry_point,
|
|
+ binary_type)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/bsec_svc.c b/core/arch/arm/plat-stm32mp1/service/bsec_svc.c
|
|
new file mode 100644
|
|
index 0000000..1ce7195
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/bsec_svc.c
|
|
@@ -0,0 +1,65 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <trace.h>
|
|
+#include "bsec_svc.h"
|
|
+#include "stm32mp1_smc.h"
|
|
+
|
|
+uint32_t bsec_main(uint32_t x1, uint32_t x2, uint32_t x3, uint32_t *out)
|
|
+{
|
|
+ int result;
|
|
+ uint32_t tmp;
|
|
+
|
|
+ if (bsec_check_nsec_access_rights(x2) != BSEC_OK) {
|
|
+ return BSEC_ERROR;
|
|
+ }
|
|
+
|
|
+ switch (x1) {
|
|
+ case STM32_SIP_BSEC_READ_SHADOW:
|
|
+ result = bsec_read_otp(out, x2);
|
|
+ FMSG("read shadow @%" PRIx32 " = %" PRIx32, x2, *out);
|
|
+ break;
|
|
+ case STM32_SIP_BSEC_PROG_OTP:
|
|
+ FMSG("program @%" PRIx32, x2);
|
|
+ result = bsec_program_otp(x3, x2);
|
|
+ break;
|
|
+ case STM32_SIP_BSEC_WRITE_SHADOW:
|
|
+ FMSG("write shadow @%" PRIx32, x2);
|
|
+ result = bsec_write_otp(x3, x2);
|
|
+ break;
|
|
+ case STM32_SIP_BSEC_READ_OTP:
|
|
+ result = bsec_read_otp(&tmp, x2);
|
|
+ if (result != BSEC_OK) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ result = bsec_shadow_register(x2);
|
|
+ if (result != BSEC_OK) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ result = bsec_read_otp(out, x2);
|
|
+ if (result != BSEC_OK) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ result = bsec_write_otp(tmp, x2);
|
|
+ FMSG("read @%" PRIx32 " = %" PRIx32, x2, *out);
|
|
+ break;
|
|
+ default:
|
|
+ EMSG("Invalid %" PRIx32, x1);
|
|
+ result = BSEC_ERROR;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (result) {
|
|
+ case BSEC_OK:
|
|
+ return STM32_SIP_OK;
|
|
+ case BSEC_INVALID_PARAM:
|
|
+ return STM32_SIP_INVALID_PARAMS;
|
|
+ default:
|
|
+ return STM32_SIP_FAILED;
|
|
+ }
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/bsec_svc.h b/core/arch/arm/plat-stm32mp1/service/bsec_svc.h
|
|
new file mode 100644
|
|
index 0000000..4f1babe
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/bsec_svc.h
|
|
@@ -0,0 +1,14 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_BSEC_SVC_H__
|
|
+#define __STM32MP1_BSEC_SVC_H__
|
|
+
|
|
+#include <drivers/stm32_bsec.h>
|
|
+
|
|
+uint32_t bsec_main(uint32_t x1, uint32_t x2, uint32_t x3,
|
|
+ uint32_t *ret_otp_value);
|
|
+
|
|
+#endif /*__STM32MP1_BSEC_SVC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/low_power_svc.c b/core/arch/arm/plat-stm32mp1/service/low_power_svc.c
|
|
new file mode 100644
|
|
index 0000000..626f390
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/low_power_svc.c
|
|
@@ -0,0 +1,153 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <arm32.h>
|
|
+#include <drivers/stm32mp1_ddrc.h>
|
|
+#include <dt-bindings/power/stm32mp1-power.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/interrupt.h>
|
|
+#include <kernel/misc.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <platform_config.h>
|
|
+#include <sm/pm.h>
|
|
+#include <sm/psci.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <stm32_util.h>
|
|
+#include <trace.h>
|
|
+
|
|
+#include "stm32mp1_smc.h"
|
|
+#include "low_power_svc.h"
|
|
+#include "../pm/power.h"
|
|
+#include "../pm/context.h"
|
|
+
|
|
+
|
|
+#undef DDR_SR_TEST
|
|
+
|
|
+#ifdef DDR_SR_TEST
|
|
+uint32_t sr_mode_scv_handler(uint32_t __maybe_unused x1, uint32_t x2)
|
|
+{
|
|
+ unsigned int mode = x2;
|
|
+
|
|
+ DMSG("DDR selfrefresh mode 0x%" PRIx32 ", 0x%" PRIx32, mode, x1);
|
|
+
|
|
+ switch (mode) {
|
|
+ case STM32_SIP_SR_MODE_SSR:
|
|
+ ddr_sr_mode_ssr();
|
|
+ break;
|
|
+ case STM32_SIP_SR_MODE_ASR:
|
|
+ ddr_sr_mode_asr();
|
|
+ break;
|
|
+ case STM32_SIP_SR_MODE_HSR:
|
|
+ ddr_sr_mode_hsr();
|
|
+ break;
|
|
+ default:
|
|
+ return STM32_SIP_INVALID_PARAMS;
|
|
+ }
|
|
+
|
|
+ return STM32_SIP_OK;
|
|
+}
|
|
+#else
|
|
+uint32_t sr_mode_scv_handler(uint32_t __unused x1, uint32_t __unused x2)
|
|
+{
|
|
+ return STM32_SIP_NOT_SUPPORTED;
|
|
+}
|
|
+#endif
|
|
+
|
|
+uint32_t cstop_scv_handler(struct sm_ctx __unused *ctx, uint32_t __unused x1,
|
|
+ uint32_t __unused x2, uint32_t __unused x3)
|
|
+{
|
|
+ DMSG("core %u", get_core_pos());
|
|
+
|
|
+ stm32mp1_set_lp_deepest_soc_mode(PSCI_MODE_SYSTEM_SUSPEND,
|
|
+ STM32_PM_CSTOP_ALLOW_LPLV_STOP);
|
|
+
|
|
+ return (psci_system_suspend(ctx->nsec.mon_lr, 0, &ctx->nsec) == 0) ?
|
|
+ STM32_SIP_OK : STM32_SIP_FAILED;
|
|
+}
|
|
+
|
|
+uint32_t standby_scv_handler(struct sm_ctx *ctx, uint32_t __unused x1,
|
|
+ uint32_t __unused x2, uint32_t x3)
|
|
+{
|
|
+ uint32_t nsec_resume_ep = x3;
|
|
+
|
|
+ DMSG("core %u", get_core_pos());
|
|
+
|
|
+ if (nsec_resume_ep == 0U) {
|
|
+ shutdown_scv_handler();
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ stm32mp1_set_lp_deepest_soc_mode(PSCI_MODE_SYSTEM_SUSPEND,
|
|
+ STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR);
|
|
+
|
|
+ return (psci_system_suspend(nsec_resume_ep, 0, &ctx->nsec) == 0) ?
|
|
+ STM32_SIP_OK : STM32_SIP_FAILED;
|
|
+}
|
|
+
|
|
+uint32_t shutdown_scv_handler(void)
|
|
+{
|
|
+ DMSG("core %u", get_core_pos());
|
|
+
|
|
+ if (!stm32mp_with_pmic()) {
|
|
+ return STM32_SIP_NOT_SUPPORTED;
|
|
+ }
|
|
+
|
|
+ psci_system_off();
|
|
+ panic();
|
|
+}
|
|
+
|
|
+uint32_t pm_domain_scv_handler(uint32_t id, uint32_t enable)
|
|
+{
|
|
+ unsigned int pd = id;
|
|
+
|
|
+ DMSG("%sable PD %u", enable != 0 ? "En" : "Dis", pd);
|
|
+
|
|
+ switch (pd) {
|
|
+ case STM32MP1_PD_VSW:
|
|
+ case STM32MP1_PD_CORE_RET:
|
|
+ case STM32MP1_PD_CORE:
|
|
+ break;
|
|
+ default:
|
|
+ return STM32_SIP_INVALID_PARAMS;
|
|
+ }
|
|
+
|
|
+ stm32mp1_set_pm_domain_state(pd, enable);
|
|
+
|
|
+ return STM32_SIP_OK;
|
|
+}
|
|
+
|
|
+#ifdef CFG_TEE_CORE_DEBUG
|
|
+uint32_t pm_set_lp_state_scv_handler(uint32_t request, uint32_t state)
|
|
+{
|
|
+ uint32_t power_mode;
|
|
+
|
|
+ switch (request) {
|
|
+ case STM32_OEM_LP_FORCE_SUSPEND_PARAMS:
|
|
+ DMSG("Set suspend mode to %u", state);
|
|
+ power_mode = PSCI_MODE_SYSTEM_SUSPEND;
|
|
+ break;
|
|
+ case STM32_OEM_LP_FORCE_OFF_PARAMS:
|
|
+ DMSG("Set off mode to %u", state);
|
|
+ power_mode = PSCI_MODE_SYSTEM_OFF;
|
|
+ break;
|
|
+ default:
|
|
+ return STM32_OEM_INVALID_PARAMS;
|
|
+ }
|
|
+
|
|
+ if (stm32mp1_set_lp_deepest_soc_mode(power_mode, state) < 0) {
|
|
+ return STM32_OEM_FAILED;
|
|
+ }
|
|
+
|
|
+ return STM32_OEM_OK;
|
|
+}
|
|
+#else
|
|
+uint32_t pm_set_lp_state_scv_handler(uint32_t __unused mode,
|
|
+ uint32_t __unused state)
|
|
+{
|
|
+ return STM32_SIP_NOT_SUPPORTED;
|
|
+}
|
|
+#endif
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/low_power_svc.h b/core/arch/arm/plat-stm32mp1/service/low_power_svc.h
|
|
new file mode 100644
|
|
index 0000000..5681ea2
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/low_power_svc.h
|
|
@@ -0,0 +1,22 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef LOW_POWER_SVC_H
|
|
+#define LOW_POWER_SVC_H
|
|
+
|
|
+#include <sm/sm.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+uint32_t sr_mode_scv_handler(uint32_t x1, uint32_t x2);
|
|
+uint32_t cstop_scv_handler(struct sm_ctx *nsec,
|
|
+ uint32_t x1, uint32_t x2, uint32_t x3);
|
|
+uint32_t standby_scv_handler(struct sm_ctx *nsec,
|
|
+ uint32_t x1, uint32_t x2, uint32_t x3);
|
|
+uint32_t shutdown_scv_handler(void);
|
|
+uint32_t pm_domain_scv_handler(uint32_t x1, uint32_t x2);
|
|
+
|
|
+uint32_t pm_set_lp_state_scv_handler(uint32_t x1, uint32_t x2);
|
|
+
|
|
+#endif /* LOW_POWER_SVC_H */
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/pwr_svc.c b/core/arch/arm/plat-stm32mp1/service/pwr_svc.c
|
|
new file mode 100644
|
|
index 0000000..9c314b2
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/pwr_svc.c
|
|
@@ -0,0 +1,117 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <inttypes.h>
|
|
+#include <drivers/stm32mp1_pwr.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <io.h>
|
|
+#include <kernel/spinlock.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <mm/core_mmu.h>
|
|
+#include <platform_config.h>
|
|
+#include <stm32_util.h>
|
|
+#include <trace.h>
|
|
+
|
|
+#include "pwr_svc.h"
|
|
+#include "stm32mp1_smc.h"
|
|
+
|
|
+struct pwr_reg_prop {
|
|
+ uint32_t offset;
|
|
+ uint32_t mask;
|
|
+};
|
|
+
|
|
+#define PWR_ALLOWED_MASK(_off, _mask) { .offset = (_off), .mask = (_mask), }
|
|
+
|
|
+static const struct pwr_reg_prop allowed_regs[] = {
|
|
+ PWR_ALLOWED_MASK(PWR_CR3_OFF, PWR_CR3_VBE | PWR_CR3_VBRS |
|
|
+ PWR_CR3_USB33DEN |
|
|
+ PWR_CR3_REG18EN | PWR_CR3_REG11EN),
|
|
+ PWR_ALLOWED_MASK(PWR_WKUPCR_OFF, PWR_WKUPCR_MASK),
|
|
+ PWR_ALLOWED_MASK(PWR_MPUWKUPENR_OFF, PWR_MPUWKUPENR_MASK),
|
|
+};
|
|
+
|
|
+static unsigned int lock = SPINLOCK_UNLOCK;
|
|
+
|
|
+static uint32_t pwr_regs_lock(void)
|
|
+{
|
|
+ return may_spin_lock(&lock);
|
|
+}
|
|
+
|
|
+static void pwr_regs_unlock(uint32_t exceptions)
|
|
+{
|
|
+ may_spin_unlock(&lock, exceptions);
|
|
+}
|
|
+
|
|
+uint32_t pwr_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3)
|
|
+{
|
|
+ uint32_t req = x1;
|
|
+ uint32_t offset = x2;
|
|
+ uint32_t value = x3;
|
|
+ uint32_t va;
|
|
+ uint32_t allowed;
|
|
+ uint32_t i;
|
|
+ uint32_t ret;
|
|
+ uint32_t exc;
|
|
+
|
|
+ /*
|
|
+ * Argument x2 can be either the register physical address of the
|
|
+ * register offset toward PWR_BASE.
|
|
+ */
|
|
+ if ((offset & ~PWR_OFFSET_MASK) != 0) {
|
|
+ if ((offset & ~PWR_OFFSET_MASK) != PWR_BASE) {
|
|
+ return STM32_SIP_INVALID_PARAMS;
|
|
+ }
|
|
+
|
|
+ offset &= PWR_OFFSET_MASK;
|
|
+ }
|
|
+
|
|
+ DMSG_RAW("PWR service: %s 0x%" PRIx32 " at offset 0x%" PRIx32,
|
|
+ req == STM32_SIP_REG_WRITE ? "write" :
|
|
+ req == STM32_SIP_REG_SET ? "set" : "clear",
|
|
+ value, offset);
|
|
+
|
|
+ exc = pwr_regs_lock();
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(allowed_regs); i++) {
|
|
+ if (offset != allowed_regs[i].offset) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ va = stm32_pwr_base() + offset;
|
|
+ allowed = allowed_regs[i].mask;
|
|
+ value &= allowed;
|
|
+
|
|
+ switch (req) {
|
|
+ case STM32_SIP_REG_WRITE:
|
|
+ io_mask32_stm32shregs(va, value, allowed);
|
|
+ FMSG("wrt off %" PRIx32 "=%" PRIx32 " => %" PRIx32,
|
|
+ offset, value, read32(va));
|
|
+ ret = STM32_SIP_OK;
|
|
+ break;
|
|
+ case STM32_SIP_REG_SET:
|
|
+ io_mask32_stm32shregs(va, value, value);
|
|
+ FMSG("set off %" PRIx32 "=%" PRIx32 " => %" PRIx32,
|
|
+ offset, value, read32(va));
|
|
+ ret = STM32_SIP_OK;
|
|
+ break;
|
|
+ case STM32_SIP_REG_CLEAR:
|
|
+ io_mask32_stm32shregs(va, 0, value);
|
|
+ FMSG("clr off %" PRIx32 "=%" PRIx32 " => %" PRIx32,
|
|
+ offset, value, read32(va));
|
|
+ ret = STM32_SIP_OK;
|
|
+ break;
|
|
+ default:
|
|
+ ret = STM32_SIP_INVALID_PARAMS;
|
|
+ }
|
|
+
|
|
+ pwr_regs_unlock(exc);
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pwr_regs_unlock(exc);
|
|
+
|
|
+ return STM32_SIP_INVALID_PARAMS;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/pwr_svc.h b/core/arch/arm/plat-stm32mp1/service/pwr_svc.h
|
|
new file mode 100644
|
|
index 0000000..011cac4
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/pwr_svc.h
|
|
@@ -0,0 +1,11 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __PWR_SVC_H__
|
|
+#define __PWR_SVC_H__
|
|
+
|
|
+uint32_t pwr_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3);
|
|
+
|
|
+#endif /* __PWR_SVC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/rcc_svc.c b/core/arch/arm/plat-stm32mp1/service/rcc_svc.c
|
|
new file mode 100644
|
|
index 0000000..7bdaea8
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/rcc_svc.c
|
|
@@ -0,0 +1,440 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <inttypes.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <io.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <mm/core_mmu.h>
|
|
+#include <platform_config.h>
|
|
+#include <stm32_util.h>
|
|
+#include <trace.h>
|
|
+
|
|
+#include "rcc_svc.h"
|
|
+#include "stm32mp1_smc.h"
|
|
+
|
|
+#define STD_REG 0
|
|
+#define SET_REG 1
|
|
+#define CLR_REG 2
|
|
+
|
|
+static void shared_clk_request(uint32_t request,
|
|
+ uint32_t offset, uint32_t value)
|
|
+{
|
|
+ unsigned int id;
|
|
+ unsigned int bit;
|
|
+ uint32_t enable_bits = 0;
|
|
+ int clr_std_set = STD_REG;
|
|
+
|
|
+ switch (request) {
|
|
+ case STM32_SIP_REG_WRITE:
|
|
+ case STM32_SIP_REG_SET:
|
|
+ case STM32_SIP_REG_CLEAR:
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (offset) {
|
|
+ case RCC_MP_APB5ENSETR:
|
|
+ clr_std_set = SET_REG;
|
|
+ /* Non secure backup registers requires RTCAPB clock */
|
|
+ enable_bits |= RCC_MP_APB5ENSETR_RTCAPBEN;
|
|
+ break;
|
|
+ case RCC_MP_APB5ENCLRR:
|
|
+ clr_std_set = CLR_REG;
|
|
+ /* Non secure backup registers requires RTCAPB clock */
|
|
+ enable_bits |= RCC_MP_APB5ENSETR_RTCAPBEN;
|
|
+ break;
|
|
+
|
|
+ case RCC_MP_AHB5ENSETR:
|
|
+ clr_std_set = SET_REG;
|
|
+ if (stm32mp_gpio_bank_is_shared(GPIO_BANK_Z)) {
|
|
+ enable_bits |= RCC_MP_AHB5ENSETR_GPIOZEN;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_MP_AHB5ENCLRR:
|
|
+ clr_std_set = CLR_REG;
|
|
+ if (stm32mp_gpio_bank_is_shared(GPIO_BANK_Z)) {
|
|
+ enable_bits |= RCC_MP_AHB5ENSETR_GPIOZEN;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if ((clr_std_set != STD_REG) && (request == STM32_SIP_REG_CLEAR))
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Parse bit that relate to a functional clock.
|
|
+ * Call stm32mp1_clk_enable/disable_non_secure() for that clock
|
|
+ * according to request (write/set/clear) and target register
|
|
+ * (write or set/clear).
|
|
+ */
|
|
+ for (bit = 0; enable_bits && bit < 32; bit++) {
|
|
+
|
|
+ if (!(BIT(bit) & enable_bits))
|
|
+ continue;
|
|
+
|
|
+ id = stm32mp1_clk_rcc2id(offset, bit);
|
|
+ if (id == ~0U)
|
|
+ panic();
|
|
+
|
|
+ switch (clr_std_set) {
|
|
+ case SET_REG:
|
|
+ if (BIT(bit) & value) {
|
|
+ DMSG("Enable non-secure clock %u", id);
|
|
+ stm32mp1_clk_enable_non_secure(id);
|
|
+ }
|
|
+ break;
|
|
+ case CLR_REG:
|
|
+ if (BIT(bit) & value) {
|
|
+ DMSG("Disable non-secure clock %u", id);
|
|
+ stm32mp1_clk_disable_non_secure(id);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ /* Standard registers case */
|
|
+ switch (request) {
|
|
+ case STM32_SIP_REG_WRITE:
|
|
+ if (BIT(bit) & value) {
|
|
+ DMSG("Enable non-secure clock %u", id);
|
|
+ stm32mp1_clk_enable_non_secure(id);
|
|
+ } else {
|
|
+ DMSG("Disable non-secure clock %u", id);
|
|
+ stm32mp1_clk_disable_non_secure(id);
|
|
+ }
|
|
+ break;
|
|
+ case STM32_SIP_REG_SET:
|
|
+ if (BIT(bit) & value) {
|
|
+ DMSG("Enable non-secure clock %u", id);
|
|
+ stm32mp1_clk_enable_non_secure(id);
|
|
+ }
|
|
+ break;
|
|
+ case STM32_SIP_REG_CLEAR:
|
|
+ if (BIT(bit) & value) {
|
|
+ DMSG("Disable non-secure clock %u", id);
|
|
+ stm32mp1_clk_disable_non_secure(id);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ enable_bits &= ~BIT(bit);
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool offset_is_clear_register(uint32_t offset)
|
|
+{
|
|
+ switch (offset) {
|
|
+ case RCC_OCENCLRR:
|
|
+ case RCC_MP_SREQCLRR:
|
|
+ case RCC_APB5RSTCLRR:
|
|
+ case RCC_AHB5RSTCLRR:
|
|
+ case RCC_MP_APB5ENCLRR:
|
|
+ case RCC_MP_AHB5ENCLRR:
|
|
+ case RCC_MP_APB5LPENCLRR:
|
|
+ case RCC_MP_AHB5LPENCLRR:
|
|
+ case RCC_MP_IWDGFZCLRR:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void access_allowed_mask(uint32_t request, uint32_t offset,
|
|
+ uint32_t value, uint32_t allowed_mask)
|
|
+{
|
|
+ uint32_t va = stm32_rcc_base() + offset;
|
|
+
|
|
+ switch (request) {
|
|
+ case STM32_SIP_REG_WRITE:
|
|
+ if (offset_is_clear_register(offset)) {
|
|
+ /* CLR registers show the SET state, not the CLR state */
|
|
+ write32(value & allowed_mask, va);
|
|
+ } else {
|
|
+ io_mask32_stm32shregs(va, value, allowed_mask);
|
|
+ }
|
|
+ FMSG("wrt 0x%" PRIx32 "=0x%" PRIx32 " => 0x%" PRIx32,
|
|
+ offset, value, read32(va));
|
|
+ break;
|
|
+
|
|
+ case STM32_SIP_REG_SET:
|
|
+ if (offset_is_clear_register(offset)) {
|
|
+ /* CLR registers show the SET state, not the CLR state */
|
|
+ write32(value & allowed_mask, va);
|
|
+ } else {
|
|
+ io_mask32_stm32shregs(va, value, value & allowed_mask);
|
|
+ }
|
|
+ FMSG("set 0x%" PRIx32 "=0x%" PRIx32 " => 0x%" PRIx32,
|
|
+ offset, value, read32(va));
|
|
+ break;
|
|
+
|
|
+ case STM32_SIP_REG_CLEAR:
|
|
+ if (offset_is_clear_register(offset)) {
|
|
+ /* Nothing to do on CLR registers */
|
|
+ } else {
|
|
+ io_mask32_stm32shregs(va, 0, value & allowed_mask);
|
|
+ }
|
|
+ FMSG("clear 0x%" PRIx32 "=0x%" PRIx32 " => 0x%" PRIx32,
|
|
+ offset, value, read32(va));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void raw_allowed_access_request(uint32_t request,
|
|
+ uint32_t offset, uint32_t value)
|
|
+{
|
|
+ uint32_t allowed_mask = 0;
|
|
+
|
|
+ /* Use UINT32_MAX if no secure restriction on register access */
|
|
+ switch (offset) {
|
|
+ case RCC_OCENSETR:
|
|
+ case RCC_OCENCLRR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_HSI)) {
|
|
+ allowed_mask |= RCC_OCENR_HSION | RCC_OCENR_HSIKERON;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_CSI)) {
|
|
+ allowed_mask |= RCC_OCENR_CSION | RCC_OCENR_CSIKERON;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_HSE)) {
|
|
+ allowed_mask |= RCC_OCENR_HSEON | RCC_OCENR_HSEKERON |
|
|
+ RCC_OCENR_HSEBYP | RCC_OCENR_HSECSSON |
|
|
+ RCC_OCENR_DIGBYP;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_HSICFGR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_HSI)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_CSICFGR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_CSI)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_MP_CIER:
|
|
+ case RCC_MP_CIFR:
|
|
+ /* RCC_MP_CIFR_xxxRDYF matches CIER and CIFR bit mapping */
|
|
+ allowed_mask |= RCC_MP_CIFR_WKUPF | RCC_MP_CIFR_PLL4DYF;
|
|
+
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_LSI)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_LSIRDYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_LSE)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_LSERDYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_HSI)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_HSIRDYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_HSE)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_HSERDYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_CSI)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_CSIRDYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL1)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_PLL1DYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL2)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_PLL2DYF;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL3)) {
|
|
+ allowed_mask |= RCC_MP_CIFR_PLL3DYF;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_PLL1CR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL1_P)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVPEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL1_Q)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVQEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL1_R)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVREN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL1)) {
|
|
+ allowed_mask |= RCC_PLLNCR_PLLON | RCC_PLLNCR_PLLRDY |
|
|
+ RCC_PLLNCR_SSCG_CTRL;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_PLL2CR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL2_P)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVPEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL2_Q)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVQEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL2_R)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVREN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL2)) {
|
|
+ allowed_mask |= RCC_PLLNCR_PLLON | RCC_PLLNCR_PLLRDY |
|
|
+ RCC_PLLNCR_SSCG_CTRL;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_PLL3CR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL3_P)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVPEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL3_Q)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVQEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL3_R)) {
|
|
+ allowed_mask |= RCC_PLLNCR_DIVREN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_PLL3)) {
|
|
+ allowed_mask |= RCC_PLLNCR_PLLON | RCC_PLLNCR_PLLRDY |
|
|
+ RCC_PLLNCR_SSCG_CTRL;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RCC_MP_BOOTCR: /* Allowed MPU/MCU reboot cfg */
|
|
+ case RCC_MP_GCR: /* Allowed MPU/MCU reboot cfg */
|
|
+ case RCC_MP_GRSTCSETR: /* Allowed MCU and system reset */
|
|
+ case RCC_BR_RSTSCLRR: /* Allowed system reset status */
|
|
+ case RCC_MC_RSTSCLRR: /* Allowed system reset status */
|
|
+ case RCC_MP_RSTSCLRR: /* Allowed system reset status */
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ break;
|
|
+ case RCC_APB5RSTSETR:
|
|
+ case RCC_APB5RSTCLRR:
|
|
+ case RCC_MP_APB5ENSETR:
|
|
+ case RCC_MP_APB5ENCLRR:
|
|
+ case RCC_MP_APB5LPENSETR:
|
|
+ case RCC_MP_APB5LPENCLRR:
|
|
+ /*
|
|
+ * SPI6/I2C4/I2C6/USART1/RTC/IWDG1 resources may be non secure.
|
|
+ * TZPC/TZC/BSEC/STGEN resources are secure only.
|
|
+ * Bit mask RCC_MP_APB5ENSETR_xxxEN fits EN, RST and LPEN.
|
|
+ */
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_SPI6)) {
|
|
+ allowed_mask |= RCC_MP_APB5ENSETR_SPI6EN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_I2C4)) {
|
|
+ allowed_mask |= RCC_MP_APB5ENSETR_I2C4EN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_I2C6)) {
|
|
+ allowed_mask |= RCC_MP_APB5ENSETR_I2C6EN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_USART1)) {
|
|
+ allowed_mask |= RCC_MP_APB5ENSETR_USART1EN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_IWDG1)) {
|
|
+ allowed_mask |= RCC_MP_APB5ENSETR_IWDG1APBEN;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_AHB5RSTSETR:
|
|
+ case RCC_AHB5RSTCLRR:
|
|
+ case RCC_MP_AHB5ENSETR:
|
|
+ case RCC_MP_AHB5ENCLRR:
|
|
+ case RCC_MP_AHB5LPENSETR:
|
|
+ case RCC_MP_AHB5LPENCLRR:
|
|
+ /*
|
|
+ * RNG1/HASH1/CRYP1/GPIOZ/AXIMC resources are accessible if related
|
|
+ * BKPSRAM resources are reserved to secure services.
|
|
+ * Bit mask RCC_MP_AHB5ENSETR_xxxEN fits EN, RST and LPEN.
|
|
+ */
|
|
+ if (stm32mp_gpio_bank_is_non_secure(GPIO_BANK_Z)) {
|
|
+ allowed_mask |= RCC_MP_AHB5ENSETR_GPIOZEN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_CRYP1)) {
|
|
+ allowed_mask |= RCC_MP_AHB5ENSETR_CRYP1EN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_HASH1)) {
|
|
+ allowed_mask |= RCC_MP_AHB5ENSETR_HASH1EN;
|
|
+ }
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_RNG1)) {
|
|
+ allowed_mask |= RCC_MP_AHB5ENSETR_RNG1EN;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_RTCDIVR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_RTC)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_I2C46CKSELR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_I2C4) &&
|
|
+ stm32mp_periph_is_non_secure(STM32MP1_SHRES_I2C6)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_SPI6CKSELR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_SPI6)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_UART1CKSELR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_USART1)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_RNG1CKSELR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_RNG1)) {
|
|
+ allowed_mask = UINT32_MAX;
|
|
+ }
|
|
+ break;
|
|
+ case RCC_MP_IWDGFZSETR:
|
|
+ case RCC_MP_IWDGFZCLRR:
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_IWDG1)) {
|
|
+ allowed_mask |= RCC_MP_IWDGFZSETR_IWDG1;
|
|
+ }
|
|
+ allowed_mask |= RCC_MP_IWDGFZSETR_IWDG2;
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (allowed_mask != 0U) {
|
|
+ access_allowed_mask(request, offset, value, allowed_mask);
|
|
+ }
|
|
+}
|
|
+
|
|
+uint32_t rcc_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3)
|
|
+{
|
|
+ uint32_t request = x1;
|
|
+ uint32_t offset = x2;
|
|
+ uint32_t value = x3;
|
|
+
|
|
+ /*
|
|
+ * Argument x2 can be either the register physical address of the
|
|
+ * register offset toward RCC_BASE.
|
|
+ */
|
|
+ if ((offset & ~RCC_OFFSET_MASK) != 0) {
|
|
+ if ((offset & ~RCC_OFFSET_MASK) != RCC_BASE) {
|
|
+ return STM32_SIP_INVALID_PARAMS;
|
|
+ }
|
|
+
|
|
+ offset &= RCC_OFFSET_MASK;
|
|
+ }
|
|
+
|
|
+ DMSG_RAW("RCC service: %s 0x%" PRIx32 " at offset 0x%" PRIx32,
|
|
+ request == STM32_SIP_REG_WRITE ? "write" :
|
|
+ request == STM32_SIP_REG_SET ? "set" : "clear",
|
|
+ value, offset);
|
|
+
|
|
+ /* Some clocks may be managed by some secure services */
|
|
+ shared_clk_request(request, offset, value);
|
|
+
|
|
+ /* RCC controls for non secure resource may be accessed straight */
|
|
+ raw_allowed_access_request(request, offset, value);
|
|
+
|
|
+ return STM32_SIP_OK;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/rcc_svc.h b/core/arch/arm/plat-stm32mp1/service/rcc_svc.h
|
|
new file mode 100644
|
|
index 0000000..f198ebd
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/rcc_svc.h
|
|
@@ -0,0 +1,11 @@
|
|
+/* SPDX-License-Identifier: BSD-2-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __RCC_SVC_H__
|
|
+#define __RCC_SVC_H__
|
|
+
|
|
+uint32_t rcc_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3);
|
|
+
|
|
+#endif /*__RCC_SVC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/stm32mp1_smc.h b/core/arch/arm/plat-stm32mp1/service/stm32mp1_smc.h
|
|
new file mode 100644
|
|
index 0000000..2db7b10
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/stm32mp1_smc.h
|
|
@@ -0,0 +1,211 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics
|
|
+ * Copyright (c) 2018, Linaro Limited
|
|
+ */
|
|
+#ifndef __STM32MP1_SMC_H__
|
|
+#define __STM32MP1_SMC_H__
|
|
+
|
|
+/*
|
|
+ * SIP Functions
|
|
+ */
|
|
+#define STM32_SIP_SVC_VERSION_MAJOR 0x0
|
|
+#define STM32_SIP_SVC_VERSION_MINOR 0x1
|
|
+
|
|
+/* SIP service generic return codes */
|
|
+#define STM32_SIP_OK 0x0
|
|
+#define STM32_SIP_NOT_SUPPORTED 0xffffffffU
|
|
+#define STM32_SIP_FAILED 0xfffffffeU
|
|
+#define STM32_SIP_INVALID_PARAMS 0xfffffffdU
|
|
+
|
|
+/*
|
|
+ * SMC function IDs for STM32 Service queries
|
|
+ * STM32 SMC services use the space between 0x82000000 and 0x8200FFFF
|
|
+ * like this is defined in SMC calling Convention by ARM
|
|
+ * for SiP (silicon Partner)
|
|
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_CALL_COUNT
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) dummy value 0
|
|
+ */
|
|
+#define STM32_SIP_FUNC_CALL_COUNT 0xff00
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_UID
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) Lowest 32bit of the stm32mp1 SIP service UUID
|
|
+ * Argument a1: (output) Next 32bit of the stm32mp1 SIP service UUID
|
|
+ * Argument a2: (output) Next 32bit of the stm32mp1 SIP service UUID
|
|
+ * Argument a3: (output) Last 32bit of the stm32mp1 SIP service UUID
|
|
+ */
|
|
+#define STM32_SIP_FUNC_UID 0xff01
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_VERSION
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) STM32 SIP service major
|
|
+ * Argument a1: (output) STM32 SIP service minor
|
|
+ */
|
|
+#define STM32_SIP_FUNC_VERSION 0xff03
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_RCC
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (input) Service ID (STM32_SIP_REG_xxx)
|
|
+ * Argument a2: (input) register offset or physical address
|
|
+ * (output) register read value, if applicable
|
|
+ * Argument a3: (input) register target value if applicable
|
|
+ */
|
|
+#define STM32_SIP_FUNC_RCC 0x1000
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_PWR
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (input) Service ID (STM32_SIP_REG_xxx)
|
|
+ * Argument a2: (input) register offset or physical address
|
|
+ * (output) register read value, if applicable
|
|
+ * Argument a3: (input) register target value if applicable
|
|
+ */
|
|
+#define STM32_SIP_FUNC_PWR 0x1001
|
|
+
|
|
+/* Service ID for STM32_SIP_FUNC_RCC/_PWR */
|
|
+#define STM32_SIP_REG_READ 0x0
|
|
+#define STM32_SIP_REG_WRITE 0x1
|
|
+#define STM32_SIP_REG_SET 0x2
|
|
+#define STM32_SIP_REG_CLEAR 0x3
|
|
+
|
|
+/*
|
|
+ * SIP functions STM32_SIP_FUNC_BSEC
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (input) Clock ID (from DT clock bindings)
|
|
+ */
|
|
+#define STM32_SIP_RCC_CAL 0x1002
|
|
+
|
|
+/*
|
|
+ * SIP functions STM32_SIP_FUNC_BSEC
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (input) Service ID (STM32_SIP_BSEC_xxx)
|
|
+ * Argument a2: (input) OTP index
|
|
+ * (output) OTP read value, if applicable
|
|
+ * Argument a3: (input) OTP value if applicable
|
|
+ */
|
|
+#define STM32_SIP_FUNC_BSEC 0x1003
|
|
+
|
|
+/* Service ID for STM32_SIP_FUNC_BSEC */
|
|
+#define STM32_SIP_BSEC_READ_SHADOW 0x1
|
|
+#define STM32_SIP_BSEC_PROG_OTP 0x2
|
|
+#define STM32_SIP_BSEC_WRITE_SHADOW 0x3
|
|
+#define STM32_SIP_BSEC_READ_OTP 0x4
|
|
+
|
|
+/*
|
|
+ * SIP functions STM32_SIP_FUNC_SR_MODE
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (unused)
|
|
+ * Argument a2: (input) Target selfrefresh mode
|
|
+ */
|
|
+#define STM32_SIP_FUNC_SR_MODE 0x1004
|
|
+
|
|
+/* DDR Self-Refresh modes */
|
|
+#define STM32_SIP_SR_MODE_SSR 0x0
|
|
+#define STM32_SIP_SR_MODE_ASR 0x1
|
|
+#define STM32_SIP_SR_MODE_HSR 0x2
|
|
+
|
|
+/*
|
|
+ * SIP functions STM32_SIP_FUNC_CSTOP
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (unused)
|
|
+ * Argument a2: (unused)
|
|
+ * Argument a3: (input) Target SoC mode
|
|
+ */
|
|
+#define STM32_SIP_FUNC_CSTOP 0x1005
|
|
+
|
|
+/* Valid SoC modes used for CSTOP, */
|
|
+#define STM32_SIP_CSLEEP_RUN 0x0
|
|
+#define STM32_SIP_CSTOP_ALLOW_STOP 0x1
|
|
+#define STM32_SIP_CSTOP_ALLOW_LP_STOP 0x2
|
|
+#define STM32_SIP_CSTOP_ALLOW_LPLV_STOP 0x3
|
|
+#define STM32_SIP_CSTOP_ALLOW_STANDBY 0x4
|
|
+#define STM32_SIP_CSTOP_ALLOW_STANDBY_DDR_OFF 0x5
|
|
+#define STM32_SIP_CSTOP_SHUTDOWN 0x6
|
|
+
|
|
+/*
|
|
+ * SIP functions STM32_SIP_FUNC_STANDBY
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a1: (unused)
|
|
+ * Argument a2: (unused)
|
|
+ * Argument a3: (input) non null only for DDR off standby
|
|
+ */
|
|
+#define STM32_SIP_FUNC_STANDBY 0x1006
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_SHUTDOWN
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ */
|
|
+#define STM32_SIP_FUNC_SHUTDOWN 0x1007
|
|
+
|
|
+/*
|
|
+ * SIP function STM32_SIP_FUNC_PD_DOMAIN
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a2: (index) ID of target power domain to be enabled/disabled
|
|
+ * Argument a3: (input) 0 to disable, 1 to eanble target domain
|
|
+ */
|
|
+#define STM32_SIP_FUNC_PD_DOMAIN 0x1008
|
|
+
|
|
+/* Valid IDs for power domain for function STM32_SIP_FUNC_PD_DOMAIN */
|
|
+#define STM32_SIP_PD_VSW 0x0
|
|
+#define STM32_SIP_PD_CORE_RET 0x1
|
|
+#define STM32_SIP_PD_CORE 0x2
|
|
+#define STM32_SIP_PD_MAX_PM_DOMAIN 0x3
|
|
+
|
|
+/*
|
|
+ * OEM Functions
|
|
+ */
|
|
+#define STM32_OEM_SVC_VERSION_MAJOR 0x0
|
|
+#define STM32_OEM_SVC_VERSION_MINOR 0x1
|
|
+
|
|
+/* OEM service generic return codes */
|
|
+#define STM32_OEM_OK 0x0
|
|
+#define STM32_OEM_NOT_SUPPORTED 0xffffffffU
|
|
+#define STM32_OEM_FAILED 0xfffffffeU
|
|
+#define STM32_OEM_INVALID_PARAMS 0xfffffffdU
|
|
+
|
|
+/*
|
|
+ * OEM function STM32_OEM_FUNC_LP_FORCE_PARAMS
|
|
+ *
|
|
+ * Argument a0: (input) SMCC ID
|
|
+ * (output) status return code
|
|
+ * Argument a2: (input) ID of the mode: suspend or shutdown (off)
|
|
+ * Argument a3: (input) ID of the power state to be reached for the mode
|
|
+ * Refer to stm32mp1 power bindings.
|
|
+ */
|
|
+#define STM32_OEM_FUNC_LP_FORCE_PARAMS 0x0f800
|
|
+
|
|
+/* Valid IDs for power mode configured with STM32_OEM_FUNC_LP_FORCE_PARAMS */
|
|
+#define STM32_OEM_LP_FORCE_SUSPEND_PARAMS 0
|
|
+#define STM32_OEM_LP_FORCE_OFF_PARAMS 1
|
|
+
|
|
+#endif /* __STM32MP1_SMC_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/stm32mp1_svc_setup.c b/core/arch/arm/plat-stm32mp1/service/stm32mp1_svc_setup.c
|
|
new file mode 100644
|
|
index 0000000..3c7958c
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/stm32mp1_svc_setup.c
|
|
@@ -0,0 +1,129 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <arm.h>
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32_bsec.h>
|
|
+#include <kernel/thread.h>
|
|
+#include <sm/optee_smc.h>
|
|
+#include <sm/sm.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stdint.h>
|
|
+#include <string.h>
|
|
+#include <tee_api_types.h>
|
|
+#include <trace.h>
|
|
+
|
|
+#include "bsec_svc.h"
|
|
+#include "low_power_svc.h"
|
|
+#include "pwr_svc.h"
|
|
+#include "rcc_svc.h"
|
|
+#include "stm32mp1_smc.h"
|
|
+
|
|
+static uint32_t __maybe_unused calib_scv_handler(uint32_t x1)
|
|
+{
|
|
+ unsigned long clock_id = x1;
|
|
+
|
|
+ return (stm32mp_start_clock_calib(clock_id) == 0) ?
|
|
+ STM32_SIP_OK : STM32_SIP_FAILED;
|
|
+}
|
|
+
|
|
+/* STM32 SiP Service UUID */
|
|
+static const TEE_UUID stm32mp1_sip_svc_uid = {
|
|
+ 0x50aa78a7, 0x9bf4, 0x4a14,
|
|
+ { 0x8a, 0x5e, 0x26, 0x4d, 0x59, 0x94, 0xc2, 0x14 }
|
|
+};
|
|
+
|
|
+static void get_sip_func_uid(uint32_t *a0, uint32_t *a1,
|
|
+ uint32_t *a2, uint32_t *a3)
|
|
+{
|
|
+ const void *uid = &stm32mp1_sip_svc_uid;
|
|
+
|
|
+ memcpy(a0, (char *)uid, sizeof(uint32_t));
|
|
+ memcpy(a1, (char *)uid + sizeof(uint32_t), sizeof(uint32_t));
|
|
+ memcpy(a2, (char *)uid + (sizeof(uint32_t) * 2), sizeof(uint32_t));
|
|
+ memcpy(a3, (char *)uid + (sizeof(uint32_t) * 3), sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+bool stm32_sip_service(struct sm_ctx __unused *ctx,
|
|
+ uint32_t *a0, uint32_t *a1, uint32_t *a2, uint32_t *a3)
|
|
+{
|
|
+ switch (OPTEE_SMC_FUNC_NUM(*a0)) {
|
|
+ case STM32_SIP_FUNC_CALL_COUNT:
|
|
+ /* This service is meaningless, return a dummy value */
|
|
+ *a0 = 0;
|
|
+ break;
|
|
+ case STM32_SIP_FUNC_VERSION:
|
|
+ *a0 = STM32_SIP_SVC_VERSION_MAJOR;
|
|
+ *a1 = STM32_SIP_SVC_VERSION_MINOR;
|
|
+ break;
|
|
+ case STM32_SIP_FUNC_UID:
|
|
+ get_sip_func_uid(a0, a1, a2, a3);
|
|
+ break;
|
|
+#ifdef CFG_STM32_BSEC_SIP
|
|
+ case STM32_SIP_FUNC_BSEC:
|
|
+ *a0 = bsec_main(*a1, *a2, *a3, a1);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef CFG_STM32_RCC_SIP
|
|
+ case STM32_SIP_FUNC_RCC:
|
|
+ *a0 = rcc_scv_handler(*a1, *a2, *a3);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef CFG_STM32_CLOCKSRC_CALIB
|
|
+ case STM32_SIP_RCC_CAL:
|
|
+ *a0 = calib_scv_handler(*a1);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef CFG_STM32_PWR_SIP
|
|
+ case STM32_SIP_FUNC_PWR:
|
|
+ *a0 = pwr_scv_handler(*a1, *a2, *a3);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef CFG_STM32_POWER_SERVICES
|
|
+ case STM32_SIP_FUNC_SR_MODE:
|
|
+ *a0 = sr_mode_scv_handler(*a1, *a2);
|
|
+ break;
|
|
+
|
|
+ case STM32_SIP_FUNC_CSTOP:
|
|
+ *a0 = cstop_scv_handler(ctx, *a1, *a2, *a3);
|
|
+ break;
|
|
+
|
|
+ case STM32_SIP_FUNC_STANDBY:
|
|
+ *a0 = standby_scv_handler(ctx, *a1, *a2, *a3);
|
|
+ break;
|
|
+
|
|
+ case STM32_SIP_FUNC_SHUTDOWN:
|
|
+ *a0 = shutdown_scv_handler();
|
|
+ break;
|
|
+
|
|
+ case STM32_SIP_FUNC_PD_DOMAIN:
|
|
+ *a0 = pm_domain_scv_handler(*a1, *a2);
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+ default:
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool stm32_oem_service(struct sm_ctx __unused *ctx,
|
|
+ uint32_t *a0, uint32_t *a1 __maybe_unused,
|
|
+ uint32_t *a2 __maybe_unused, uint32_t *a3 __unused)
|
|
+{
|
|
+ switch (OPTEE_SMC_FUNC_NUM(*a0)) {
|
|
+#ifdef CFG_STM32_POWER_SERVICES
|
|
+ case STM32_OEM_FUNC_LP_FORCE_PARAMS:
|
|
+ *a0 = pm_set_lp_state_scv_handler(*a1, *a2);
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+ default:
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/service/sub.mk b/core/arch/arm/plat-stm32mp1/service/sub.mk
|
|
new file mode 100644
|
|
index 0000000..0797b8f
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/service/sub.mk
|
|
@@ -0,0 +1,7 @@
|
|
+global-incdirs-y += .
|
|
+
|
|
+srcs-y += stm32mp1_svc_setup.c
|
|
+srcs-$(CFG_STM32_BSEC_SIP) += bsec_svc.c
|
|
+srcs-$(CFG_STM32_PWR_SIP) += pwr_svc.c
|
|
+srcs-$(CFG_STM32_RCC_SIP) += rcc_svc.c
|
|
+srcs-$(CFG_STM32_POWER_SERVICES) += low_power_svc.c
|
|
diff --git a/core/arch/arm/plat-stm32mp1/shared_resources.c b/core/arch/arm/plat-stm32mp1/shared_resources.c
|
|
new file mode 100644
|
|
index 0000000..0eb929c
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/shared_resources.c
|
|
@@ -0,0 +1,1007 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <drivers/stm32_gpio.h>
|
|
+#include <drivers/stm32_etzpc.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_rcc.h>
|
|
+#include <dt-bindings/etzpc/stm32-etzpc.h>
|
|
+#include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+#include <initcall.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <kernel/spinlock.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <stdbool.h>
|
|
+#include <string.h>
|
|
+
|
|
+static unsigned int shregs_lock = SPINLOCK_UNLOCK;
|
|
+static bool registering_locked;
|
|
+
|
|
+/* Shared resource lock: assume not required if MMU is disabled */
|
|
+uint32_t lock_stm32shregs(void)
|
|
+{
|
|
+ return may_spin_lock(&shregs_lock);
|
|
+}
|
|
+
|
|
+void unlock_stm32shregs(uint32_t exceptions)
|
|
+{
|
|
+ may_spin_unlock(&shregs_lock, exceptions);
|
|
+}
|
|
+
|
|
+/* Shared register access: upon shared resource lock */
|
|
+void io_mask32_stm32shregs(uintptr_t va, uint32_t value, uint32_t mask)
|
|
+{
|
|
+ uint32_t exceptions = lock_stm32shregs();
|
|
+
|
|
+ io_mask32(va, value, mask);
|
|
+
|
|
+ unlock_stm32shregs(exceptions);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Generic clock enable/disable from secure world.
|
|
+ * Some drivers may use non secure resources in specific execution context:
|
|
+ * when the other SMP core(s) are offline and non secure is never reached.
|
|
+ * In such cases, drivers shall enable/disable the HW clock only if it was not
|
|
+ * left enabled by the non secure world.
|
|
+ *
|
|
+ * During driver initializations, before registering_locked is locked, all
|
|
+ * driver simply enable/disable the clock as if the peripheral was secure.
|
|
+ */
|
|
+void stm32_clock_enable(unsigned long id)
|
|
+{
|
|
+ if (registering_locked) {
|
|
+ if (stm32mp_clock_is_non_secure(id)) {
|
|
+ assert(!stm32mp1_clk_get_refcount(id));
|
|
+
|
|
+ if (stm32mp1_clk_is_enabled(id)) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ stm32mp1_clk_enable_secure(id);
|
|
+}
|
|
+
|
|
+void stm32_clock_disable(unsigned long id)
|
|
+{
|
|
+ if (registering_locked) {
|
|
+ if (stm32mp_clock_is_non_secure(id)) {
|
|
+ if (!stm32mp1_clk_get_refcount(id)) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ stm32mp1_clk_disable_secure(id);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Shared peripherals and resources.
|
|
+ * Defines resource that may be non secure, secure or shared.
|
|
+ * May be a device, a bus, a clock, a memory.
|
|
+ *
|
|
+ * State default to PERIPH_UNREGISTERED resource is not explicitly
|
|
+ * set here.
|
|
+ *
|
|
+ * Resource driver not built (upon CFG_xxx), the resource defaults
|
|
+ * to non secure ownership.
|
|
+ *
|
|
+ * Each IO of the GPIOZ IO can be secure or non secure.
|
|
+ * When the GPIO driver is enabled, the GPIOZ bank is fully non secure
|
|
+ * only if each IO is non secure and the GPIOZ bank is shared if it
|
|
+ * includes secure and non secure IOs.
|
|
+ *
|
|
+ * BKPSRAM is assumed shared.
|
|
+ * DDR control (DDRC and DDRPHY) is secure.
|
|
+ * Inits will define the resource state according the device tree
|
|
+ * and the driver initialization sequences.
|
|
+ *
|
|
+ * The platform initialization uses these information to set the ETZPC
|
|
+ * configuration. Non secure services (as clocks or regulator accesses)
|
|
+ * rely on these information to drive the related service execution.
|
|
+ */
|
|
+#define SHRES_NON_SECURE 3
|
|
+#define SHRES_SHARED 2
|
|
+#define SHRES_SECURE 1
|
|
+#define SHRES_UNREGISTERED 0
|
|
+
|
|
+static uint8_t shres_state[STM32MP1_SHRES_COUNT] = {
|
|
+#if !defined(CFG_STM32_IWDG)
|
|
+ [STM32MP1_SHRES_IWDG1] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_UART)
|
|
+ [STM32MP1_SHRES_USART1] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_SPI)
|
|
+ [STM32MP1_SHRES_SPI6] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_I2C)
|
|
+ [STM32MP1_SHRES_I2C4] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_I2C6] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_GPIO)
|
|
+ [STM32MP1_SHRES_GPIOZ(0)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(1)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(2)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(3)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(4)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(5)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(6)] = SHRES_NON_SECURE,
|
|
+ [STM32MP1_SHRES_GPIOZ(7)] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_RNG)
|
|
+ [STM32MP1_SHRES_RNG1] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_HASH)
|
|
+ [STM32MP1_SHRES_HASH1] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_CRYP)
|
|
+ [STM32MP1_SHRES_CRYP1] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+#if !defined(CFG_STM32_RTC)
|
|
+ [STM32MP1_SHRES_RTC] = SHRES_NON_SECURE,
|
|
+#endif
|
|
+};
|
|
+
|
|
+#if CFG_TEE_CORE_LOG_LEVEL > 0
|
|
+static const char *shres2str_id_tbl[STM32MP1_SHRES_COUNT] = {
|
|
+ [STM32MP1_SHRES_GPIOZ(0)] = "GPIOZ0",
|
|
+ [STM32MP1_SHRES_GPIOZ(1)] = "GPIOZ1",
|
|
+ [STM32MP1_SHRES_GPIOZ(2)] = "GPIOZ2",
|
|
+ [STM32MP1_SHRES_GPIOZ(3)] = "GPIOZ3",
|
|
+ [STM32MP1_SHRES_GPIOZ(4)] = "GPIOZ4",
|
|
+ [STM32MP1_SHRES_GPIOZ(5)] = "GPIOZ5",
|
|
+ [STM32MP1_SHRES_GPIOZ(6)] = "GPIOZ6",
|
|
+ [STM32MP1_SHRES_GPIOZ(7)] = "GPIOZ7",
|
|
+ [STM32MP1_SHRES_IWDG1] = "IWDG1",
|
|
+ [STM32MP1_SHRES_USART1] = "USART1",
|
|
+ [STM32MP1_SHRES_SPI6] = "SPI6",
|
|
+ [STM32MP1_SHRES_I2C4] = "I2C4",
|
|
+ [STM32MP1_SHRES_RNG1] = "RNG1",
|
|
+ [STM32MP1_SHRES_HASH1] = "HASH1",
|
|
+ [STM32MP1_SHRES_CRYP1] = "CRYP1",
|
|
+ [STM32MP1_SHRES_I2C6] = "I2C6",
|
|
+ [STM32MP1_SHRES_RTC] = "RTC",
|
|
+ [STM32MP1_SHRES_MCU] = "MCU",
|
|
+ [STM32MP1_SHRES_HSI] = "HSI",
|
|
+ [STM32MP1_SHRES_LSI] = "LSI",
|
|
+ [STM32MP1_SHRES_HSE] = "HSE",
|
|
+ [STM32MP1_SHRES_LSE] = "LSE",
|
|
+ [STM32MP1_SHRES_CSI] = "CSI",
|
|
+ [STM32MP1_SHRES_PLL1] = "PLL1",
|
|
+ [STM32MP1_SHRES_PLL1_P] = "PLL1_P",
|
|
+ [STM32MP1_SHRES_PLL1_Q] = "PLL1_Q",
|
|
+ [STM32MP1_SHRES_PLL1_R] = "PLL1_R",
|
|
+ [STM32MP1_SHRES_PLL2] = "PLL2",
|
|
+ [STM32MP1_SHRES_PLL2_P] = "PLL2_P",
|
|
+ [STM32MP1_SHRES_PLL2_Q] = "PLL2_Q",
|
|
+ [STM32MP1_SHRES_PLL2_R] = "PLL2_R",
|
|
+ [STM32MP1_SHRES_PLL3] = "PLL3",
|
|
+ [STM32MP1_SHRES_PLL3_P] = "PLL3_P",
|
|
+ [STM32MP1_SHRES_PLL3_Q] = "PLL3_Q",
|
|
+ [STM32MP1_SHRES_PLL3_R] = "PLL3_R",
|
|
+};
|
|
+
|
|
+static const char *shres2str_id(unsigned int id)
|
|
+{
|
|
+ return shres2str_id_tbl[id];
|
|
+}
|
|
+
|
|
+static const char *shres2str_state_tbl[4] = {
|
|
+ [SHRES_SHARED] = "shared",
|
|
+ [SHRES_NON_SECURE] = "non secure",
|
|
+ [SHRES_SECURE] = "secure",
|
|
+ [SHRES_UNREGISTERED] = "unregistered",
|
|
+};
|
|
+
|
|
+static const char *shres2str_state(unsigned int id)
|
|
+{
|
|
+ return shres2str_state_tbl[id];
|
|
+}
|
|
+#else
|
|
+static __maybe_unused const char *shres2str_id(unsigned int __unused id)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static __maybe_unused const char *shres2str_state(unsigned int __unused id)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+#endif
|
|
+
|
|
+struct shres2decprot {
|
|
+ unsigned int shres_id;
|
|
+ unsigned int decprot_id;
|
|
+ const char *decprot_str;
|
|
+};
|
|
+
|
|
+#define SHRES2DECPROT(shres, decprot, str) { \
|
|
+ .shres_id = shres, \
|
|
+ .decprot_id = decprot, \
|
|
+ .decprot_str = str, \
|
|
+ }
|
|
+
|
|
+#define SHRES_INVALID ~0U
|
|
+static const struct shres2decprot shres2decprot_tbl[] = {
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_IWDG1, STM32MP1_ETZPC_IWDG1_ID, "IWDG1"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_USART1, STM32MP1_ETZPC_USART1_ID, "UART1"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_SPI6, STM32MP1_ETZPC_SPI6_ID, "SPI6"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_I2C4, STM32MP1_ETZPC_I2C4_ID, "I2C4"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_RNG1, STM32MP1_ETZPC_RNG1_ID, "RNG1"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_HASH1, STM32MP1_ETZPC_HASH1_ID, "HASH1"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_CRYP1, STM32MP1_ETZPC_CRYP1_ID, "CRYP1"),
|
|
+ SHRES2DECPROT(STM32MP1_SHRES_I2C6, STM32MP1_ETZPC_I2C6_ID, "I2C6"),
|
|
+ /* Below are specific IDs without a 1-to-1 mapping to SHRES IDs */
|
|
+ SHRES2DECPROT(SHRES_INVALID, STM32MP1_ETZPC_STGENC_ID, "STGEN"),
|
|
+ SHRES2DECPROT(SHRES_INVALID, STM32MP1_ETZPC_BKPSRAM_ID, "BKPSRAM"),
|
|
+ SHRES2DECPROT(SHRES_INVALID, STM32MP1_ETZPC_GPIOZ_ID, "GPIOZ"),
|
|
+ SHRES2DECPROT(SHRES_INVALID, STM32MP1_ETZPC_DDRCTRL_ID, "DDRCTRL"),
|
|
+ SHRES2DECPROT(SHRES_INVALID, STM32MP1_ETZPC_DDRPHYC_ID, "DDRPHY"),
|
|
+};
|
|
+
|
|
+static unsigned int decprot2shres(unsigned int decprot_id)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(shres2decprot_tbl); i++) {
|
|
+ if (shres2decprot_tbl[i].decprot_id == decprot_id) {
|
|
+ return shres2decprot_tbl[i].shres_id;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ DMSG("No share resource ID %u", decprot_id);
|
|
+ return SHRES_INVALID;
|
|
+}
|
|
+
|
|
+static const char *decprot2str(unsigned int decprot_id)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(shres2decprot_tbl); i++) {
|
|
+ if (shres2decprot_tbl[i].decprot_id == decprot_id) {
|
|
+ return shres2decprot_tbl[i].decprot_str;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ EMSG("Invalid ID %u", decprot_id);
|
|
+ panic();
|
|
+}
|
|
+
|
|
+/* GPIOZ bank may have several number of pins */
|
|
+#if CFG_DT
|
|
+static int gpioz_nbpin = -1;
|
|
+
|
|
+static unsigned int get_gpioz_nbpin_unpg(void)
|
|
+{
|
|
+ if (gpioz_nbpin >= 0) {
|
|
+ return (unsigned int)gpioz_nbpin;
|
|
+ }
|
|
+
|
|
+ panic();
|
|
+}
|
|
+KEEP_PAGER(get_gpioz_nbpin_unpg);
|
|
+
|
|
+static unsigned int get_gpioz_nbpin(void)
|
|
+{
|
|
+ if (gpioz_nbpin < 0) {
|
|
+ void *fdt;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ gpioz_nbpin = fdt_get_gpioz_nbpins_from_dt(fdt);
|
|
+ assert((gpioz_nbpin == 0) ||
|
|
+ (gpioz_nbpin == STM32MP1_GPIOZ_PIN_MAX_COUNT));
|
|
+ }
|
|
+
|
|
+ return (unsigned int)gpioz_nbpin;
|
|
+}
|
|
+#else
|
|
+static unsigned int get_gpioz_nbpin_unpg(void)
|
|
+{
|
|
+ return STM32MP1_GPIOZ_PIN_MAX_COUNT;
|
|
+}
|
|
+
|
|
+static unsigned int get_gpioz_nbpin(void)
|
|
+{
|
|
+ return get_gpioz_nbpin_unpg();
|
|
+}
|
|
+#endif
|
|
+
|
|
+static bool shareable_resource(unsigned int id)
|
|
+{
|
|
+ switch (id) {
|
|
+ default:
|
|
+ /* Currently no shareable resource */
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool mckprot_resource(unsigned int id)
|
|
+{
|
|
+ switch (id) {
|
|
+ case STM32MP1_SHRES_MCU:
|
|
+ case STM32MP1_SHRES_PLL3:
|
|
+ case STM32MP1_SHRES_PLL3_P:
|
|
+ case STM32MP1_SHRES_PLL3_Q:
|
|
+ case STM32MP1_SHRES_PLL3_R:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void register_periph(unsigned int id, unsigned int state)
|
|
+{
|
|
+ assert(id < STM32MP1_SHRES_COUNT &&
|
|
+ state > SHRES_UNREGISTERED &&
|
|
+ state <= SHRES_NON_SECURE);
|
|
+
|
|
+ if (registering_locked) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ if ((state == SHRES_SHARED && !shareable_resource(id)) ||
|
|
+ ((shres_state[id] != SHRES_UNREGISTERED) &&
|
|
+ (shres_state[id] != state))) {
|
|
+ DMSG("Cannot change %s from %s to %s",
|
|
+ shres2str_id(id),
|
|
+ shres2str_state(shres_state[id]),
|
|
+ shres2str_state(state));
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ if (shres_state[id] == SHRES_UNREGISTERED) {
|
|
+ DMSG("Register %s as %s",
|
|
+ shres2str_id(id), shres2str_state(state));
|
|
+ }
|
|
+
|
|
+ switch (id) {
|
|
+ case STM32MP1_SHRES_GPIOZ(0):
|
|
+ case STM32MP1_SHRES_GPIOZ(1):
|
|
+ case STM32MP1_SHRES_GPIOZ(2):
|
|
+ case STM32MP1_SHRES_GPIOZ(3):
|
|
+ case STM32MP1_SHRES_GPIOZ(4):
|
|
+ case STM32MP1_SHRES_GPIOZ(5):
|
|
+ case STM32MP1_SHRES_GPIOZ(6):
|
|
+ case STM32MP1_SHRES_GPIOZ(7):
|
|
+ if ((id - STM32MP1_SHRES_GPIOZ(0)) >= get_gpioz_nbpin()) {
|
|
+ EMSG("Invalid GPIO pin %u, %u pin(s) available",
|
|
+ id - STM32MP1_SHRES_GPIOZ(0), get_gpioz_nbpin());
|
|
+ panic();
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ shres_state[id] = (uint8_t)state;
|
|
+
|
|
+ /* Explore clock tree to lock dependencies */
|
|
+ if ((state == SHRES_SECURE) || (state == SHRES_SHARED)) {
|
|
+ switch (id) {
|
|
+ case STM32MP1_SHRES_GPIOZ(0):
|
|
+ case STM32MP1_SHRES_GPIOZ(1):
|
|
+ case STM32MP1_SHRES_GPIOZ(2):
|
|
+ case STM32MP1_SHRES_GPIOZ(3):
|
|
+ case STM32MP1_SHRES_GPIOZ(4):
|
|
+ case STM32MP1_SHRES_GPIOZ(5):
|
|
+ case STM32MP1_SHRES_GPIOZ(6):
|
|
+ case STM32MP1_SHRES_GPIOZ(7):
|
|
+ stm32mp_register_clock_parents_secure(GPIOZ);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_IWDG1:
|
|
+ stm32mp_register_clock_parents_secure(IWDG1);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_USART1:
|
|
+ stm32mp_register_clock_parents_secure(USART1_K);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_SPI6:
|
|
+ stm32mp_register_clock_parents_secure(SPI6_K);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_I2C4:
|
|
+ stm32mp_register_clock_parents_secure(I2C4_K);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_RNG1:
|
|
+ stm32mp_register_clock_parents_secure(RNG1_K);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_HASH1:
|
|
+ stm32mp_register_clock_parents_secure(HASH1);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_CRYP1:
|
|
+ stm32mp_register_clock_parents_secure(CRYP1);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_I2C6:
|
|
+ stm32mp_register_clock_parents_secure(I2C6_K);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_RTC:
|
|
+ stm32mp_register_clock_parents_secure(RTC);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_PLL1_P:
|
|
+ case STM32MP1_SHRES_PLL1_Q:
|
|
+ case STM32MP1_SHRES_PLL1_R:
|
|
+ register_periph(STM32MP1_SHRES_PLL1, SHRES_SECURE);
|
|
+ stm32mp_register_clock_parents_secure(PLL1);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_PLL2_P:
|
|
+ case STM32MP1_SHRES_PLL2_Q:
|
|
+ case STM32MP1_SHRES_PLL2_R:
|
|
+ register_periph(STM32MP1_SHRES_PLL2, SHRES_SECURE);
|
|
+ stm32mp_register_clock_parents_secure(PLL2);
|
|
+ break;
|
|
+ case STM32MP1_SHRES_PLL3_P:
|
|
+ case STM32MP1_SHRES_PLL3_Q:
|
|
+ case STM32MP1_SHRES_PLL3_R:
|
|
+ register_periph(STM32MP1_SHRES_PLL3, SHRES_SECURE);
|
|
+ stm32mp_register_clock_parents_secure(PLL3);
|
|
+ break;
|
|
+ default:
|
|
+ /* No expected resource dependency */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Register resource by ID */
|
|
+void stm32mp_register_secure_periph(unsigned int id)
|
|
+{
|
|
+ register_periph(id, SHRES_SECURE);
|
|
+}
|
|
+
|
|
+void stm32mp_register_shared_periph(unsigned int id)
|
|
+{
|
|
+ register_periph(id, SHRES_SHARED);
|
|
+}
|
|
+
|
|
+void stm32mp_register_non_secure_periph(unsigned int id)
|
|
+{
|
|
+ register_periph(id, SHRES_NON_SECURE);
|
|
+}
|
|
+
|
|
+/* Register resource by IO memory base address */
|
|
+static void register_periph_iomem(uintptr_t base, unsigned int state)
|
|
+{
|
|
+ unsigned int id;
|
|
+
|
|
+ switch (base) {
|
|
+ case IWDG1_BASE:
|
|
+ id = STM32MP1_SHRES_IWDG1;
|
|
+ break;
|
|
+ case USART1_BASE:
|
|
+ id = STM32MP1_SHRES_USART1;
|
|
+ break;
|
|
+ case SPI6_BASE:
|
|
+ id = STM32MP1_SHRES_SPI6;
|
|
+ break;
|
|
+ case I2C4_BASE:
|
|
+ id = STM32MP1_SHRES_I2C4;
|
|
+ break;
|
|
+ case I2C6_BASE:
|
|
+ id = STM32MP1_SHRES_I2C6;
|
|
+ break;
|
|
+ case RTC_BASE:
|
|
+ id = STM32MP1_SHRES_RTC;
|
|
+ break;
|
|
+ case RNG1_BASE:
|
|
+ id = STM32MP1_SHRES_RNG1;
|
|
+ break;
|
|
+ case CRYP1_BASE:
|
|
+ id = STM32MP1_SHRES_CRYP1;
|
|
+ break;
|
|
+ case HASH1_BASE:
|
|
+ id = STM32MP1_SHRES_HASH1;
|
|
+ break;
|
|
+
|
|
+#ifdef CFG_WITH_NSEC_GPIOS
|
|
+ case GPIOA_BASE:
|
|
+ case GPIOB_BASE:
|
|
+ case GPIOC_BASE:
|
|
+ case GPIOD_BASE:
|
|
+ case GPIOE_BASE:
|
|
+ case GPIOF_BASE:
|
|
+ case GPIOG_BASE:
|
|
+ case GPIOH_BASE:
|
|
+ case GPIOI_BASE:
|
|
+ case GPIOJ_BASE:
|
|
+ case GPIOK_BASE:
|
|
+#endif
|
|
+#ifdef CFG_WITH_NSEC_UARTS
|
|
+ case USART2_BASE:
|
|
+ case USART3_BASE:
|
|
+ case UART4_BASE:
|
|
+ case UART5_BASE:
|
|
+ case USART6_BASE:
|
|
+ case UART7_BASE:
|
|
+ case UART8_BASE:
|
|
+#endif
|
|
+ case IWDG2_BASE:
|
|
+ /* Allow drivers to register some non secure resources */
|
|
+ DMSG("IO for non secure resource 0x%lx", base);
|
|
+ if (state != SHRES_NON_SECURE)
|
|
+ panic();
|
|
+
|
|
+ return;
|
|
+
|
|
+ default:
|
|
+ panic();
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ register_periph(id, state);
|
|
+}
|
|
+
|
|
+void stm32mp_register_secure_periph_iomem(uintptr_t base)
|
|
+{
|
|
+ register_periph_iomem(base, SHRES_SECURE);
|
|
+}
|
|
+
|
|
+void stm32mp_register_non_secure_periph_iomem(uintptr_t base)
|
|
+{
|
|
+ register_periph_iomem(base, SHRES_NON_SECURE);
|
|
+}
|
|
+
|
|
+/* Register GPIO resource */
|
|
+void stm32mp_register_secure_gpio(unsigned int bank, unsigned int pin)
|
|
+{
|
|
+ switch (bank) {
|
|
+ case GPIO_BANK_Z:
|
|
+ register_periph(STM32MP1_SHRES_GPIOZ(pin), SHRES_SECURE);
|
|
+ break;
|
|
+ default:
|
|
+ EMSG("GPIO bank %u cannot be secured", bank);
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin)
|
|
+{
|
|
+ switch (bank) {
|
|
+ case GPIO_BANK_Z:
|
|
+ register_periph(STM32MP1_SHRES_GPIOZ(pin), SHRES_NON_SECURE);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void stm32mp_register_etzpc_decprot(unsigned int id,
|
|
+ enum etzpc_decprot_attributes attr)
|
|
+{
|
|
+ unsigned int state = SHRES_SECURE;
|
|
+ unsigned int id_shres;
|
|
+
|
|
+ switch (attr) {
|
|
+ case TZPC_DECPROT_S_RW:
|
|
+ break;
|
|
+ case TZPC_DECPROT_NS_R_S_W:
|
|
+ case TZPC_DECPROT_MCU_ISOLATION:
|
|
+ case TZPC_DECPROT_NS_RW:
|
|
+ state = SHRES_NON_SECURE;
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ switch (id) {
|
|
+ case STM32MP1_ETZPC_GPIOZ_ID:
|
|
+ if (state == SHRES_SECURE) {
|
|
+ /* GPIOZ cannot be hardened from the ETZPC */
|
|
+ panic();
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ id_shres = decprot2shres(id);
|
|
+ if (id_shres == SHRES_INVALID) {
|
|
+ if (state != SHRES_NON_SECURE)
|
|
+ panic();
|
|
+ } else {
|
|
+ register_periph(id_shres, state);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Get resource state: these accesses lock the registering support */
|
|
+static void lock_registering(void)
|
|
+{
|
|
+ registering_locked = true;
|
|
+}
|
|
+
|
|
+bool stm32mp_periph_is_shared(unsigned long id)
|
|
+{
|
|
+ lock_registering();
|
|
+
|
|
+ return shres_state[id] == SHRES_SHARED;
|
|
+}
|
|
+
|
|
+bool stm32mp_periph_is_non_secure(unsigned long id)
|
|
+{
|
|
+ lock_registering();
|
|
+
|
|
+ return shres_state[id] == SHRES_NON_SECURE;
|
|
+}
|
|
+
|
|
+bool stm32mp_periph_is_secure(unsigned long id)
|
|
+{
|
|
+ lock_registering();
|
|
+
|
|
+ return shres_state[id] == SHRES_SECURE;
|
|
+}
|
|
+
|
|
+bool stm32mp_periph_is_unregistered(unsigned long id)
|
|
+{
|
|
+ lock_registering();
|
|
+
|
|
+ return shres_state[id] == SHRES_UNREGISTERED;
|
|
+}
|
|
+
|
|
+bool stm32mp_gpio_bank_is_shared(unsigned int bank)
|
|
+{
|
|
+ unsigned int non_secure = 0;
|
|
+ unsigned int i;
|
|
+
|
|
+ lock_registering();
|
|
+
|
|
+ if (bank != GPIO_BANK_Z) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < get_gpioz_nbpin_unpg(); i++) {
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_GPIOZ(i)) ||
|
|
+ stm32mp_periph_is_unregistered(STM32MP1_SHRES_GPIOZ(i))) {
|
|
+ non_secure++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (non_secure != 0) && (non_secure < get_gpioz_nbpin_unpg());
|
|
+}
|
|
+
|
|
+bool stm32mp_gpio_bank_is_non_secure(unsigned int bank)
|
|
+{
|
|
+ unsigned int non_secure = 0;
|
|
+ unsigned int i;
|
|
+
|
|
+ lock_registering();
|
|
+
|
|
+ if (bank != GPIO_BANK_Z) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < get_gpioz_nbpin_unpg(); i++) {
|
|
+ if (stm32mp_periph_is_non_secure(STM32MP1_SHRES_GPIOZ(i)) ||
|
|
+ stm32mp_periph_is_unregistered(STM32MP1_SHRES_GPIOZ(i))) {
|
|
+ non_secure++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return non_secure == get_gpioz_nbpin_unpg();
|
|
+}
|
|
+
|
|
+bool stm32mp_gpio_bank_is_secure(unsigned int bank)
|
|
+{
|
|
+ unsigned int secure = 0;
|
|
+ unsigned int i;
|
|
+
|
|
+ lock_registering();
|
|
+
|
|
+ if (bank != GPIO_BANK_Z) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < get_gpioz_nbpin_unpg(); i++) {
|
|
+ if (stm32mp_periph_is_secure(STM32MP1_SHRES_GPIOZ(i))) {
|
|
+ secure++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return secure == get_gpioz_nbpin_unpg();
|
|
+}
|
|
+
|
|
+bool stm32mp_clock_is_shareable(unsigned long clock_id)
|
|
+{
|
|
+ switch (clock_id) {
|
|
+ case GPIOZ:
|
|
+ return get_gpioz_nbpin() > 0;
|
|
+ case RTCAPB:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+bool stm32mp_clock_is_shared(unsigned long clock_id)
|
|
+{
|
|
+ lock_registering();
|
|
+
|
|
+ switch (clock_id) {
|
|
+ case GPIOZ:
|
|
+ if (get_gpioz_nbpin_unpg() > 0) {
|
|
+ return stm32mp_gpio_bank_is_shared(GPIO_BANK_Z);
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ case RTCAPB:
|
|
+ /* RTCAPB is shared for non secure backup registers */
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+bool stm32mp_clock_is_non_secure(unsigned long clock_id)
|
|
+{
|
|
+ unsigned int shres_id;
|
|
+
|
|
+ lock_registering();
|
|
+
|
|
+ if (stm32mp_clock_is_shared(clock_id)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ switch (clock_id) {
|
|
+ case BSEC:
|
|
+ case BKPSRAM:
|
|
+ case TZPC:
|
|
+ case TZC1:
|
|
+ case TZC2:
|
|
+ case STGEN_K:
|
|
+ case DDRC1:
|
|
+ case DDRC1LP:
|
|
+ case DDRC2:
|
|
+ case DDRC2LP:
|
|
+ case DDRPHYC:
|
|
+ case DDRPHYCLP:
|
|
+ case DDRCAPB:
|
|
+ case DDRCAPBLP:
|
|
+ case AXIDCG:
|
|
+ case DDRPHYCAPB:
|
|
+ case DDRPHYCAPBLP:
|
|
+ return false;
|
|
+ case IWDG1:
|
|
+ shres_id = STM32MP1_SHRES_IWDG1;
|
|
+ break;
|
|
+ case USART1_K:
|
|
+ shres_id = STM32MP1_SHRES_USART1;
|
|
+ break;
|
|
+ case SPI6_K:
|
|
+ shres_id = STM32MP1_SHRES_SPI6;
|
|
+ break;
|
|
+ case I2C4_K:
|
|
+ shres_id = STM32MP1_SHRES_I2C4;
|
|
+ break;
|
|
+ case RNG1_K:
|
|
+ shres_id = STM32MP1_SHRES_RNG1;
|
|
+ break;
|
|
+ case HASH1:
|
|
+ shres_id = STM32MP1_SHRES_HASH1;
|
|
+ break;
|
|
+ case CRYP1:
|
|
+ shres_id = STM32MP1_SHRES_CRYP1;
|
|
+ break;
|
|
+ case I2C6_K:
|
|
+ shres_id = STM32MP1_SHRES_I2C6;
|
|
+ break;
|
|
+ case RTC:
|
|
+ shres_id = STM32MP1_SHRES_RTC;
|
|
+ break;
|
|
+ default:
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return stm32mp_periph_is_non_secure(shres_id);
|
|
+}
|
|
+
|
|
+/* ETZPC configuration at drivers initialization completion */
|
|
+static enum etzpc_decprot_attributes decprot_periph_attr(unsigned int id)
|
|
+{
|
|
+ switch (id) {
|
|
+ case STM32MP1_SHRES_GPIOZ(0):
|
|
+ case STM32MP1_SHRES_GPIOZ(1):
|
|
+ case STM32MP1_SHRES_GPIOZ(2):
|
|
+ case STM32MP1_SHRES_GPIOZ(3):
|
|
+ case STM32MP1_SHRES_GPIOZ(4):
|
|
+ case STM32MP1_SHRES_GPIOZ(5):
|
|
+ case STM32MP1_SHRES_GPIOZ(6):
|
|
+ case STM32MP1_SHRES_GPIOZ(7):
|
|
+ assert((id - STM32MP1_SHRES_GPIOZ(0)) < get_gpioz_nbpin_unpg());
|
|
+ return TZPC_DECPROT_NS_RW;
|
|
+ default:
|
|
+ if (stm32mp_periph_is_non_secure(id)) {
|
|
+ return TZPC_DECPROT_NS_RW;
|
|
+ }
|
|
+
|
|
+ return TZPC_DECPROT_S_RW;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool check_decprot(unsigned int id, enum etzpc_decprot_attributes exp)
|
|
+{
|
|
+ enum etzpc_decprot_attributes cur = etzpc_get_decprot(id);
|
|
+
|
|
+ if (cur == exp) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ switch (exp) {
|
|
+ case TZPC_DECPROT_NS_RW:
|
|
+ if (cur == TZPC_DECPROT_S_RW) {
|
|
+#ifdef CFG_DT
|
|
+ IMSG("Warning ETZPC: %s could be non secure",
|
|
+ decprot2str(id));
|
|
+#else
|
|
+ etzpc_configure_decprot(id, TZPC_DECPROT_NS_RW);
|
|
+ IMSG("Warning ETZPC: %s forced non secure",
|
|
+ decprot2str(id));
|
|
+#endif
|
|
+ }
|
|
+ return true;
|
|
+
|
|
+ case TZPC_DECPROT_S_RW:
|
|
+ EMSG("ETZPC DECPROT: %s (%u) expected secure but DECPROT=%d",
|
|
+ decprot2str(id), id, cur);
|
|
+ break;
|
|
+
|
|
+ case TZPC_DECPROT_NS_R_S_W:
|
|
+ case TZPC_DECPROT_MCU_ISOLATION:
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void check_etzpc_secure_configuration(void)
|
|
+{
|
|
+ bool error = false;
|
|
+
|
|
+ assert(registering_locked);
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_STGENC_ID,
|
|
+ TZPC_DECPROT_S_RW);
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_BKPSRAM_ID,
|
|
+ TZPC_DECPROT_S_RW);
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_IWDG1_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_IWDG1));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_USART1_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_USART1));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_SPI6_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_SPI6));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_I2C4_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_I2C4));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_RNG1_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_RNG1));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_HASH1_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_HASH1));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_CRYP1_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_CRYP1));
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_DDRCTRL_ID,
|
|
+ TZPC_DECPROT_S_RW);
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_DDRPHYC_ID,
|
|
+ TZPC_DECPROT_S_RW);
|
|
+
|
|
+ error |= !check_decprot(STM32MP1_ETZPC_I2C6_ID,
|
|
+ decprot_periph_attr(STM32MP1_SHRES_I2C6));
|
|
+
|
|
+ if (!error) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ panic();
|
|
+}
|
|
+
|
|
+static void check_rcc_secure_configuration(void)
|
|
+{
|
|
+ bool secure = stm32_rcc_is_secure();
|
|
+ bool mckprot = stm32_rcc_is_mckprot();
|
|
+ unsigned int n;
|
|
+ unsigned int error = 0;
|
|
+
|
|
+ for (n = 0; n < ARRAY_SIZE(shres_state); n++) {
|
|
+ if ((shres_state[n] == SHRES_SECURE) ||
|
|
+ (shres_state[n] == SHRES_SHARED)) {
|
|
+ if ((mckprot_resource(n) && (!mckprot)) || !secure) {
|
|
+ EMSG("RCC %s MCKPROT %s and %s (%u) secure",
|
|
+ secure ? "secure" : "non secure",
|
|
+ mckprot ? "set" : "not set",
|
|
+ shres2str_id(n), n);
|
|
+ error++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (error != 0U) {
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void gpio_secure_suspend_resume(enum pm_op op, void __unused *handle)
|
|
+{
|
|
+ unsigned int pin;
|
|
+
|
|
+ switch (op) {
|
|
+ case PM_OP_SUSPEND:
|
|
+ return;
|
|
+ case PM_OP_RESUME:
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ /* Release secure hardening of non secure pins at resume */
|
|
+ for (pin = 0; pin < get_gpioz_nbpin_unpg(); pin++) {
|
|
+ unsigned int id = STM32MP1_SHRES_GPIOZ(pin);
|
|
+
|
|
+ if (stm32mp_periph_is_non_secure(id) ||
|
|
+ stm32mp_periph_is_unregistered(id)) {
|
|
+ stm32_gpio_set_secure_cfg(GPIO_BANK_Z, pin, false);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+KEEP_PAGER(gpio_secure_suspend_resume);
|
|
+
|
|
+static void check_gpio_secure_configuration(void)
|
|
+{
|
|
+ unsigned int pin;
|
|
+ bool secure;
|
|
+
|
|
+ for (pin = 0; pin < get_gpioz_nbpin(); pin++) {
|
|
+ secure = stm32mp_periph_is_secure(STM32MP1_SHRES_GPIOZ(pin));
|
|
+ stm32_gpio_set_secure_cfg(GPIO_BANK_Z, pin, secure);
|
|
+ }
|
|
+
|
|
+ stm32mp_register_pm_cb(gpio_secure_suspend_resume, NULL);
|
|
+}
|
|
+
|
|
+static TEE_Result stm32mp1_init_drivers(void)
|
|
+{
|
|
+ size_t id;
|
|
+
|
|
+ registering_locked = true;
|
|
+
|
|
+ for (id = 0; id < STM32MP1_SHRES_COUNT; id++) {
|
|
+ uint8_t *state = &shres_state[id];
|
|
+
|
|
+#if TRACE_LEVEL == TRACE_INFO
|
|
+ /* Display only the secure and shared resources */
|
|
+ if ((*state == SHRES_NON_SECURE) ||
|
|
+ ((*state == SHRES_UNREGISTERED))) {
|
|
+ continue;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ IMSG("stm32mp %-8s (%2u): %-14s",
|
|
+ shres2str_id(id), id, shres2str_state(*state));
|
|
+ }
|
|
+
|
|
+ stm32mp_update_earlyboot_clocks_state();
|
|
+
|
|
+ check_rcc_secure_configuration();
|
|
+ check_etzpc_secure_configuration();
|
|
+ check_gpio_secure_configuration();
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init_late(stm32mp1_init_drivers);
|
|
diff --git a/core/arch/arm/plat-stm32mp1/stm32_util.h b/core/arch/arm/plat-stm32mp1/stm32_util.h
|
|
new file mode 100644
|
|
index 0000000..0164855
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/stm32_util.h
|
|
@@ -0,0 +1,287 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_UTIL_H__
|
|
+#define __STM32_UTIL_H__
|
|
+
|
|
+#include <io.h>
|
|
+#include <drivers/stm32_etzpc.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_mmu.h>
|
|
+#include <sm/sm.h>
|
|
+#include <stdint.h>
|
|
+#include <stdbool.h>
|
|
+
|
|
+/* SoC versioning */
|
|
+uint32_t stm32mp1_dbgmcu_get_chip_version(void);
|
|
+
|
|
+/* SiP & OEM platform services */
|
|
+bool stm32_sip_service(struct sm_ctx *ctx,
|
|
+ uint32_t *a0, uint32_t *a1, uint32_t *a2, uint32_t *a3);
|
|
+bool stm32_oem_service(struct sm_ctx *ctx,
|
|
+ uint32_t *a0, uint32_t *a1, uint32_t *a2, uint32_t *a3);
|
|
+
|
|
+
|
|
+/* Platform util for the STGEN driver */
|
|
+uintptr_t stm32_get_stgen_base(void);
|
|
+
|
|
+/* Platform util for the GIC */
|
|
+uintptr_t get_gicc_base(void);
|
|
+uintptr_t get_gicd_base(void);
|
|
+
|
|
+/* Platform util for clock gating. ID refers to clock DT bindings ID. */
|
|
+void stm32_clock_enable(unsigned long id);
|
|
+void stm32_clock_disable(unsigned long id);
|
|
+
|
|
+static inline unsigned long stm32_clock_get_rate(unsigned long id)
|
|
+{
|
|
+ return stm32mp1_clk_get_rate(id);
|
|
+}
|
|
+
|
|
+static inline unsigned long stm32_clock_is_enabled(unsigned long id)
|
|
+{
|
|
+ return stm32mp1_clk_is_enabled(id);
|
|
+}
|
|
+
|
|
+/* Platform util for the GPIO driver */
|
|
+uintptr_t stm32_get_gpio_bank_base(unsigned int bank);
|
|
+uint32_t stm32_get_gpio_bank_offset(unsigned int bank);
|
|
+int stm32_get_gpio_bank_clock(unsigned int bank);
|
|
+
|
|
+/* Platform util for the IWDG driver */
|
|
+unsigned long stm32_get_iwdg_otp_config(uintptr_t pbase);
|
|
+int stm32mp_iwdg_irq2instance(size_t irq);
|
|
+size_t stm32mp_iwdg_instance2irq(int instance);
|
|
+unsigned int stm32mp_iwdg_iomem2instance(uintptr_t pbase);
|
|
+
|
|
+/* Platform util for the BSEC driver */
|
|
+unsigned int stm32mp_get_otp_max(void);
|
|
+unsigned int stm32mp_get_otp_upper_start(void);
|
|
+
|
|
+/* Platform util for the ETZPC driver */
|
|
+uintptr_t stm32mp_get_etzpc_base(void);
|
|
+enum etzpc_decprot_attributes stm32mp_etzpc_binding2decprot(uint32_t mode);
|
|
+
|
|
+/* Platform util for the RTC driver */
|
|
+bool stm32_rtc_get_read_twice(void);
|
|
+
|
|
+/* Backup registers and RAM utils */
|
|
+uintptr_t stm32mp_bkpreg(unsigned int idx);
|
|
+
|
|
+uintptr_t stm32mp1_bkpsram_base(void);
|
|
+
|
|
+/* Platform util for PMIC support */
|
|
+bool stm32mp_with_pmic(void);
|
|
+
|
|
+/* Power management service */
|
|
+void stm32mp_register_online_cpu(void);
|
|
+
|
|
+/*
|
|
+ * Lock/unlock access to shared registers
|
|
+ *
|
|
+ * @lock - NULL or pointer to spin lock
|
|
+ */
|
|
+uint32_t lock_stm32shregs(void);
|
|
+void unlock_stm32shregs(uint32_t exceptions);
|
|
+
|
|
+void io_mask32_stm32shregs(uintptr_t va, uint32_t value, uint32_t mask);
|
|
+
|
|
+static inline void stm32shregs_setbits(uintptr_t va, uint32_t value)
|
|
+{
|
|
+ io_mask32_stm32shregs(va, value, value);
|
|
+}
|
|
+
|
|
+static inline void stm32shregs_clrbits(uintptr_t va, uint32_t value)
|
|
+{
|
|
+ io_mask32_stm32shregs(va, 0, value);
|
|
+}
|
|
+
|
|
+static inline void stm32shregs_clrsetbits(uintptr_t va, uint32_t mask,
|
|
+ uint32_t value)
|
|
+{
|
|
+ io_mask32_stm32shregs(va, value, mask);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Generic spinlock function that bypass spinlock if MMU is disabled or
|
|
+ * lock is NULL.
|
|
+ */
|
|
+uint32_t may_spin_lock(unsigned int *lock);
|
|
+void may_spin_unlock(unsigned int *lock, uint32_t exceptions);
|
|
+
|
|
+/* Reset function for early watchdog management */
|
|
+void stm32mp_platform_reset(int cpu);
|
|
+
|
|
+/* Clock calibration */
|
|
+int stm32mp_start_clock_calib(unsigned int clock_id);
|
|
+
|
|
+/*
|
|
+ * Shared reference counter: increments by 2 on secure increment
|
|
+ * request, decrements by 2 on secure decrement request. Bit #0
|
|
+ * is set to 1 on non-secure increment request and reset to 0 on
|
|
+ * non-secure decrement request. These counter initializes to
|
|
+ * either 0, 1 or 2 upon their expect default state.
|
|
+ * Increment refcount and return if incremented from 0.
|
|
+ * Counters saturates once above UINT_MAX / 2.
|
|
+ */
|
|
+#define SHREFCNT_NONSECURE_FLAG 0x1ul
|
|
+#define SHREFCNT_SECURE_STEP 0x2ul
|
|
+#define SHREFCNT_MAX (UINT_MAX / 2)
|
|
+
|
|
+/* Return 1 if refcnt decrements to 0, else return 0 */
|
|
+static inline int incr_shrefcnt(unsigned int *refcnt, bool secure)
|
|
+{
|
|
+ int rc = !*refcnt;
|
|
+
|
|
+ if (secure) {
|
|
+ if (*refcnt < SHREFCNT_MAX) {
|
|
+ *refcnt += SHREFCNT_SECURE_STEP;
|
|
+ assert(*refcnt < SHREFCNT_MAX);
|
|
+ }
|
|
+ } else {
|
|
+ *refcnt |= SHREFCNT_NONSECURE_FLAG;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Return 1 if refcnt decrements to 0, else return 0 */
|
|
+static inline int decr_shrefcnt(unsigned int *refcnt, bool secure)
|
|
+{
|
|
+ int rc = 0;
|
|
+
|
|
+ if (secure) {
|
|
+ if (*refcnt < SHREFCNT_MAX) {
|
|
+ if (*refcnt < SHREFCNT_SECURE_STEP) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ *refcnt -= SHREFCNT_SECURE_STEP;
|
|
+ rc = !*refcnt;
|
|
+ }
|
|
+ } else {
|
|
+ rc = (*refcnt == SHREFCNT_NONSECURE_FLAG);
|
|
+ *refcnt &= ~SHREFCNT_NONSECURE_FLAG;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int incr_refcnt(unsigned int *refcnt)
|
|
+{
|
|
+ return incr_shrefcnt(refcnt, true);
|
|
+}
|
|
+
|
|
+static inline int decr_refcnt(unsigned int *refcnt)
|
|
+{
|
|
+ return decr_shrefcnt(refcnt, true);
|
|
+}
|
|
+
|
|
+#define STM32MP1_SHRES_GPIOZ(i) (STM32MP1_SHRES_GPIOZ_0 + i)
|
|
+
|
|
+enum stm32mp_shres {
|
|
+ STM32MP1_SHRES_GPIOZ_0 = 0,
|
|
+ STM32MP1_SHRES_GPIOZ_1,
|
|
+ STM32MP1_SHRES_GPIOZ_2,
|
|
+ STM32MP1_SHRES_GPIOZ_3,
|
|
+ STM32MP1_SHRES_GPIOZ_4,
|
|
+ STM32MP1_SHRES_GPIOZ_5,
|
|
+ STM32MP1_SHRES_GPIOZ_6,
|
|
+ STM32MP1_SHRES_GPIOZ_7,
|
|
+ STM32MP1_SHRES_IWDG1,
|
|
+ STM32MP1_SHRES_USART1,
|
|
+ STM32MP1_SHRES_SPI6,
|
|
+ STM32MP1_SHRES_I2C4,
|
|
+ STM32MP1_SHRES_RNG1,
|
|
+ STM32MP1_SHRES_HASH1,
|
|
+ STM32MP1_SHRES_CRYP1,
|
|
+ STM32MP1_SHRES_I2C6,
|
|
+ STM32MP1_SHRES_RTC,
|
|
+ STM32MP1_SHRES_MCU,
|
|
+ STM32MP1_SHRES_HSI,
|
|
+ STM32MP1_SHRES_LSI,
|
|
+ STM32MP1_SHRES_HSE,
|
|
+ STM32MP1_SHRES_LSE,
|
|
+ STM32MP1_SHRES_CSI,
|
|
+ STM32MP1_SHRES_PLL1,
|
|
+ STM32MP1_SHRES_PLL1_P,
|
|
+ STM32MP1_SHRES_PLL1_Q,
|
|
+ STM32MP1_SHRES_PLL1_R,
|
|
+ STM32MP1_SHRES_PLL2,
|
|
+ STM32MP1_SHRES_PLL2_P,
|
|
+ STM32MP1_SHRES_PLL2_Q,
|
|
+ STM32MP1_SHRES_PLL2_R,
|
|
+ STM32MP1_SHRES_PLL3,
|
|
+ STM32MP1_SHRES_PLL3_P,
|
|
+ STM32MP1_SHRES_PLL3_Q,
|
|
+ STM32MP1_SHRES_PLL3_R,
|
|
+
|
|
+ STM32MP1_SHRES_COUNT
|
|
+};
|
|
+
|
|
+void stm32mp_register_secure_periph(unsigned int id);
|
|
+void stm32mp_register_shared_periph(unsigned int id);
|
|
+void stm32mp_register_non_secure_periph(unsigned int id);
|
|
+void stm32mp_register_secure_periph_iomem(uintptr_t base);
|
|
+void stm32mp_register_non_secure_periph_iomem(uintptr_t base);
|
|
+void stm32mp_register_secure_gpio(unsigned int bank, unsigned int pin);
|
|
+void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin);
|
|
+void stm32mp_register_etzpc_decprot(unsigned int id,
|
|
+ enum etzpc_decprot_attributes attr);
|
|
+
|
|
+bool stm32mp_periph_is_shared(unsigned long id);
|
|
+bool stm32mp_periph_is_non_secure(unsigned long id);
|
|
+bool stm32mp_periph_is_secure(unsigned long id);
|
|
+bool stm32mp_periph_is_unregistered(unsigned long id);
|
|
+
|
|
+bool stm32mp_gpio_bank_is_shared(unsigned int bank);
|
|
+bool stm32mp_gpio_bank_is_non_secure(unsigned int bank);
|
|
+bool stm32mp_gpio_bank_is_secure(unsigned int bank);
|
|
+
|
|
+bool stm32mp_clock_is_shareable(unsigned long clock_id);
|
|
+bool stm32mp_clock_is_shared(unsigned long clock_id);
|
|
+bool stm32mp_clock_is_non_secure(unsigned long clock_id);
|
|
+
|
|
+/*
|
|
+ * Set bit fields, clear bit flieds, clear bits and set bits utils
|
|
+ */
|
|
+static inline void mmio_write_8(uintptr_t addr, uint8_t value)
|
|
+{
|
|
+ write8(value, addr);
|
|
+}
|
|
+
|
|
+static inline uint8_t mmio_read_8(uintptr_t addr)
|
|
+{
|
|
+ return read8(addr);
|
|
+}
|
|
+
|
|
+static inline void mmio_write_32(uintptr_t addr, uint32_t value)
|
|
+{
|
|
+ write32(value, addr);
|
|
+}
|
|
+
|
|
+static inline uint32_t mmio_read_32(uintptr_t addr)
|
|
+{
|
|
+ return read32(addr);
|
|
+}
|
|
+
|
|
+static inline void mmio_setbits_32(uintptr_t addr, uint32_t mask)
|
|
+{
|
|
+ write32(read32(addr) | mask, addr);
|
|
+}
|
|
+
|
|
+static inline void mmio_clrbits_32(uintptr_t addr, uint32_t mask)
|
|
+{
|
|
+ write32(read32(addr) & ~mask, addr);
|
|
+}
|
|
+
|
|
+static inline void mmio_clrsetbits_32(uintptr_t addr, uint32_t clear_mask,
|
|
+ uint32_t set_mask)
|
|
+{
|
|
+ write32((read32(addr) & ~clear_mask) | set_mask, addr);
|
|
+}
|
|
+
|
|
+#endif /*__STM32_UTIL_H__*/
|
|
diff --git a/core/arch/arm/plat-stm32mp1/stm32mp1_dt.c b/core/arch/arm/plat-stm32mp1/stm32mp1_dt.c
|
|
new file mode 100644
|
|
index 0000000..9ce6ba8
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/stm32mp1_dt.c
|
|
@@ -0,0 +1,338 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32mp1_clk.h>
|
|
+#include <drivers/stm32mp1_clkfunc.h>
|
|
+#include <drivers/stm32_gpio.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <libfdt.h>
|
|
+#include <platform_config.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <trace.h>
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function check the presence of a node (generic use of fdt library).
|
|
+ * Returns true if present else returns false.
|
|
+ ******************************************************************************/
|
|
+bool fdt_check_node(void *fdt, int node)
|
|
+{
|
|
+ int len;
|
|
+ const char *cchar;
|
|
+
|
|
+ cchar = fdt_get_name(fdt, node, &len);
|
|
+
|
|
+ return (cchar != NULL) && (len >= 0);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads a value of a node property (generic use of fdt
|
|
+ * library).
|
|
+ * Returns value if success, and a default value if property not found.
|
|
+ * Default value is passed as parameter.
|
|
+ ******************************************************************************/
|
|
+uint32_t fdt_read_uint32_default(void *fdt, int node, const char *prop_name,
|
|
+ uint32_t dflt_value)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+ int lenp;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, prop_name, &lenp);
|
|
+ if (cuint == NULL) {
|
|
+ return dflt_value;
|
|
+ }
|
|
+
|
|
+ return fdt32_to_cpu(*cuint);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads a series of parameters in a node property
|
|
+ * (generic use of fdt library).
|
|
+ * It reads the values inside the device tree, from property name and node.
|
|
+ * The number of parameters is also indicated as entry parameter.
|
|
+ * Returns 0 if success, and a negative value else.
|
|
+ * If success, values are stored at the third parameter address.
|
|
+ ******************************************************************************/
|
|
+int fdt_read_uint32_array(void *fdt, int node, const char *prop_name,
|
|
+ uint32_t *array, uint32_t count)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+ int len;
|
|
+ uint32_t i;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, prop_name, &len);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ if ((uint32_t)len != (count * sizeof(uint32_t))) {
|
|
+ return -FDT_ERR_BADLAYOUT;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
|
|
+ *array = fdt32_to_cpu(*cuint);
|
|
+ array++;
|
|
+ cuint++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the clock ID of the given node.
|
|
+ * It reads the value indicated inside the device tree.
|
|
+ * Returns ID on success and a negative FDT error code on failure.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_clock_id(void *fdt, int node)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "clocks", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ cuint++;
|
|
+ return (int)fdt32_to_cpu(*cuint);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the clock ID of the given node using clock-names.
|
|
+ * It reads the value indicated inside the device tree.
|
|
+ * Returns ID on success and a negative FDT error code on failure.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_clock_id_by_name(void *fdt, int node, const char *name)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+ int index;
|
|
+ int len;
|
|
+
|
|
+ index = fdt_stringlist_search(fdt, node, "clock-names", name);
|
|
+ if (index < 0) {
|
|
+ return index;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "clocks", &len);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ if ((index * (int)sizeof(uint32_t)) > len) {
|
|
+ return -FDT_ERR_BADVALUE;
|
|
+ }
|
|
+
|
|
+ cuint += (index << 1) + 1;
|
|
+ return (int)fdt32_to_cpu(*cuint);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function fills the generic information from a given node.
|
|
+ ******************************************************************************/
|
|
+void fdt_fill_device_info(void *fdt, struct dt_node_info *info, int node)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "reg", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ info->base = fdt32_to_cpu(*cuint);
|
|
+ } else {
|
|
+ info->base = 0;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "clocks", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ cuint++;
|
|
+ info->clock = (int)fdt32_to_cpu(*cuint);
|
|
+ } else {
|
|
+ info->clock = -1;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "resets", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ cuint++;
|
|
+ info->reset = (int)fdt32_to_cpu(*cuint);
|
|
+ } else {
|
|
+ info->reset = -1;
|
|
+ }
|
|
+
|
|
+ info->status = _fdt_get_status(fdt, node);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function retrieve the generic information from DT.
|
|
+ * Returns node if success, and a negative value else.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_node(void *fdt, struct dt_node_info *info, int offset,
|
|
+ const char *compat)
|
|
+{
|
|
+ int node;
|
|
+
|
|
+ node = fdt_node_offset_by_compatible(fdt, offset, compat);
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ fdt_fill_device_info(fdt, info, node);
|
|
+
|
|
+ return node;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the stdout path node.
|
|
+ * It reads the value indicated inside the device tree.
|
|
+ * Returns node on success and a negative FDT error code on failure.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_stdout_node_offset(void *fdt)
|
|
+{
|
|
+ int node;
|
|
+ const char *cchar;
|
|
+
|
|
+ node = fdt_path_offset(fdt, "/secure-chosen");
|
|
+ if (node < 0) {
|
|
+ node = fdt_path_offset(fdt, "/chosen");
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cchar = fdt_getprop(fdt, node, "stdout-path", NULL);
|
|
+ if (cchar == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ node = -FDT_ERR_NOTFOUND;
|
|
+ if (strchr(cchar, (int)':') != NULL) {
|
|
+ const char *name;
|
|
+ char *str = (char *)cchar;
|
|
+ int len = 0;
|
|
+
|
|
+ while (strncmp(":", str, 1)) {
|
|
+ len++;
|
|
+ str++;
|
|
+ }
|
|
+
|
|
+ name = fdt_get_alias_namelen(fdt, cchar, len);
|
|
+
|
|
+ if (name != NULL) {
|
|
+ node = fdt_path_offset(fdt, name);
|
|
+ }
|
|
+ } else {
|
|
+ node = fdt_path_offset(fdt, cchar);
|
|
+ }
|
|
+
|
|
+ return node;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets DDR size information from the DT.
|
|
+ * Returns value in bytes if success, and STM32MP1_DDR_SIZE_DFLT else.
|
|
+ ******************************************************************************/
|
|
+uint32_t fdt_get_ddr_size(void *fdt)
|
|
+{
|
|
+ int node;
|
|
+
|
|
+ node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT);
|
|
+ if (node < 0) {
|
|
+ IMSG("%s: Cannot read DDR node in DT\n", __func__);
|
|
+ return STM32MP1_DDR_SIZE_DFLT;
|
|
+ }
|
|
+
|
|
+ return fdt_read_uint32_default(fdt, node, "st,mem-size",
|
|
+ STM32MP1_DDR_SIZE_DFLT);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function retrieves board model from DT.
|
|
+ * Returns string taken from model node, NULL otherwise
|
|
+ ******************************************************************************/
|
|
+const char *fdt_get_board_model(void *fdt)
|
|
+{
|
|
+ int node = fdt_path_offset(fdt, "/");
|
|
+
|
|
+ if (node < 0) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return (const char *)fdt_getprop(fdt, node, "model", NULL);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets GPIO bank PINCTRL node information from the DT.
|
|
+ * Returns node value.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_gpio_bank_pinctrl_node(void *fdt, unsigned int bank)
|
|
+{
|
|
+ switch (bank) {
|
|
+ case GPIO_BANK_A:
|
|
+ case GPIO_BANK_B:
|
|
+ case GPIO_BANK_C:
|
|
+ case GPIO_BANK_D:
|
|
+ case GPIO_BANK_E:
|
|
+ case GPIO_BANK_F:
|
|
+ case GPIO_BANK_G:
|
|
+ case GPIO_BANK_H:
|
|
+ case GPIO_BANK_I:
|
|
+ case GPIO_BANK_J:
|
|
+ case GPIO_BANK_K:
|
|
+ return fdt_path_offset(fdt, "/soc/pin-controller");
|
|
+ case GPIO_BANK_Z:
|
|
+ return fdt_path_offset(fdt, "/soc/pin-controller-z");
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets GPIOZ pin number information from the DT.
|
|
+ * It also checks node consistency.
|
|
+ ******************************************************************************/
|
|
+int fdt_get_gpioz_nbpins_from_dt(void *fdt)
|
|
+{
|
|
+ int pinctrl_node;
|
|
+ int pinctrl_subnode;
|
|
+
|
|
+ pinctrl_node = fdt_get_gpio_bank_pinctrl_node(fdt, GPIO_BANK_Z);
|
|
+ if (pinctrl_node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) {
|
|
+ uint32_t bank_offset;
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ if (fdt_getprop(fdt, pinctrl_subnode,
|
|
+ "gpio-controller", NULL) == NULL) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ bank_offset = stm32_get_gpio_bank_offset(GPIO_BANK_Z);
|
|
+ if (fdt32_to_cpu(*cuint) != bank_offset) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (_fdt_get_status(fdt, pinctrl_subnode) ==
|
|
+ DT_STATUS_DISABLED) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, pinctrl_subnode, "ngpios", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return (int)fdt32_to_cpu(*cuint);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/core/arch/arm/plat-stm32mp1/stm32mp_dt.h b/core/arch/arm/plat-stm32mp1/stm32mp_dt.h
|
|
new file mode 100644
|
|
index 0000000..ec8aa4d
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/stm32mp_dt.h
|
|
@@ -0,0 +1,39 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifndef __STM32MP1_DT_H__
|
|
+#define __STM32MP1_DT_H__
|
|
+
|
|
+#include <stdbool.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+#define DT_DDR_COMPAT "st,stm32mp1-ddr"
|
|
+
|
|
+struct dt_node_info {
|
|
+ uint32_t base;
|
|
+ int32_t clock;
|
|
+ int32_t reset;
|
|
+ unsigned int status;
|
|
+};
|
|
+
|
|
+bool fdt_check_node(void *fdt, int node);
|
|
+uint32_t fdt_read_uint32_default(void *fdt, int node, const char *prop_name,
|
|
+ uint32_t dflt_value);
|
|
+int fdt_read_uint32_array(void *fdt, int node, const char *prop_name,
|
|
+ uint32_t *array, uint32_t count);
|
|
+void fdt_fill_device_info(void *fdt, struct dt_node_info *info, int node);
|
|
+int fdt_get_node(void *fdt, struct dt_node_info *info, int offset,
|
|
+ const char *compat);
|
|
+int fdt_get_stdout_node_offset(void *fdt);
|
|
+uint32_t fdt_get_ddr_size(void *fdt);
|
|
+const char *fdt_get_board_model(void *fdt);
|
|
+
|
|
+int fdt_get_clock_id(void *fdt, int node);
|
|
+int fdt_get_clock_id_by_name(void *fdt, int node, const char *name);
|
|
+int fdt_get_gpio_bank_pinctrl_node(void *fdt, unsigned int bank);
|
|
+int fdt_get_gpioz_nbpins_from_dt(void *fdt);
|
|
+
|
|
+#endif /* __STM32MP1_DT_H__ */
|
|
diff --git a/core/arch/arm/plat-stm32mp1/stm32mp_pm.h b/core/arch/arm/plat-stm32mp1/stm32mp_pm.h
|
|
new file mode 100644
|
|
index 0000000..8395ccc
|
|
--- /dev/null
|
|
+++ b/core/arch/arm/plat-stm32mp1/stm32mp_pm.h
|
|
@@ -0,0 +1,49 @@
|
|
+/* SPDX-License-Identifier: BSD-2-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+#ifndef __STM32MP_PM_H__
|
|
+#define __STM32MP_PM_H__
|
|
+
|
|
+#ifndef ASM
|
|
+
|
|
+#include <stdbool.h>
|
|
+#include <stddef.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+#define PSCI_MODE_SYSTEM_SUSPEND 0
|
|
+#define PSCI_MODE_SYSTEM_OFF 1
|
|
+
|
|
+enum stm32mp1_pm_domain {
|
|
+ STM32MP1_PD_VSW,
|
|
+ STM32MP1_PD_CORE_RET,
|
|
+ STM32MP1_PD_CORE,
|
|
+ STM32MP1_PD_MAX_PM_DOMAIN
|
|
+};
|
|
+
|
|
+enum pm_op {
|
|
+ PM_OP_SUSPEND,
|
|
+ PM_OP_RESUME,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Drivers can register a callback for the suspend and resume sequences and
|
|
+ * a private cookie passed as argument to the callback.
|
|
+ * The same callback is used for suspend and resume. First argument of the
|
|
+ * callback defines whether the device shall suspend or resume.
|
|
+ *
|
|
+ * Driver should tag its callback resource as unpaged (i.e KEEP_PAGER())
|
|
+ * since the callback is called from an unpaged execution context.
|
|
+ */
|
|
+void stm32mp_register_pm_cb(void (*callback)(enum pm_op op, void *handle),
|
|
+ void *handle);
|
|
+
|
|
+void stm32_cores_reset(void);
|
|
+
|
|
+void stm32mp_gic_suspend_resume(enum pm_op op);
|
|
+void stm32mp_clock_suspend_resume(enum pm_op op);
|
|
+
|
|
+#endif /*ASM*/
|
|
+
|
|
+#endif /*__STM32MP_PM_H__*/
|
|
+
|
|
diff --git a/core/arch/arm/plat-stm32mp1/sub.mk b/core/arch/arm/plat-stm32mp1/sub.mk
|
|
index 4a559ea..c5a0deb 100644
|
|
--- a/core/arch/arm/plat-stm32mp1/sub.mk
|
|
+++ b/core/arch/arm/plat-stm32mp1/sub.mk
|
|
@@ -2,3 +2,9 @@ global-incdirs-y += .
|
|
|
|
srcs-y += main.c
|
|
srcs-y += reset.S
|
|
+srcs-y += shared_resources.c
|
|
+srcs-$(CFG_DT) += stm32mp1_dt.c
|
|
+
|
|
+subdirs-y += service
|
|
+subdirs-y += drivers
|
|
+subdirs-y += pm
|
|
diff --git a/core/arch/arm/sm/pm_a32.S b/core/arch/arm/sm/pm_a32.S
|
|
index 9421b2d..8a59cd2 100644
|
|
--- a/core/arch/arm/sm/pm_a32.S
|
|
+++ b/core/arch/arm/sm/pm_a32.S
|
|
@@ -69,25 +69,30 @@ a9_suspend:
|
|
a7_suspend:
|
|
read_fcseidr r4
|
|
read_tpidruro r5
|
|
- stmia r0!, {r4 - r5}
|
|
- read_dacr r4
|
|
+ read_dacr r6
|
|
+ stmia r0!, {r4 - r6}
|
|
#ifdef CFG_WITH_LPAE
|
|
-#error "Not supported"
|
|
+ read_ttbr0_64bit r4, r5
|
|
+ read_ttbr1_64bit r6, r7
|
|
+ read_mair0 r8
|
|
+ read_mair1 r9
|
|
+ stmia r0!, {r4 - r9}
|
|
#else
|
|
- read_ttbr0 r5
|
|
- read_ttbr1 r6
|
|
- read_ttbcr r7
|
|
+ read_ttbr0 r4
|
|
+ read_ttbr1 r5
|
|
+ read_prrr r6
|
|
+ read_nmrr r7
|
|
+ stmia r0!, {r4 - r7}
|
|
#endif
|
|
- read_sctlr r8
|
|
- read_actlr r9
|
|
- read_cpacr r10
|
|
- read_mvbar r11
|
|
- stmia r0!, {r4 - r11}
|
|
- read_prrr r4
|
|
- read_nmrr r5
|
|
- read_vbar r6
|
|
- read_nsacr r7
|
|
- stmia r0, {r4 - r7}
|
|
+ read_ttbcr r4
|
|
+ read_actlr r5
|
|
+ read_cpacr r6
|
|
+ read_mvbar r7
|
|
+ read_vbar r8
|
|
+ read_nsacr r9
|
|
+ read_sctlr r10
|
|
+ stmia r0, {r4 - r10}
|
|
+
|
|
pop {r4 - r11}
|
|
bx lr
|
|
UNWIND( .fnend)
|
|
@@ -154,50 +159,50 @@ UNWIND( .cantunwind)
|
|
cmp r5, r4
|
|
beq a7_resume
|
|
|
|
- /*
|
|
- * A9 needs PCR/DIAG
|
|
- */
|
|
+ /* A9 needs PCR/DIAG */
|
|
ldmia r0!, {r4 - r5}
|
|
write_pcr r4
|
|
write_diag r5
|
|
-
|
|
a7_resume:
|
|
- /* v7 resume */
|
|
+ /* Armv7 generic resume */
|
|
mov ip, #0
|
|
/* Invalidate icache to PoU */
|
|
write_iciallu
|
|
/* set reserved context */
|
|
write_contextidr ip
|
|
- ldmia r0!, {r4 - r5}
|
|
+ ldmia r0!, {r4 - r6}
|
|
write_fcseidr r4
|
|
write_tpidruro r5
|
|
- ldmia r0!, {r4 - r11}
|
|
/* Invalidate entire TLB */
|
|
write_tlbiall
|
|
- write_dacr r4
|
|
+ write_dacr r6
|
|
#ifdef CFG_WITH_LPAE
|
|
-#error "Not supported -"
|
|
+ ldmia r0!, {r4 - r9}
|
|
+ write_ttbr0_64bit r4, r5
|
|
+ write_ttbr1_64bit r6, r7
|
|
+ write_mair0 r8
|
|
+ write_mair1 r9
|
|
#else
|
|
- write_ttbr0 r5
|
|
- write_ttbr1 r6
|
|
- write_ttbcr r7
|
|
+ ldmia r0!, {r4 - r7}
|
|
+ write_ttbr0 r4
|
|
+ write_ttbr1 r5
|
|
+ write_prrr r6
|
|
+ write_nmrr r7
|
|
#endif
|
|
-
|
|
- ldmia r0, {r4 - r7}
|
|
- write_prrr r4
|
|
- write_nmrr r5
|
|
- write_vbar r6
|
|
- write_nsacr r7
|
|
-
|
|
- write_actlr r9
|
|
- write_cpacr r10
|
|
- write_mvbar r11
|
|
+ ldmia r0!, {r4 - r10}
|
|
+ write_ttbcr r4
|
|
+ write_actlr r5
|
|
+ write_cpacr r6
|
|
+ write_mvbar r7
|
|
+ write_vbar r8
|
|
+ write_nsacr r9
|
|
write_bpiall
|
|
isb
|
|
dsb
|
|
/* MMU will be enabled here */
|
|
- write_sctlr r8
|
|
+ write_sctlr r10
|
|
isb
|
|
+
|
|
mov r0, #0
|
|
b suspend_return
|
|
UNWIND( .fnend)
|
|
diff --git a/core/arch/arm/tee/entry_std.c b/core/arch/arm/tee/entry_std.c
|
|
index 92fdf0f..e0836ed 100644
|
|
--- a/core/arch/arm/tee/entry_std.c
|
|
+++ b/core/arch/arm/tee/entry_std.c
|
|
@@ -602,4 +602,4 @@ static TEE_Result default_mobj_init(void)
|
|
return TEE_SUCCESS;
|
|
}
|
|
|
|
-driver_init_late(default_mobj_init);
|
|
+service_init(default_mobj_init);
|
|
diff --git a/core/drivers/gic.c b/core/drivers/gic.c
|
|
index 9baffee..a000bff 100644
|
|
--- a/core/drivers/gic.c
|
|
+++ b/core/drivers/gic.c
|
|
@@ -1,5 +1,6 @@
|
|
// SPDX-License-Identifier: BSD-2-Clause
|
|
/*
|
|
+ * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
|
|
* Copyright (c) 2016-2017, Linaro Limited
|
|
* Copyright (c) 2014, STMicroelectronics International N.V.
|
|
*/
|
|
@@ -7,12 +8,14 @@
|
|
#include <arm.h>
|
|
#include <assert.h>
|
|
#include <drivers/gic.h>
|
|
+#include <io.h>
|
|
#include <keep.h>
|
|
#include <kernel/interrupt.h>
|
|
#include <kernel/panic.h>
|
|
+#include <malloc.h>
|
|
#include <util.h>
|
|
-#include <io.h>
|
|
#include <trace.h>
|
|
+#include <string.h>
|
|
|
|
/* Offsets from gic.gicc_base */
|
|
#define GICC_CTLR (0x000)
|
|
@@ -32,8 +35,12 @@
|
|
#define GICD_ICENABLER(n) (0x180 + (n) * 4)
|
|
#define GICD_ISPENDR(n) (0x200 + (n) * 4)
|
|
#define GICD_ICPENDR(n) (0x280 + (n) * 4)
|
|
+#define GICD_ISACTIVER(n) (0x300 + (n) * 4)
|
|
+#define GICD_ICACTIVER(n) (0x380 + (n) * 4)
|
|
#define GICD_IPRIORITYR(n) (0x400 + (n) * 4)
|
|
#define GICD_ITARGETSR(n) (0x800 + (n) * 4)
|
|
+#define GICD_ICFGR(n) (0xC00 + (n) * 4)
|
|
+#define GICD_NSACR(n) (0xE00 + (n) * 4)
|
|
#define GICD_SGIR (0xF00)
|
|
|
|
#define GICD_CTLR_ENABLEGRP0 (1 << 0)
|
|
@@ -74,6 +81,12 @@ static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
|
|
static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
|
|
uint8_t cpu_mask);
|
|
|
|
+#if !defined(CFG_ARM_GICV3)
|
|
+static uint8_t gic_op_set_pmr(struct itr_chip *chip, uint8_t mask);
|
|
+static uint8_t gic_op_set_ipriority(struct itr_chip *chip, size_t it,
|
|
+ uint8_t mask);
|
|
+#endif
|
|
+
|
|
static const struct itr_ops gic_ops = {
|
|
.add = gic_op_add,
|
|
.enable = gic_op_enable,
|
|
@@ -81,6 +94,10 @@ static const struct itr_ops gic_ops = {
|
|
.raise_pi = gic_op_raise_pi,
|
|
.raise_sgi = gic_op_raise_sgi,
|
|
.set_affinity = gic_op_set_affinity,
|
|
+#if !defined(CFG_ARM_GICV3)
|
|
+ .set_pmr = gic_op_set_pmr,
|
|
+ .set_ipriority = gic_op_set_ipriority,
|
|
+#endif
|
|
};
|
|
KEEP_PAGER(gic_ops);
|
|
|
|
@@ -146,11 +163,11 @@ void gic_cpu_init(struct gic_data *gd)
|
|
* allow the Non-secure world to adjust the priority mask itself
|
|
*/
|
|
#if defined(CFG_ARM_GICV3)
|
|
- write_icc_pmr(0x80);
|
|
+ write_icc_pmr(GIC_HIGHEST_NS_PRIORITY);
|
|
write_icc_ctlr(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 |
|
|
GICC_CTLR_FIQEN);
|
|
#else
|
|
- write32(0x80, gd->gicc_base + GICC_PMR);
|
|
+ write32(GIC_HIGHEST_NS_PRIORITY, gd->gicc_base + GICC_PMR);
|
|
|
|
/* Enable GIC */
|
|
write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
|
|
@@ -189,11 +206,11 @@ void gic_init(struct gic_data *gd, vaddr_t gicc_base __maybe_unused,
|
|
* allow the Non-secure world to adjust the priority mask itself
|
|
*/
|
|
#if defined(CFG_ARM_GICV3)
|
|
- write_icc_pmr(0x80);
|
|
+ write_icc_pmr(GIC_HIGHEST_NS_PRIORITY);
|
|
write_icc_ctlr(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 |
|
|
GICC_CTLR_FIQEN);
|
|
#else
|
|
- write32(0x80, gd->gicc_base + GICC_PMR);
|
|
+ write32(GIC_HIGHEST_NS_PRIORITY, gd->gicc_base + GICC_PMR);
|
|
|
|
/* Enable GIC */
|
|
write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
|
|
@@ -254,9 +271,20 @@ static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
|
|
{
|
|
size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
|
|
uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
|
|
+ bool group0 = ((read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask) == 0);
|
|
|
|
- /* Assigned to group0 */
|
|
- assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
|
|
+ /*
|
|
+ * Check priority against ARM recommendation: Group1 interrupts always
|
|
+ * have a lower priority than group0 interrupts.
|
|
+ * Note, lower numerical values have higher priorities so the comparison
|
|
+ * checks below are reversed from what might be expected.
|
|
+ */
|
|
+ if (group0) {
|
|
+ assert(prio <= GIC_LOWEST_SEC_PRIORITY);
|
|
+ } else {
|
|
+ assert(prio >= GIC_HIGHEST_NS_PRIORITY &&
|
|
+ prio <= GIC_LOWEST_NS_PRIORITY);
|
|
+ }
|
|
|
|
/* Set prio it to selected CPUs */
|
|
DMSG("prio: writing 0x%x to 0x%" PRIxVA,
|
|
@@ -264,6 +292,34 @@ static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
|
|
write8(prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
|
|
}
|
|
|
|
+static uint8_t gic_op_set_pmr(struct itr_chip *chip, uint8_t mask)
|
|
+{
|
|
+ struct gic_data *gd = container_of(chip, struct gic_data, chip);
|
|
+ uint32_t pmr = read32(gd->gicc_base + GICC_PMR);
|
|
+
|
|
+ /*
|
|
+ * Order memory updates w.r.t. PMR write, and ensure they're visible
|
|
+ * before potential out of band interrupt trigger because of PMR update.
|
|
+ */
|
|
+ dsb_ishst();
|
|
+ write32(mask, gd->gicc_base + GICC_PMR);
|
|
+ dsb_ishst();
|
|
+
|
|
+ return (uint8_t)pmr;
|
|
+}
|
|
+
|
|
+static uint8_t gic_op_set_ipriority(struct itr_chip *chip, size_t it,
|
|
+ uint8_t mask)
|
|
+{
|
|
+ struct gic_data *gd = container_of(chip, struct gic_data, chip);
|
|
+ uint8_t prio = read8(gd->gicd_base + GICD_IPRIORITYR(0) + it);
|
|
+
|
|
+ gic_it_set_prio(gd, it, mask);
|
|
+
|
|
+ return prio;
|
|
+}
|
|
+
|
|
+
|
|
static void gic_it_enable(struct gic_data *gd, size_t it)
|
|
{
|
|
size_t idx = it / NUM_INTS_PER_REG;
|
|
@@ -472,3 +528,143 @@ static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
|
|
|
|
gic_it_set_cpu_mask(gd, it, cpu_mask);
|
|
}
|
|
+
|
|
+#define IT_PM_GPOUP1_BIT BIT(0)
|
|
+#define IT_PM_ENABLE_BIT BIT(1)
|
|
+#define IT_PM_PENDING_BIT BIT(2)
|
|
+#define IT_PM_ACTIVE_BIT BIT(3)
|
|
+#define IT_PM_CONFIG_MASK GENMASK_32(1, 0)
|
|
+
|
|
+struct gic_it_pm {
|
|
+ uint16_t it;
|
|
+ uint8_t flags;
|
|
+ uint8_t iprio;
|
|
+ uint8_t itarget;
|
|
+ uint8_t icfg;
|
|
+};
|
|
+
|
|
+static void gic_save_it(struct gic_data *gd,
|
|
+ unsigned int it, struct gic_it_pm *pm)
|
|
+{
|
|
+ size_t idx;
|
|
+ uint32_t bit_mask = BIT(it % NUM_INTS_PER_REG);
|
|
+ uint32_t shift2 = it % (NUM_INTS_PER_REG / 2);
|
|
+ uint32_t shift8 = it % (NUM_INTS_PER_REG / 8);
|
|
+ uint32_t data32;
|
|
+
|
|
+ assert(it < UINT16_MAX);
|
|
+ pm->it = (uint16_t)it;
|
|
+
|
|
+ idx = it / NUM_INTS_PER_REG;
|
|
+
|
|
+ pm->flags = 0;
|
|
+ if ((read32(gd->gicd_base + GICD_IGROUPR(idx)) & bit_mask) != 0) {
|
|
+ pm->flags |= IT_PM_GPOUP1_BIT;
|
|
+ }
|
|
+ if ((read32(gd->gicd_base + GICD_ISENABLER(idx)) & bit_mask) != 0) {
|
|
+ pm->flags |= IT_PM_ENABLE_BIT;
|
|
+ }
|
|
+ if ((read32(gd->gicd_base + GICD_ISPENDR(idx)) & bit_mask) != 0) {
|
|
+ pm->flags |= IT_PM_PENDING_BIT;
|
|
+ }
|
|
+ if ((read32(gd->gicd_base + GICD_ISACTIVER(idx)) & bit_mask) != 0) {
|
|
+ pm->flags |= IT_PM_ACTIVE_BIT;
|
|
+ }
|
|
+
|
|
+ idx = (8 * it) / NUM_INTS_PER_REG;
|
|
+
|
|
+ data32 = read32(gd->gicd_base + GICD_IPRIORITYR(idx)) >> shift8;
|
|
+ pm->iprio = (uint8_t)data32;
|
|
+
|
|
+ data32 = read32(gd->gicd_base + GICD_ITARGETSR(idx)) >> shift8;
|
|
+ pm->itarget = (uint8_t)data32;
|
|
+
|
|
+ /* Note: ICFGR is RAO for SPIs and PPIs */
|
|
+ idx = (2 * it) / NUM_INTS_PER_REG;
|
|
+ data32 = read32(gd->gicd_base + GICD_ICFGR(idx)) >> shift2;
|
|
+ pm->icfg = (uint8_t)data32 & IT_PM_CONFIG_MASK;
|
|
+}
|
|
+
|
|
+static void gic_restore_it(struct gic_data *gd, struct gic_it_pm *pm)
|
|
+{
|
|
+ size_t idx;
|
|
+ unsigned int it = (unsigned int)pm->it;
|
|
+ uint32_t mask = BIT(it % NUM_INTS_PER_REG);
|
|
+ uint32_t shift2 = it % (NUM_INTS_PER_REG / 2);
|
|
+ uint32_t shift8 = it % (NUM_INTS_PER_REG / 8);
|
|
+
|
|
+ idx = it / NUM_INTS_PER_REG;
|
|
+
|
|
+ io_mask32(gd->gicd_base + GICD_IGROUPR(idx),
|
|
+ (pm->flags & IT_PM_GPOUP1_BIT) != 0 ? mask : 0, mask);
|
|
+
|
|
+ io_mask32(gd->gicd_base + GICD_ISENABLER(idx),
|
|
+ (pm->flags & IT_PM_ENABLE_BIT) != 0 ? mask : 0, mask);
|
|
+
|
|
+ io_mask32(gd->gicd_base + GICD_ISPENDR(idx),
|
|
+ (pm->flags & IT_PM_PENDING_BIT) != 0 ? mask : 0, mask);
|
|
+
|
|
+ io_mask32(gd->gicd_base + GICD_ISACTIVER(idx),
|
|
+ (pm->flags & IT_PM_ACTIVE_BIT) != 0 ? mask : 0, mask);
|
|
+
|
|
+ idx = (8 * it) / NUM_INTS_PER_REG;
|
|
+
|
|
+ io_mask32(gd->gicd_base + GICD_IPRIORITYR(idx),
|
|
+ (uint32_t)pm->iprio << shift8, UINT8_MAX << shift8);
|
|
+
|
|
+ io_mask32(gd->gicd_base + GICD_ITARGETSR(idx),
|
|
+ (uint32_t)pm->itarget << shift8, UINT8_MAX << shift8);
|
|
+
|
|
+ /* Note: ICFGR is WI for SPIs and PPIs */
|
|
+ idx = (2 * it) / NUM_INTS_PER_REG;
|
|
+ io_mask32(gd->gicd_base + GICD_ICFGR(idx),
|
|
+ (uint32_t)pm->icfg << shift2, IT_PM_CONFIG_MASK << shift2);
|
|
+}
|
|
+
|
|
+static bool it_is_group0(struct gic_data *gd, unsigned int it)
|
|
+{
|
|
+ size_t idx = it / NUM_INTS_PER_REG;
|
|
+ uint32_t groupr = read32(gd->gicd_base + GICD_IGROUPR(idx));
|
|
+ uint32_t bit_mask = BIT(it % NUM_INTS_PER_REG);
|
|
+
|
|
+ return (groupr & bit_mask) == 0;
|
|
+}
|
|
+
|
|
+/* Save the configuration for interrupts in group0 only */
|
|
+void gic_suspend(struct gic_data *gd)
|
|
+{
|
|
+ struct gic_pm *pm = &gd->pm;
|
|
+ unsigned int n;
|
|
+ size_t count;
|
|
+
|
|
+ for (count = 0, n = 0; n <= gd->max_it; n++) {
|
|
+ if (it_is_group0(gd, n)) {
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+ pm->count = count;
|
|
+
|
|
+ if (count == 0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pm->pm_cfg = realloc(pm->pm_cfg, count * sizeof(*pm->pm_cfg));
|
|
+ assert(pm->pm_cfg != NULL);
|
|
+
|
|
+ for (count = 0, n = 0; n <= gd->max_it; n++) {
|
|
+ if (it_is_group0(gd, n)) {
|
|
+ gic_save_it(gd, n, &pm->pm_cfg[count]);
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void gic_resume(struct gic_data *gd)
|
|
+{
|
|
+ struct gic_pm *pm = &gd->pm;
|
|
+ size_t n;
|
|
+
|
|
+ for (n = 0; n < pm->count; n++) {
|
|
+ gic_restore_it(gd, &pm->pm_cfg[n]);
|
|
+ }
|
|
+}
|
|
diff --git a/core/drivers/stm32_bsec.c b/core/drivers/stm32_bsec.c
|
|
new file mode 100644
|
|
index 0000000..3355e0a
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_bsec.c
|
|
@@ -0,0 +1,824 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32_bsec.h>
|
|
+#include <io.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/spinlock.h>
|
|
+#include <limits.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <platform_config.h>
|
|
+#include <stdint.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <util.h>
|
|
+
|
|
+#ifdef CFG_DT
|
|
+#include <libfdt.h>
|
|
+#endif
|
|
+
|
|
+#define BSEC_COMPAT "st,stm32mp15-bsec"
|
|
+#define BITS_PER_WORD (CHAR_BIT * sizeof(uint32_t))
|
|
+#define OTP_ACCESS_SIZE (ROUNDUP(OTP_MAX_SIZE, BITS_PER_WORD) / BITS_PER_WORD)
|
|
+
|
|
+static uint32_t __maybe_unused otp_nsec_access[OTP_ACCESS_SIZE];
|
|
+
|
|
+static uint32_t bsec_power_safmem(bool enable);
|
|
+
|
|
+/* Bsec access protection */
|
|
+static unsigned int lock = SPINLOCK_UNLOCK;
|
|
+
|
|
+static uint32_t bsec_lock(void)
|
|
+{
|
|
+ return may_spin_lock(&lock);
|
|
+}
|
|
+
|
|
+static void bsec_unlock(uint32_t exceptions)
|
|
+{
|
|
+ may_spin_unlock(&lock, exceptions);
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static int bsec_get_dt_node(void *fdt, struct dt_node_info *info)
|
|
+{
|
|
+ int node;
|
|
+
|
|
+ node = fdt_get_node(fdt, info, -1, BSEC_COMPAT);
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return node;
|
|
+}
|
|
+
|
|
+static void enable_non_secure_access(uint32_t otp)
|
|
+{
|
|
+ otp_nsec_access[otp / BITS_PER_WORD] |= BIT(otp % BITS_PER_WORD);
|
|
+
|
|
+ if (bsec_shadow_register(otp) != BSEC_OK) {
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool non_secure_can_access(uint32_t otp)
|
|
+{
|
|
+ return (otp_nsec_access[otp / BITS_PER_WORD] &
|
|
+ BIT(otp % BITS_PER_WORD)) != 0;
|
|
+}
|
|
+
|
|
+static int bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
|
|
+{
|
|
+ int bsec_subnode;
|
|
+
|
|
+ fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
|
|
+ const fdt32_t *cuint;
|
|
+ uint32_t reg;
|
|
+ uint32_t i;
|
|
+ uint32_t size;
|
|
+ uint8_t status;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, bsec_subnode, "reg", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ reg = fdt32_to_cpu(*cuint) / sizeof(uint32_t);
|
|
+ if (reg < STM32MP1_UPPER_OTP_START) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ status = _fdt_get_status(fdt, bsec_subnode);
|
|
+ if ((status & DT_STATUS_OK_NSEC) == 0U) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ size = fdt32_to_cpu(*(cuint + 1)) / sizeof(uint32_t);
|
|
+
|
|
+ if ((fdt32_to_cpu(*(cuint + 1)) % sizeof(uint32_t)) != 0) {
|
|
+ size++;
|
|
+ }
|
|
+
|
|
+ for (i = reg; i < (reg + size); i++) {
|
|
+ enable_non_secure_access(i);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static uint32_t otp_bank_offset(uint32_t otp)
|
|
+{
|
|
+ assert(otp <= stm32mp_get_otp_max());
|
|
+
|
|
+ return ((otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
|
|
+ sizeof(uint32_t);
|
|
+}
|
|
+
|
|
+static uintptr_t bsec_get_base(void)
|
|
+{
|
|
+ static void *va;
|
|
+
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return BSEC_BASE;
|
|
+
|
|
+ if (!va)
|
|
+ va = phys_to_virt(BSEC_BASE, MEM_AREA_IO_SEC);
|
|
+
|
|
+ return (vaddr_t)va;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_check_error
|
|
+ * otp : OTP number.
|
|
+ * return value : BSEC_OK if no error.
|
|
+ */
|
|
+static uint32_t bsec_check_error(uint32_t otp)
|
|
+{
|
|
+ uint32_t bit = BIT(otp & BSEC_OTP_MASK);
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+
|
|
+ if ((read32(bsec_get_base() + BSEC_DISTURBED_OFF + bank) & bit) != 0U) {
|
|
+ return BSEC_DISTURBED;
|
|
+ }
|
|
+
|
|
+ if ((read32(bsec_get_base() + BSEC_ERROR_OFF + bank) & bit) != 0U) {
|
|
+ return BSEC_ERROR;
|
|
+ }
|
|
+
|
|
+ return BSEC_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_shadow_register: copy SAFMEM OTP to BSEC data.
|
|
+ * otp: OTP number.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_shadow_register(uint32_t otp)
|
|
+{
|
|
+ uint32_t result;
|
|
+ uint32_t exc;
|
|
+
|
|
+ if (otp > stm32mp_get_otp_max()) {
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ /* Check if shadowing of OTP is locked */
|
|
+ if (bsec_read_sr_lock(otp)) {
|
|
+ IMSG("BSEC: OTP locked, register will not be refreshed");
|
|
+ }
|
|
+
|
|
+ result = bsec_power_safmem(true);
|
|
+ if (result != BSEC_OK) {
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ write32(otp | BSEC_READ, bsec_get_base() + BSEC_OTP_CTRL_OFF);
|
|
+
|
|
+ while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
|
+ ;
|
|
+ }
|
|
+
|
|
+ result = bsec_check_error(otp);
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ bsec_power_safmem(false);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_read_otp: read an OTP data value.
|
|
+ * val: read value.
|
|
+ * otp: OTP number.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_read_otp(uint32_t *val, uint32_t otp)
|
|
+{
|
|
+ uint32_t exc;
|
|
+ uint32_t result;
|
|
+
|
|
+ if (otp > stm32mp_get_otp_max()) {
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ *val = read32(bsec_get_base() + BSEC_OTP_DATA_OFF +
|
|
+ (otp * sizeof(uint32_t)));
|
|
+
|
|
+ result = bsec_check_error(otp);
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_write_otp: write value in BSEC data register.
|
|
+ * val: value to write.
|
|
+ * otp: OTP number.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_write_otp(uint32_t val, uint32_t otp)
|
|
+{
|
|
+ uint32_t exc;
|
|
+ uint32_t result;
|
|
+
|
|
+ if (otp > stm32mp_get_otp_max()) {
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ /* Check if programming of OTP is locked */
|
|
+ if (bsec_read_sw_lock(otp)) {
|
|
+ IMSG("BSEC: OTP locked, write will be ignored");
|
|
+ }
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ write32(val, bsec_get_base() + BSEC_OTP_DATA_OFF +
|
|
+ (otp * sizeof(uint32_t)));
|
|
+
|
|
+ result = bsec_check_error(otp);
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_program_otp: program a bit in SAFMEM after the prog.
|
|
+ * The OTP data is not refreshed.
|
|
+ * val: value to program.
|
|
+ * otp: OTP number.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_program_otp(uint32_t val, uint32_t otp)
|
|
+{
|
|
+ uint32_t result;
|
|
+ uint32_t exc;
|
|
+
|
|
+ if (otp > stm32mp_get_otp_max()) {
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ /* Check if programming of OTP is locked */
|
|
+ if (bsec_read_sp_lock(otp)) {
|
|
+ IMSG("BSEC: OTP locked, prog will be ignored");
|
|
+ }
|
|
+
|
|
+ if ((read32(bsec_get_base() + BSEC_OTP_LOCK_OFF) &
|
|
+ BIT(BSEC_LOCK_PROGRAM)) != 0U) {
|
|
+ IMSG("BSEC: GPLOCK activated, prog will be ignored");
|
|
+ }
|
|
+
|
|
+ result = bsec_power_safmem(true);
|
|
+ if (result != BSEC_OK) {
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ write32(val, bsec_get_base() + BSEC_OTP_WRDATA_OFF);
|
|
+ write32(otp | BSEC_WRITE, bsec_get_base() + BSEC_OTP_CTRL_OFF);
|
|
+
|
|
+ while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
|
+ ;
|
|
+ }
|
|
+
|
|
+ if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) {
|
|
+ result = BSEC_PROG_FAIL;
|
|
+ } else {
|
|
+ result = bsec_check_error(otp);
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ bsec_power_safmem(false);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_permanent_lock_otp: permanent lock of OTP in SAFMEM.
|
|
+ * otp: OTP number.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_permanent_lock_otp(uint32_t otp)
|
|
+{
|
|
+ uint32_t result;
|
|
+ uint32_t data;
|
|
+ uint32_t addr;
|
|
+ uint32_t exc;
|
|
+
|
|
+ if (otp > stm32mp_get_otp_max()) {
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ result = bsec_power_safmem(true);
|
|
+ if (result != BSEC_OK) {
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ if (otp < stm32mp_get_otp_upper_start()) {
|
|
+ addr = otp >> ADDR_LOWER_OTP_PERLOCK_SHIFT;
|
|
+ data = DATA_LOWER_OTP_PERLOCK_BIT <<
|
|
+ ((otp & DATA_LOWER_OTP_PERLOCK_MASK) << 1U);
|
|
+ } else {
|
|
+ addr = (otp >> ADDR_UPPER_OTP_PERLOCK_SHIFT) + 2U;
|
|
+ data = DATA_UPPER_OTP_PERLOCK_BIT <<
|
|
+ (otp & DATA_UPPER_OTP_PERLOCK_MASK);
|
|
+ }
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ write32(data, bsec_get_base() + BSEC_OTP_WRDATA_OFF);
|
|
+ write32(addr | BSEC_WRITE | BSEC_LOCK,
|
|
+ bsec_get_base() + BSEC_OTP_CTRL_OFF);
|
|
+
|
|
+ while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
|
+ ;
|
|
+ }
|
|
+
|
|
+ if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) {
|
|
+ result = BSEC_PROG_FAIL;
|
|
+ } else {
|
|
+ result = bsec_check_error(otp);
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ bsec_power_safmem(false);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_write_debug_conf: write value in debug feature
|
|
+ * to enable/disable debug service.
|
|
+ * val: value to write.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_write_debug_conf(uint32_t val)
|
|
+{
|
|
+ uint32_t result = BSEC_ERROR;
|
|
+ uint32_t masked_val = val & BSEC_DEN_ALL_MSK;
|
|
+ unsigned int exc;
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ write32(val, bsec_get_base() + BSEC_DEN_OFF);
|
|
+
|
|
+ if ((read32(bsec_get_base() + BSEC_DEN_OFF) ^ masked_val) == 0U) {
|
|
+ result = BSEC_OK;
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_read_debug_conf : read debug configuration.
|
|
+ */
|
|
+uint32_t bsec_read_debug_conf(void)
|
|
+{
|
|
+ return read32(bsec_get_base() + BSEC_DEN_OFF);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_get_status : return status register value.
|
|
+ */
|
|
+uint32_t bsec_get_status(void)
|
|
+{
|
|
+ return read32(bsec_get_base() + BSEC_OTP_STATUS_OFF);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_get_hw_conf : return hardware configuration.
|
|
+ */
|
|
+uint32_t bsec_get_hw_conf(void)
|
|
+{
|
|
+ return read32(bsec_get_base() + BSEC_IPHW_CFG_OFF);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_get_version : return BSEC version.
|
|
+ */
|
|
+uint32_t bsec_get_version(void)
|
|
+{
|
|
+ return read32(bsec_get_base() + BSEC_IPVR_OFF);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_get_id : return BSEC ID.
|
|
+ */
|
|
+uint32_t bsec_get_id(void)
|
|
+{
|
|
+ return read32(bsec_get_base() + BSEC_IP_ID_OFF);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_get_magic_id : return BSEC magic number.
|
|
+ */
|
|
+uint32_t bsec_get_magic_id(void)
|
|
+{
|
|
+ return read32(bsec_get_base() + BSEC_IP_MAGIC_ID_OFF);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_write_sr_lock: write shadow-read lock.
|
|
+ * otp: OTP number.
|
|
+ * value: value to write in the register.
|
|
+ * Must be always 1.
|
|
+ * return: true if OTP is locked, else false.
|
|
+ */
|
|
+bool bsec_write_sr_lock(uint32_t otp, uint32_t value)
|
|
+{
|
|
+ bool result = false;
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t bank_value;
|
|
+ uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
+ uint32_t exc;
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ bank_value = read32(bsec_get_base() + BSEC_SRLOCK_OFF + bank);
|
|
+
|
|
+ if ((bank_value & otp_mask) == value) {
|
|
+ /*
|
|
+ * In case of write don't need to write,
|
|
+ * the lock is already set.
|
|
+ */
|
|
+ if (value != 0U) {
|
|
+ result = true;
|
|
+ }
|
|
+ } else {
|
|
+ if (value != 0U) {
|
|
+ bank_value = bank_value | otp_mask;
|
|
+ } else {
|
|
+ bank_value = bank_value & ~otp_mask;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * We can write 0 in all other OTP
|
|
+ * if the lock is activated in one of other OTP.
|
|
+ * Write 0 has no effect.
|
|
+ */
|
|
+ write32(bank_value, bsec_get_base() + BSEC_SRLOCK_OFF + bank);
|
|
+ result = true;
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_read_sr_lock: read shadow-read lock.
|
|
+ * otp: OTP number.
|
|
+ * return: true if otp is locked, else false.
|
|
+ */
|
|
+bool bsec_read_sr_lock(uint32_t otp)
|
|
+{
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
+ uint32_t bank_value = read32(bsec_get_base() + BSEC_SRLOCK_OFF + bank);
|
|
+
|
|
+ return (bank_value & otp_mask) != 0U;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_write_sw_lock: write shadow-write lock.
|
|
+ * otp: OTP number.
|
|
+ * value: Value to write in the register.
|
|
+ * Must be always 1.
|
|
+ * return: true if OTP is locked, else false.
|
|
+ */
|
|
+bool bsec_write_sw_lock(uint32_t otp, uint32_t value)
|
|
+{
|
|
+ bool result = false;
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
+ uint32_t bank_value;
|
|
+ unsigned int exc;
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ bank_value = read32(bsec_get_base() + BSEC_SWLOCK_OFF + bank);
|
|
+
|
|
+ if ((bank_value & otp_mask) == value) {
|
|
+ /*
|
|
+ * In case of write don't need to write,
|
|
+ * the lock is already set.
|
|
+ */
|
|
+ if (value != 0U) {
|
|
+ result = true;
|
|
+ }
|
|
+ } else {
|
|
+ if (value != 0U) {
|
|
+ bank_value = bank_value | otp_mask;
|
|
+ } else {
|
|
+ bank_value = bank_value & ~otp_mask;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * We can write 0 in all other OTP
|
|
+ * if the lock is activated in one of other OTP.
|
|
+ * Write 0 has no effect.
|
|
+ */
|
|
+ write32(bank_value, bsec_get_base() + BSEC_SWLOCK_OFF + bank);
|
|
+ result = true;
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_read_sw_lock: read shadow-write lock.
|
|
+ * otp: OTP number.
|
|
+ * return: true if OTP is locked, else false.
|
|
+ */
|
|
+bool bsec_read_sw_lock(uint32_t otp)
|
|
+{
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
+ uint32_t bank_value = read32(bsec_get_base() + BSEC_SWLOCK_OFF + bank);
|
|
+
|
|
+ return (bank_value & otp_mask) != 0U;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_write_sp_lock: write shadow-program lock.
|
|
+ * otp: OTP number.
|
|
+ * value: Value to write in the register.
|
|
+ * Must be always 1.
|
|
+ * return: true if OTP is locked, else false.
|
|
+ */
|
|
+bool bsec_write_sp_lock(uint32_t otp, uint32_t value)
|
|
+{
|
|
+ bool result = false;
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t bank_value;
|
|
+ uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
+ unsigned int exc;
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ bank_value = read32(bsec_get_base() + BSEC_SPLOCK_OFF + bank);
|
|
+
|
|
+ if ((bank_value & otp_mask) == value) {
|
|
+ /*
|
|
+ * In case of write don't need to write,
|
|
+ * the lock is already set.
|
|
+ */
|
|
+ if (value != 0U) {
|
|
+ result = true;
|
|
+ }
|
|
+ } else {
|
|
+ if (value != 0U) {
|
|
+ bank_value = bank_value | otp_mask;
|
|
+ } else {
|
|
+ bank_value = bank_value & ~otp_mask;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * We can write 0 in all other OTP
|
|
+ * if the lock is activated in one of other OTP.
|
|
+ * Write 0 has no effect.
|
|
+ */
|
|
+ write32(bank_value, bsec_get_base() + BSEC_SPLOCK_OFF + bank);
|
|
+ result = true;
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_read_sp_lock: read shadow-program lock.
|
|
+ * otp: OTP number.
|
|
+ * return: true if OTP is locked, else false.
|
|
+ */
|
|
+bool bsec_read_sp_lock(uint32_t otp)
|
|
+{
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
+ uint32_t bank_value = read32(bsec_get_base() + BSEC_SPLOCK_OFF + bank);
|
|
+
|
|
+ return (bank_value & otp_mask) != 0U;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_wr_lock: Read permanent lock status.
|
|
+ * otp: OTP number.
|
|
+ * return: true if OTP is locked, else false.
|
|
+ */
|
|
+bool bsec_wr_lock(uint32_t otp)
|
|
+{
|
|
+ uint32_t bank = otp_bank_offset(otp);
|
|
+ uint32_t lock_bit = BIT(otp & BSEC_OTP_MASK);
|
|
+
|
|
+ if ((read32(bsec_get_base() + BSEC_WRLOCK_OFF + bank) &
|
|
+ lock_bit) != 0U) {
|
|
+ /*
|
|
+ * In case of write don't need to write,
|
|
+ * the lock is already set.
|
|
+ */
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_otp_lock: Lock Upper OTP or Global programming or debug enable
|
|
+ * service: Service to lock see header file.
|
|
+ * value: Value to write must always set to 1 (only use for debug purpose).
|
|
+ * return: BSEC_OK if succeed.
|
|
+ */
|
|
+uint32_t bsec_otp_lock(uint32_t service, uint32_t value)
|
|
+{
|
|
+ uintptr_t reg = bsec_get_base() + BSEC_OTP_LOCK_OFF;
|
|
+
|
|
+ switch (service) {
|
|
+ case BSEC_LOCK_UPPER_OTP:
|
|
+ write32(value << BSEC_LOCK_UPPER_OTP, reg);
|
|
+ break;
|
|
+ case BSEC_LOCK_DEBUG:
|
|
+ write32(value << BSEC_LOCK_DEBUG, reg);
|
|
+ break;
|
|
+ case BSEC_LOCK_PROGRAM:
|
|
+ write32(value << BSEC_LOCK_PROGRAM, reg);
|
|
+ break;
|
|
+ default:
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ return BSEC_OK;
|
|
+}
|
|
+
|
|
+static uint32_t enable_power(void)
|
|
+{
|
|
+ size_t cntdown;
|
|
+
|
|
+ io_mask32(bsec_get_base() + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP_MASK,
|
|
+ BSEC_CONF_POWER_UP_MASK);
|
|
+
|
|
+ for (cntdown = BSEC_TIMEOUT_VALUE; cntdown; cntdown--) {
|
|
+ if (bsec_get_status() & BSEC_MODE_PWR_MASK) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return cntdown ? BSEC_OK : BSEC_TIMEOUT;
|
|
+}
|
|
+
|
|
+static uint32_t disable_power(void)
|
|
+{
|
|
+ size_t cntdown;
|
|
+
|
|
+ io_mask32(bsec_get_base() + BSEC_OTP_CONF_OFF, 0,
|
|
+ BSEC_CONF_POWER_UP_MASK);
|
|
+
|
|
+ for (cntdown = BSEC_TIMEOUT_VALUE; cntdown; cntdown--) {
|
|
+ if (!(bsec_get_status() & BSEC_MODE_PWR_MASK)) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return cntdown ? BSEC_OK : BSEC_TIMEOUT;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_power_safmem: Activate or deactivate SAFMEM power.
|
|
+ * power: true to power up, false to power down.
|
|
+ * return: BSEC_OK if succeed.
|
|
+ */
|
|
+static uint32_t bsec_power_safmem(bool enable)
|
|
+{
|
|
+ static unsigned int refcnt = ~0UL;
|
|
+ uint32_t result = BSEC_OK;
|
|
+ uint32_t exc = 0;
|
|
+
|
|
+ /* Get the initial state */
|
|
+ if (refcnt == ~0UL) {
|
|
+ refcnt = !!(bsec_get_status() & BSEC_MODE_PWR_MASK);
|
|
+ DMSG("Reset SAFMEM refcnt to %u", refcnt);
|
|
+ }
|
|
+
|
|
+ exc = bsec_lock();
|
|
+
|
|
+ if (enable && (incr_refcnt(&refcnt) != 0U)) {
|
|
+ result = enable_power();
|
|
+ }
|
|
+
|
|
+ if (!enable && (decr_refcnt(&refcnt) != 0U)) {
|
|
+ result = disable_power();
|
|
+ }
|
|
+
|
|
+ bsec_unlock(exc);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_mode_is_closed_device: read OTP secure sub-mode.
|
|
+ * return: false if open_device and true of closed_device.
|
|
+ */
|
|
+bool bsec_mode_is_closed_device(void)
|
|
+{
|
|
+ uint32_t value;
|
|
+
|
|
+ if ((bsec_shadow_register(DATA0_OTP) != BSEC_OK) ||
|
|
+ (bsec_read_otp(&value, DATA0_OTP) != BSEC_OK)) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return (value & DATA0_OTP_SECURED) == DATA0_OTP_SECURED;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value
|
|
+ * otp_value: read value.
|
|
+ * word: OTP number.
|
|
+ * return value: BSEC_OK if no error.
|
|
+ */
|
|
+uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word)
|
|
+{
|
|
+ uint32_t result;
|
|
+
|
|
+ result = bsec_shadow_register(word);
|
|
+ if (result != BSEC_OK) {
|
|
+ EMSG("BSEC: %u Shadowing Error %i\n", word, result);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ result = bsec_read_otp(otp_value, word);
|
|
+ if (result != BSEC_OK) {
|
|
+ EMSG("BSEC: %u Read Error %i\n", word, result);
|
|
+ }
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * bsec_check_nsec_access_rights: check non-secure access rights to target OTP.
|
|
+ * otp: OTP number.
|
|
+ * return: BSEC_OK if authorized access.
|
|
+ */
|
|
+uint32_t bsec_check_nsec_access_rights(uint32_t otp)
|
|
+{
|
|
+ if (otp > stm32mp_get_otp_max()) {
|
|
+ return BSEC_INVALID_PARAM;
|
|
+ }
|
|
+
|
|
+ if (otp >= stm32mp_get_otp_upper_start()) {
|
|
+ /* Check if BSEC is in OTP-SECURED closed_device state. */
|
|
+ if (bsec_mode_is_closed_device()) {
|
|
+#ifdef CFG_DT
|
|
+ if (!non_secure_can_access(otp)) {
|
|
+ return BSEC_ERROR;
|
|
+ }
|
|
+#else
|
|
+ return BSEC_ERROR;
|
|
+#endif
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return BSEC_OK;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static TEE_Result initialize_bsec(void)
|
|
+{
|
|
+ void *fdt;
|
|
+ int node;
|
|
+ struct dt_node_info bsec_info;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (fdt == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ node = bsec_get_dt_node(fdt, &bsec_info);
|
|
+ if (node < 0) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ bsec_dt_otp_nsec_access(fdt, node);
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(initialize_bsec);
|
|
+#endif
|
|
diff --git a/core/drivers/stm32_etzpc.c b/core/drivers/stm32_etzpc.c
|
|
new file mode 100644
|
|
index 0000000..fcb32e6
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_etzpc.c
|
|
@@ -0,0 +1,337 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32_etzpc.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <initcall.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <stdint.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <util.h>
|
|
+
|
|
+#ifdef CFG_DT
|
|
+#include <libfdt.h>
|
|
+#endif
|
|
+
|
|
+#define ETZPC_COMPAT "st,stm32-etzpc"
|
|
+#define ETZPC_LOCK_MASK 0x1U
|
|
+#define ETZPC_MODE_SHIFT 8
|
|
+#define ETZPC_MODE_MASK GENMASK_32(1, 0)
|
|
+#define ETZPC_ID_SHIFT 16
|
|
+#define ETZPC_ID_MASK GENMASK_32(7, 0)
|
|
+
|
|
+/* ID Registers */
|
|
+#define ETZPC_TZMA0_SIZE 0x000U
|
|
+#define ETZPC_DECPROT0 0x010U
|
|
+#define ETZPC_DECPROT_LOCK0 0x030U
|
|
+#define ETZPC_HWCFGR 0x3F0U
|
|
+#define ETZPC_VERR 0x3F4U
|
|
+
|
|
+/* ID Registers fields */
|
|
+#define ETZPC_TZMA0_SIZE_LOCK BIT(31)
|
|
+#define ETZPC_DECPROT0_MASK GENMASK_32(1, 0)
|
|
+#define ETZPC_HWCFGR_NUM_TZMA_SHIFT 0
|
|
+#define ETZPC_HWCFGR_NUM_PER_SEC_SHIFT 8
|
|
+#define ETZPC_HWCFGR_NUM_AHB_SEC_SHIFT 16
|
|
+#define ETZPC_HWCFGR_CHUNCKS1N4_SHIFT 24
|
|
+
|
|
+#define DECPROT_SHIFT 1
|
|
+#define IDS_PER_DECPROT_REGS 16U
|
|
+#define IDS_PER_DECPROT_LOCK_REGS 32U
|
|
+
|
|
+#define ETZPC_TZMA_ALL_SECURE 0x3FF
|
|
+#define ETZPC_TZMA_ALL_NO_SECURE 0x000
|
|
+
|
|
+/*
|
|
+ * etzpc_instance.
|
|
+ * base : register virtual base address set during init given by user
|
|
+ * pbase : register physical base address set during init given by user
|
|
+ * chunk_size : supported TZMA size steps
|
|
+ * num_tzma: number of TZMA zone read from register at init
|
|
+ * num_ahb_sec : number of securable AHB master zone read from register
|
|
+ * num_per_sec : number of securable AHB & APB Peripherals read from register
|
|
+ * revision : IP revision read from register at init
|
|
+ * periph_cfg : buffer storing holding the DECPROT configuration for peripherals
|
|
+ */
|
|
+struct etzpc_instance {
|
|
+ uintptr_t base;
|
|
+ paddr_t pbase;
|
|
+ uint8_t chunck_size;
|
|
+ uint8_t num_tzma;
|
|
+ uint8_t num_per_sec;
|
|
+ uint8_t num_ahb_sec;
|
|
+ uint8_t revision;
|
|
+ uint8_t *periph_cfg;
|
|
+};
|
|
+
|
|
+/* Only 1 instance of the ETZPC is expected per platform */
|
|
+static struct etzpc_instance etzpc_dev;
|
|
+
|
|
+#ifdef CFG_DT
|
|
+struct dt_id_attr {
|
|
+ /* The effective size of the array is meaningless here */
|
|
+ fdt32_t id_attr[1];
|
|
+};
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Implementation uses uint8_t to store each securable DECPROT configuration.
|
|
+ * When resuming from deep suspend, the DECPROT configurations are restored.
|
|
+ */
|
|
+#define PERIPH_LOCK_BIT BIT(7)
|
|
+#define PERIPH_ATTR_MASK GENMASK_32(2, 0)
|
|
+
|
|
+static uintptr_t etzpc_base(void)
|
|
+{
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return etzpc_dev.pbase;
|
|
+
|
|
+ if (!etzpc_dev.base)
|
|
+ etzpc_dev.base = (uintptr_t)phys_to_virt(etzpc_dev.pbase,
|
|
+ MEM_AREA_IO_SEC);
|
|
+
|
|
+ return etzpc_dev.base;
|
|
+}
|
|
+
|
|
+static bool valid_decprot_id(unsigned int id)
|
|
+{
|
|
+ return id < (unsigned int)etzpc_dev.num_per_sec;
|
|
+}
|
|
+
|
|
+static bool valid_tzma_id(unsigned int id)
|
|
+{
|
|
+ return id < (unsigned int)etzpc_dev.num_tzma;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static int etzpc_dt_conf_decprot(void *fdt, int node)
|
|
+{
|
|
+ const struct dt_id_attr *conf_list;
|
|
+ unsigned int i;
|
|
+ int len = 0;
|
|
+
|
|
+ conf_list = (const struct dt_id_attr *)fdt_getprop(fdt, node,
|
|
+ "st,decprot", &len);
|
|
+ if (conf_list == NULL) {
|
|
+ IMSG("No ETZPC configuration in DT, use default");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < len / sizeof(uint32_t); i++) {
|
|
+ enum etzpc_decprot_attributes attr;
|
|
+ uint32_t value;
|
|
+ uint32_t id;
|
|
+ uint32_t mode;
|
|
+
|
|
+ value = fdt32_to_cpu(conf_list->id_attr[i]);
|
|
+
|
|
+ id = ((value >> ETZPC_ID_SHIFT) & ETZPC_ID_MASK);
|
|
+ if (!valid_decprot_id(id)) {
|
|
+ EMSG("Invalid DECPROT %" PRIu32, id);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ mode = (value >> ETZPC_MODE_SHIFT) & ETZPC_MODE_MASK;
|
|
+ attr = stm32mp_etzpc_binding2decprot(mode);
|
|
+
|
|
+ stm32mp_register_etzpc_decprot(id, attr);
|
|
+
|
|
+ etzpc_configure_decprot(id, attr);
|
|
+
|
|
+ if ((value & ETZPC_LOCK_MASK) != 0U) {
|
|
+ etzpc_lock_decprot(id);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+void etzpc_configure_decprot(uint32_t decprot_id,
|
|
+ enum etzpc_decprot_attributes decprot_attr)
|
|
+{
|
|
+ uintptr_t offset = 4U * (decprot_id / IDS_PER_DECPROT_REGS);
|
|
+ uint32_t shift = (decprot_id % IDS_PER_DECPROT_REGS) << DECPROT_SHIFT;
|
|
+ uint32_t masked_decprot = (uint32_t)decprot_attr & ETZPC_DECPROT0_MASK;
|
|
+ uintptr_t base = etzpc_base();
|
|
+
|
|
+ assert(valid_decprot_id(decprot_id));
|
|
+
|
|
+ mmio_clrsetbits_32(base + ETZPC_DECPROT0 + offset,
|
|
+ (uint32_t)ETZPC_DECPROT0_MASK << shift,
|
|
+ masked_decprot << shift);
|
|
+
|
|
+ etzpc_dev.periph_cfg[decprot_id] = (uint8_t)decprot_attr;
|
|
+
|
|
+ assert((decprot_attr & ~PERIPH_ATTR_MASK) == 0);
|
|
+ COMPILE_TIME_ASSERT(TZPC_DECPROT_MAX <= UINT8_MAX);
|
|
+}
|
|
+
|
|
+enum etzpc_decprot_attributes etzpc_get_decprot(uint32_t decprot_id)
|
|
+{
|
|
+ uintptr_t offset = 4U * (decprot_id / IDS_PER_DECPROT_REGS);
|
|
+ uint32_t shift = (decprot_id % IDS_PER_DECPROT_REGS) << DECPROT_SHIFT;
|
|
+ uintptr_t base_decprot = etzpc_base() + offset;
|
|
+ uint32_t value;
|
|
+
|
|
+ assert(valid_decprot_id(decprot_id));
|
|
+
|
|
+ value = (read32(base_decprot + ETZPC_DECPROT0) >> shift) &
|
|
+ ETZPC_DECPROT0_MASK;
|
|
+
|
|
+ return (enum etzpc_decprot_attributes)value;
|
|
+}
|
|
+
|
|
+void etzpc_lock_decprot(uint32_t decprot_id)
|
|
+{
|
|
+ uintptr_t offset = 4U * (decprot_id / IDS_PER_DECPROT_LOCK_REGS);
|
|
+ uint32_t shift = BIT(decprot_id % IDS_PER_DECPROT_LOCK_REGS);
|
|
+ uintptr_t base_decprot = etzpc_base() + offset;
|
|
+
|
|
+ assert(valid_decprot_id(decprot_id));
|
|
+
|
|
+ write32(shift, base_decprot + ETZPC_DECPROT_LOCK0);
|
|
+
|
|
+ etzpc_dev.periph_cfg[decprot_id] |= PERIPH_LOCK_BIT;
|
|
+}
|
|
+
|
|
+void etzpc_configure_tzma(uint32_t tzma_id, uint16_t tzma_value)
|
|
+{
|
|
+ assert(valid_tzma_id(tzma_id));
|
|
+
|
|
+ write32(tzma_value, etzpc_base() + ETZPC_TZMA0_SIZE +
|
|
+ (sizeof(uint32_t) * tzma_id));
|
|
+}
|
|
+
|
|
+uint16_t etzpc_get_tzma(uint32_t tzma_id)
|
|
+{
|
|
+ assert(valid_tzma_id(tzma_id));
|
|
+
|
|
+ return read32(etzpc_base() + ETZPC_TZMA0_SIZE +
|
|
+ (sizeof(uint32_t) * tzma_id));
|
|
+}
|
|
+
|
|
+void etzpc_lock_tzma(uint32_t tzma_id)
|
|
+{
|
|
+ assert(valid_tzma_id(tzma_id));
|
|
+
|
|
+ mmio_setbits_32(etzpc_base() + ETZPC_TZMA0_SIZE +
|
|
+ (sizeof(uint32_t) * tzma_id), ETZPC_TZMA0_SIZE_LOCK);
|
|
+}
|
|
+
|
|
+bool etzpc_get_lock_tzma(uint32_t tzma_id)
|
|
+{
|
|
+ uint32_t tzma_size;
|
|
+
|
|
+ assert(valid_tzma_id(tzma_id));
|
|
+
|
|
+ tzma_size = read32(etzpc_base() + ETZPC_TZMA0_SIZE +
|
|
+ (sizeof(uint32_t) * tzma_id));
|
|
+
|
|
+ return (tzma_size & ETZPC_TZMA0_SIZE_LOCK) != 0;
|
|
+}
|
|
+
|
|
+static void etzpc_suspend_resume(enum pm_op op, void __unused *handle)
|
|
+{
|
|
+ unsigned int n;
|
|
+
|
|
+ if (op == PM_OP_SUSPEND) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* OP-TEE owns the whole in SYSRAM */
|
|
+ etzpc_configure_tzma(1, ETZPC_TZMA_ALL_SECURE);
|
|
+ etzpc_lock_tzma(1);
|
|
+
|
|
+ for (n = 0; n < etzpc_dev.num_per_sec; n++) {
|
|
+ unsigned int attr = etzpc_dev.periph_cfg[n] & PERIPH_ATTR_MASK;
|
|
+
|
|
+ etzpc_configure_decprot(n, (enum etzpc_decprot_attributes)attr);
|
|
+
|
|
+ if (etzpc_dev.periph_cfg[n] & PERIPH_LOCK_BIT) {
|
|
+ etzpc_lock_decprot(n);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+KEEP_PAGER(etzpc_suspend_resume);
|
|
+
|
|
+static TEE_Result etzpc_init(void)
|
|
+{
|
|
+ void *fdt __maybe_unused;
|
|
+ int node __maybe_unused;
|
|
+ struct dt_node_info etzpc_info __maybe_unused;
|
|
+ uintptr_t base;
|
|
+ uint32_t hwcfg;
|
|
+ size_t n;
|
|
+
|
|
+#ifdef CFG_DT
|
|
+ fdt = get_dt_blob();
|
|
+ if (!fdt) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ node = fdt_get_node(fdt, &etzpc_info, -1, ETZPC_COMPAT);
|
|
+ if (node >= 0) {
|
|
+ etzpc_dev.pbase = etzpc_info.base;
|
|
+ assert(etzpc_dev.pbase == stm32mp_get_etzpc_base());
|
|
+
|
|
+ if (etzpc_info.status != DT_STATUS_OK_SEC) {
|
|
+ EMSG("ETZPC DT status");
|
|
+ panic();
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (!etzpc_dev.pbase) {
|
|
+ etzpc_dev.pbase = stm32mp_get_etzpc_base();
|
|
+ }
|
|
+
|
|
+ base = etzpc_base();
|
|
+
|
|
+ hwcfg = read32(base + ETZPC_HWCFGR);
|
|
+ etzpc_dev.num_tzma = (uint8_t)(hwcfg >> ETZPC_HWCFGR_NUM_TZMA_SHIFT);
|
|
+ etzpc_dev.num_per_sec = (uint8_t)(hwcfg >>
|
|
+ ETZPC_HWCFGR_NUM_PER_SEC_SHIFT);
|
|
+ etzpc_dev.num_ahb_sec = (uint8_t)(hwcfg >>
|
|
+ ETZPC_HWCFGR_NUM_AHB_SEC_SHIFT);
|
|
+ etzpc_dev.chunck_size = (uint8_t)(hwcfg >>
|
|
+ ETZPC_HWCFGR_CHUNCKS1N4_SHIFT);
|
|
+
|
|
+ etzpc_dev.revision = read8(base + ETZPC_VERR);
|
|
+
|
|
+ etzpc_dev.periph_cfg = calloc(etzpc_dev.num_per_sec,
|
|
+ sizeof(*etzpc_dev.periph_cfg));
|
|
+ if (etzpc_dev.periph_cfg == NULL) {
|
|
+ panic();
|
|
+ }
|
|
+ for (n = 0; n < etzpc_dev.num_per_sec; n++) {
|
|
+ etzpc_configure_decprot(n, etzpc_get_decprot(n));
|
|
+ }
|
|
+
|
|
+ DMSG("ETZPC version 0x%" PRIx8, etzpc_dev.revision);
|
|
+
|
|
+#ifdef CFG_DT
|
|
+ if (etzpc_dt_conf_decprot(fdt, node)) {
|
|
+ panic();
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* OP-TEE owns the whole in SYSRAM */
|
|
+ etzpc_configure_tzma(1, ETZPC_TZMA_ALL_SECURE);
|
|
+ etzpc_lock_tzma(1);
|
|
+
|
|
+ stm32mp_register_pm_cb(etzpc_suspend_resume, NULL);
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(etzpc_init);
|
|
diff --git a/core/drivers/stm32_gpio.c b/core/drivers/stm32_gpio.c
|
|
new file mode 100644
|
|
index 0000000..cb18271
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_gpio.c
|
|
@@ -0,0 +1,417 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32_gpio.h>
|
|
+#include <io.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <stdbool.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <trace.h>
|
|
+#include <util.h>
|
|
+
|
|
+#ifdef CFG_DT
|
|
+#include <libfdt.h>
|
|
+#endif
|
|
+
|
|
+#define DT_GPIO_BANK_SHIFT 12
|
|
+#define DT_GPIO_BANK_MASK GENMASK_32(16, 12)
|
|
+#define DT_GPIO_PIN_SHIFT 8
|
|
+#define DT_GPIO_PIN_MASK GENMASK_32(11, 8)
|
|
+#define DT_GPIO_MODE_MASK GENMASK_32(7, 0)
|
|
+
|
|
+static void get_gpio_cfg(uint32_t bank, uint32_t pin, struct gpio_cfg *cfg)
|
|
+{
|
|
+ uintptr_t base = stm32_get_gpio_bank_base(bank);
|
|
+ int clock = stm32_get_gpio_bank_clock(bank);
|
|
+
|
|
+ stm32_clock_enable((unsigned long)clock);
|
|
+
|
|
+ cfg->moder = (mmio_read_32(base + GPIO_MODE_OFFSET) >> (pin << 1)) &
|
|
+ GPIO_MODE_MAX;
|
|
+
|
|
+ cfg->otyper = (mmio_read_32(base + GPIO_TYPE_OFFSET) >> pin) & 1;
|
|
+ cfg->ospeedr = (mmio_read_32(base + GPIO_SPEED_OFFSET) >> (pin << 1)) &
|
|
+ GPIO_SPEED_MAX;
|
|
+ cfg->pupdr = (mmio_read_32(base + GPIO_PUPD_OFFSET) >> (pin << 1)) &
|
|
+ GPIO_PULL_MAX;
|
|
+
|
|
+ cfg->odr = (mmio_read_32(base + GPIO_ODR_OFFSET) >> (pin << 1)) & 1;
|
|
+
|
|
+ if (pin < GPIO_ALT_LOWER_LIMIT) {
|
|
+ cfg->afr = (mmio_read_32(base + GPIO_AFRL_OFFSET) >>
|
|
+ (pin << 2)) & GPIO_ALTERNATE_MAX;
|
|
+ } else {
|
|
+ cfg->afr = (mmio_read_32(base + GPIO_AFRH_OFFSET) >>
|
|
+ ((pin - GPIO_ALT_LOWER_LIMIT) << 2)) &
|
|
+ GPIO_ALTERNATE_MAX;
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable((unsigned long)clock);
|
|
+}
|
|
+
|
|
+static void set_gpio_cfg(uint32_t bank, uint32_t pin, struct gpio_cfg *cfg)
|
|
+{
|
|
+ uintptr_t base = stm32_get_gpio_bank_base(bank);
|
|
+ int clock = stm32_get_gpio_bank_clock(bank);
|
|
+
|
|
+ stm32_clock_enable((unsigned long)clock);
|
|
+
|
|
+ mmio_clrsetbits_32(base + GPIO_MODE_OFFSET,
|
|
+ GPIO_MODE_MAX << (pin << 1),
|
|
+ cfg->moder << (pin << 1));
|
|
+
|
|
+ mmio_clrsetbits_32(base + GPIO_TYPE_OFFSET,
|
|
+ BIT(pin),
|
|
+ cfg->otyper << pin);
|
|
+
|
|
+ mmio_clrsetbits_32(base + GPIO_SPEED_OFFSET,
|
|
+ GPIO_SPEED_MAX << (pin << 1),
|
|
+ cfg->ospeedr << (pin << 1));
|
|
+
|
|
+ mmio_clrsetbits_32(base + GPIO_PUPD_OFFSET,
|
|
+ BIT(pin),
|
|
+ cfg->pupdr << (pin << 1));
|
|
+
|
|
+ if (pin < GPIO_ALT_LOWER_LIMIT) {
|
|
+ mmio_clrsetbits_32(base + GPIO_AFRL_OFFSET,
|
|
+ GPIO_ALTERNATE_MAX << (pin << 2),
|
|
+ cfg->afr << (pin << 2));
|
|
+ } else {
|
|
+ size_t shift = (pin - GPIO_ALT_LOWER_LIMIT) << 2;
|
|
+
|
|
+ mmio_clrsetbits_32(base + GPIO_AFRH_OFFSET,
|
|
+ GPIO_ALTERNATE_MAX << shift,
|
|
+ cfg->afr << shift);
|
|
+ }
|
|
+
|
|
+ mmio_clrsetbits_32(base + GPIO_ODR_OFFSET,
|
|
+ BIT(pin), cfg->odr << pin);
|
|
+
|
|
+ stm32_clock_disable((unsigned long)clock);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_gpio_set_output_level - Set level of an output GPIO instance
|
|
+ *
|
|
+ * @bank: GPIO bank
|
|
+ * @pin: GPIO pin position in bank
|
|
+ * @level: target level, either 0 (level low) or non zero (level high)
|
|
+ */
|
|
+void stm32_gpio_set_output_level(uint32_t bank, uint32_t pin, int level)
|
|
+{
|
|
+ uintptr_t base = stm32_get_gpio_bank_base(bank);
|
|
+ int clock = stm32_get_gpio_bank_clock(bank);
|
|
+
|
|
+ assert(pin <= GPIO_PIN_MAX);
|
|
+
|
|
+ stm32_clock_enable((unsigned long)clock);
|
|
+
|
|
+ if (level) {
|
|
+ write32(BIT(pin), base + GPIO_BSRR_OFFSET);
|
|
+ } else {
|
|
+ write32(BIT(pin + 16), base + GPIO_BSRR_OFFSET);
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable((unsigned long)clock);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_pinctrl_load_active_cfg - Load the PINCTRLs active configuration
|
|
+ *
|
|
+ * @pinctrl: array of PINCTRL configurations to apply
|
|
+ * @cnt: number of elements in array pinctrl
|
|
+ */
|
|
+void stm32_pinctrl_load_active_cfg(struct stm32_pinctrl *pinctrl, size_t cnt)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ for (n = 0; n < cnt; n++) {
|
|
+ set_gpio_cfg(pinctrl[n].bank, pinctrl[n].pin,
|
|
+ &pinctrl[n].active_cfg);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_pinctrl_load_standby_cfg - Load the PINCTRLs standby configuration
|
|
+ *
|
|
+ * @pinctrl: array of PINCTRL configurations to apply
|
|
+ * @cnt: number of elements in array pinctrl
|
|
+ */
|
|
+void stm32_pinctrl_load_standby_cfg(struct stm32_pinctrl *pinctrl, size_t cnt)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ for (n = 0; n < cnt; n++) {
|
|
+ set_gpio_cfg(pinctrl[n].bank, pinctrl[n].pin,
|
|
+ &pinctrl[n].standby_cfg);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_pinctrl_store_standby_cfg - Save current PINCTRLs config as standby
|
|
+ *
|
|
+ * @pinctrl: array of PINCTRL configurations to store
|
|
+ * @cnt: number of elements in array pinctrl
|
|
+ */
|
|
+void stm32_pinctrl_store_standby_cfg(struct stm32_pinctrl *pinctrl, size_t cnt)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ for (n = 0; n < cnt; n++) {
|
|
+ get_gpio_cfg(pinctrl[n].bank, pinctrl[n].pin,
|
|
+ &pinctrl[n].standby_cfg);
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+/* Return GPIO bank node if status is okay in DT, else return 0 */
|
|
+static int ckeck_gpio_bank(void *fdt, uint32_t bank, int pinctrl_node)
|
|
+{
|
|
+ int pinctrl_subnode;
|
|
+
|
|
+ fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) {
|
|
+ uint32_t bank_offset;
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ if (fdt_getprop(fdt, pinctrl_subnode,
|
|
+ "gpio-controller", NULL) == NULL) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ bank_offset = stm32_get_gpio_bank_offset(bank);
|
|
+
|
|
+ if ((fdt32_to_cpu(*cuint) == bank_offset) &&
|
|
+ (_fdt_get_status(fdt,pinctrl_subnode) !=
|
|
+ DT_STATUS_DISABLED)) {
|
|
+ return pinctrl_subnode;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int get_pinctrl_from_fdt(void *fdt, int node,
|
|
+ struct stm32_pinctrl *pinctrl, size_t count)
|
|
+{
|
|
+ const fdt32_t *cuint, *slewrate;
|
|
+ int len;
|
|
+ int pinctrl_node;
|
|
+ int bank_node;
|
|
+ int clk;
|
|
+ uint32_t i;
|
|
+ uint32_t speed = GPIO_SPEED_LOW;
|
|
+ uint32_t pull = GPIO_NO_PULL;
|
|
+ size_t found = 0;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "pinmux", &len);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ pinctrl_node = fdt_parent_offset(fdt, fdt_parent_offset(fdt, node));
|
|
+ if (pinctrl_node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ slewrate = fdt_getprop(fdt, node, "slew-rate", NULL);
|
|
+ if (slewrate != NULL) {
|
|
+ speed = fdt32_to_cpu(*slewrate);
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, node, "bias-pull-up", NULL) != NULL) {
|
|
+ pull = GPIO_PULL_UP;
|
|
+ } else if (fdt_getprop(fdt, node, "bias-pull-down", NULL) != NULL) {
|
|
+ pull = GPIO_PULL_DOWN;
|
|
+ } else {
|
|
+ DMSG("No bias configured in node %d\n", node);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) {
|
|
+ uint32_t pincfg;
|
|
+ uint32_t bank;
|
|
+ uint32_t pin;
|
|
+ uint32_t mode;
|
|
+ uint32_t alternate = GPIO_ALTERNATE_0;
|
|
+
|
|
+ pincfg = fdt32_to_cpu(*cuint);
|
|
+ cuint++;
|
|
+
|
|
+ bank = (pincfg & DT_GPIO_BANK_MASK) >> DT_GPIO_BANK_SHIFT;
|
|
+
|
|
+ pin = (pincfg & DT_GPIO_PIN_MASK) >> DT_GPIO_PIN_SHIFT;
|
|
+
|
|
+ mode = pincfg & DT_GPIO_MODE_MASK;
|
|
+
|
|
+ switch (mode) {
|
|
+ case 0:
|
|
+ mode = GPIO_MODE_INPUT;
|
|
+ break;
|
|
+ case 1:
|
|
+ case 2:
|
|
+ case 3:
|
|
+ case 4:
|
|
+ case 5:
|
|
+ case 6:
|
|
+ case 7:
|
|
+ case 8:
|
|
+ case 9:
|
|
+ case 10:
|
|
+ case 11:
|
|
+ case 12:
|
|
+ case 13:
|
|
+ case 14:
|
|
+ case 15:
|
|
+ case 16:
|
|
+ alternate = mode - 1U;
|
|
+ mode = GPIO_MODE_ALTERNATE;
|
|
+ break;
|
|
+ case 17:
|
|
+ mode = GPIO_MODE_ANALOG;
|
|
+ break;
|
|
+ default:
|
|
+ mode = GPIO_MODE_OUTPUT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, node, "drive-open-drain", NULL) != NULL) {
|
|
+ mode |= GPIO_OPEN_DRAIN;
|
|
+ }
|
|
+
|
|
+ bank_node = ckeck_gpio_bank(fdt, bank, pinctrl_node);
|
|
+ if (bank_node == 0) {
|
|
+ panic("PINCTRL inconsistent in DT");
|
|
+ }
|
|
+
|
|
+ clk = fdt_get_clock_id(fdt, bank_node);
|
|
+ if (clk < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ /* Platform knows the clock: assert it is okay */
|
|
+ assert(clk == stm32_get_gpio_bank_clock(bank));
|
|
+ stm32_clock_enable((unsigned int)clk);
|
|
+ stm32_clock_disable((unsigned int)clk);
|
|
+
|
|
+ if (found < count) {
|
|
+ struct stm32_pinctrl *cfg = &pinctrl[found];
|
|
+
|
|
+ cfg->bank = (uint8_t)bank;
|
|
+ cfg->pin = (uint8_t)pin;
|
|
+ cfg->active_cfg.moder = mode & ~GPIO_OPEN_DRAIN;
|
|
+ cfg->active_cfg.otyper = mode & GPIO_OPEN_DRAIN ? 1 : 0;
|
|
+ cfg->active_cfg.ospeedr = speed;
|
|
+ cfg->active_cfg.pupdr = pull;
|
|
+ cfg->active_cfg.odr = 0;
|
|
+ cfg->active_cfg.afr = alternate;
|
|
+ cfg->standby_cfg.moder = GPIO_MODE_ANALOG;
|
|
+ cfg->standby_cfg.pupdr = GPIO_NO_PULL;
|
|
+ }
|
|
+
|
|
+ found++;
|
|
+ }
|
|
+
|
|
+ return (int)found;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * stm32_pinctrl_fdt_get_pinctrl - get PINCTRL config from a DT device node
|
|
+ *
|
|
+ * Argument cfg can be set to NULL or count to 0: the function will return only
|
|
+ * the number of PINCTRL instances found in the device tree for the target
|
|
+ * device.
|
|
+ *
|
|
+ * If more instances than count are found then the function returns the
|
|
+ * effective number of PINCTRL found but will fill array cfg only according
|
|
+ * to the value of count.
|
|
+ *
|
|
+ * @fdt: device tree
|
|
+ * @node: device node in the device tree
|
|
+ * @cfg: NULL or pointer to array of struct stm32_pinctrl
|
|
+ * @count: number of elements pointed by argument cfg
|
|
+ *
|
|
+ * Return the number of PINCTRL instances found and a negative value on error
|
|
+ */
|
|
+int stm32_pinctrl_fdt_get_pinctrl(void *fdt, int device_node,
|
|
+ struct stm32_pinctrl *cfg, size_t count)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+ int lenp;
|
|
+ uint32_t i;
|
|
+ size_t found = 0;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, device_node, "pinctrl-0", &lenp);
|
|
+ if (cuint == NULL) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ((uint32_t)lenp / 4U); i++) {
|
|
+ int node;
|
|
+ int subnode;
|
|
+
|
|
+ node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint));
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ fdt_for_each_subnode(subnode, fdt, node) {
|
|
+ size_t n;
|
|
+ int rc;
|
|
+
|
|
+ if (count > found) {
|
|
+ n = count - found;
|
|
+ } else {
|
|
+ n = 0;
|
|
+ }
|
|
+
|
|
+ rc = get_pinctrl_from_fdt(fdt, subnode, &cfg[found], n);
|
|
+ if (rc < 0) {
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ found += (size_t)rc;
|
|
+ }
|
|
+
|
|
+ cuint++;
|
|
+ }
|
|
+
|
|
+ return (int)found;
|
|
+}
|
|
+#endif /*CFG_DT*/
|
|
+
|
|
+/*
|
|
+ * stm32_gpio_set_secure_cfg - Set secure field of an output GPIO instance
|
|
+ *
|
|
+ * @bank: GPIO bank
|
|
+ * @pin: GPIO pin position in bank
|
|
+ * @secure: Secure field, either false (non secure) or true (secure)
|
|
+ */
|
|
+void stm32_gpio_set_secure_cfg(uint32_t bank, uint32_t pin, bool secure)
|
|
+{
|
|
+ uintptr_t base = stm32_get_gpio_bank_base(bank);
|
|
+ int clock = stm32_get_gpio_bank_clock(bank);
|
|
+
|
|
+ assert(pin <= GPIO_PIN_MAX);
|
|
+
|
|
+ assert(!(secure && stm32mp_gpio_bank_is_non_secure(bank)));
|
|
+
|
|
+ stm32_clock_enable((unsigned long)clock);
|
|
+
|
|
+ if (secure) {
|
|
+ mmio_setbits_32(base + GPIO_SECR_OFFSET, BIT(pin));
|
|
+ } else {
|
|
+ mmio_clrbits_32(base + GPIO_SECR_OFFSET, BIT(pin));
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable((unsigned long)clock);
|
|
+}
|
|
diff --git a/core/drivers/stm32_i2c.c b/core/drivers/stm32_i2c.c
|
|
new file mode 100644
|
|
index 0000000..f7f0f70
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_i2c.c
|
|
@@ -0,0 +1,1629 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <arm32.h>
|
|
+#include <drivers/stm32_i2c.h>
|
|
+#include <drivers/stm32_gpio.h>
|
|
+#include <io.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <libfdt.h>
|
|
+#include <stdbool.h>
|
|
+#include <stdlib.h>
|
|
+#include <stm32_util.h>
|
|
+
|
|
+/* STM32 I2C registers offsets */
|
|
+#define I2C_CR1 0x00U
|
|
+#define I2C_CR2 0x04U
|
|
+#define I2C_OAR1 0x08U
|
|
+#define I2C_OAR2 0x0CU
|
|
+#define I2C_TIMINGR 0x10U
|
|
+#define I2C_TIMEOUTR 0x14U
|
|
+#define I2C_ISR 0x18U
|
|
+#define I2C_ICR 0x1CU
|
|
+#define I2C_PECR 0x20U
|
|
+#define I2C_RXDR 0x24U
|
|
+#define I2C_TXDR 0x28U
|
|
+
|
|
+#define TIMINGR_CLEAR_MASK 0xF0FFFFFFU
|
|
+
|
|
+#define MAX_NBYTE_SIZE 255U
|
|
+
|
|
+#define I2C_NSEC_PER_SEC 1000000000L
|
|
+
|
|
+/*
|
|
+ * struct i2c_spec_s - Private I2C timing specifications.
|
|
+ * @rate: I2C bus speed (Hz)
|
|
+ * @rate_min: 80% of I2C bus speed (Hz)
|
|
+ * @rate_max: 120% of I2C bus speed (Hz)
|
|
+ * @fall_max: Max fall time of both SDA and SCL signals (ns)
|
|
+ * @rise_max: Max rise time of both SDA and SCL signals (ns)
|
|
+ * @hddat_min: Min data hold time (ns)
|
|
+ * @vddat_max: Max data valid time (ns)
|
|
+ * @sudat_min: Min data setup time (ns)
|
|
+ * @l_min: Min low period of the SCL clock (ns)
|
|
+ * @h_min: Min high period of the SCL clock (ns)
|
|
+ */
|
|
+struct i2c_spec_s {
|
|
+ uint32_t rate;
|
|
+ uint32_t rate_min;
|
|
+ uint32_t rate_max;
|
|
+ uint32_t fall_max;
|
|
+ uint32_t rise_max;
|
|
+ uint32_t hddat_min;
|
|
+ uint32_t vddat_max;
|
|
+ uint32_t sudat_min;
|
|
+ uint32_t l_min;
|
|
+ uint32_t h_min;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * struct i2c_timing_s - Private I2C output parameters.
|
|
+ * @scldel: Data setup time
|
|
+ * @sdadel: Data hold time
|
|
+ * @sclh: SCL high period (master mode)
|
|
+ * @sclh: SCL low period (master mode)
|
|
+ * @is_saved: True if relating to a configuration candidate
|
|
+ */
|
|
+struct i2c_timing_s {
|
|
+ uint8_t scldel;
|
|
+ uint8_t sdadel;
|
|
+ uint8_t sclh;
|
|
+ uint8_t scll;
|
|
+ bool is_saved;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * All these values are coming from I2C Specification, Version 6.0, 4th of
|
|
+ * April 2014.
|
|
+ *
|
|
+ * Table10. Characteristics of the SDA and SCL bus lines for Standard, Fast,
|
|
+ * and Fast-mode Plus I2C-bus devices.
|
|
+ */
|
|
+static const struct i2c_spec_s i2c_specs[] = {
|
|
+ [I2C_SPEED_STANDARD] = {
|
|
+ .rate = STANDARD_RATE,
|
|
+ .rate_min = 8000,
|
|
+ .rate_max = 120000,
|
|
+ .fall_max = 300,
|
|
+ .rise_max = 1000,
|
|
+ .hddat_min = 0,
|
|
+ .vddat_max = 3450,
|
|
+ .sudat_min = 250,
|
|
+ .l_min = 4700,
|
|
+ .h_min = 4000,
|
|
+ },
|
|
+ [I2C_SPEED_FAST] = {
|
|
+ .rate = FAST_RATE,
|
|
+ .rate_min = 320000,
|
|
+ .rate_max = 480000,
|
|
+ .fall_max = 300,
|
|
+ .rise_max = 300,
|
|
+ .hddat_min = 0,
|
|
+ .vddat_max = 900,
|
|
+ .sudat_min = 100,
|
|
+ .l_min = 1300,
|
|
+ .h_min = 600,
|
|
+ },
|
|
+ [I2C_SPEED_FAST_PLUS] = {
|
|
+ .rate = FAST_PLUS_RATE,
|
|
+ .rate_min = 800000,
|
|
+ .rate_max = 1200000,
|
|
+ .fall_max = 100,
|
|
+ .rise_max = 120,
|
|
+ .hddat_min = 0,
|
|
+ .vddat_max = 450,
|
|
+ .sudat_min = 50,
|
|
+ .l_min = 500,
|
|
+ .h_min = 260,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int i2c_request_memory_write(struct i2c_handle_s *hi2c,
|
|
+ uint16_t dev_addr, uint16_t mem_addr,
|
|
+ uint16_t mem_add_size,
|
|
+ uint64_t tick_to, uint64_t tick_start);
|
|
+static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint64_t tick_to, uint64_t tick_start);
|
|
+
|
|
+/* Private functions to handle flags during polling transfer */
|
|
+static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag,
|
|
+ uint8_t awaited_value,
|
|
+ uint64_t tick_to, uint64_t tick_start);
|
|
+static int i2c_wait_txis(struct i2c_handle_s *hi2c,
|
|
+ uint64_t tick_to, uint64_t tick_start);
|
|
+static int i2c_wait_stop(struct i2c_handle_s *hi2c,
|
|
+ uint64_t tick_to, uint64_t tick_start);
|
|
+static int i2c_ack_failed(struct i2c_handle_s *hi2c,
|
|
+ uint64_t tick_to, uint64_t tick_start);
|
|
+
|
|
+/* Private function to flush TXDR register */
|
|
+static void i2c_flush_txdr(struct i2c_handle_s *hi2c);
|
|
+
|
|
+/* Private function to start, restart or stop a transfer */
|
|
+static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t size, uint32_t i2c_mode,
|
|
+ uint32_t request);
|
|
+
|
|
+static uintptr_t get_base(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return hi2c->pbase;
|
|
+
|
|
+ return hi2c->vbase;
|
|
+}
|
|
+
|
|
+static bool i2c_is_secure(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ return hi2c->dt_status == DT_STATUS_OK_SEC;
|
|
+}
|
|
+
|
|
+static uint64_t ms2tick(uint32_t timeout_ms)
|
|
+{
|
|
+ return ((uint64_t)timeout_ms * read_cntfrq()) / 1000;
|
|
+}
|
|
+
|
|
+static uint64_t timeout_start(void)
|
|
+{
|
|
+ return read_cntpct();
|
|
+}
|
|
+
|
|
+static bool timeout_elapsed(uint64_t tick_start, uint64_t tick_to)
|
|
+{
|
|
+ return (tick_to != 0U) && ((read_cntpct() - tick_start) > tick_to);
|
|
+}
|
|
+
|
|
+static void notif_i2c_timeout(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+}
|
|
+
|
|
+static void save_cfg(struct i2c_handle_s *hi2c, struct i2c_cfg *cfg)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ cfg->cr1 = mmio_read_32(base + I2C_CR1);
|
|
+ cfg->cr2 = mmio_read_32(base + I2C_CR2);
|
|
+ cfg->oar1 = mmio_read_32(base + I2C_OAR1);
|
|
+ cfg->oar2 = mmio_read_32(base + I2C_OAR2);
|
|
+ cfg->timingr = mmio_read_32(base + I2C_TIMINGR);
|
|
+
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+}
|
|
+
|
|
+static void restore_cfg(struct i2c_handle_s *hi2c, struct i2c_cfg *cfg)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ if (hi2c->lock) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ mmio_clrbits_32(base + I2C_CR1, I2C_CR1_PE);
|
|
+ mmio_write_32(base + I2C_TIMINGR, cfg->timingr & TIMINGR_CLEAR_MASK);
|
|
+ mmio_write_32(base + I2C_OAR1, cfg->oar1);
|
|
+ mmio_write_32(base + I2C_CR2, cfg->cr2);
|
|
+ mmio_write_32(base + I2C_OAR2, cfg->oar2);
|
|
+ mmio_write_32(base + I2C_CR1, cfg->cr1 & ~I2C_CR1_PE);
|
|
+ mmio_setbits_32(base + I2C_CR1, cfg->cr1 & I2C_CR1_PE);
|
|
+
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+}
|
|
+
|
|
+static void __maybe_unused dump_cfg(struct i2c_cfg *cfg __maybe_unused)
|
|
+{
|
|
+ DMSG("CR1: %x", (unsigned)cfg->cr1);
|
|
+ DMSG("CR2: %x", (unsigned)cfg->cr2);
|
|
+ DMSG("OAR1: %x", (unsigned)cfg->oar1);
|
|
+ DMSG("OAR2: %x", (unsigned)cfg->oar2);
|
|
+ DMSG("TIM: %x", (unsigned)cfg->timingr);
|
|
+}
|
|
+
|
|
+static void __maybe_unused dump_i2c(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ uintptr_t __maybe_unused base = get_base(hi2c);
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ DMSG("CR1: %x", (unsigned)mmio_read_32(base + I2C_CR1));
|
|
+ DMSG("CR2: %x", (unsigned)mmio_read_32(base + I2C_CR2));
|
|
+ DMSG("OAR1: %x", (unsigned)mmio_read_32(base + I2C_OAR1));
|
|
+ DMSG("OAR2: %x", (unsigned)mmio_read_32(base + I2C_OAR2));
|
|
+ DMSG("TIM: %x", (unsigned)mmio_read_32(base + I2C_TIMINGR));
|
|
+
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Compute the I2C device timings.
|
|
+ * @param init: Ref to the initialization configuration structure
|
|
+ * @param clock_src: I2C clock source frequency (Hz)
|
|
+ * @param timing: Pointer to the final computed timing result
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_compute_timing(struct stm32_i2c_init_s *init,
|
|
+ uint32_t clock_src, uint32_t *timing)
|
|
+{
|
|
+ enum i2c_speed_e mode = init->speed_mode;
|
|
+ uint32_t speed_freq;
|
|
+ uint32_t i2cclk = UDIV_ROUND_NEAREST(I2C_NSEC_PER_SEC, clock_src);
|
|
+ uint32_t i2cbus;
|
|
+ uint32_t p_prev = I2C_TIMINGR_PRESC_MAX;
|
|
+ uint32_t af_delay_min;
|
|
+ uint32_t af_delay_max;
|
|
+ uint32_t dnf_delay;
|
|
+ uint32_t tsync;
|
|
+ uint32_t clk_min;
|
|
+ uint32_t clk_max;
|
|
+ int clk_error_prev;
|
|
+ uint16_t p;
|
|
+ uint16_t l;
|
|
+ uint16_t a;
|
|
+ uint16_t h;
|
|
+ unsigned int sdadel_min;
|
|
+ unsigned int sdadel_max;
|
|
+ unsigned int scldel_min;
|
|
+ unsigned int delay;
|
|
+ int s = -1;
|
|
+ struct i2c_timing_s solutions[I2C_TIMINGR_PRESC_MAX];
|
|
+
|
|
+ switch (mode) {
|
|
+ case I2C_SPEED_STANDARD:
|
|
+ case I2C_SPEED_FAST:
|
|
+ case I2C_SPEED_FAST_PLUS:
|
|
+ break;
|
|
+ default:
|
|
+ EMSG("I2C speed out of bound {%d/%d}\n",
|
|
+ mode, I2C_SPEED_FAST_PLUS);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ speed_freq = i2c_specs[mode].rate;
|
|
+ i2cbus = UDIV_ROUND_NEAREST(I2C_NSEC_PER_SEC, speed_freq);
|
|
+ clk_error_prev = INT_MAX;
|
|
+
|
|
+ if ((init->rise_time > i2c_specs[mode].rise_max) ||
|
|
+ (init->fall_time > i2c_specs[mode].fall_max)) {
|
|
+ EMSG(" I2C timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
|
|
+ init->rise_time, i2c_specs[mode].rise_max,
|
|
+ init->fall_time, i2c_specs[mode].fall_max);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (init->digital_filter_coef > STM32_I2C_DIGITAL_FILTER_MAX) {
|
|
+ EMSG("DNF out of bound %d/%d\n",
|
|
+ init->digital_filter_coef, STM32_I2C_DIGITAL_FILTER_MAX);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Analog and Digital Filters */
|
|
+ af_delay_min = (init->analog_filter ?
|
|
+ STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0);
|
|
+ af_delay_max = (init->analog_filter ?
|
|
+ STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0);
|
|
+ dnf_delay = init->digital_filter_coef * i2cclk;
|
|
+
|
|
+ sdadel_min = i2c_specs[mode].hddat_min + init->fall_time;
|
|
+ delay = af_delay_min - ((init->digital_filter_coef + 3) * i2cclk);
|
|
+ if (SUB_OVERFLOW(sdadel_min, delay ,&sdadel_min))
|
|
+ sdadel_min = 0;
|
|
+
|
|
+ sdadel_max = i2c_specs[mode].vddat_max - init->rise_time;
|
|
+ delay = af_delay_max - ((init->digital_filter_coef + 4) * i2cclk);
|
|
+ if (SUB_OVERFLOW(sdadel_max, delay ,&sdadel_max))
|
|
+ sdadel_max = 0;
|
|
+
|
|
+ scldel_min = init->rise_time + i2c_specs[mode].sudat_min;
|
|
+
|
|
+ DMSG("I2C SDADEL(min/max): %u/%u, SCLDEL(Min): %u\n",
|
|
+ sdadel_min, sdadel_max, scldel_min);
|
|
+
|
|
+ memset(&solutions, 0, sizeof(solutions));
|
|
+
|
|
+ /* Compute possible values for PRESC, SCLDEL and SDADEL */
|
|
+ for (p = 0; p < I2C_TIMINGR_PRESC_MAX; p++) {
|
|
+ for (l = 0; l < I2C_TIMINGR_SCLDEL_MAX; l++) {
|
|
+ uint32_t scldel = (l + 1) * (p + 1) * i2cclk;
|
|
+
|
|
+ if (scldel < scldel_min) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (a = 0; a < I2C_TIMINGR_SDADEL_MAX; a++) {
|
|
+ uint32_t sdadel = (a * (p + 1) + 1) * i2cclk;
|
|
+
|
|
+ if ((sdadel >= sdadel_min) &&
|
|
+ (sdadel <= sdadel_max) &&
|
|
+ (p != p_prev)) {
|
|
+ solutions[p].scldel = l;
|
|
+ solutions[p].sdadel = a;
|
|
+ solutions[p].is_saved = true;
|
|
+ p_prev = p;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (p_prev == p) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (p_prev == I2C_TIMINGR_PRESC_MAX) {
|
|
+ EMSG(" I2C no Prescaler solution\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ tsync = af_delay_min + dnf_delay + (2 * i2cclk);
|
|
+ clk_max = I2C_NSEC_PER_SEC / i2c_specs[mode].rate_min;
|
|
+ clk_min = I2C_NSEC_PER_SEC / i2c_specs[mode].rate_max;
|
|
+
|
|
+ /*
|
|
+ * Among prescaler possibilities discovered above figures out SCL Low
|
|
+ * and High Period. Provided:
|
|
+ * - SCL Low Period has to be higher than Low Period of the SCL Clock
|
|
+ * defined by I2C Specification. I2C Clock has to be lower than
|
|
+ * (SCL Low Period - Analog/Digital filters) / 4.
|
|
+ * - SCL High Period has to be lower than High Period of the SCL Clock
|
|
+ * defined by I2C Specification.
|
|
+ * - I2C Clock has to be lower than SCL High Period.
|
|
+ */
|
|
+ for (p = 0; p < I2C_TIMINGR_PRESC_MAX; p++) {
|
|
+ uint32_t prescaler = (p + 1) * i2cclk;
|
|
+
|
|
+ if (!solutions[p].is_saved) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (l = 0; l < I2C_TIMINGR_SCLL_MAX; l++) {
|
|
+ uint32_t tscl_l = ((l + 1) * prescaler) + tsync;
|
|
+
|
|
+ if ((tscl_l < i2c_specs[mode].l_min) ||
|
|
+ (i2cclk >=
|
|
+ ((tscl_l - af_delay_min - dnf_delay) / 4))) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (h = 0; h < I2C_TIMINGR_SCLH_MAX; h++) {
|
|
+ uint32_t tscl_h = ((h + 1) * prescaler) + tsync;
|
|
+ uint32_t tscl = tscl_l + tscl_h +
|
|
+ init->rise_time +
|
|
+ init->fall_time;
|
|
+
|
|
+ if ((tscl >= clk_min) && (tscl <= clk_max) &&
|
|
+ (tscl_h >= i2c_specs[mode].h_min) &&
|
|
+ (i2cclk < tscl_h)) {
|
|
+ int clk_error = tscl - i2cbus;
|
|
+
|
|
+ if (clk_error < 0) {
|
|
+ clk_error = -clk_error;
|
|
+ }
|
|
+
|
|
+ if (clk_error < clk_error_prev) {
|
|
+ clk_error_prev = clk_error;
|
|
+ solutions[p].scll = l;
|
|
+ solutions[p].sclh = h;
|
|
+ s = p;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (s < 0) {
|
|
+ EMSG(" I2C no solution at all\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Finalize timing settings */
|
|
+ *timing = I2C_SET_TIMINGR_PRESC(s) |
|
|
+ I2C_SET_TIMINGR_SCLDEL(solutions[s].scldel) |
|
|
+ I2C_SET_TIMINGR_SDADEL(solutions[s].sdadel) |
|
|
+ I2C_SET_TIMINGR_SCLH(solutions[s].sclh) |
|
|
+ I2C_SET_TIMINGR_SCLL(solutions[s].scll);
|
|
+
|
|
+ DMSG("I2C TIMINGR (PRESC/SCLDEL/SDADEL): %i/%i/%i\n",
|
|
+ s, solutions[s].scldel, solutions[s].sdadel);
|
|
+ DMSG("I2C TIMINGR (SCLH/SCLL): %i/%i\n",
|
|
+ solutions[s].sclh, solutions[s].scll);
|
|
+ DMSG("I2C TIMINGR: 0x%x\n", *timing);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Setup the I2C device timings.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param init: Ref to the initialization configuration structure
|
|
+ * @param timing: Pointer to the final computed timing result
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_setup_timing(struct i2c_handle_s *hi2c,
|
|
+ struct stm32_i2c_init_s *init,
|
|
+ uint32_t *timing)
|
|
+{
|
|
+ int rc = 0;
|
|
+ uint32_t clock_src;
|
|
+
|
|
+ clock_src = stm32_clock_get_rate(hi2c->clock);
|
|
+ if (clock_src == 0U) {
|
|
+ EMSG("I2C clock rate is 0\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ do {
|
|
+ rc = i2c_compute_timing(init, clock_src, timing);
|
|
+ if (rc != 0) {
|
|
+ EMSG("Failed to compute I2C timings\n");
|
|
+ if (init->speed_mode > I2C_SPEED_STANDARD) {
|
|
+ init->speed_mode--;
|
|
+ IMSG("Downgrade I2C speed to %uHz)\n",
|
|
+ i2c_specs[init->speed_mode].rate);
|
|
+ } else {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } while (rc != 0);
|
|
+
|
|
+ if (rc != 0) {
|
|
+ EMSG("Impossible to compute I2C timings\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ DMSG("I2C Speed Mode(%i), Freq(%i), Clk Source(%i)\n",
|
|
+ init->speed_mode, i2c_specs[init->speed_mode].rate, clock_src);
|
|
+ DMSG("I2C Rise(%i) and Fall(%i) Time\n",
|
|
+ init->rise_time, init->fall_time);
|
|
+ DMSG("I2C Analog Filter(%s), DNF(%i)\n",
|
|
+ (init->analog_filter ? "On" : "Off"), init->digital_filter_coef);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Configure I2C Analog noise filter.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C peripheral.
|
|
+ * @param analog_filter: New state of the Analog filter
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_config_analog_filter(struct i2c_handle_s *hi2c,
|
|
+ uint32_t analog_filter)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ hi2c->lock = 1;
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_BUSY;
|
|
+
|
|
+ /* Disable the selected I2C peripheral */
|
|
+ mmio_clrbits_32(base + I2C_CR1, I2C_CR1_PE);
|
|
+
|
|
+ /* Reset I2Cx ANOFF bit */
|
|
+ mmio_clrbits_32(base + I2C_CR1, I2C_CR1_ANFOFF);
|
|
+
|
|
+ /* Set analog filter bit*/
|
|
+ mmio_setbits_32(base + I2C_CR1, analog_filter);
|
|
+
|
|
+ /* Enable the selected I2C peripheral */
|
|
+ mmio_setbits_32(base + I2C_CR1, I2C_CR1_PE);
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+
|
|
+ hi2c->lock = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Get I2C setup information from the device tree and set pinctrl
|
|
+ * configuration.
|
|
+ * @param fdt: Pointer to the device tree
|
|
+ * @param node: I2C node offset
|
|
+ * @param init: Ref to the initialization configuration structure
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+int stm32_i2c_get_setup_from_fdt(void *fdt, int node,
|
|
+ struct stm32_i2c_init_s *init,
|
|
+ struct stm32_pinctrl **pinctrl,
|
|
+ size_t *pinctrl_count)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+ int count;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "i2c-scl-rising-time-ns", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ init->rise_time = STM32_I2C_RISE_TIME_DEFAULT;
|
|
+ } else {
|
|
+ init->rise_time = fdt32_to_cpu(*cuint);
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "i2c-scl-falling-time-ns", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ init->fall_time = STM32_I2C_FALL_TIME_DEFAULT;
|
|
+ } else {
|
|
+ init->fall_time = fdt32_to_cpu(*cuint);
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "clock-frequency", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ init->speed_mode = STM32_I2C_SPEED_DEFAULT;
|
|
+ } else {
|
|
+ switch (fdt32_to_cpu(*cuint)) {
|
|
+ case STANDARD_RATE:
|
|
+ init->speed_mode = I2C_SPEED_STANDARD;
|
|
+ break;
|
|
+ case FAST_RATE:
|
|
+ init->speed_mode = I2C_SPEED_FAST;
|
|
+ break;
|
|
+ case FAST_PLUS_RATE:
|
|
+ init->speed_mode = I2C_SPEED_FAST_PLUS;
|
|
+ break;
|
|
+ default:
|
|
+ init->speed_mode = STM32_I2C_SPEED_DEFAULT;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ count = stm32_pinctrl_fdt_get_pinctrl(fdt, node, NULL, 0);
|
|
+ if (count <= 0) {
|
|
+ *pinctrl = NULL;
|
|
+ *pinctrl_count = 0;
|
|
+ return count;
|
|
+ }
|
|
+
|
|
+ if (count > 2) {
|
|
+ panic("Too many PINCTRLs found");
|
|
+ }
|
|
+
|
|
+ *pinctrl = calloc(count, sizeof(**pinctrl));
|
|
+ if (!*pinctrl) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ *pinctrl_count = stm32_pinctrl_fdt_get_pinctrl(fdt, node,
|
|
+ *pinctrl, count);
|
|
+ assert(*pinctrl_count == (unsigned int)count);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Initialize the I2C device.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param init_data: Ref to the initialization configuration structure
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+int stm32_i2c_init(struct i2c_handle_s *hi2c,
|
|
+ struct stm32_i2c_init_s *init_data)
|
|
+{
|
|
+ int rc = 0;
|
|
+ uint32_t timing;
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ if (hi2c == NULL) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (hi2c->i2c_state == I2C_STATE_RESET) {
|
|
+ hi2c->lock = 0;
|
|
+ }
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_BUSY;
|
|
+
|
|
+ rc = i2c_setup_timing(hi2c, init_data, &timing);
|
|
+ if (rc != 0) {
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ /* Disable the selected I2C peripheral */
|
|
+ mmio_clrbits_32(base + I2C_CR1, I2C_CR1_PE);
|
|
+
|
|
+ /* Configure I2Cx: Frequency range */
|
|
+ mmio_write_32(base + I2C_TIMINGR, timing & TIMINGR_CLEAR_MASK);
|
|
+
|
|
+ /* Disable Own Address1 before set the Own Address1 configuration */
|
|
+ mmio_clrbits_32(base + I2C_OAR1, I2C_OAR1_OA1EN);
|
|
+
|
|
+ /* Configure I2Cx: Own Address1 and ack own address1 mode */
|
|
+ if (init_data->addressing_mode == I2C_ADDRESSINGMODE_7BIT) {
|
|
+ mmio_write_32(base + I2C_OAR1,
|
|
+ I2C_OAR1_OA1EN | init_data->own_address1);
|
|
+ } else { /* I2C_ADDRESSINGMODE_10BIT */
|
|
+ mmio_write_32(base + I2C_OAR1,
|
|
+ I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE |
|
|
+ init_data->own_address1);
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_CR2, 0);
|
|
+
|
|
+ /* Configure I2Cx: Addressing Master mode */
|
|
+ if (init_data->addressing_mode == I2C_ADDRESSINGMODE_10BIT) {
|
|
+ mmio_setbits_32(base + I2C_CR2, I2C_CR2_ADD10);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Enable the AUTOEND by default, and enable NACK
|
|
+ * (should be disabled only during Slave process).
|
|
+ */
|
|
+ mmio_setbits_32(base + I2C_CR2, I2C_CR2_AUTOEND | I2C_CR2_NACK);
|
|
+
|
|
+ /* Disable Own Address2 before set the Own Address2 configuration */
|
|
+ mmio_clrbits_32(base + I2C_OAR2, I2C_DUALADDRESS_ENABLE);
|
|
+
|
|
+ /* Configure I2Cx: Dual mode and Own Address2 */
|
|
+ mmio_write_32(base + I2C_OAR2,
|
|
+ init_data->dual_address_mode |
|
|
+ init_data->own_address2 |
|
|
+ (init_data->own_address2_masks << 8));
|
|
+
|
|
+ /* Configure I2Cx: Generalcall and NoStretch mode */
|
|
+ mmio_write_32(base + I2C_CR1,
|
|
+ init_data->general_call_mode |
|
|
+ init_data->no_stretch_mode);
|
|
+
|
|
+ /* Enable the selected I2C peripheral */
|
|
+ mmio_setbits_32(base + I2C_CR1, I2C_CR1_PE);
|
|
+
|
|
+ hi2c->i2c_err = I2C_ERROR_NONE;
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+
|
|
+ rc = i2c_config_analog_filter(hi2c, init_data->analog_filter ?
|
|
+ I2C_ANALOGFILTER_ENABLE :
|
|
+ I2C_ANALOGFILTER_DISABLE);
|
|
+ if (rc != 0) {
|
|
+ EMSG("Cannot initialize I2C analog filter (%d)\n", rc);
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Generic function to write an amount of data in blocking mode
|
|
+ * (for Memory Mode and Master Mode)
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param mem_addr: Internal memory address (if Memory Mode)
|
|
+ * @param mem_add_size: Size of internal memory address (if Memory Mode)
|
|
+ * @param p_data: Pointer to data buffer
|
|
+ * @param size: Amount of data to be sent
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @param mode: Communication mode
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_write(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint8_t *p_data, uint16_t size, uint32_t timeout_ms,
|
|
+ enum i2c_mode_e mode)
|
|
+{
|
|
+ uint64_t tick_start;
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+ int rc = -1;
|
|
+ uint64_t tick_to = ms2tick(timeout_ms);
|
|
+ uint8_t *p_buff = p_data;
|
|
+ size_t xfer_size;
|
|
+ size_t xfer_count = size;
|
|
+
|
|
+ if ((mode != I2C_MODE_MASTER) && (mode != I2C_MODE_MEM)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ((p_data == NULL) || (size == 0U)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ hi2c->lock = 1;
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, ms2tick(I2C_TIMEOUT_BUSY_MS),
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_BUSY_TX;
|
|
+ hi2c->i2c_mode = mode;
|
|
+ hi2c->i2c_err = I2C_ERROR_NONE;
|
|
+
|
|
+ if (mode == I2C_MODE_MEM) {
|
|
+ /* In Memory Mode, Send Slave Address and Memory Address */
|
|
+ if (i2c_request_memory_write(hi2c, dev_addr,
|
|
+ mem_addr, mem_add_size,
|
|
+ tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (xfer_count > MAX_NBYTE_SIZE) {
|
|
+ xfer_size = MAX_NBYTE_SIZE;
|
|
+ i2c_transfer_config(hi2c, dev_addr, xfer_size,
|
|
+ I2C_RELOAD_MODE, I2C_NO_STARTSTOP);
|
|
+ } else {
|
|
+ xfer_size = xfer_count;
|
|
+ i2c_transfer_config(hi2c, dev_addr, xfer_size,
|
|
+ I2C_AUTOEND_MODE, I2C_NO_STARTSTOP);
|
|
+ }
|
|
+ } else {
|
|
+ /* In Master Mode, Send Slave Address */
|
|
+ if (xfer_count > MAX_NBYTE_SIZE) {
|
|
+ xfer_size = MAX_NBYTE_SIZE;
|
|
+ i2c_transfer_config(hi2c, dev_addr, xfer_size,
|
|
+ I2C_RELOAD_MODE,
|
|
+ I2C_GENERATE_START_WRITE);
|
|
+ } else {
|
|
+ xfer_size = xfer_count;
|
|
+ i2c_transfer_config(hi2c, dev_addr, xfer_size,
|
|
+ I2C_AUTOEND_MODE,
|
|
+ I2C_GENERATE_START_WRITE);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ do {
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_8(base + I2C_TXDR, *p_buff);
|
|
+ p_buff++;
|
|
+ xfer_count--;
|
|
+ xfer_size--;
|
|
+
|
|
+ if ((xfer_count != 0U) && (xfer_size == 0U)) {
|
|
+ /* Wait until TCR flag is set */
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, tick_to,
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (xfer_count > MAX_NBYTE_SIZE) {
|
|
+ xfer_size = MAX_NBYTE_SIZE;
|
|
+ i2c_transfer_config(hi2c, dev_addr,
|
|
+ xfer_size,
|
|
+ I2C_RELOAD_MODE,
|
|
+ I2C_NO_STARTSTOP);
|
|
+ } else {
|
|
+ xfer_size = xfer_count;
|
|
+ i2c_transfer_config(hi2c, dev_addr,
|
|
+ xfer_size,
|
|
+ I2C_AUTOEND_MODE,
|
|
+ I2C_NO_STARTSTOP);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ } while (xfer_count > 0U);
|
|
+
|
|
+ /*
|
|
+ * No need to Check TC flag, with AUTOEND mode the stop
|
|
+ * is automatically generated.
|
|
+ * Wait until STOPF flag is reset.
|
|
+ */
|
|
+ if (i2c_wait_stop(hi2c, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+
|
|
+ mmio_clrbits_32(base + I2C_CR2, I2C_RESET_CR2);
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+
|
|
+ rc = 0;
|
|
+
|
|
+bail:
|
|
+ hi2c->lock = 0;
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Write an amount of data in blocking mode to a specific memory
|
|
+ * address.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param mem_addr: Internal memory address
|
|
+ * @param mem_add_size: Size of internal memory address
|
|
+ * @param p_data: Pointer to data buffer
|
|
+ * @param size: Amount of data to be sent
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+int stm32_i2c_mem_write(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint8_t *p_data, uint16_t size, uint32_t timeout_ms)
|
|
+{
|
|
+ return i2c_write(hi2c, dev_addr, mem_addr, mem_add_size,
|
|
+ p_data, size, timeout_ms, I2C_MODE_MEM);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Transmits in master mode an amount of data in blocking mode.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param p_data: Pointer to data buffer
|
|
+ * @param size: Amount of data to be sent
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+int stm32_i2c_master_transmit(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint8_t *p_data, uint16_t size,
|
|
+ uint32_t timeout_ms)
|
|
+{
|
|
+ return i2c_write(hi2c, dev_addr, 0, 0,
|
|
+ p_data, size, timeout_ms, I2C_MODE_MASTER);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Optimized 1 byte read/write function for unpaged sequences.
|
|
+ * 8-bit addressing mode / single byte transferred / use default I2C timeout.
|
|
+ * 'unpg88' refers to 8bit address/8bit value.
|
|
+ */
|
|
+int stm32_i2c_read_write_membyte(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ unsigned int mem_addr, uint8_t *p_data,
|
|
+ bool write)
|
|
+{
|
|
+ uint64_t tick_start;
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+ int rc = 1;
|
|
+ uint64_t tick_to = ms2tick(I2C_TIMEOUT_BUSY_MS);
|
|
+ uint8_t *p_buff = p_data;
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_READY) ||
|
|
+ (hi2c->lock != 0U) ||
|
|
+ (p_data == NULL)) {
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ hi2c->lock = 1;
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ hi2c->i2c_state = write ? I2C_STATE_BUSY_TX : I2C_STATE_BUSY_RX;
|
|
+ hi2c->i2c_mode = I2C_MODE_MEM;
|
|
+ hi2c->i2c_err = I2C_ERROR_NONE;
|
|
+
|
|
+ i2c_transfer_config(hi2c, dev_addr, I2C_MEMADD_SIZE_8BIT,
|
|
+ write ? I2C_RELOAD_MODE : I2C_SOFTEND_MODE,
|
|
+ I2C_GENERATE_START_WRITE);
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_8(base + I2C_TXDR, (uint8_t)(mem_addr & 0x0FFU));
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+ if (i2c_wait_flag(hi2c, write ? I2C_FLAG_TCR : I2C_FLAG_TC, 0,
|
|
+ tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ i2c_transfer_config(hi2c, dev_addr, I2C_MEMADD_SIZE_8BIT,
|
|
+ I2C_AUTOEND_MODE,
|
|
+ write ? I2C_NO_STARTSTOP : I2C_GENERATE_START_READ);
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+ if (write) {
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+ mmio_write_8(base + I2C_TXDR, *p_buff);
|
|
+
|
|
+ } else {
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_RXNE, 0,
|
|
+ tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+ *p_buff = mmio_read_8(base + I2C_RXDR);
|
|
+ }
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+ if (i2c_wait_stop(hi2c, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+ mmio_clrbits_32(base + I2C_CR2, I2C_RESET_CR2);
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+
|
|
+ rc = 0;
|
|
+
|
|
+bail:
|
|
+ hi2c->lock = 0;
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Generic function to read an amount of data in blocking mode
|
|
+ * (for Memory Mode and Master Mode)
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param mem_addr: Internal memory address (if Memory Mode)
|
|
+ * @param mem_add_size: Size of internal memory address (if Memory Mode)
|
|
+ * @param p_data: Pointer to data buffer
|
|
+ * @param size: Amount of data to be sent
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @param mode: Communication mode
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint8_t *p_data, uint16_t size, uint32_t timeout_ms,
|
|
+ enum i2c_mode_e mode)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+ uint64_t tick_start;
|
|
+ int rc = -1;
|
|
+ uint64_t tick_to = ms2tick(timeout_ms);
|
|
+ uint8_t *p_buff = p_data;
|
|
+ size_t xfer_count = size;
|
|
+ size_t xfer_size;
|
|
+
|
|
+ if ((mode != I2C_MODE_MASTER) && (mode != I2C_MODE_MEM)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ((p_data == NULL) || (size == 0U)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ hi2c->lock = 1;
|
|
+
|
|
+ tick_start = timeout_start();
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, ms2tick(I2C_TIMEOUT_BUSY_MS),
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_BUSY_RX;
|
|
+ hi2c->i2c_mode = mode;
|
|
+ hi2c->i2c_err = I2C_ERROR_NONE;
|
|
+
|
|
+ if (mode == I2C_MODE_MEM) {
|
|
+ /* Send Memory Address */
|
|
+ if (i2c_request_memory_read(hi2c, dev_addr,
|
|
+ mem_addr, mem_add_size,
|
|
+ tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Send Slave Address.
|
|
+ * Set NBYTES to write and reload if xfer_count > MAX_NBYTE_SIZE
|
|
+ * and generate RESTART.
|
|
+ */
|
|
+ if (xfer_count > MAX_NBYTE_SIZE) {
|
|
+ xfer_size = MAX_NBYTE_SIZE;
|
|
+ i2c_transfer_config(hi2c, dev_addr, xfer_size,
|
|
+ I2C_RELOAD_MODE, I2C_GENERATE_START_READ);
|
|
+ } else {
|
|
+ xfer_size = xfer_count;
|
|
+ i2c_transfer_config(hi2c, dev_addr, xfer_size,
|
|
+ I2C_AUTOEND_MODE, I2C_GENERATE_START_READ);
|
|
+ }
|
|
+
|
|
+ do {
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_RXNE, 0, tick_to,
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ *p_buff = mmio_read_8(base + I2C_RXDR);
|
|
+ p_buff++;
|
|
+ xfer_size--;
|
|
+ xfer_count--;
|
|
+
|
|
+ if ((xfer_count != 0U) && (xfer_size == 0U)) {
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, tick_to,
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (xfer_count > MAX_NBYTE_SIZE) {
|
|
+ xfer_size = MAX_NBYTE_SIZE;
|
|
+ i2c_transfer_config(hi2c, dev_addr,
|
|
+ xfer_size,
|
|
+ I2C_RELOAD_MODE,
|
|
+ I2C_NO_STARTSTOP);
|
|
+ } else {
|
|
+ xfer_size = xfer_count;
|
|
+ i2c_transfer_config(hi2c, dev_addr,
|
|
+ xfer_size,
|
|
+ I2C_AUTOEND_MODE,
|
|
+ I2C_NO_STARTSTOP);
|
|
+ }
|
|
+ }
|
|
+ } while (xfer_count > 0U);
|
|
+
|
|
+ /*
|
|
+ * No need to Check TC flag, with AUTOEND mode the stop
|
|
+ * is automatically generated.
|
|
+ * Wait until STOPF flag is reset.
|
|
+ */
|
|
+ if (i2c_wait_stop(hi2c, tick_to, tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+
|
|
+ mmio_clrbits_32(base + I2C_CR2, I2C_RESET_CR2);
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+
|
|
+ rc = 0;
|
|
+
|
|
+bail:
|
|
+ hi2c->lock = 0;
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Read an amount of data in blocking mode from a specific memory
|
|
+ * address.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param mem_addr: Internal memory address
|
|
+ * @param mem_add_size: Size of internal memory address
|
|
+ * @param p_data: Pointer to data buffer
|
|
+ * @param size: Amount of data to be sent
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+int stm32_i2c_mem_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint8_t *p_data, uint16_t size, uint32_t timeout_ms)
|
|
+{
|
|
+ return i2c_read(hi2c, dev_addr, mem_addr, mem_add_size,
|
|
+ p_data, size, timeout_ms, I2C_MODE_MEM);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Receives in master mode an amount of data in blocking mode.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param p_data: Pointer to data buffer
|
|
+ * @param size: Amount of data to be sent
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+int stm32_i2c_master_receive(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint8_t *p_data, uint16_t size,
|
|
+ uint32_t timeout_ms)
|
|
+{
|
|
+ return i2c_read(hi2c, dev_addr, 0, 0,
|
|
+ p_data, size, timeout_ms, I2C_MODE_MASTER);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Checks if target device is ready for communication.
|
|
+ * @note This function is used with Memory devices
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param trials: Number of trials
|
|
+ * @param timeout_ms: Timeout duration in milliseconds
|
|
+ * @retval True if device is ready, false else
|
|
+ */
|
|
+bool stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c,
|
|
+ uint16_t dev_addr, uint32_t trials,
|
|
+ uint32_t timeout_ms)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+ uint32_t i2c_trials = 0U;
|
|
+ bool rc = false;
|
|
+ uint64_t tick_to = ms2tick(timeout_ms);
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(hi2c->clock);
|
|
+
|
|
+ hi2c->lock = 1;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+
|
|
+ if ((mmio_read_32(base + I2C_ISR) & I2C_FLAG_BUSY) != 0U) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_BUSY;
|
|
+ hi2c->i2c_err = I2C_ERROR_NONE;
|
|
+
|
|
+ do {
|
|
+ uint64_t tick_start;
|
|
+ uint32_t isr;
|
|
+
|
|
+ /* Generate Start */
|
|
+ if ((mmio_read_32(base + I2C_OAR1) & I2C_OAR1_OA1MODE) == 0) {
|
|
+ mmio_write_32(base + I2C_CR2,
|
|
+ (((uint32_t)dev_addr & I2C_CR2_SADD) |
|
|
+ I2C_CR2_START | I2C_CR2_AUTOEND) &
|
|
+ ~I2C_CR2_RD_WRN);
|
|
+ } else {
|
|
+ mmio_write_32(base + I2C_CR2,
|
|
+ (((uint32_t)dev_addr & I2C_CR2_SADD) |
|
|
+ I2C_CR2_START | I2C_CR2_ADD10) &
|
|
+ ~I2C_CR2_RD_WRN);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * No need to Check TC flag, with AUTOEND mode the stop
|
|
+ * is automatically generated.
|
|
+ * Wait until STOPF flag is set or a NACK flag is set.
|
|
+ */
|
|
+ tick_start = timeout_start();
|
|
+ do {
|
|
+ if (timeout_elapsed(tick_start, tick_to)) {
|
|
+ notif_i2c_timeout(hi2c);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ isr = mmio_read_32(base + I2C_ISR);
|
|
+ } while ((isr & (I2C_FLAG_STOPF | I2C_FLAG_AF)) == 0);
|
|
+
|
|
+ if ((mmio_read_32(base + I2C_ISR) & I2C_FLAG_AF) == 0U) {
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, tick_to,
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+
|
|
+ rc = true;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, tick_to,
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_AF);
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+
|
|
+ if (i2c_trials == trials) {
|
|
+ mmio_setbits_32(base + I2C_CR2, I2C_CR2_STOP);
|
|
+
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, tick_to,
|
|
+ tick_start) != 0) {
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+ }
|
|
+
|
|
+ i2c_trials++;
|
|
+ } while (i2c_trials < trials);
|
|
+
|
|
+ notif_i2c_timeout(hi2c);
|
|
+
|
|
+bail:
|
|
+#ifdef STM32_I2C_MAYBE_NON_SECURE
|
|
+ if (rc) {
|
|
+ /* Save the active secure configuraton */
|
|
+ save_cfg(hi2c, &hi2c->sec_cfg);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ hi2c->lock = 0;
|
|
+ stm32_clock_disable(hi2c->clock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Master sends target device address followed by internal memory
|
|
+ * address for write request.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param mem_addr: Internal memory address
|
|
+ * @param mem_add_size: Size of internal memory address
|
|
+ * @param tick_to: Tick timeout duration
|
|
+ * @param tick_start: Tick start value
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_request_memory_write(struct i2c_handle_s *hi2c,
|
|
+ uint16_t dev_addr, uint16_t mem_addr,
|
|
+ uint16_t mem_add_size, uint64_t tick_to,
|
|
+ uint64_t tick_start)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_RELOAD_MODE,
|
|
+ I2C_GENERATE_START_WRITE);
|
|
+
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (mem_add_size == I2C_MEMADD_SIZE_8BIT) {
|
|
+ /* Send Memory Address */
|
|
+ mmio_write_8(base + I2C_TXDR,
|
|
+ (uint8_t)(mem_addr & 0x00FFU));
|
|
+ } else {
|
|
+ /* Send MSB of Memory Address */
|
|
+ mmio_write_8(base + I2C_TXDR,
|
|
+ (uint8_t)((mem_addr & 0xFF00U) >> 8));
|
|
+
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Send LSB of Memory Address */
|
|
+ mmio_write_8(base + I2C_TXDR,
|
|
+ (uint8_t)(mem_addr & 0x00FFU));
|
|
+ }
|
|
+
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Master sends target device address followed by internal memory
|
|
+ * address for read request.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param dev_addr: Target device address
|
|
+ * @param mem_addr: Internal memory address
|
|
+ * @param mem_add_size: Size of internal memory address
|
|
+ * @param tick_to: Tick timeout duration
|
|
+ * @param tick_start: Tick start value
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint64_t tick_to, uint64_t tick_start)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_SOFTEND_MODE,
|
|
+ I2C_GENERATE_START_WRITE);
|
|
+
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (mem_add_size == I2C_MEMADD_SIZE_8BIT) {
|
|
+ /* Send Memory Address */
|
|
+ mmio_write_8(base + I2C_TXDR,
|
|
+ (uint8_t)(mem_addr & 0x00FFU));
|
|
+ } else {
|
|
+ /* Send MSB of Memory Address */
|
|
+ mmio_write_8(base + I2C_TXDR,
|
|
+ (uint8_t)((mem_addr & 0xFF00U) >> 8));
|
|
+
|
|
+ if (i2c_wait_txis(hi2c, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Send LSB of Memory Address */
|
|
+ mmio_write_8(base + I2C_TXDR,
|
|
+ (uint8_t)(mem_addr & 0x00FFU));
|
|
+ }
|
|
+
|
|
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TC, 0, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief I2C Tx data register flush process.
|
|
+ * @param hi2c: I2C handle
|
|
+ * @retval None
|
|
+ */
|
|
+static void i2c_flush_txdr(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ /*
|
|
+ * If a pending TXIS flag is set,
|
|
+ * write a dummy data in TXDR to clear it.
|
|
+ */
|
|
+ if ((mmio_read_32(base + I2C_ISR) & I2C_FLAG_TXIS) != 0U) {
|
|
+ mmio_write_32(base + I2C_TXDR, 0);
|
|
+ }
|
|
+
|
|
+ /* Flush TX register if not empty */
|
|
+ if ((mmio_read_32(base + I2C_ISR) & I2C_FLAG_TXE) == 0U) {
|
|
+ mmio_setbits_32(base + I2C_ISR, I2C_FLAG_TXE);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief This function handles I2C Communication timeout.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param flag: Specifies the I2C flag to check
|
|
+ * @param awaited_value: The awaited bit value for the flag (0 or 1)
|
|
+ * @param tick_to: Tick timeout duration
|
|
+ * @param tick_start: Tick start value
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag,
|
|
+ uint8_t awaited_value, uint64_t tick_to,
|
|
+ uint64_t tick_start)
|
|
+{
|
|
+ for ( ; ; ) {
|
|
+ uint32_t isr = read32(get_base(hi2c) + I2C_ISR);
|
|
+
|
|
+ if (!!(isr & flag) != !!awaited_value) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (timeout_elapsed(tick_start, tick_to)) {
|
|
+ notif_i2c_timeout(hi2c);
|
|
+ hi2c->lock = 0;
|
|
+
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief This function handles I2C Communication timeout for specific usage
|
|
+ * of TXIS flag.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param tick_to: Tick timeout duration
|
|
+ * @param tick_start: Tick start value
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_wait_txis(struct i2c_handle_s *hi2c, uint64_t tick_to,
|
|
+ uint64_t tick_start)
|
|
+{
|
|
+ while ((read32(get_base(hi2c) + I2C_ISR) & I2C_FLAG_TXIS) == 0U) {
|
|
+ if (i2c_ack_failed(hi2c, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (timeout_elapsed(tick_start, tick_to)) {
|
|
+ notif_i2c_timeout(hi2c);
|
|
+ hi2c->lock = 0;
|
|
+
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief This function handles I2C Communication timeout for specific
|
|
+ * usage of STOP flag.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param tick_to: Tick timeout duration
|
|
+ * @param tick_start: Tick start value
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_wait_stop(struct i2c_handle_s *hi2c, uint64_t tick_to,
|
|
+ uint64_t tick_start)
|
|
+{
|
|
+ while ((read32(get_base(hi2c) + I2C_ISR) & I2C_FLAG_STOPF) == 0U) {
|
|
+ if (i2c_ack_failed(hi2c, tick_to, tick_start) != 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (timeout_elapsed(tick_start, tick_to)) {
|
|
+ notif_i2c_timeout(hi2c);
|
|
+ hi2c->lock = 0;
|
|
+
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief This function handles Acknowledge failed detection during
|
|
+ * an I2C Communication.
|
|
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
|
|
+ * the configuration information for the specified I2C.
|
|
+ * @param tick_to: Tick timeout duration
|
|
+ * @param tick_start: Tick start value
|
|
+ * @retval 0 if OK, negative value else
|
|
+ */
|
|
+static int i2c_ack_failed(struct i2c_handle_s *hi2c, uint64_t tick_to,
|
|
+ uint64_t tick_start)
|
|
+{
|
|
+ uintptr_t base = get_base(hi2c);
|
|
+
|
|
+ if ((mmio_read_32(base + I2C_ISR) & I2C_FLAG_AF) == 0U) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Wait until STOP Flag is reset.
|
|
+ * AutoEnd should be initiate after AF.
|
|
+ */
|
|
+ while ((mmio_read_32(base + I2C_ISR) & I2C_FLAG_STOPF) == 0U) {
|
|
+ if (timeout_elapsed(tick_start, tick_to)) {
|
|
+ notif_i2c_timeout(hi2c);
|
|
+ hi2c->lock = 0;
|
|
+
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_AF);
|
|
+
|
|
+ mmio_write_32(base + I2C_ICR, I2C_FLAG_STOPF);
|
|
+
|
|
+ i2c_flush_txdr(hi2c);
|
|
+
|
|
+ mmio_clrbits_32(base + I2C_CR2, I2C_RESET_CR2);
|
|
+
|
|
+ hi2c->i2c_err |= I2C_ERROR_AF;
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+ hi2c->i2c_mode = I2C_MODE_NONE;
|
|
+
|
|
+ hi2c->lock = 0;
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * @brief Handles I2Cx communication when starting transfer or during transfer
|
|
+ * (TC or TCR flag are set).
|
|
+ * @param hi2c: I2C handle
|
|
+ * @param dev_addr: Specifies the slave address to be programmed
|
|
+ * @param size: Specifies the number of bytes to be programmed.
|
|
+ * This parameter must be a value between 0 and 255.
|
|
+ * @param i2c_mode: New state of the I2C START condition generation.
|
|
+ * This parameter can be one of the following values:
|
|
+ * @arg @ref I2C_RELOAD_MODE: Enable Reload mode.
|
|
+ * @arg @ref I2C_AUTOEND_MODE: Enable Automatic end mode.
|
|
+ * @arg @ref I2C_SOFTEND_MODE: Enable Software end mode.
|
|
+ * @param request: New state of the I2C START condition generation.
|
|
+ * This parameter can be one of the following values:
|
|
+ * @arg @ref I2C_NO_STARTSTOP: Don't Generate stop and start condition.
|
|
+ * @arg @ref I2C_GENERATE_STOP: Generate stop condition
|
|
+ * (size should be set to 0).
|
|
+ * @arg @ref I2C_GENERATE_START_READ: Generate Restart for read request.
|
|
+ * @arg @ref I2C_GENERATE_START_WRITE: Generate Restart for write request.
|
|
+ * @retval None
|
|
+ */
|
|
+static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t size, uint32_t i2c_mode,
|
|
+ uint32_t request)
|
|
+{
|
|
+ uint32_t clr_value, set_value;
|
|
+
|
|
+ clr_value = (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD |
|
|
+ I2C_CR2_AUTOEND | I2C_CR2_START | I2C_CR2_STOP) |
|
|
+ (I2C_CR2_RD_WRN & (request >> (31U - I2C_CR2_RD_WRN_OFFSET)));
|
|
+
|
|
+ set_value = ((uint32_t)dev_addr & I2C_CR2_SADD) |
|
|
+ (((uint32_t)size << I2C_CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES) |
|
|
+ i2c_mode | request;
|
|
+
|
|
+ mmio_clrsetbits_32(get_base(hi2c) + I2C_CR2, clr_value, set_value);
|
|
+}
|
|
+
|
|
+#ifdef STM32_I2C_MAYBE_NON_SECURE
|
|
+/*
|
|
+ * Secure side needs the PMIC. If it is secure, it can be freely used.
|
|
+ * If the PMIC is non secure, the configuration must be save/restored
|
|
+ * when used by the secure side.
|
|
+ */
|
|
+#endif
|
|
+
|
|
+void stm32_i2c_resume(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ if (hi2c->i2c_state == I2C_STATE_READY) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_RESET) &&
|
|
+ (hi2c->i2c_state != I2C_STATE_SUSPENDED)) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+#ifdef STM32_I2C_MAYBE_NON_SECURE
|
|
+ if (!i2c_is_secure(hi2c)) {
|
|
+ stm32_pinctrl_store_standby_cfg(hi2c->pinctrl,
|
|
+ hi2c->pinctrl_count);
|
|
+ save_cfg(hi2c, &hi2c->alt_cfg);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ stm32_pinctrl_load_active_cfg(hi2c->pinctrl, hi2c->pinctrl_count);
|
|
+
|
|
+ if (hi2c->i2c_state == I2C_STATE_RESET) {
|
|
+ /* This is no valid I2C configuration loaded yet */
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ restore_cfg(hi2c, &hi2c->sec_cfg);
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_READY;
|
|
+}
|
|
+
|
|
+void stm32_i2c_suspend(struct i2c_handle_s *hi2c)
|
|
+{
|
|
+ if ((hi2c->i2c_state == I2C_STATE_SUSPENDED)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if ((hi2c->i2c_state != I2C_STATE_READY)) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ save_cfg(hi2c, &hi2c->sec_cfg);
|
|
+
|
|
+ stm32_pinctrl_load_standby_cfg(hi2c->pinctrl,
|
|
+ hi2c->pinctrl_count);
|
|
+
|
|
+#ifdef STM32_I2C_MAYBE_NON_SECURE
|
|
+ if (!i2c_is_secure(hi2c)) {
|
|
+ restore_cfg(hi2c, &hi2c->alt_cfg);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ hi2c->i2c_state = I2C_STATE_SUSPENDED;
|
|
+}
|
|
diff --git a/core/drivers/stm32_iwdg.c b/core/drivers/stm32_iwdg.c
|
|
new file mode 100644
|
|
index 0000000..b2c8a68
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_iwdg.c
|
|
@@ -0,0 +1,308 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32_iwdg.h>
|
|
+#include <io.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/interrupt.h>
|
|
+#include <kernel/misc.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <libfdt.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32mp_pm.h>
|
|
+#include <string.h>
|
|
+#include <trace.h>
|
|
+
|
|
+/* IWDG Compatibility */
|
|
+#define IWDG_COMPAT "st,stm32mp1-iwdg"
|
|
+#define IWDG_TIMEOUT_US 100000U
|
|
+
|
|
+/* IWDG registers offsets */
|
|
+#define IWDG_KR_OFFSET 0x00U
|
|
+#define IWDG_PR_OFFSET 0x04U
|
|
+#define IWDG_RLR_OFFSET 0x08U
|
|
+#define IWDG_SR_OFFSET 0x0CU
|
|
+#define IWDG_EWCR_OFFSET 0x14U
|
|
+
|
|
+/* Registers values */
|
|
+#define IWDG_KR_ACCESS_KEY 0x5555
|
|
+#define IWDG_KR_RELOAD_KEY 0xAAAA
|
|
+#define IWDG_KR_START_KEY 0xCCCC
|
|
+
|
|
+#define IWDG_PR_DIV_4 0x00
|
|
+#define IWDG_PR_DIV_256 0x06
|
|
+
|
|
+#define IWDG_RLR_MAX_VAL 0xFFF
|
|
+
|
|
+#define IWDG_SR_EWU BIT(3)
|
|
+
|
|
+#define IWDG_EWCR_EWIE BIT(15)
|
|
+#define IWDG_EWCR_EWIC BIT(14)
|
|
+#define IWDG_EWCR_EWIT_MASK GENMASK_32(11, 0)
|
|
+
|
|
+struct stm32_iwdg_instance {
|
|
+ uintptr_t pbase;
|
|
+ uintptr_t vbase;
|
|
+ unsigned long clock;
|
|
+ uint8_t instance;
|
|
+ uint8_t flags;
|
|
+};
|
|
+
|
|
+static struct stm32_iwdg_instance *stm32_iwdg;
|
|
+static size_t stm32_iwdg_count;
|
|
+
|
|
+static uintptr_t get_base(struct stm32_iwdg_instance *iwdg)
|
|
+{
|
|
+ if (!cpu_mmu_enabled()) {
|
|
+ return iwdg->pbase;
|
|
+ }
|
|
+
|
|
+ return iwdg->vbase;
|
|
+}
|
|
+
|
|
+static int stm32_iwdg_get_dt_node(void *fdt, struct dt_node_info *info,
|
|
+ int offset)
|
|
+{
|
|
+ int node;
|
|
+
|
|
+ node = fdt_get_node(fdt, info, offset, IWDG_COMPAT);
|
|
+ if (node < 0) {
|
|
+ if (offset == -1) {
|
|
+ DMSG("No IDWG found");
|
|
+ }
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return node;
|
|
+}
|
|
+
|
|
+static struct stm32_iwdg_instance *get_iwdg(unsigned int instance)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < stm32_iwdg_count; i++) {
|
|
+ if (stm32_iwdg[i].instance == instance) {
|
|
+ return &stm32_iwdg[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static enum itr_return stm32_iwdg_it_handler(struct itr_handler *handler)
|
|
+{
|
|
+ unsigned int __maybe_unused cpu = get_core_pos();
|
|
+ int instance = stm32mp_iwdg_irq2instance(handler->it);
|
|
+ struct stm32_iwdg_instance *iwdg = get_iwdg(instance);
|
|
+ uintptr_t iwdg_base = get_base(iwdg);
|
|
+
|
|
+ DMSG("CPU %u IT Watchdog %d\n", cpu, instance + 1);
|
|
+
|
|
+ stm32_iwdg_refresh(instance);
|
|
+
|
|
+ stm32_clock_enable(iwdg->clock);
|
|
+
|
|
+ mmio_setbits_32(iwdg_base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIC);
|
|
+
|
|
+ stm32_clock_disable(iwdg->clock);
|
|
+
|
|
+ stm32_cores_reset();
|
|
+
|
|
+ return ITRR_HANDLED;
|
|
+}
|
|
+KEEP_PAGER(stm32_iwdg_it_handler);
|
|
+
|
|
+static int stm32_iwdg_get_secure_timeout(void *fdt, int node)
|
|
+{
|
|
+ const fdt32_t *cuint;
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "secure-timeout-sec", NULL);
|
|
+ if (cuint == NULL) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return (int)fdt32_to_cpu(*cuint);
|
|
+}
|
|
+
|
|
+static int stm32_iwdg_conf_etimeout(void *fdt, int node,
|
|
+ struct stm32_iwdg_instance *iwdg)
|
|
+{
|
|
+ int id_lsi;
|
|
+ int dt_secure_timeout = stm32_iwdg_get_secure_timeout(fdt, node);
|
|
+ uint32_t reload, status;
|
|
+ uint64_t timeout_ref;
|
|
+ unsigned long long reload_ll;
|
|
+ uintptr_t iwdg_base = get_base(iwdg);
|
|
+ struct itr_handler *itr;
|
|
+
|
|
+ if (dt_secure_timeout < 0) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (dt_secure_timeout == 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ id_lsi = fdt_get_clock_id_by_name(fdt, node, "lsi");
|
|
+ if (id_lsi < 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* Prescaler fix to 256 */
|
|
+ reload_ll = (unsigned long long)dt_secure_timeout *
|
|
+ stm32mp1_clk_get_rate(id_lsi);
|
|
+ reload = ((uint32_t)(reload_ll >> 8) - 1U) & IWDG_EWCR_EWIT_MASK;
|
|
+
|
|
+ stm32_clock_enable(iwdg->clock);
|
|
+
|
|
+ write32(IWDG_KR_START_KEY, iwdg_base + IWDG_KR_OFFSET);
|
|
+ write32(IWDG_KR_ACCESS_KEY, iwdg_base + IWDG_KR_OFFSET);
|
|
+ write32(IWDG_PR_DIV_256, iwdg_base + IWDG_PR_OFFSET);
|
|
+ write32(IWDG_EWCR_EWIE | reload, iwdg_base + IWDG_EWCR_OFFSET);
|
|
+
|
|
+ timeout_ref = utimeout_init(IWDG_TIMEOUT_US);
|
|
+ do {
|
|
+ status = read32(iwdg_base + IWDG_SR_OFFSET) & IWDG_SR_EWU;
|
|
+ if (utimeout_elapsed(IWDG_TIMEOUT_US, timeout_ref)) {
|
|
+ stm32_clock_disable(iwdg->clock);
|
|
+ return -1;
|
|
+ }
|
|
+ } while (status != 0U);
|
|
+
|
|
+ stm32_clock_disable(iwdg->clock);
|
|
+
|
|
+ itr = calloc(1, sizeof(struct itr_handler));
|
|
+ if (itr == NULL) {
|
|
+ panic("out of memory");
|
|
+ }
|
|
+
|
|
+ itr->it = stm32mp_iwdg_instance2irq(iwdg->instance);
|
|
+ itr->handler = stm32_iwdg_it_handler;
|
|
+ itr_add(itr);
|
|
+ itr_enable(itr->it);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void stm32_iwdg_refresh(uint32_t instance)
|
|
+{
|
|
+ struct stm32_iwdg_instance *iwdg = get_iwdg(instance);
|
|
+ uintptr_t iwdg_base = get_base(iwdg);
|
|
+
|
|
+ assert(iwdg);
|
|
+
|
|
+ stm32_clock_enable(iwdg->clock);
|
|
+
|
|
+ write32(IWDG_KR_RELOAD_KEY, iwdg_base + IWDG_KR_OFFSET);
|
|
+
|
|
+ stm32_clock_disable(iwdg->clock);
|
|
+}
|
|
+
|
|
+static TEE_Result iwdg_init(void)
|
|
+{
|
|
+ int node = -1;
|
|
+ int res;
|
|
+ struct dt_node_info dt_info;
|
|
+ void *fdt;
|
|
+ size_t count;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (!fdt) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ assert((stm32_iwdg == NULL) && (stm32_iwdg_count == 0));
|
|
+ count = 0;
|
|
+
|
|
+ for (node = stm32_iwdg_get_dt_node(fdt, &dt_info, node);
|
|
+ node != -FDT_ERR_NOTFOUND;
|
|
+ node = stm32_iwdg_get_dt_node(fdt, &dt_info, node)) {
|
|
+ struct stm32_iwdg_instance iwdg;
|
|
+ enum teecore_memtypes memtype;
|
|
+ uint32_t hw_init;
|
|
+
|
|
+ memset(&iwdg, 0, sizeof(iwdg));
|
|
+ iwdg.pbase = dt_info.base;
|
|
+ iwdg.clock = (unsigned long)dt_info.clock;
|
|
+ iwdg.instance = (uint8_t)stm32mp_iwdg_iomem2instance(iwdg.pbase);
|
|
+
|
|
+ memtype = ((dt_info.status & DT_STATUS_OK_NSEC) != 0) ?
|
|
+ MEM_AREA_IO_NSEC : MEM_AREA_IO_SEC;
|
|
+ iwdg.vbase = (uintptr_t)phys_to_virt(iwdg.pbase, memtype);
|
|
+
|
|
+ /* DT can specify low power cases */
|
|
+ if (fdt_getprop(fdt, node, "stm32,enable-on-stop", NULL) !=
|
|
+ NULL) {
|
|
+ iwdg.flags |= IWDG_ENABLE_ON_STOP;
|
|
+ }
|
|
+
|
|
+ if (fdt_getprop(fdt, node, "stm32,enable-on-standby", NULL) !=
|
|
+ NULL) {
|
|
+ iwdg.flags |= IWDG_ENABLE_ON_STANDBY;
|
|
+ }
|
|
+
|
|
+ hw_init = stm32_get_iwdg_otp_config(iwdg.pbase);
|
|
+
|
|
+ if ((hw_init & IWDG_HW_ENABLED) != 0) {
|
|
+ if (dt_info.status == DT_STATUS_DISABLED) {
|
|
+ panic("IWDG HW enabled");
|
|
+ }
|
|
+ iwdg.flags |= IWDG_HW_ENABLED;
|
|
+ }
|
|
+
|
|
+ if ((hw_init & IWDG_ENABLE_ON_STOP) != 0) {
|
|
+ iwdg.flags |= IWDG_ENABLE_ON_STOP;
|
|
+ }
|
|
+
|
|
+ if ((hw_init & IWDG_ENABLE_ON_STANDBY) != 0) {
|
|
+ iwdg.flags |= IWDG_ENABLE_ON_STANDBY;
|
|
+ }
|
|
+
|
|
+ if (dt_info.status == DT_STATUS_DISABLED) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ DMSG("IWDG%u found, %ssecure", iwdg.instance + 1,
|
|
+ ((dt_info.status & DT_STATUS_OK_NSEC) != 0) ? "non " : "");
|
|
+
|
|
+ if ((dt_info.status & DT_STATUS_OK_NSEC) != 0) {
|
|
+ stm32mp_register_non_secure_periph_iomem(iwdg.pbase);
|
|
+ } else {
|
|
+ stm32mp_register_secure_periph_iomem(iwdg.pbase);
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(iwdg.clock);
|
|
+ stm32_clock_disable(iwdg.clock);
|
|
+
|
|
+ res = stm32_iwdg_conf_etimeout(fdt, node, &iwdg);
|
|
+ if (res != 0) {
|
|
+ EMSG("IWDG%x early timeout config failed (%d)\n",
|
|
+ iwdg.instance + 1, res);
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ stm32_iwdg = realloc(stm32_iwdg, (count + 1) * sizeof(iwdg));
|
|
+ if (stm32_iwdg == NULL) {
|
|
+ panic("out of memory");
|
|
+ }
|
|
+
|
|
+ memcpy(&stm32_iwdg[count], &iwdg, sizeof(iwdg));
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ stm32_iwdg_count = count;
|
|
+
|
|
+ DMSG("%u IWDG instance%s found", count, count > 1 ? "s" : "");
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(iwdg_init);
|
|
diff --git a/core/drivers/stm32_rng.c b/core/drivers/stm32_rng.c
|
|
new file mode 100644
|
|
index 0000000..75bbef7
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_rng.c
|
|
@@ -0,0 +1,200 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <drivers/stm32_rng.h>
|
|
+#include <drivers/stm32_reset.h>
|
|
+#include <io.h>
|
|
+#include <libfdt.h>
|
|
+#include <platform_config.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <libfdt.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <stdbool.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <stm32_util.h>
|
|
+
|
|
+#define DT_RNG_COMPAT "st,stm32-rng"
|
|
+#define RNG_CR 0x00U
|
|
+#define RNG_SR 0x04U
|
|
+#define RNG_DR 0x08U
|
|
+
|
|
+#define RNG_CR_RNGEN BIT(2)
|
|
+#define RNG_CR_IE BIT(3)
|
|
+#define RNG_CR_CED BIT(5)
|
|
+
|
|
+#define RNG_SR_DRDY BIT(0)
|
|
+#define RNG_SR_CECS BIT(1)
|
|
+#define RNG_SR_SECS BIT(2)
|
|
+#define RNG_SR_CEIS BIT(5)
|
|
+#define RNG_SR_SEIS BIT(6)
|
|
+
|
|
+#define RNG_TIMEOUT_US 100000
|
|
+
|
|
+struct stm32_rng_instance {
|
|
+ uintptr_t pbase;
|
|
+ uintptr_t vbase;
|
|
+ unsigned long clock;
|
|
+};
|
|
+
|
|
+static struct stm32_rng_instance *stm32_rng;
|
|
+
|
|
+static uintptr_t get_base(void)
|
|
+{
|
|
+ if (!cpu_mmu_enabled()) {
|
|
+ return stm32_rng->pbase;
|
|
+ }
|
|
+
|
|
+ return stm32_rng->vbase;
|
|
+}
|
|
+
|
|
+int stm32_rng_read(uint8_t *out, size_t size)
|
|
+{
|
|
+ uint8_t *buf = out;
|
|
+ size_t len = size;
|
|
+ uint64_t timeout_ref;
|
|
+ uint32_t data32;
|
|
+ uintptr_t rng_base = get_base();
|
|
+ int rc = 0;
|
|
+ int count;
|
|
+
|
|
+ if (stm32_rng == 0) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ stm32_clock_enable(stm32_rng->clock);
|
|
+
|
|
+ if ((read32(rng_base + RNG_CR) & RNG_CR_RNGEN) == 0U) {
|
|
+ write32(RNG_CR_RNGEN | RNG_CR_CED, rng_base + RNG_CR);
|
|
+ }
|
|
+
|
|
+ while (len != 0) {
|
|
+ timeout_ref = utimeout_init(RNG_TIMEOUT_US);
|
|
+ do {
|
|
+ uint32_t status = read32(rng_base + RNG_SR);
|
|
+
|
|
+ if ((status & (RNG_SR_SECS | RNG_SR_SEIS)) != 0U) {
|
|
+ size_t i;
|
|
+
|
|
+ /* Recommended by the SoC reference manual */
|
|
+ io_mask32(rng_base + RNG_SR, 0, RNG_SR_SEIS);
|
|
+ dmb();
|
|
+ for (i = 12; i != 0; i--) {
|
|
+ (void)read32(rng_base + RNG_DR);
|
|
+ }
|
|
+ dmb();
|
|
+
|
|
+ if ((read32(rng_base + RNG_SR) & RNG_SR_SEIS) !=
|
|
+ 0U) {
|
|
+ panic("RNG noise");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (utimeout_elapsed(RNG_TIMEOUT_US, timeout_ref)) {
|
|
+ rc = -1;
|
|
+ goto bail;
|
|
+ }
|
|
+ } while ((read32(rng_base + RNG_SR) & RNG_SR_DRDY) == 0U);
|
|
+
|
|
+ count = 4;
|
|
+ while (len != 0) {
|
|
+ data32 = read32(rng_base + RNG_DR);
|
|
+ count--;
|
|
+
|
|
+ memcpy(buf, &data32, MIN(len, sizeof(uint32_t)));
|
|
+ buf += MIN(len, sizeof(uint32_t));
|
|
+ len -= MIN(len, sizeof(uint32_t));
|
|
+
|
|
+ if (count == 0) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+bail:
|
|
+ stm32_clock_disable(stm32_rng->clock);
|
|
+
|
|
+ if (rc != 0) {
|
|
+ memset(out, 0, buf - out);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static TEE_Result stm32_rng_init(void)
|
|
+{
|
|
+ void *fdt;
|
|
+ struct dt_node_info dt_rng;
|
|
+ int node;
|
|
+ uint8_t __maybe_unused test[43];
|
|
+ enum teecore_memtypes memtype;
|
|
+
|
|
+ fdt = get_dt_blob();
|
|
+ if (!fdt) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ node = fdt_get_node(fdt, &dt_rng, -1, DT_RNG_COMPAT);
|
|
+ if (node < 0) {
|
|
+ return TEE_SUCCESS;
|
|
+ }
|
|
+
|
|
+ if ((dt_rng.status & DT_STATUS_OK_SEC) == 0) {
|
|
+ return TEE_SUCCESS;
|
|
+ }
|
|
+
|
|
+ assert(dt_rng.base == RNG1_BASE);
|
|
+
|
|
+ if (stm32_rng) {
|
|
+ panic();
|
|
+ }
|
|
+ stm32_rng = calloc(1, sizeof(*stm32_rng));
|
|
+ if (!stm32_rng) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ stm32_rng->pbase = dt_rng.base;
|
|
+
|
|
+ if ((dt_rng.status & DT_STATUS_OK_NSEC) != 0) {
|
|
+ memtype = MEM_AREA_IO_NSEC;
|
|
+ stm32mp_register_non_secure_periph_iomem(stm32_rng->pbase);
|
|
+ } else {
|
|
+ memtype = MEM_AREA_IO_SEC;
|
|
+ stm32mp_register_secure_periph_iomem(stm32_rng->pbase);
|
|
+ }
|
|
+
|
|
+ stm32_rng->vbase = (uintptr_t)phys_to_virt(stm32_rng->pbase, memtype);
|
|
+
|
|
+ if (dt_rng.clock < 0) {
|
|
+ panic();
|
|
+ }
|
|
+ stm32_rng->clock = (unsigned long)dt_rng.clock;
|
|
+
|
|
+ stm32_clock_enable(stm32_rng->clock);
|
|
+ stm32_clock_disable(stm32_rng->clock);
|
|
+
|
|
+ if (dt_rng.reset >= 0) {
|
|
+ stm32_reset_assert((unsigned long)dt_rng.reset);
|
|
+ udelay(20);
|
|
+ stm32_reset_deassert((unsigned long)dt_rng.reset);
|
|
+ }
|
|
+
|
|
+ DMSG("Init RNG done");
|
|
+
|
|
+#if TRACE_LEVEL >= TRACE_DEBUG
|
|
+ memset(test, 0xa5, sizeof(test));
|
|
+ if (stm32_rng_read(test, sizeof(test))) {
|
|
+ panic("RNG test");
|
|
+ }
|
|
+
|
|
+ DHEXDUMP(test, sizeof(test));
|
|
+#endif
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(stm32_rng_init);
|
|
diff --git a/core/drivers/stm32_rtc.c b/core/drivers/stm32_rtc.c
|
|
new file mode 100644
|
|
index 0000000..709fafa
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_rtc.c
|
|
@@ -0,0 +1,503 @@
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ *
|
|
+ * SPDX-License-Identifier: BSD-3-Clause
|
|
+ */
|
|
+
|
|
+#include <arm32.h>
|
|
+#include <drivers/stm32_rtc.h>
|
|
+#include <initcall.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+
|
|
+#define RTC_COMPAT "st,stm32mp1-rtc"
|
|
+
|
|
+#define RTC_TR_SU_MASK 0x0000000FU
|
|
+#define RTC_TR_ST_MASK 0x00000070U
|
|
+#define RTC_TR_ST_SHIFT 4
|
|
+#define RTC_TR_MNU_MASK 0x00000F00U
|
|
+#define RTC_TR_MNU_SHIFT 8
|
|
+#define RTC_TR_MNT_MASK 0x00007000U
|
|
+#define RTC_TR_MNT_SHIFT 12
|
|
+#define RTC_TR_HU_MASK 0x000F0000U
|
|
+#define RTC_TR_HU_SHIFT 16
|
|
+#define RTC_TR_HT_MASK 0x00300000U
|
|
+#define RTC_TR_HT_SHIFT 20
|
|
+#define RTC_TR_PM BIT(22)
|
|
+
|
|
+#define RTC_DR_DU_MASK 0x0000000FU
|
|
+#define RTC_DR_DT_MASK 0x00000030U
|
|
+#define RTC_DR_DT_SHIFT 4
|
|
+#define RTC_DR_MU_MASK 0x00000F00U
|
|
+#define RTC_DR_MU_SHIFT 8
|
|
+#define RTC_DR_MT BIT(12)
|
|
+#define RTC_DR_MT_SHIFT 12
|
|
+#define RTC_DR_WDU_MASK 0x0000E000U
|
|
+#define RTC_DR_WDU_SHIFT 13
|
|
+#define RTC_DR_YU_MASK 0x000F0000U
|
|
+#define RTC_DR_YU_SHIFT 16
|
|
+#define RTC_DR_YT_MASK 0x00F00000U
|
|
+#define RTC_DR_YT_SHIFT 20
|
|
+
|
|
+#define RTC_SSR_SS_MASK 0x0000FFFFU
|
|
+
|
|
+#define RTC_ICSR_RSF BIT(5)
|
|
+
|
|
+#define RTC_PRER_PREDIV_S_MASK 0x0000FFFFU
|
|
+
|
|
+#define RTC_CR_BYPSHAD BIT(5)
|
|
+#define RTC_CR_BYPSHAD_SHIFT 5
|
|
+#define RTC_CR_TAMPTS BIT(25)
|
|
+
|
|
+#define RTC_SMCR_TS_DPROT BIT(3)
|
|
+#define RTC_SR_TSF BIT(3)
|
|
+#define RTC_SCR_CTSF BIT(3)
|
|
+#define RTC_SR_TSOVF BIT(4)
|
|
+#define RTC_SCR_CTSOVF BIT(4)
|
|
+
|
|
+#define RTC_TSDR_MU_MASK 0x00000F00U
|
|
+#define RTC_TSDR_MU_SHIFT 8
|
|
+#define RTC_TSDR_DT_MASK 0x00000030U
|
|
+#define RTC_TSDR_DT_SHIFT 4
|
|
+#define RTC_TSDR_DU_MASK 0x0000000FU
|
|
+#define RTC_TSDR_DU_SHIFT 0
|
|
+
|
|
+#define RTC_WPR_KEY1 0xCA
|
|
+#define RTC_WPR_KEY2 0x53
|
|
+#define RTC_WPR_KEY_LOCK 0xFF
|
|
+
|
|
+#define RTC_FLAGS_READ_TWICE BIT(0)
|
|
+#define RTC_FLAGS_SECURE BIT(1)
|
|
+
|
|
+struct rtc_device {
|
|
+ uintptr_t pbase;
|
|
+ uintptr_t vbase;
|
|
+ uint16_t clock;
|
|
+ uint8_t flags;
|
|
+};
|
|
+
|
|
+struct rtc_device rtc_dev;
|
|
+
|
|
+static uintptr_t get_base(void)
|
|
+{
|
|
+ if (!cpu_mmu_enabled())
|
|
+ return rtc_dev.pbase;
|
|
+
|
|
+ return rtc_dev.vbase;
|
|
+}
|
|
+
|
|
+static void stm32_rtc_write_unprotect(void)
|
|
+{
|
|
+ uintptr_t rtc_base = get_base();
|
|
+
|
|
+ mmio_write_32(rtc_base + RTC_WPR, RTC_WPR_KEY1);
|
|
+ mmio_write_32(rtc_base + RTC_WPR, RTC_WPR_KEY2);
|
|
+}
|
|
+
|
|
+static void stm32_rtc_write_protect(void)
|
|
+{
|
|
+ uintptr_t rtc_base = get_base();
|
|
+
|
|
+ mmio_write_32(rtc_base + RTC_WPR, RTC_WPR_KEY_LOCK);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the BYPSHAD bit value of the RTC_CR register.
|
|
+ * It will determine if we need to reset RTC_ISCR.RSF after each RTC calendar
|
|
+ * read, and also wait for RTC_ISCR.RSF=1 before next read.
|
|
+ * Returns true or false depending on the bit value.
|
|
+ ******************************************************************************/
|
|
+static bool stm32_rtc_get_bypshad(void)
|
|
+{
|
|
+ return ((mmio_read_32(get_base() + RTC_CR) & RTC_CR_BYPSHAD) >>
|
|
+ RTC_CR_BYPSHAD_SHIFT) != 0U;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads the RTC calendar register values.
|
|
+ * If shadow registers are not bypassed, then a reset/poll is done.
|
|
+ ******************************************************************************/
|
|
+static void stm32_rtc_read_calendar(struct stm32_rtc_calendar *calendar)
|
|
+{
|
|
+ uintptr_t rtc_base = get_base();
|
|
+ bool bypshad = stm32_rtc_get_bypshad();
|
|
+
|
|
+ if (!bypshad) {
|
|
+ mmio_clrbits_32((uint32_t)(rtc_base + RTC_ICSR), RTC_ICSR_RSF);
|
|
+ while ((mmio_read_32(rtc_base + RTC_ICSR) & RTC_ICSR_RSF) !=
|
|
+ RTC_ICSR_RSF) {
|
|
+ ;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ calendar->ssr = mmio_read_32(rtc_base + RTC_SSR);
|
|
+ calendar->tr = mmio_read_32(rtc_base + RTC_TR);
|
|
+ calendar->dr = mmio_read_32(rtc_base + RTC_DR);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function fill the rtc_time structure based on rtc_calendar register.
|
|
+ ******************************************************************************/
|
|
+static void stm32_rtc_get_time(struct stm32_rtc_calendar *cal,
|
|
+ struct stm32_rtc_time *tm)
|
|
+{
|
|
+ tm->hour = (((cal->tr & RTC_TR_HT_MASK) >> RTC_TR_HT_SHIFT) * 10U) +
|
|
+ ((cal->tr & RTC_TR_HU_MASK) >> RTC_TR_HU_SHIFT);
|
|
+
|
|
+ if ((cal->tr & RTC_TR_PM) != 0U) {
|
|
+ tm->hour += 12U;
|
|
+ }
|
|
+
|
|
+ tm->min = (((cal->tr & RTC_TR_MNT_MASK) >> RTC_TR_MNT_SHIFT) * 10U) +
|
|
+ ((cal->tr & RTC_TR_MNU_MASK) >> RTC_TR_MNU_SHIFT);
|
|
+ tm->sec = (((cal->tr & RTC_TR_ST_MASK) >> RTC_TR_ST_SHIFT) * 10U) +
|
|
+ (cal->tr & RTC_TR_SU_MASK);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function fill the rtc_time structure with the given date register.
|
|
+ ******************************************************************************/
|
|
+static void stm32_rtc_get_date(struct stm32_rtc_calendar *cal,
|
|
+ struct stm32_rtc_time *tm)
|
|
+{
|
|
+ tm->wday = (((cal->dr & RTC_DR_WDU_MASK) >> RTC_DR_WDU_SHIFT));
|
|
+
|
|
+ tm->day = (((cal->dr & RTC_DR_DT_MASK) >> RTC_DR_DT_SHIFT) * 10U) +
|
|
+ (cal->dr & RTC_DR_DU_MASK);
|
|
+
|
|
+ tm->month = (((cal->dr & RTC_DR_MT) >> RTC_DR_MT_SHIFT) * 10U) +
|
|
+ ((cal->dr & RTC_DR_MU_MASK) >> RTC_DR_MU_SHIFT);
|
|
+
|
|
+ tm->year = (((cal->dr & RTC_DR_YT_MASK) >> RTC_DR_YT_SHIFT) * 10U) +
|
|
+ ((cal->dr & RTC_DR_YU_MASK) >> RTC_DR_YU_SHIFT) + 2000U;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function reads the RTC timestamp register values and update time
|
|
+ * structure with the corresponding value.
|
|
+ ******************************************************************************/
|
|
+static void stm32_rtc_read_timestamp(struct stm32_rtc_time *time)
|
|
+{
|
|
+ struct stm32_rtc_calendar cal_tamp;
|
|
+ uintptr_t rtc_base = get_base();
|
|
+
|
|
+ cal_tamp.tr = mmio_read_32(rtc_base + RTC_TSTR);
|
|
+ cal_tamp.dr = mmio_read_32(rtc_base + RTC_TSDR);
|
|
+ stm32_rtc_get_time(&cal_tamp, time);
|
|
+ stm32_rtc_get_date(&cal_tamp, time);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function gets the RTC calendar register values.
|
|
+ * It takes into account the need of reading twice or not, depending on
|
|
+ * frequencies previously setted, and the bypass or not of the shadow
|
|
+ * registers. This service is exposed externally.
|
|
+ ******************************************************************************/
|
|
+void stm32_rtc_get_calendar(struct stm32_rtc_calendar *calendar)
|
|
+{
|
|
+ stm32_clock_enable(rtc_dev.clock);
|
|
+
|
|
+ stm32_rtc_read_calendar(calendar);
|
|
+
|
|
+ if (rtc_dev.flags & RTC_FLAGS_READ_TWICE) {
|
|
+ uint32_t tr_save = calendar->tr;
|
|
+
|
|
+ stm32_rtc_read_calendar(calendar);
|
|
+
|
|
+ if (calendar->tr != tr_save) {
|
|
+ stm32_rtc_read_calendar(calendar);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable(rtc_dev.clock);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function computes the second fraction in milliseconds.
|
|
+ * The returned value is a uint32_t between 0 and 1000.
|
|
+ ******************************************************************************/
|
|
+static uint32_t stm32_rtc_get_second_fraction(struct stm32_rtc_calendar *cal)
|
|
+{
|
|
+ uint32_t prediv_s = mmio_read_32(get_base() + RTC_PRER) &
|
|
+ RTC_PRER_PREDIV_S_MASK;
|
|
+ uint32_t ss = cal->ssr & RTC_SSR_SS_MASK;
|
|
+
|
|
+ return ((prediv_s - ss) * 1000U) / (prediv_s + 1U);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function computes the fraction difference between two timestamps.
|
|
+ * Here again the returned value is in milliseconds.
|
|
+ ******************************************************************************/
|
|
+static unsigned long long stm32_rtc_diff_frac(struct stm32_rtc_calendar *cur,
|
|
+ struct stm32_rtc_calendar *ref)
|
|
+{
|
|
+ unsigned long long val_r;
|
|
+ unsigned long long val_c;
|
|
+
|
|
+ val_r = stm32_rtc_get_second_fraction(ref);
|
|
+ val_c = stm32_rtc_get_second_fraction(cur);
|
|
+
|
|
+ if (val_c >= val_r) {
|
|
+ return val_c - val_r;
|
|
+ } else {
|
|
+ return 1000U - val_r + val_c;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function computes the time difference between two timestamps.
|
|
+ * It includes seconds, minutes and hours.
|
|
+ * Here again the returned value is in milliseconds.
|
|
+ ******************************************************************************/
|
|
+static unsigned long long stm32_rtc_diff_time(struct stm32_rtc_time *current,
|
|
+ struct stm32_rtc_time *ref)
|
|
+{
|
|
+ signed long long diff_in_s;
|
|
+ signed long long curr_s;
|
|
+ signed long long ref_s;
|
|
+
|
|
+ curr_s = (signed long long)current->sec +
|
|
+ (((signed long long)current->min +
|
|
+ (((signed long long)current->hour * 60))) * 60);
|
|
+
|
|
+ ref_s = (signed long long)ref->sec +
|
|
+ (((signed long long)ref->min +
|
|
+ (((signed long long)ref->hour * 60))) * 60);
|
|
+
|
|
+ diff_in_s = curr_s - ref_s;
|
|
+ if (diff_in_s < 0) {
|
|
+ diff_in_s += 24 * 60 * 60;
|
|
+ }
|
|
+
|
|
+ return (unsigned long long)diff_in_s * 1000U;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function determines if the year is leap or not.
|
|
+ * Returned value is true or false.
|
|
+ ******************************************************************************/
|
|
+static bool stm32_is_a_leap_year(uint32_t year)
|
|
+{
|
|
+ return ((year % 4U) == 0U) &&
|
|
+ (((year % 100U) != 0U) || ((year % 400U) == 0U));
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function computes the date difference between two timestamps.
|
|
+ * It includes days, months, years, with exceptions.
|
|
+ * Here again the returned value is in milliseconds.
|
|
+ ******************************************************************************/
|
|
+static unsigned long long stm32_rtc_diff_date(struct stm32_rtc_time *current,
|
|
+ struct stm32_rtc_time *ref)
|
|
+{
|
|
+ uint32_t diff_in_days = 0;
|
|
+ uint32_t m;
|
|
+ static const uint8_t month_len[NB_MONTHS] = {
|
|
+ 31, 28, 31, 30, 31, 30,
|
|
+ 31, 31, 30, 31, 30, 31
|
|
+ };
|
|
+
|
|
+ /* Get the number of non-entire month days */
|
|
+ if (current->day >= ref->day) {
|
|
+ diff_in_days += current->day - ref->day;
|
|
+ } else {
|
|
+ diff_in_days += (uint32_t)month_len[ref->month - 1U] -
|
|
+ ref->day + current->day;
|
|
+ }
|
|
+
|
|
+ /* Get the number of entire months, and compute the related days */
|
|
+ if (current->month > (ref->month + 1U)) {
|
|
+ for (m = (ref->month + 1U); (m < current->month) &&
|
|
+ (m < 12U); m++) {
|
|
+ diff_in_days += (uint32_t)month_len[m - 1U];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (current->month < (ref->month - 1U)) {
|
|
+ for (m = 1U; (m < current->month) && (m < 12U); m++) {
|
|
+ diff_in_days += (uint32_t)month_len[m - 1U];
|
|
+ }
|
|
+
|
|
+ for (m = (ref->month + 1U); m < 12U; m++) {
|
|
+ diff_in_days += (uint32_t)month_len[m - 1U];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Get complete years */
|
|
+ if (current->year > (ref->year + 1U)) {
|
|
+ diff_in_days += (current->year - ref->year - 1U) * 365U;
|
|
+ }
|
|
+
|
|
+ /* Particular cases: leap years (one day more) */
|
|
+ if (diff_in_days > 0U) {
|
|
+ if (current->year == ref->year) {
|
|
+ if (stm32_is_a_leap_year(current->year)) {
|
|
+ if ((ref->month <= 2U) &&
|
|
+ (current->month >= 3U) &&
|
|
+ (current->day <= 28U)) {
|
|
+ diff_in_days++;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ uint32_t y;
|
|
+
|
|
+ /* Ref year is leap */
|
|
+ if ((stm32_is_a_leap_year(ref->year)) &&
|
|
+ (ref->month <= 2U) && (ref->day <= 28U)) {
|
|
+ diff_in_days++;
|
|
+ }
|
|
+
|
|
+ /* Current year is leap */
|
|
+ if ((stm32_is_a_leap_year(current->year)) &&
|
|
+ (current->month >= 3U)) {
|
|
+ diff_in_days++;
|
|
+ }
|
|
+
|
|
+ /* Interleaved years are leap */
|
|
+ for (y = ref->year + 1U; y < current->year; y++) {
|
|
+ if (stm32_is_a_leap_year(y)) {
|
|
+ diff_in_days++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (24ULL * 60U * 60U * 1000U) * (unsigned long long)diff_in_days;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function computes the date difference between two rtc value.
|
|
+ * Here again the returned value is in milliseconds.
|
|
+ ******************************************************************************/
|
|
+unsigned long long stm32_rtc_diff_calendar(struct stm32_rtc_calendar *cur,
|
|
+ struct stm32_rtc_calendar *ref)
|
|
+{
|
|
+ unsigned long long diff_in_ms = 0;
|
|
+ struct stm32_rtc_time curr_t;
|
|
+ struct stm32_rtc_time ref_t;
|
|
+
|
|
+ stm32_clock_enable(rtc_dev.clock);
|
|
+
|
|
+ stm32_rtc_get_date(cur, &curr_t);
|
|
+ stm32_rtc_get_date(ref, &ref_t);
|
|
+ stm32_rtc_get_time(cur, &curr_t);
|
|
+ stm32_rtc_get_time(ref, &ref_t);
|
|
+
|
|
+ diff_in_ms += stm32_rtc_diff_frac(cur, ref);
|
|
+ diff_in_ms += stm32_rtc_diff_time(&curr_t, &ref_t);
|
|
+ diff_in_ms += stm32_rtc_diff_date(&curr_t, &ref_t);
|
|
+
|
|
+ stm32_clock_disable(rtc_dev.clock);
|
|
+
|
|
+ return diff_in_ms;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function fill the RTC timestamp structure.
|
|
+ ******************************************************************************/
|
|
+void stm32_rtc_get_timestamp(struct stm32_rtc_time *tamp_ts)
|
|
+{
|
|
+ uintptr_t rtc_base = get_base();
|
|
+
|
|
+ stm32_clock_enable(rtc_dev.clock);
|
|
+
|
|
+ if ((mmio_read_32(rtc_base + RTC_SR) & RTC_SR_TSF) != 0U) {
|
|
+ /* Print timestamp for tamper event */
|
|
+ stm32_rtc_read_timestamp(tamp_ts);
|
|
+ mmio_setbits_32(rtc_base + RTC_SCR, RTC_SCR_CTSF);
|
|
+ if ((mmio_read_32(rtc_base + RTC_SR) & RTC_SR_TSOVF) !=
|
|
+ 0U) {
|
|
+ /* Overflow detected */
|
|
+ mmio_setbits_32(rtc_base + RTC_SCR, RTC_SCR_CTSOVF);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable(rtc_dev.clock);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function enable the timestamp bit for tamper and secure timestamp
|
|
+ * access.
|
|
+ ******************************************************************************/
|
|
+void stm32_rtc_set_tamper_timestamp(void)
|
|
+{
|
|
+ uintptr_t rtc_base = get_base();
|
|
+
|
|
+ stm32_clock_enable(rtc_dev.clock);
|
|
+
|
|
+ stm32_rtc_write_unprotect();
|
|
+
|
|
+ /* Enable tamper timestamper */
|
|
+ mmio_setbits_32(rtc_base + RTC_CR, RTC_CR_TAMPTS);
|
|
+
|
|
+ /* Secure Timestamp bit */
|
|
+ mmio_clrbits_32(rtc_base + RTC_SMCR, RTC_SMCR_TS_DPROT);
|
|
+
|
|
+ stm32_rtc_write_protect();
|
|
+
|
|
+ stm32_clock_disable(rtc_dev.clock);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * This function return state of tamper timestamp.
|
|
+ ******************************************************************************/
|
|
+bool stm32_rtc_is_timestamp_enable(void)
|
|
+{
|
|
+ bool ret;
|
|
+
|
|
+ stm32_clock_enable(rtc_dev.clock);
|
|
+
|
|
+ ret = (mmio_read_32(get_base() + RTC_CR) & RTC_CR_TAMPTS) != 0U;
|
|
+
|
|
+ stm32_clock_disable(rtc_dev.clock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+/*******************************************************************************
|
|
+ * RTC initialisation function.
|
|
+ ******************************************************************************/
|
|
+static TEE_Result stm32_rtc_init(void)
|
|
+{
|
|
+ int node;
|
|
+ struct dt_node_info dt_info;
|
|
+ void *fdt = get_dt_blob();
|
|
+
|
|
+ if (!fdt) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ node = fdt_get_node(fdt, &dt_info, -1, RTC_COMPAT);
|
|
+ if (node < 0) {
|
|
+ return node;
|
|
+ }
|
|
+
|
|
+ rtc_dev.pbase = dt_info.base;
|
|
+
|
|
+ if (dt_info.status == DT_STATUS_OK_SEC) {
|
|
+ rtc_dev.flags |= RTC_FLAGS_SECURE;
|
|
+ stm32mp_register_secure_periph_iomem(rtc_dev.pbase);
|
|
+ rtc_dev.vbase = (uintptr_t)phys_to_virt(rtc_dev.pbase,
|
|
+ MEM_AREA_IO_SEC);
|
|
+ } else {
|
|
+ stm32mp_register_non_secure_periph_iomem(rtc_dev.pbase);
|
|
+ rtc_dev.vbase = (uintptr_t)phys_to_virt(rtc_dev.pbase,
|
|
+ MEM_AREA_IO_NSEC);
|
|
+ }
|
|
+
|
|
+ rtc_dev.clock = (unsigned long)dt_info.clock;
|
|
+
|
|
+ if (stm32_rtc_get_read_twice()) {
|
|
+ rtc_dev.flags |= RTC_FLAGS_READ_TWICE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+driver_init(stm32_rtc_init);
|
|
+#endif
|
|
diff --git a/core/drivers/stm32_timer.c b/core/drivers/stm32_timer.c
|
|
new file mode 100644
|
|
index 0000000..be03577
|
|
--- /dev/null
|
|
+++ b/core/drivers/stm32_timer.c
|
|
@@ -0,0 +1,272 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#include <arm.h>
|
|
+#include <drivers/serial.h>
|
|
+#include <drivers/stm32_timer.h>
|
|
+#include <initcall.h>
|
|
+#include <keep.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <libfdt.h>
|
|
+#include <mm/core_memprot.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+
|
|
+#define TIM_CR1 0x00U /* Control Register 1 */
|
|
+#define TIM_CR2 0x04U /* Control Register 2 */
|
|
+#define TIM_SMCR 0x08U /* Slave mode control reg */
|
|
+#define TIM_DIER 0x0CU /* DMA/interrupt register */
|
|
+#define TIM_SR 0x10U /* Status register */
|
|
+#define TIM_EGR 0x14U /* Event Generation Reg */
|
|
+#define TIM_CCMR1 0x18U /* Capt/Comp 1 Mode Reg */
|
|
+#define TIM_CCMR2 0x1CU /* Capt/Comp 2 Mode Reg */
|
|
+#define TIM_CCER 0x20U /* Capt/Comp Enable Reg */
|
|
+#define TIM_CNT 0x24U /* Counter */
|
|
+#define TIM_PSC 0x28U /* Prescaler */
|
|
+#define TIM_ARR 0x2CU /* Auto-Reload Register */
|
|
+#define TIM_CCR1 0x34U /* Capt/Comp Register 1 */
|
|
+#define TIM_CCR2 0x38U /* Capt/Comp Register 2 */
|
|
+#define TIM_CCR3 0x3CU /* Capt/Comp Register 3 */
|
|
+#define TIM_CCR4 0x40U /* Capt/Comp Register 4 */
|
|
+#define TIM_BDTR 0x44U /* Break and Dead-Time Reg */
|
|
+#define TIM_DCR 0x48U /* DMA control register */
|
|
+#define TIM_DMAR 0x4CU /* DMA transfer register */
|
|
+#define TIM_AF1 0x60U /* Alt Function Reg 1 */
|
|
+#define TIM_AF2 0x64U /* Alt Function Reg 2 */
|
|
+#define TIM_TISEL 0x68U /* Input Selection */
|
|
+
|
|
+#define TIM_CR1_CEN BIT(0)
|
|
+#define TIM_SMCR_SMS GENMASK_32(2, 0) /* Slave mode selection */
|
|
+#define TIM_SMCR_TS GENMASK_32(6, 4) /* Trigger selection */
|
|
+#define TIM_CCMR_CC1S_TI1 BIT(0) /* IC1/IC3 selects TI1/TI3 */
|
|
+#define TIM_CCMR_CC1S_TI2 BIT(1) /* IC1/IC3 selects TI2/TI4 */
|
|
+#define TIM_CCMR_CC2S_TI2 BIT(8) /* IC2/IC4 selects TI2/TI4 */
|
|
+#define TIM_CCMR_CC2S_TI1 BIT(9) /* IC2/IC4 selects TI1/TI3 */
|
|
+#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
|
|
+#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
|
|
+#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
|
|
+#define TIM_SR_UIF BIT(0) /* UIF interrupt flag */
|
|
+#define TIM_SR_CC1IF BIT(1) /* CC1 interrupt flag */
|
|
+#define TIM_TISEL_TI1SEL_MASK GENMASK_32(3, 0)
|
|
+#define TIM_SMCR_SMS_RESET 0x4U
|
|
+#define TIM_SMCR_TS_SHIFT 4U
|
|
+#define TIM_SMCR_TS_TI1FP1 0x5U
|
|
+
|
|
+#define TIM_COMPAT "st,stm32-timers"
|
|
+#define TIM_TIMEOUT_US 100000
|
|
+#define TIM_TIMEOUT_STEP_US 10
|
|
+#define TIM_PRESCAL_HSI 10U
|
|
+#define TIM_PRESCAL_CSI 7U
|
|
+
|
|
+struct stm32_timer_instance {
|
|
+ struct io_pa_va base;
|
|
+ unsigned long clk;
|
|
+ unsigned long freq;
|
|
+ uint8_t cal_input;
|
|
+};
|
|
+
|
|
+static uintptr_t timer_base(struct stm32_timer_instance *timer)
|
|
+{
|
|
+ return (uintptr_t)io_pa_or_va(&timer->base);
|
|
+}
|
|
+
|
|
+/* Currently support HSI and CSI calibratrion */
|
|
+#define TIM_MAX_INSTANCE 2
|
|
+
|
|
+static struct stm32_timer_instance stm32_timer[TIM_MAX_INSTANCE];
|
|
+
|
|
+static int timer_get_dt_node(void *fdt, struct dt_node_info *info, int offset)
|
|
+{
|
|
+ int node;
|
|
+
|
|
+ node = fdt_get_node(fdt, info, offset, TIM_COMPAT);
|
|
+ if (node < 0) {
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+ }
|
|
+
|
|
+ return node;
|
|
+}
|
|
+
|
|
+static void timer_config(struct stm32_timer_instance *timer)
|
|
+{
|
|
+ uintptr_t base = timer_base(timer);
|
|
+
|
|
+ stm32_clock_enable(timer->clk);
|
|
+
|
|
+ timer->freq = stm32_clock_get_rate(timer->clk);
|
|
+
|
|
+ if ((mmio_read_32(base + TIM_TISEL) & TIM_TISEL_TI1SEL_MASK) !=
|
|
+ timer->cal_input) {
|
|
+ mmio_clrsetbits_32(base + TIM_CCMR1,
|
|
+ TIM_CCMR_CC1S_TI1 | TIM_CCMR_CC1S_TI2,
|
|
+ TIM_CCMR_CC1S_TI1);
|
|
+
|
|
+ mmio_clrbits_32(base + TIM_CCER,
|
|
+ TIM_CCER_CC1P | TIM_CCER_CC1NP);
|
|
+
|
|
+ mmio_clrsetbits_32(base + TIM_SMCR,
|
|
+ TIM_SMCR_TS | TIM_SMCR_SMS,
|
|
+ (TIM_SMCR_TS_TI1FP1 << TIM_SMCR_TS_SHIFT) |
|
|
+ TIM_SMCR_SMS_RESET);
|
|
+
|
|
+ mmio_write_32(base + TIM_TISEL, timer->cal_input);
|
|
+ mmio_setbits_32(base + TIM_CR1, TIM_CR1_CEN);
|
|
+ mmio_setbits_32(base + TIM_CCER, TIM_CCER_CC1E);
|
|
+ }
|
|
+
|
|
+ stm32_clock_disable(timer->clk);
|
|
+}
|
|
+
|
|
+static uint32_t timer_start_capture(struct stm32_timer_instance *timer)
|
|
+{
|
|
+ uint64_t timeout_ref;
|
|
+ uint32_t counter = 0U;
|
|
+ uint32_t old_counter = 0U;
|
|
+ int twice;
|
|
+ uintptr_t base = timer_base(timer);
|
|
+
|
|
+ timer_config(timer);
|
|
+
|
|
+ stm32_clock_enable(timer->clk);
|
|
+
|
|
+ mmio_write_32(base + TIM_SR, 0U);
|
|
+
|
|
+ timeout_ref = utimeout_init(TIM_TIMEOUT_US);
|
|
+
|
|
+ while ((mmio_read_32(base + TIM_SR) & TIM_SR_UIF) == 0U) {
|
|
+ if (utimeout_elapsed(TIM_TIMEOUT_US, timeout_ref)) {
|
|
+ goto bail;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mmio_write_32(base + TIM_SR, 0U);
|
|
+
|
|
+ for (twice = 0; (twice < 2) || (counter != old_counter); twice++) {
|
|
+ timeout_ref = utimeout_init(TIM_TIMEOUT_US);
|
|
+
|
|
+ while ((mmio_read_32(base + TIM_SR) & TIM_SR_CC1IF) == 0U) {
|
|
+ if (utimeout_elapsed(TIM_TIMEOUT_US, timeout_ref)) {
|
|
+ counter = 0U;
|
|
+ goto bail;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ old_counter = counter;
|
|
+ counter = mmio_read_32(base + TIM_CCR1);
|
|
+ }
|
|
+
|
|
+bail:
|
|
+ stm32_clock_disable(timer->clk);
|
|
+
|
|
+ return counter;
|
|
+}
|
|
+
|
|
+unsigned long stm32_timer_hsi_freq(void)
|
|
+{
|
|
+ struct stm32_timer_instance *timer = &stm32_timer[HSI_CAL];
|
|
+ uint32_t counter = 0;
|
|
+
|
|
+ if (timer->base.pa != 0) {
|
|
+ counter = timer_start_capture(timer);
|
|
+ }
|
|
+
|
|
+ return (counter == 0) ? 0 : (timer->freq / counter) << TIM_PRESCAL_HSI;
|
|
+}
|
|
+KEEP_PAGER(stm32_timer_hsi_freq);
|
|
+
|
|
+unsigned long stm32_timer_csi_freq(void)
|
|
+{
|
|
+ struct stm32_timer_instance *timer = &stm32_timer[CSI_CAL];
|
|
+ uint32_t counter = 0;
|
|
+
|
|
+ if (timer->base.pa != 0) {
|
|
+ counter = timer_start_capture(timer);
|
|
+ }
|
|
+
|
|
+ return (counter == 0U) ? 0 : (timer->freq / counter) << TIM_PRESCAL_CSI;
|
|
+}
|
|
+KEEP_PAGER(stm32_timer_csi_freq);
|
|
+
|
|
+static void _init_stm32_timer(void)
|
|
+{
|
|
+ void *fdt = get_dt_blob();
|
|
+ struct dt_node_info dt_timer;
|
|
+ int node = -1;
|
|
+ static bool inited;
|
|
+
|
|
+ if (inited)
|
|
+ return;
|
|
+ inited = true;
|
|
+
|
|
+ if (!fdt) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ for (node = timer_get_dt_node(fdt, &dt_timer, node);
|
|
+ node != -FDT_ERR_NOTFOUND;
|
|
+ node = timer_get_dt_node(fdt, &dt_timer, node)) {
|
|
+ struct stm32_timer_instance *timer;
|
|
+ const uint32_t *cuint;
|
|
+
|
|
+ if (!(dt_timer.status & DT_STATUS_OK_SEC)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "st,hsi-cal-input", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ timer = &stm32_timer[HSI_CAL];
|
|
+ timer->base.pa = dt_timer.base;
|
|
+ timer->clk = dt_timer.clock;
|
|
+ timer->freq = stm32_clock_get_rate(timer->clk);
|
|
+ timer->cal_input = (uint8_t)fdt32_to_cpu(*cuint);
|
|
+ timer_config(timer);
|
|
+ }
|
|
+
|
|
+ cuint = fdt_getprop(fdt, node, "st,csi_cal-input", NULL);
|
|
+ if (cuint != NULL) {
|
|
+ timer = &stm32_timer[CSI_CAL];
|
|
+ timer->base.pa = dt_timer.base;
|
|
+ timer->clk = dt_timer.clock;
|
|
+ timer->freq = stm32_clock_get_rate(timer->clk);
|
|
+ timer->cal_input = (uint8_t)fdt32_to_cpu(*cuint);
|
|
+ timer_config(timer);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void stm32_timer_freq_func(unsigned long (**timer_freq_cb)(void),
|
|
+ enum timer_cal type)
|
|
+{
|
|
+ _init_stm32_timer();
|
|
+
|
|
+ *timer_freq_cb = NULL;
|
|
+
|
|
+ switch (type) {
|
|
+ case HSI_CAL:
|
|
+ if (stm32_timer[HSI_CAL].base.pa != 0) {
|
|
+ *timer_freq_cb = stm32_timer_hsi_freq;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case CSI_CAL:
|
|
+ if (stm32_timer[CSI_CAL].base.pa != 0) {
|
|
+ *timer_freq_cb = stm32_timer_csi_freq;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ panic();
|
|
+ }
|
|
+}
|
|
+
|
|
+static TEE_Result init_stm32_timer(void)
|
|
+{
|
|
+ _init_stm32_timer();
|
|
+
|
|
+ return TEE_SUCCESS;
|
|
+}
|
|
+driver_init(init_stm32_timer);
|
|
diff --git a/core/drivers/stm32_uart.c b/core/drivers/stm32_uart.c
|
|
index 1febd84..4afbac8 100644
|
|
--- a/core/drivers/stm32_uart.c
|
|
+++ b/core/drivers/stm32_uart.c
|
|
@@ -4,10 +4,18 @@
|
|
*/
|
|
|
|
#include <compiler.h>
|
|
+#include <console.h>
|
|
#include <drivers/serial.h>
|
|
#include <drivers/stm32_uart.h>
|
|
+#include <initcall.h>
|
|
#include <io.h>
|
|
#include <keep.h>
|
|
+#include <kernel/delay.h>
|
|
+#include <kernel/dt.h>
|
|
+#include <kernel/generic_boot.h>
|
|
+#include <stm32_util.h>
|
|
+#include <stm32mp_dt.h>
|
|
+#include <string.h>
|
|
#include <util.h>
|
|
|
|
#define UART_REG_CR1 0x00 /* Control register 1 */
|
|
@@ -21,6 +29,11 @@
|
|
#define UART_REG_TDR 0x28 /* Transmit data register */
|
|
#define UART_REG_PRESC 0x2c /* Prescaler register */
|
|
|
|
+#define STM32_UART_IOMEM_SIZE 0x400
|
|
+
|
|
+#define PUTC_TIMEOUT_US 1000
|
|
+#define FLUSH_TIMEOUT_US 16000
|
|
+
|
|
/*
|
|
* Uart Interrupt & status register bits
|
|
*
|
|
@@ -36,26 +49,52 @@
|
|
|
|
static vaddr_t loc_chip_to_base(struct serial_chip *chip)
|
|
{
|
|
- struct console_pdata *pd =
|
|
- container_of(chip, struct console_pdata, chip);
|
|
-
|
|
- return io_pa_or_va(&pd->base);
|
|
+ struct stm32_uart_pdata *pd =
|
|
+ container_of(chip, struct stm32_uart_pdata, chip);
|
|
+
|
|
+ if (cpu_mmu_enabled()) {
|
|
+ if (!pd->base.va) {
|
|
+ pd->base.va = (vaddr_t)phys_to_virt(pd->base.pa,
|
|
+ pd->secure ?
|
|
+ MEM_AREA_IO_SEC :
|
|
+ MEM_AREA_IO_NSEC);
|
|
+ assert(pd->base.va);
|
|
+ }
|
|
+
|
|
+ return pd->base.va;
|
|
+ }
|
|
+
|
|
+ return pd->base.pa;
|
|
}
|
|
|
|
static void loc_flush(struct serial_chip *chip)
|
|
{
|
|
vaddr_t base = loc_chip_to_base(chip);
|
|
-
|
|
- while (!(read32(base + UART_REG_ISR) & USART_ISR_TXFE))
|
|
- ;
|
|
+ uint64_t timeout_ref = utimeout_init(FLUSH_TIMEOUT_US);
|
|
+
|
|
+ while (!(read32(base + UART_REG_ISR) & USART_ISR_TXFE)) {
|
|
+ if (utimeout_elapsed(FLUSH_TIMEOUT_US, timeout_ref)) {
|
|
+ if (read32(base + UART_REG_ISR) & USART_ISR_TXFE) {
|
|
+ break;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
static void loc_putc(struct serial_chip *chip, int ch)
|
|
{
|
|
vaddr_t base = loc_chip_to_base(chip);
|
|
+ uint64_t timeout_ref = utimeout_init(PUTC_TIMEOUT_US);
|
|
|
|
- while (!(read32(base + UART_REG_ISR) & USART_ISR_TXE_TXFNF))
|
|
- ;
|
|
+ while (!(read32(base + UART_REG_ISR) & USART_ISR_TXE_TXFNF)) {
|
|
+ if (utimeout_elapsed(PUTC_TIMEOUT_US, timeout_ref)) {
|
|
+ if (read32(base + UART_REG_ISR) & USART_ISR_TXE_TXFNF) {
|
|
+ break;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
|
|
write32(ch, base + UART_REG_TDR);
|
|
}
|
|
@@ -77,17 +116,94 @@ static int loc_getchar(struct serial_chip *chip)
|
|
return read32(base + UART_REG_RDR) & 0xff;
|
|
}
|
|
|
|
-static const struct serial_ops serial_ops = {
|
|
+static const struct serial_ops stm32_uart_serial_ops = {
|
|
.flush = loc_flush,
|
|
.putc = loc_putc,
|
|
.have_rx_data = loc_have_rx_data,
|
|
.getchar = loc_getchar,
|
|
|
|
};
|
|
-KEEP_PAGER(serial_ops);
|
|
+KEEP_PAGER(stm32_uart_serial_ops);
|
|
|
|
-void stm32_uart_init(struct console_pdata *pd, vaddr_t base)
|
|
+void stm32_uart_init(struct stm32_uart_pdata *pd, vaddr_t base)
|
|
{
|
|
pd->base.pa = base;
|
|
- pd->chip.ops = &serial_ops;
|
|
+ pd->chip.ops = &stm32_uart_serial_ops;
|
|
+}
|
|
+
|
|
+#ifdef CFG_DT
|
|
+static void register_secure_uart(struct stm32_uart_pdata *pd)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ stm32mp_register_secure_periph_iomem(pd->base.pa);
|
|
+ for (n = 0; n < pd->pinctrl_count; n++) {
|
|
+ stm32mp_register_secure_gpio(pd->pinctrl[n].bank,
|
|
+ pd->pinctrl[n].pin);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void register_non_secure_uart(struct stm32_uart_pdata *pd)
|
|
+{
|
|
+ size_t n;
|
|
+
|
|
+ stm32mp_register_non_secure_periph_iomem(pd->base.pa);
|
|
+ for (n = 0; n < pd->pinctrl_count; n++) {
|
|
+ stm32mp_register_non_secure_gpio(pd->pinctrl[n].bank,
|
|
+ pd->pinctrl[n].pin);
|
|
+ }
|
|
}
|
|
+
|
|
+struct stm32_uart_pdata *probe_uart_from_dt_node(void *fdt, int node)
|
|
+{
|
|
+ struct stm32_uart_pdata *pd;
|
|
+ struct dt_node_info info;
|
|
+ struct stm32_pinctrl *pinctrl_cfg = NULL;
|
|
+ int count;
|
|
+
|
|
+ fdt_fill_device_info(fdt, &info, node);
|
|
+
|
|
+ if (info.status == DT_STATUS_DISABLED) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ pd = calloc(1, sizeof(*pd));
|
|
+ if (!pd) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ pd->secure = (info.status == DT_STATUS_OK_SEC);
|
|
+ pd->base.pa = info.base;
|
|
+
|
|
+ if (info.clock < 0) {
|
|
+ panic();
|
|
+ }
|
|
+ pd->clock = (unsigned int)info.clock;
|
|
+
|
|
+ count = stm32_pinctrl_fdt_get_pinctrl(fdt, node, NULL, 0);
|
|
+ if (count < 0) {
|
|
+ panic();
|
|
+ }
|
|
+ if (count != 0) {
|
|
+ pinctrl_cfg = calloc(count, sizeof(*pinctrl_cfg));
|
|
+ if (!pinctrl_cfg) {
|
|
+ panic();
|
|
+ }
|
|
+ stm32_pinctrl_fdt_get_pinctrl(fdt, node, pinctrl_cfg, count);
|
|
+ stm32_pinctrl_load_active_cfg(pinctrl_cfg, count);
|
|
+ }
|
|
+ pd->pinctrl = pinctrl_cfg;
|
|
+ pd->pinctrl_count = count;
|
|
+
|
|
+ stm32_uart_init(pd, info.base);
|
|
+
|
|
+ if (pd->secure) {
|
|
+ register_secure_uart(pd);
|
|
+ } else {
|
|
+ register_non_secure_uart(pd);
|
|
+ }
|
|
+
|
|
+ return pd;
|
|
+}
|
|
+#endif /*CFG_DT*/
|
|
+
|
|
diff --git a/core/drivers/stpmic1.c b/core/drivers/stpmic1.c
|
|
new file mode 100644
|
|
index 0000000..3aa3afc
|
|
--- /dev/null
|
|
+++ b/core/drivers/stpmic1.c
|
|
@@ -0,0 +1,954 @@
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#include <drivers/stpmic1.h>
|
|
+#include <kernel/panic.h>
|
|
+#include <platform_config.h>
|
|
+#include <stdint.h>
|
|
+#include <string.h>
|
|
+#include <trace.h>
|
|
+#include <assert.h>
|
|
+
|
|
+#define I2C_TIMEOUT_MS 25
|
|
+
|
|
+struct regul_struct {
|
|
+ const char *dt_node_name;
|
|
+ const uint16_t *voltage_table;
|
|
+ uint8_t voltage_table_size;
|
|
+ uint8_t control_reg;
|
|
+ uint8_t low_power_reg;
|
|
+ uint8_t pull_down_reg;
|
|
+ uint8_t pull_down;
|
|
+ uint8_t mask_reset_reg;
|
|
+ uint8_t mask_reset;
|
|
+};
|
|
+
|
|
+static struct i2c_handle_s *pmic_i2c_handle;
|
|
+static uint16_t pmic_i2c_addr;
|
|
+
|
|
+/* Voltage tables in mV */
|
|
+static const uint16_t buck1_voltage_table[] = {
|
|
+ 725,
|
|
+ 725,
|
|
+ 725,
|
|
+ 725,
|
|
+ 725,
|
|
+ 725,
|
|
+ 750,
|
|
+ 775,
|
|
+ 800,
|
|
+ 825,
|
|
+ 850,
|
|
+ 875,
|
|
+ 900,
|
|
+ 925,
|
|
+ 950,
|
|
+ 975,
|
|
+ 1000,
|
|
+ 1025,
|
|
+ 1050,
|
|
+ 1075,
|
|
+ 1100,
|
|
+ 1125,
|
|
+ 1150,
|
|
+ 1175,
|
|
+ 1200,
|
|
+ 1225,
|
|
+ 1250,
|
|
+ 1275,
|
|
+ 1300,
|
|
+ 1325,
|
|
+ 1350,
|
|
+ 1375,
|
|
+ 1400,
|
|
+ 1425,
|
|
+ 1450,
|
|
+ 1475,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+ 1500,
|
|
+};
|
|
+
|
|
+static const uint16_t buck2_voltage_table[] = {
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1050,
|
|
+ 1050,
|
|
+ 1100,
|
|
+ 1100,
|
|
+ 1150,
|
|
+ 1150,
|
|
+ 1200,
|
|
+ 1200,
|
|
+ 1250,
|
|
+ 1250,
|
|
+ 1300,
|
|
+ 1300,
|
|
+ 1350,
|
|
+ 1350,
|
|
+ 1400,
|
|
+ 1400,
|
|
+ 1450,
|
|
+ 1450,
|
|
+ 1500,
|
|
+};
|
|
+
|
|
+static const uint16_t buck3_voltage_table[] = {
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1000,
|
|
+ 1100,
|
|
+ 1100,
|
|
+ 1100,
|
|
+ 1100,
|
|
+ 1200,
|
|
+ 1200,
|
|
+ 1200,
|
|
+ 1200,
|
|
+ 1300,
|
|
+ 1300,
|
|
+ 1300,
|
|
+ 1300,
|
|
+ 1400,
|
|
+ 1400,
|
|
+ 1400,
|
|
+ 1400,
|
|
+ 1500,
|
|
+ 1600,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+ 3400,
|
|
+};
|
|
+
|
|
+static const uint16_t buck4_voltage_table[] = {
|
|
+ 600,
|
|
+ 625,
|
|
+ 650,
|
|
+ 675,
|
|
+ 700,
|
|
+ 725,
|
|
+ 750,
|
|
+ 775,
|
|
+ 800,
|
|
+ 825,
|
|
+ 850,
|
|
+ 875,
|
|
+ 900,
|
|
+ 925,
|
|
+ 950,
|
|
+ 975,
|
|
+ 1000,
|
|
+ 1025,
|
|
+ 1050,
|
|
+ 1075,
|
|
+ 1100,
|
|
+ 1125,
|
|
+ 1150,
|
|
+ 1175,
|
|
+ 1200,
|
|
+ 1225,
|
|
+ 1250,
|
|
+ 1275,
|
|
+ 1300,
|
|
+ 1300,
|
|
+ 1350,
|
|
+ 1350,
|
|
+ 1400,
|
|
+ 1400,
|
|
+ 1450,
|
|
+ 1450,
|
|
+ 1500,
|
|
+ 1600,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+ 3400,
|
|
+ 3500,
|
|
+ 3600,
|
|
+ 3700,
|
|
+ 3800,
|
|
+ 3900,
|
|
+};
|
|
+
|
|
+static const uint16_t ldo1_voltage_table[] = {
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+};
|
|
+
|
|
+static const uint16_t ldo2_voltage_table[] = {
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+};
|
|
+
|
|
+static const uint16_t ldo3_voltage_table[] = {
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+ 3300,
|
|
+ 3300,
|
|
+ 3300,
|
|
+ 3300,
|
|
+ 3300,
|
|
+ 3300,
|
|
+ 0xFFFF, /* VREFDDR */
|
|
+};
|
|
+
|
|
+static const uint16_t ldo5_voltage_table[] = {
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+ 3400,
|
|
+ 3500,
|
|
+ 3600,
|
|
+ 3700,
|
|
+ 3800,
|
|
+ 3900,
|
|
+};
|
|
+
|
|
+static const uint16_t ldo6_voltage_table[] = {
|
|
+ 900,
|
|
+ 1000,
|
|
+ 1100,
|
|
+ 1200,
|
|
+ 1300,
|
|
+ 1400,
|
|
+ 1500,
|
|
+ 1600,
|
|
+ 1700,
|
|
+ 1800,
|
|
+ 1900,
|
|
+ 2000,
|
|
+ 2100,
|
|
+ 2200,
|
|
+ 2300,
|
|
+ 2400,
|
|
+ 2500,
|
|
+ 2600,
|
|
+ 2700,
|
|
+ 2800,
|
|
+ 2900,
|
|
+ 3000,
|
|
+ 3100,
|
|
+ 3200,
|
|
+ 3300,
|
|
+};
|
|
+
|
|
+static const uint16_t ldo4_voltage_table[] = {
|
|
+ 3300,
|
|
+};
|
|
+
|
|
+static const uint16_t vref_ddr_voltage_table[] = {
|
|
+ 3300,
|
|
+};
|
|
+
|
|
+/* Table of Regulators in PMIC SoC */
|
|
+static const struct regul_struct regulators_table[] = {
|
|
+ {
|
|
+ .dt_node_name = "buck1",
|
|
+ .voltage_table = buck1_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(buck1_voltage_table),
|
|
+ .control_reg = BUCK1_CONTROL_REG,
|
|
+ .low_power_reg = BUCK1_PWRCTRL_REG,
|
|
+ .pull_down_reg = BUCK_PULL_DOWN_REG,
|
|
+ .pull_down = BUCK1_PULL_DOWN_SHIFT,
|
|
+ .mask_reset_reg = MASK_RESET_BUCK_REG,
|
|
+ .mask_reset = BUCK1_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "buck2",
|
|
+ .voltage_table = buck2_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(buck2_voltage_table),
|
|
+ .control_reg = BUCK2_CONTROL_REG,
|
|
+ .low_power_reg = BUCK2_PWRCTRL_REG,
|
|
+ .pull_down_reg = BUCK_PULL_DOWN_REG,
|
|
+ .pull_down = BUCK2_PULL_DOWN_SHIFT,
|
|
+ .mask_reset_reg = MASK_RESET_BUCK_REG,
|
|
+ .mask_reset = BUCK2_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "buck3",
|
|
+ .voltage_table = buck3_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(buck3_voltage_table),
|
|
+ .control_reg = BUCK3_CONTROL_REG,
|
|
+ .low_power_reg = BUCK3_PWRCTRL_REG,
|
|
+ .pull_down_reg = BUCK_PULL_DOWN_REG,
|
|
+ .pull_down = BUCK3_PULL_DOWN_SHIFT,
|
|
+ .mask_reset_reg = MASK_RESET_BUCK_REG,
|
|
+ .mask_reset = BUCK3_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "buck4",
|
|
+ .voltage_table = buck4_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(buck4_voltage_table),
|
|
+ .control_reg = BUCK4_CONTROL_REG,
|
|
+ .low_power_reg = BUCK4_PWRCTRL_REG,
|
|
+ .pull_down_reg = BUCK_PULL_DOWN_REG,
|
|
+ .pull_down = BUCK4_PULL_DOWN_SHIFT,
|
|
+ .mask_reset_reg = MASK_RESET_BUCK_REG,
|
|
+ .mask_reset = BUCK4_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "ldo1",
|
|
+ .voltage_table = ldo1_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(ldo1_voltage_table),
|
|
+ .control_reg = LDO1_CONTROL_REG,
|
|
+ .low_power_reg = LDO1_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = LDO1_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "ldo2",
|
|
+ .voltage_table = ldo2_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(ldo2_voltage_table),
|
|
+ .control_reg = LDO2_CONTROL_REG,
|
|
+ .low_power_reg = LDO2_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = LDO2_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "ldo3",
|
|
+ .voltage_table = ldo3_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(ldo3_voltage_table),
|
|
+ .control_reg = LDO3_CONTROL_REG,
|
|
+ .low_power_reg = LDO3_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = LDO3_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "ldo4",
|
|
+ .voltage_table = ldo4_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(ldo4_voltage_table),
|
|
+ .control_reg = LDO4_CONTROL_REG,
|
|
+ .low_power_reg = LDO4_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = LDO4_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "ldo5",
|
|
+ .voltage_table = ldo5_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(ldo5_voltage_table),
|
|
+ .control_reg = LDO5_CONTROL_REG,
|
|
+ .low_power_reg = LDO5_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = LDO5_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "ldo6",
|
|
+ .voltage_table = ldo6_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(ldo6_voltage_table),
|
|
+ .control_reg = LDO6_CONTROL_REG,
|
|
+ .low_power_reg = LDO6_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = LDO6_MASK_RESET_SHIFT,
|
|
+ },
|
|
+ {
|
|
+ .dt_node_name = "vref_ddr",
|
|
+ .voltage_table = vref_ddr_voltage_table,
|
|
+ .voltage_table_size = ARRAY_SIZE(vref_ddr_voltage_table),
|
|
+ .control_reg = VREF_DDR_CONTROL_REG,
|
|
+ .low_power_reg = VREF_DDR_PWRCTRL_REG,
|
|
+ .mask_reset_reg = MASK_RESET_LDO_REG,
|
|
+ .mask_reset = VREF_DDR_MASK_RESET_SHIFT,
|
|
+ },
|
|
+};
|
|
+
|
|
+#define MAX_REGUL ARRAY_SIZE(regulators_table)
|
|
+
|
|
+static const struct regul_struct *get_regulator_data(const char *name)
|
|
+{
|
|
+ uint8_t i;
|
|
+
|
|
+ for (i = 0 ; i < MAX_REGUL ; i++) {
|
|
+ if (strncmp(name, regulators_table[i].dt_node_name,
|
|
+ strlen(regulators_table[i].dt_node_name)) == 0) {
|
|
+ return ®ulators_table[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Regulator not found */
|
|
+ panic();
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static uint8_t voltage_to_index(const char *name, uint16_t millivolts)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+ uint8_t i;
|
|
+
|
|
+ for (i = 0 ; i < regul->voltage_table_size ; i++) {
|
|
+ if (regul->voltage_table[i] == millivolts) {
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Voltage not found */
|
|
+ panic();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_powerctrl_on(void)
|
|
+{
|
|
+ return stpmic1_register_update(MAIN_CONTROL_REG, PWRCTRL_PIN_VALID,
|
|
+ PWRCTRL_PIN_VALID);
|
|
+}
|
|
+
|
|
+int stpmic1_switch_off(void)
|
|
+{
|
|
+ return stpmic1_register_update(MAIN_CONTROL_REG, 1,
|
|
+ SOFTWARE_SWITCH_OFF_ENABLED);
|
|
+}
|
|
+
|
|
+int stpmic1_regulator_enable(const char *name)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ return stpmic1_register_update(regul->control_reg, BIT(0), BIT(0));
|
|
+}
|
|
+
|
|
+int stpmic1_regulator_disable(const char *name)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ return stpmic1_register_update(regul->control_reg, 0, BIT(0));
|
|
+}
|
|
+
|
|
+uint8_t stpmic1_is_regulator_enabled(const char *name)
|
|
+{
|
|
+ uint8_t val;
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ if (stpmic1_register_read(regul->control_reg, &val) != 0) {
|
|
+ panic();
|
|
+ }
|
|
+
|
|
+ return (val & 0x1U);
|
|
+}
|
|
+
|
|
+int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts)
|
|
+{
|
|
+ uint8_t voltage_index = voltage_to_index(name, millivolts);
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+ uint8_t mask;
|
|
+
|
|
+ /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
|
|
+ if (strncmp(name, "buck", 4) == 0) {
|
|
+ mask = BUCK_VOLTAGE_MASK;
|
|
+ } else if ((strncmp(name, "ldo", 3) == 0) &&
|
|
+ (strncmp(name, "ldo4", 4) != 0)) {
|
|
+ mask = LDO_VOLTAGE_MASK;
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return stpmic1_register_update(regul->control_reg,
|
|
+ voltage_index << LDO_BUCK_VOLTAGE_SHIFT,
|
|
+ mask);
|
|
+}
|
|
+
|
|
+int stpmic1_regulator_pull_down_set(const char *name)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ if (regul->pull_down_reg != 0) {
|
|
+ return stpmic1_register_update(regul->pull_down_reg,
|
|
+ BIT(regul->pull_down),
|
|
+ LDO_BUCK_PULL_DOWN_MASK <<
|
|
+ regul->pull_down);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_regulator_mask_reset_set(const char *name)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ return stpmic1_register_update(regul->mask_reset_reg,
|
|
+ BIT(regul->mask_reset),
|
|
+ LDO_BUCK_RESET_MASK <<
|
|
+ regul->mask_reset);
|
|
+}
|
|
+
|
|
+int stpmic1_bo_enable_unpg(struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ return stpmic1_register_update(cfg->ctrl_reg, BIT(0), BIT(0));
|
|
+}
|
|
+
|
|
+/* Returns 1 if no configuration are expected applied at runtime, 0 otherwise */
|
|
+int stpmic1_bo_voltage_cfg(const char *name, uint16_t millivolts,
|
|
+ struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ uint8_t voltage_index = voltage_to_index(name, millivolts);
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+ uint8_t mask;
|
|
+
|
|
+ /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
|
|
+ if (strncmp(name, "buck", 4) == 0) {
|
|
+ mask = BUCK_VOLTAGE_MASK;
|
|
+ } else if ((strncmp(name, "ldo", 3) == 0) &&
|
|
+ (strncmp(name, "ldo4", 4) != 0)) {
|
|
+ mask = LDO_VOLTAGE_MASK;
|
|
+ } else {
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ cfg->ctrl_reg = regul->control_reg;
|
|
+ cfg->value = voltage_index << LDO_BUCK_VOLTAGE_SHIFT;
|
|
+ cfg->mask = mask;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_bo_voltage_unpg(struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ return stpmic1_register_update(cfg->ctrl_reg, cfg->value, cfg->mask);
|
|
+}
|
|
+
|
|
+int stpmic1_bo_pull_down_cfg(const char *name, struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ cfg->pd_reg = regul->pull_down_reg;
|
|
+ cfg->pd_value = BIT(regul->pull_down);
|
|
+ cfg->pd_mask = LDO_BUCK_PULL_DOWN_MASK << regul->pull_down;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_bo_pull_down_unpg(struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ return stpmic1_register_update(cfg->pd_reg, cfg->pd_value,
|
|
+ cfg->pd_mask);
|
|
+}
|
|
+
|
|
+int stpmic1_bo_mask_reset_cfg(const char *name, struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ cfg->mrst_reg = regul->mask_reset_reg;
|
|
+ cfg->mrst_value = BIT(regul->mask_reset);
|
|
+ cfg->mrst_mask = LDO_BUCK_RESET_MASK << regul->mask_reset;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_bo_mask_reset_unpg(struct stpmic1_bo_cfg *cfg)
|
|
+{
|
|
+ return stpmic1_register_update(cfg->mrst_reg, cfg->mrst_value,
|
|
+ cfg->mrst_mask);
|
|
+}
|
|
+
|
|
+int stpmic1_regulator_voltage_get(const char *name)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+ uint8_t value;
|
|
+ uint8_t mask;
|
|
+
|
|
+ /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
|
|
+ if (strncmp(name, "buck", 4) == 0) {
|
|
+ mask = BUCK_VOLTAGE_MASK;
|
|
+ } else if ((strncmp(name, "ldo", 3) == 0) &&
|
|
+ (strncmp(name, "ldo4", 4) != 0)) {
|
|
+ mask = LDO_VOLTAGE_MASK;
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (stpmic1_register_read(regul->control_reg, &value))
|
|
+ return -1;
|
|
+
|
|
+ value = (value & mask) >> LDO_BUCK_VOLTAGE_SHIFT;
|
|
+
|
|
+ if (value > regul->voltage_table_size)
|
|
+ return -1;
|
|
+
|
|
+ return (int)regul->voltage_table[value];
|
|
+}
|
|
+
|
|
+int stpmic1_lp_copy_reg(const char *name)
|
|
+{
|
|
+ uint8_t val;
|
|
+ int status;
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ status = stpmic1_register_read(regul->control_reg, &val);
|
|
+ if (status != 0) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ return stpmic1_register_write(regul->low_power_reg, val);
|
|
+}
|
|
+
|
|
+int stpmic1_lp_cfg(const char *name, struct stpmic1_lp_cfg *cfg)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ cfg->ctrl_reg = regul->control_reg;
|
|
+ cfg->lp_reg = regul->low_power_reg;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_lp_load_unpg(struct stpmic1_lp_cfg *cfg)
|
|
+{
|
|
+ uint8_t val;
|
|
+ int status;
|
|
+
|
|
+ status = stpmic1_register_read(cfg->ctrl_reg, &val);
|
|
+ if (status == 0) {
|
|
+ status = stpmic1_register_write(cfg->lp_reg, val);
|
|
+ }
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int stpmic1_lp_reg_on_off(const char *name, uint8_t enable)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ return stpmic1_register_update(regul->low_power_reg, enable,
|
|
+ LDO_BUCK_ENABLE_MASK);
|
|
+}
|
|
+
|
|
+int stpmic1_lp_on_off_unpg(struct stpmic1_lp_cfg *cfg, int enable)
|
|
+{
|
|
+ assert(enable == 0 || enable == 1);
|
|
+ return stpmic1_register_update(cfg->lp_reg, enable,
|
|
+ LDO_BUCK_ENABLE_MASK);
|
|
+}
|
|
+
|
|
+int stpmic1_lp_set_mode(const char *name, uint8_t hplp)
|
|
+{
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+
|
|
+ return stpmic1_register_update(regul->low_power_reg,
|
|
+ hplp << LDO_BUCK_HPLP_SHIFT,
|
|
+ LDO_BUCK_HPLP_ENABLE_MASK);
|
|
+}
|
|
+
|
|
+int stpmic1_lp_mode_unpg(struct stpmic1_lp_cfg *cfg, unsigned int mode)
|
|
+{
|
|
+ assert(mode == 0 || mode == 1);
|
|
+ return stpmic1_register_update(cfg->lp_reg,
|
|
+ mode << LDO_BUCK_HPLP_SHIFT,
|
|
+ LDO_BUCK_HPLP_ENABLE_MASK);
|
|
+}
|
|
+
|
|
+int stpmic1_lp_set_voltage(const char *name, uint16_t millivolts)
|
|
+{
|
|
+ uint8_t voltage_index = voltage_to_index(name, millivolts);
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+ uint8_t mask;
|
|
+
|
|
+ /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
|
|
+ if (strncmp(name, "buck", 4) == 0) {
|
|
+ mask = BUCK_VOLTAGE_MASK;
|
|
+ } else if ((strncmp(name, "ldo", 3) == 0) &&
|
|
+ (strncmp(name, "ldo4", 4) != 0)) {
|
|
+ mask = LDO_VOLTAGE_MASK;
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return stpmic1_register_update(regul->low_power_reg, voltage_index << 2,
|
|
+ mask);
|
|
+}
|
|
+
|
|
+/* Returns 1 if no configuration are expected applied at runtime, 0 otherwise */
|
|
+int stpmic1_lp_voltage_cfg(const char *name, uint16_t millivolts,
|
|
+ struct stpmic1_lp_cfg *cfg)
|
|
+
|
|
+{
|
|
+ uint8_t voltage_index = voltage_to_index(name, millivolts);
|
|
+ const struct regul_struct *regul = get_regulator_data(name);
|
|
+ uint8_t mask;
|
|
+
|
|
+ /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
|
|
+ if (strncmp(name, "buck", 4) == 0) {
|
|
+ mask = BUCK_VOLTAGE_MASK;
|
|
+ } else if ((strncmp(name, "ldo", 3) == 0) &&
|
|
+ (strncmp(name, "ldo4", 4) != 0)) {
|
|
+ mask = LDO_VOLTAGE_MASK;
|
|
+ } else {
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ assert(cfg->lp_reg == regul->low_power_reg);
|
|
+ cfg->value = voltage_index << 2;
|
|
+ cfg->mask = mask;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int stpmic1_lp_voltage_unpg(struct stpmic1_lp_cfg *cfg)
|
|
+{
|
|
+ return stpmic1_register_update(cfg->lp_reg, cfg->value, cfg->mask);
|
|
+}
|
|
+
|
|
+int stpmic1_register_read(uint8_t register_id, uint8_t *value)
|
|
+{
|
|
+ struct i2c_handle_s *i2c = pmic_i2c_handle;
|
|
+
|
|
+ return stm32_i2c_read_write_membyte(i2c, pmic_i2c_addr,
|
|
+ register_id, value, false);
|
|
+}
|
|
+
|
|
+int stpmic1_register_write(uint8_t register_id, uint8_t value)
|
|
+{
|
|
+ int status;
|
|
+ struct i2c_handle_s *i2c = pmic_i2c_handle;
|
|
+
|
|
+ status = stm32_i2c_read_write_membyte(i2c, pmic_i2c_addr,
|
|
+ register_id, &value, true);
|
|
+
|
|
+#ifdef CFG_TEE_CORE_DEBUG
|
|
+ if (status != 0) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ if ((register_id != WATCHDOG_CONTROL_REG) && (register_id <= 0x40U)) {
|
|
+ uint8_t readval;
|
|
+
|
|
+ status = stpmic1_register_read(register_id, &readval);
|
|
+ if (status != 0) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ if (readval != value) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int stpmic1_register_update(uint8_t register_id, uint8_t value, uint8_t mask)
|
|
+{
|
|
+ int status;
|
|
+ uint8_t val;
|
|
+
|
|
+ status = stpmic1_register_read(register_id, &val);
|
|
+ if (status != 0) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ val = (val & ~mask) | (value & mask);
|
|
+
|
|
+ return stpmic1_register_write(register_id, val);
|
|
+}
|
|
+
|
|
+void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr)
|
|
+{
|
|
+ pmic_i2c_handle = i2c_handle;
|
|
+ pmic_i2c_addr = i2c_addr;
|
|
+}
|
|
+
|
|
+void stpmic1_dump_regulators(void)
|
|
+{
|
|
+ size_t i;
|
|
+ char __maybe_unused const *name;
|
|
+
|
|
+ for (i = 0 ; i < MAX_REGUL ; i++) {
|
|
+ name = regulators_table[i].dt_node_name;
|
|
+
|
|
+ DMSG("PMIC regul %s: %sable, %dmV",
|
|
+ name,
|
|
+ stpmic1_is_regulator_enabled(name) ? "en" : "dis",
|
|
+ stpmic1_regulator_voltage_get(name));
|
|
+ }
|
|
+}
|
|
+
|
|
+int stpmic1_get_version(unsigned long *version)
|
|
+{
|
|
+ int rc;
|
|
+ uint8_t read_val;
|
|
+
|
|
+ rc = stpmic1_register_read(VERSION_STATUS_REG, &read_val);
|
|
+ if (rc) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ *version = (unsigned long)read_val;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/core/drivers/sub.mk b/core/drivers/sub.mk
|
|
index b2bcf15..41f01fa 100644
|
|
--- a/core/drivers/sub.mk
|
|
+++ b/core/drivers/sub.mk
|
|
@@ -19,4 +19,13 @@ srcs-$(CFG_DRA7_RNG) += dra7_rng.c
|
|
srcs-$(CFG_STIH_UART) += stih_asc.c
|
|
srcs-$(CFG_ATMEL_UART) += atmel_uart.c
|
|
srcs-$(CFG_MVEBU_UART) += mvebu_uart.c
|
|
+srcs-$(CFG_STM32_BSEC) += stm32_bsec.c
|
|
+srcs-$(CFG_STM32_ETZPC) += stm32_etzpc.c
|
|
+srcs-$(CFG_STM32_GPIO) += stm32_gpio.c
|
|
+srcs-$(CFG_STM32_I2C) += stm32_i2c.c
|
|
+srcs-$(CFG_STM32_IWDG) += stm32_iwdg.c
|
|
+srcs-$(CFG_STM32_RNG) += stm32_rng.c
|
|
+srcs-$(CFG_STM32_RTC) += stm32_rtc.c
|
|
+srcs-$(CFG_STM32_TIMER) += stm32_timer.c
|
|
srcs-$(CFG_STM32_UART) += stm32_uart.c
|
|
+srcs-$(CFG_STPMIC1) += stpmic1.c
|
|
diff --git a/core/include/drivers/gic.h b/core/include/drivers/gic.h
|
|
index a4f60b7..df1e9af 100644
|
|
--- a/core/include/drivers/gic.h
|
|
+++ b/core/include/drivers/gic.h
|
|
@@ -9,14 +9,39 @@
|
|
#include <types_ext.h>
|
|
#include <kernel/interrupt.h>
|
|
|
|
+/* Constants to categorize priorities */
|
|
+#define GIC_HIGHEST_SEC_PRIORITY 0x0U
|
|
+#define GIC_LOWEST_SEC_PRIORITY 0x7fU
|
|
+#define GIC_HIGHEST_NS_PRIORITY 0x80U
|
|
+#define GIC_LOWEST_NS_PRIORITY 0xfeU
|
|
+/* 0xff would disable all interrupts */
|
|
+
|
|
#define GIC_DIST_REG_SIZE 0x10000
|
|
#define GIC_CPU_REG_SIZE 0x10000
|
|
|
|
+#if !defined(CFG_WITH_ARM_TRUSTED_FW) && defined(CFG_ARM_GICV3)
|
|
+#error CFG_ARM_GICV3 is not supported without CFG_WITH_ARM_TRUSTED_FW
|
|
+#endif
|
|
+
|
|
+struct gic_it_pm;
|
|
+
|
|
+/*
|
|
+ * Save and restore some interrupts configuration during low power sequences.
|
|
+ * This is used on platforms using OP-TEE secure monitor.
|
|
+ */
|
|
+struct gic_pm {
|
|
+ struct gic_it_pm *pm_cfg;
|
|
+ size_t count;
|
|
+};
|
|
+
|
|
struct gic_data {
|
|
vaddr_t gicc_base;
|
|
vaddr_t gicd_base;
|
|
size_t max_it;
|
|
struct itr_chip chip;
|
|
+#if !defined(CFG_WITH_ARM_TRUSTED_FW)
|
|
+ struct gic_pm pm;
|
|
+#endif
|
|
};
|
|
|
|
/*
|
|
@@ -34,4 +59,8 @@ void gic_cpu_init(struct gic_data *gd);
|
|
void gic_it_handle(struct gic_data *gd);
|
|
|
|
void gic_dump_state(struct gic_data *gd);
|
|
+
|
|
+void gic_suspend(struct gic_data *gd);
|
|
+void gic_resume(struct gic_data *gd);
|
|
+
|
|
#endif /*__DRIVERS_GIC_H*/
|
|
diff --git a/core/include/drivers/stm32_bsec.h b/core/include/drivers/stm32_bsec.h
|
|
new file mode 100644
|
|
index 0000000..de4d20b
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_bsec.h
|
|
@@ -0,0 +1,143 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2015-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_BSEC_H__
|
|
+#define __STM32_BSEC_H__
|
|
+
|
|
+#include <stdint.h>
|
|
+#include <io.h>
|
|
+
|
|
+#define BSEC_OTP_MASK GENMASK_32(4, 0)
|
|
+#define BSEC_OTP_BANK_SHIFT 5
|
|
+
|
|
+#define BSEC_TIMEOUT_VALUE 0xFFFF
|
|
+
|
|
+#define ADDR_LOWER_OTP_PERLOCK_SHIFT 3
|
|
+#define DATA_LOWER_OTP_PERLOCK_BIT 3
|
|
+#define DATA_LOWER_OTP_PERLOCK_MASK GENMASK_32(2, 0)
|
|
+#define ADDR_UPPER_OTP_PERLOCK_SHIFT 4
|
|
+#define DATA_UPPER_OTP_PERLOCK_BIT 1
|
|
+#define DATA_UPPER_OTP_PERLOCK_MASK GENMASK_32(3, 0)
|
|
+
|
|
+/* Return status */
|
|
+#define BSEC_OK 0U
|
|
+#define BSEC_ERROR 0xFFFFFFFFU
|
|
+#define BSEC_DISTURBED 0xFFFFFFFEU
|
|
+#define BSEC_FEATURE_LOCK 0xFFFFFFFDU
|
|
+#define BSEC_INVALID_PARAM 0xFFFFFFFCU
|
|
+#define BSEC_PROG_FAIL 0xFFFFFFFBU
|
|
+#define BSEC_LOCK_FAIL 0xFFFFFFFAU
|
|
+#define BSEC_WRITE_FAIL 0xFFFFFFF9U
|
|
+#define BSEC_SHADOW_FAIL 0xFFFFFFF8U
|
|
+#define BSEC_TIMEOUT 0xFFFFFFF7U
|
|
+
|
|
+/* BSEC REGISTER OFFSET (base relative) */
|
|
+#define BSEC_OTP_CONF_OFF 0x000U
|
|
+#define BSEC_OTP_CTRL_OFF 0x004U
|
|
+#define BSEC_OTP_WRDATA_OFF 0x008U
|
|
+#define BSEC_OTP_STATUS_OFF 0x00CU
|
|
+#define BSEC_OTP_LOCK_OFF 0x010U
|
|
+#define BSEC_DEN_OFF 0x014U
|
|
+#define BSEC_FEN_OFF 0x018U
|
|
+#define BSEC_DISTURBED_OFF 0x01CU
|
|
+#define BSEC_DISTURBED1_OFF 0x020U
|
|
+#define BSEC_DISTURBED2_OFF 0x024U
|
|
+#define BSEC_ERROR_OFF 0x034U
|
|
+#define BSEC_ERROR1_OFF 0x038U
|
|
+#define BSEC_ERROR2_OFF 0x03CU
|
|
+#define BSEC_WRLOCK_OFF 0x04CU
|
|
+#define BSEC_WRLOCK1_OFF 0x050U
|
|
+#define BSEC_WRLOCK2_OFF 0x054U
|
|
+#define BSEC_SPLOCK_OFF 0x064U
|
|
+#define BSEC_SPLOCK1_OFF 0x068U
|
|
+#define BSEC_SPLOCK2_OFF 0x06CU
|
|
+#define BSEC_SWLOCK_OFF 0x07CU
|
|
+#define BSEC_SWLOCK1_OFF 0x080U
|
|
+#define BSEC_SWLOCK2_OFF 0x084U
|
|
+#define BSEC_SRLOCK_OFF 0x094U
|
|
+#define BSEC_SRLOCK1_OFF 0x098U
|
|
+#define BSEC_SRLOCK2_OFF 0x09CU
|
|
+#define BSEC_JTAG_IN_OFF 0x0ACU
|
|
+#define BSEC_JTAG_OUT_OFF 0x0B0U
|
|
+#define BSEC_SCRATCH_OFF 0x0B4U
|
|
+#define BSEC_OTP_DATA_OFF 0x200U
|
|
+#define BSEC_IPHW_CFG_OFF 0xFF0U
|
|
+#define BSEC_IPVR_OFF 0xFF4U
|
|
+#define BSEC_IP_ID_OFF 0xFF8U
|
|
+#define BSEC_IP_MAGIC_ID_OFF 0xFFCU
|
|
+
|
|
+/* BSEC_CONFIGURATION Register */
|
|
+#define BSEC_CONF_POWER_UP_MASK BIT(0)
|
|
+#define BSEC_CONF_POWER_UP_SHIFT 0
|
|
+#define BSEC_CONF_FRQ_MASK GENMASK_32(2, 1)
|
|
+#define BSEC_CONF_FRQ_SHIFT 1
|
|
+#define BSEC_CONF_PRG_WIDTH_MASK GENMASK_32(6, 3)
|
|
+#define BSEC_CONF_PRG_WIDTH_SHIFT 3
|
|
+#define BSEC_CONF_TREAD_MASK GENMASK_32(8, 7)
|
|
+#define BSEC_CONF_TREAD_SHIFT 7
|
|
+
|
|
+/* BSEC_CONTROL Register */
|
|
+#define BSEC_READ 0x000U
|
|
+#define BSEC_WRITE 0x100U
|
|
+#define BSEC_LOCK 0x200U
|
|
+
|
|
+/* STATUS Register */
|
|
+#define BSEC_MODE_STATUS_MASK GENMASK_32(2, 0)
|
|
+#define BSEC_MODE_BUSY_MASK BIT(3)
|
|
+#define BSEC_MODE_PROGFAIL_MASK BIT(4)
|
|
+#define BSEC_MODE_PWR_MASK BIT(5)
|
|
+#define BSEC_MODE_BIST1_LOCK_MASK BIT(6)
|
|
+#define BSEC_MODE_BIST2_LOCK_MASK BIT(7)
|
|
+
|
|
+/* Debug */
|
|
+#define BSEC_HDPEN BIT(4)
|
|
+#define BSEC_SPIDEN BIT(5)
|
|
+#define BSEC_SPINDEN BIT(6)
|
|
+#define BSEC_DBGSWGEN BIT(10)
|
|
+#define BSEC_DEN_ALL_MSK GENMASK_32(10, 0)
|
|
+
|
|
+/*
|
|
+ * OTP Lock services definition
|
|
+ * Value must corresponding to the bit number in the register
|
|
+ */
|
|
+#define BSEC_LOCK_UPPER_OTP 0x00
|
|
+#define BSEC_LOCK_DEBUG 0x02
|
|
+#define BSEC_LOCK_PROGRAM 0x03
|
|
+
|
|
+/* Values for struct bsec_config::freq */
|
|
+#define FREQ_10_20_MHZ 0x0
|
|
+#define FREQ_20_30_MHZ 0x1
|
|
+#define FREQ_30_45_MHZ 0x2
|
|
+#define FREQ_45_67_MHZ 0x3
|
|
+
|
|
+uint32_t bsec_shadow_register(uint32_t otp);
|
|
+uint32_t bsec_read_otp(uint32_t *val, uint32_t otp);
|
|
+uint32_t bsec_write_otp(uint32_t val, uint32_t otp);
|
|
+uint32_t bsec_program_otp(uint32_t val, uint32_t otp);
|
|
+uint32_t bsec_permanent_lock_otp(uint32_t otp);
|
|
+
|
|
+uint32_t bsec_write_debug_conf(uint32_t val);
|
|
+uint32_t bsec_read_debug_conf(void);
|
|
+
|
|
+uint32_t bsec_get_status(void);
|
|
+uint32_t bsec_get_hw_conf(void);
|
|
+uint32_t bsec_get_version(void);
|
|
+uint32_t bsec_get_id(void);
|
|
+uint32_t bsec_get_magic_id(void);
|
|
+
|
|
+bool bsec_write_sr_lock(uint32_t otp, uint32_t value);
|
|
+bool bsec_read_sr_lock(uint32_t otp);
|
|
+bool bsec_write_sw_lock(uint32_t otp, uint32_t value);
|
|
+bool bsec_read_sw_lock(uint32_t otp);
|
|
+bool bsec_write_sp_lock(uint32_t otp, uint32_t value);
|
|
+bool bsec_read_sp_lock(uint32_t otp);
|
|
+bool bsec_wr_lock(uint32_t otp);
|
|
+uint32_t bsec_otp_lock(uint32_t service, uint32_t value);
|
|
+
|
|
+bool bsec_mode_is_closed_device(void);
|
|
+uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word);
|
|
+uint32_t bsec_check_nsec_access_rights(uint32_t otp);
|
|
+
|
|
+#endif /*__STM32_BSEC_H__*/
|
|
diff --git a/core/include/drivers/stm32_etzpc.h b/core/include/drivers/stm32_etzpc.h
|
|
new file mode 100644
|
|
index 0000000..bd34955
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_etzpc.h
|
|
@@ -0,0 +1,75 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_ETZPC_H__
|
|
+#define __STM32_ETZPC_H__
|
|
+
|
|
+#include <io.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+#define ETZPC_V1_0 0x10
|
|
+#define ETZPC_V2_0 0x20
|
|
+
|
|
+/*
|
|
+ * Define security level for each peripheral (DECPROT)
|
|
+ */
|
|
+enum etzpc_decprot_attributes {
|
|
+ TZPC_DECPROT_S_RW = 0,
|
|
+ TZPC_DECPROT_NS_R_S_W = 1,
|
|
+ TZPC_DECPROT_MCU_ISOLATION = 2,
|
|
+ TZPC_DECPROT_NS_RW = 3,
|
|
+ TZPC_DECPROT_MAX = 4,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * etzpc_configure_decprot : Load a DECPROT configuration
|
|
+ * decprot_id : ID of the IP
|
|
+ * decprot_attr : Restriction access attributes
|
|
+ */
|
|
+void etzpc_configure_decprot(uint32_t decprot_id,
|
|
+ enum etzpc_decprot_attributes decprot_attr);
|
|
+
|
|
+/*
|
|
+ * etzpc_get_decprot : Get the DECPROT attribute
|
|
+ * decprot_id : ID of the IP
|
|
+ * return : Attribute of this DECPROT
|
|
+ */
|
|
+enum etzpc_decprot_attributes etzpc_get_decprot(uint32_t decprot_id);
|
|
+
|
|
+/*
|
|
+ * etzpc_lock_decprot : Lock access to the DECPROT attributes
|
|
+ * decprot_id : ID of the IP
|
|
+ */
|
|
+void etzpc_lock_decprot(uint32_t decprot_id);
|
|
+
|
|
+/*
|
|
+ * etzpc_configure_tzma : Configure the target TZMA read only size
|
|
+ * tzma_id : ID of the memory
|
|
+ * tzma_value : read-only size
|
|
+ */
|
|
+void etzpc_configure_tzma(uint32_t tzma_id, uint16_t tzma_value);
|
|
+
|
|
+/*
|
|
+ * etzpc_get_tzma : Get the target TZMA read only size
|
|
+ * tzma_id : TZMA ID
|
|
+ * return : Size of read only size
|
|
+ */
|
|
+uint16_t etzpc_get_tzma(uint32_t tzma_id);
|
|
+
|
|
+/*
|
|
+ * etzpc_lock_tzma : Lock the target TZMA
|
|
+ * tzma_id : TZMA ID
|
|
+ */
|
|
+void etzpc_lock_tzma(uint32_t tzma_id);
|
|
+
|
|
+/*
|
|
+ * etzpc_get_lock_tzma : Return the lock status of the target TZMA
|
|
+ * tzma_id : TZMA ID
|
|
+ * return : True if TZMA is locked, false otherwise
|
|
+ */
|
|
+bool etzpc_get_lock_tzma(uint32_t tzma_id);
|
|
+
|
|
+#endif /*__STM32_ETZPC_H__*/
|
|
diff --git a/core/include/drivers/stm32_gpio.h b/core/include/drivers/stm32_gpio.h
|
|
new file mode 100644
|
|
index 0000000..67de10f
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_gpio.h
|
|
@@ -0,0 +1,107 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_GPIO_H__
|
|
+#define __STM32_GPIO_H__
|
|
+
|
|
+#include <stdbool.h>
|
|
+#include <stdint.h>
|
|
+#include <stddef.h>
|
|
+
|
|
+#define GPIO_MODE_OFFSET 0x00U
|
|
+#define GPIO_TYPE_OFFSET 0x04U
|
|
+#define GPIO_SPEED_OFFSET 0x08U
|
|
+#define GPIO_PUPD_OFFSET 0x0CU
|
|
+#define GPIO_ODR_OFFSET 0x14U
|
|
+#define GPIO_BSRR_OFFSET 0x18U
|
|
+#define GPIO_AFRL_OFFSET 0x20U
|
|
+#define GPIO_AFRH_OFFSET 0x24U
|
|
+#define GPIO_SECR_OFFSET 0x30U
|
|
+
|
|
+#define GPIO_ALT_LOWER_LIMIT 0x08U
|
|
+
|
|
+#define GPIO_PIN_0 0x00U
|
|
+#define GPIO_PIN_1 0x01U
|
|
+#define GPIO_PIN_2 0x02U
|
|
+#define GPIO_PIN_3 0x03U
|
|
+#define GPIO_PIN_4 0x04U
|
|
+#define GPIO_PIN_5 0x05U
|
|
+#define GPIO_PIN_6 0x06U
|
|
+#define GPIO_PIN_7 0x07U
|
|
+#define GPIO_PIN_8 0x08U
|
|
+#define GPIO_PIN_9 0x09U
|
|
+#define GPIO_PIN_10 0x0AU
|
|
+#define GPIO_PIN_11 0x0BU
|
|
+#define GPIO_PIN_12 0x0CU
|
|
+#define GPIO_PIN_13 0x0DU
|
|
+#define GPIO_PIN_14 0x0EU
|
|
+#define GPIO_PIN_15 0x0FU
|
|
+#define GPIO_PIN_MAX GPIO_PIN_15
|
|
+
|
|
+#define GPIO_ALTERNATE_0 0x00
|
|
+#define GPIO_ALTERNATE_1 0x01
|
|
+#define GPIO_ALTERNATE_2 0x02
|
|
+#define GPIO_ALTERNATE_3 0x03
|
|
+#define GPIO_ALTERNATE_4 0x04
|
|
+#define GPIO_ALTERNATE_5 0x05
|
|
+#define GPIO_ALTERNATE_6 0x06
|
|
+#define GPIO_ALTERNATE_7 0x07
|
|
+#define GPIO_ALTERNATE_8 0x08
|
|
+#define GPIO_ALTERNATE_9 0x09
|
|
+#define GPIO_ALTERNATE_10 0x0A
|
|
+#define GPIO_ALTERNATE_11 0x0B
|
|
+#define GPIO_ALTERNATE_12 0x0C
|
|
+#define GPIO_ALTERNATE_13 0x0D
|
|
+#define GPIO_ALTERNATE_14 0x0E
|
|
+#define GPIO_ALTERNATE_15 0x0F
|
|
+#define GPIO_ALTERNATE_MAX 0x0FU
|
|
+
|
|
+#define GPIO_MODE_INPUT 0x00
|
|
+#define GPIO_MODE_OUTPUT 0x01
|
|
+#define GPIO_MODE_ALTERNATE 0x02
|
|
+#define GPIO_MODE_ANALOG 0x03
|
|
+#define GPIO_MODE_MAX 0x03U
|
|
+
|
|
+#define GPIO_OPEN_DRAIN 0x10U
|
|
+
|
|
+#define GPIO_SPEED_LOW 0x00
|
|
+#define GPIO_SPEED_MEDIUM 0x01
|
|
+#define GPIO_SPEED_HIGH 0x02
|
|
+#define GPIO_SPEED_VERY_HIGH 0x03
|
|
+#define GPIO_SPEED_MAX 0x03U
|
|
+
|
|
+#define GPIO_NO_PULL 0x00
|
|
+#define GPIO_PULL_UP 0x01
|
|
+#define GPIO_PULL_DOWN 0x02
|
|
+#define GPIO_PULL_MAX 0x03U
|
|
+
|
|
+struct gpio_cfg {
|
|
+ uint16_t moder: 2;
|
|
+ uint16_t otyper: 1;
|
|
+ uint16_t ospeedr: 2;
|
|
+ uint16_t pupdr: 2;
|
|
+ uint16_t odr: 1;
|
|
+ uint16_t afr: 4;
|
|
+};
|
|
+
|
|
+struct stm32_pinctrl {
|
|
+ uint8_t bank;
|
|
+ uint8_t pin;
|
|
+ struct gpio_cfg active_cfg;
|
|
+ struct gpio_cfg standby_cfg;
|
|
+};
|
|
+
|
|
+void stm32_pinctrl_load_active_cfg(struct stm32_pinctrl *pinctrl, size_t cnt);
|
|
+void stm32_pinctrl_load_standby_cfg(struct stm32_pinctrl *pinctrl, size_t cnt);
|
|
+void stm32_pinctrl_store_standby_cfg(struct stm32_pinctrl *pinctrl, size_t cnt);
|
|
+
|
|
+int stm32_pinctrl_fdt_get_pinctrl(void *fdt, int node,
|
|
+ struct stm32_pinctrl *cfg, size_t count);
|
|
+
|
|
+void stm32_gpio_set_output_level(uint32_t bank, uint32_t pin, int high);
|
|
+
|
|
+void stm32_gpio_set_secure_cfg(uint32_t bank, uint32_t pin, bool secure);
|
|
+
|
|
+#endif /*__STM32_GPIO_H__*/
|
|
diff --git a/core/include/drivers/stm32_i2c.h b/core/include/drivers/stm32_i2c.h
|
|
new file mode 100644
|
|
index 0000000..3c6e326
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_i2c.h
|
|
@@ -0,0 +1,377 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_I2C_H
|
|
+#define __STM32_I2C_H
|
|
+
|
|
+#include <drivers/stm32_gpio.h>
|
|
+#include <stdbool.h>
|
|
+#include <stdint.h>
|
|
+#include <util.h>
|
|
+
|
|
+/*
|
|
+ * When STM32_I2C_MAYBE_NON_SECURE is defined, the I2C bus maybe
|
|
+ * non secure and yet used by the secure world. In such case,
|
|
+ * secure world may use the I2C only in atomic conditions
|
|
+ * where the non secure world cannot be executed.
|
|
+ */
|
|
+#define STM32_I2C_MAYBE_NON_SECURE
|
|
+
|
|
+/* Bit definition for I2C_CR1 register */
|
|
+#define I2C_CR1_PE BIT(0)
|
|
+#define I2C_CR1_TXIE BIT(1)
|
|
+#define I2C_CR1_RXIE BIT(2)
|
|
+#define I2C_CR1_ADDRIE BIT(3)
|
|
+#define I2C_CR1_NACKIE BIT(4)
|
|
+#define I2C_CR1_STOPIE BIT(5)
|
|
+#define I2C_CR1_TCIE BIT(6)
|
|
+#define I2C_CR1_ERRIE BIT(7)
|
|
+#define I2C_CR1_DNF GENMASK_32(11, 8)
|
|
+#define I2C_CR1_ANFOFF BIT(12)
|
|
+#define I2C_CR1_SWRST BIT(13)
|
|
+#define I2C_CR1_TXDMAEN BIT(14)
|
|
+#define I2C_CR1_RXDMAEN BIT(15)
|
|
+#define I2C_CR1_SBC BIT(16)
|
|
+#define I2C_CR1_NOSTRETCH BIT(17)
|
|
+#define I2C_CR1_WUPEN BIT(18)
|
|
+#define I2C_CR1_GCEN BIT(19)
|
|
+#define I2C_CR1_SMBHEN BIT(22)
|
|
+#define I2C_CR1_SMBDEN BIT(21)
|
|
+#define I2C_CR1_ALERTEN BIT(22)
|
|
+#define I2C_CR1_PECEN BIT(23)
|
|
+
|
|
+/* Bit definition for I2C_CR2 register */
|
|
+#define I2C_CR2_SADD GENMASK_32(9, 0)
|
|
+#define I2C_CR2_RD_WRN BIT(10)
|
|
+#define I2C_CR2_RD_WRN_OFFSET 10U
|
|
+#define I2C_CR2_ADD10 BIT(11)
|
|
+#define I2C_CR2_HEAD10R BIT(12)
|
|
+#define I2C_CR2_START BIT(13)
|
|
+#define I2C_CR2_STOP BIT(14)
|
|
+#define I2C_CR2_NACK BIT(15)
|
|
+#define I2C_CR2_NBYTES GENMASK_32(23, 16)
|
|
+#define I2C_CR2_NBYTES_OFFSET 16U
|
|
+#define I2C_CR2_RELOAD BIT(24)
|
|
+#define I2C_CR2_AUTOEND BIT(25)
|
|
+#define I2C_CR2_PECBYTE BIT(26)
|
|
+
|
|
+/* Bit definition for I2C_OAR1 register */
|
|
+#define I2C_OAR1_OA1 GENMASK_32(9, 0)
|
|
+#define I2C_OAR1_OA1MODE BIT(10)
|
|
+#define I2C_OAR1_OA1EN BIT(15)
|
|
+
|
|
+/* Bit definition for I2C_OAR2 register */
|
|
+#define I2C_OAR2_OA2 GENMASK_32(7, 1)
|
|
+#define I2C_OAR2_OA2MSK GENMASK_32(10, 8)
|
|
+#define I2C_OAR2_OA2NOMASK 0
|
|
+#define I2C_OAR2_OA2MASK01 BIT(8)
|
|
+#define I2C_OAR2_OA2MASK02 BIT(9)
|
|
+#define I2C_OAR2_OA2MASK03 GENMASK_32(9, 8)
|
|
+#define I2C_OAR2_OA2MASK04 BIT(10)
|
|
+#define I2C_OAR2_OA2MASK05 (BIT(8) | BIT(10))
|
|
+#define I2C_OAR2_OA2MASK06 (BIT(9) | BIT(10))
|
|
+#define I2C_OAR2_OA2MASK07 GENMASK_32(10, 8)
|
|
+#define I2C_OAR2_OA2EN BIT(15)
|
|
+
|
|
+/* Bit definition for I2C_TIMINGR register */
|
|
+#define I2C_TIMINGR_SCLL GENMASK_32(7, 0)
|
|
+#define I2C_TIMINGR_SCLH GENMASK_32(15, 8)
|
|
+#define I2C_TIMINGR_SDADEL GENMASK_32(19, 16)
|
|
+#define I2C_TIMINGR_SCLDEL GENMASK_32(23, 20)
|
|
+#define I2C_TIMINGR_PRESC GENMASK_32(31, 28)
|
|
+#define I2C_TIMINGR_SCLL_MAX (I2C_TIMINGR_SCLL + 1)
|
|
+#define I2C_TIMINGR_SCLH_MAX ((I2C_TIMINGR_SCLH >> 8) + 1)
|
|
+#define I2C_TIMINGR_SDADEL_MAX ((I2C_TIMINGR_SDADEL >> 16) + 1)
|
|
+#define I2C_TIMINGR_SCLDEL_MAX ((I2C_TIMINGR_SCLDEL >> 20) + 1)
|
|
+#define I2C_TIMINGR_PRESC_MAX ((I2C_TIMINGR_PRESC >> 28) + 1)
|
|
+#define I2C_SET_TIMINGR_SCLL(n) ((n) & \
|
|
+ (I2C_TIMINGR_SCLL_MAX - 1))
|
|
+#define I2C_SET_TIMINGR_SCLH(n) (((n) & \
|
|
+ (I2C_TIMINGR_SCLH_MAX - 1)) << 8)
|
|
+#define I2C_SET_TIMINGR_SDADEL(n) (((n) & \
|
|
+ (I2C_TIMINGR_SDADEL_MAX - 1)) << 16)
|
|
+#define I2C_SET_TIMINGR_SCLDEL(n) (((n) & \
|
|
+ (I2C_TIMINGR_SCLDEL_MAX - 1)) << 20)
|
|
+#define I2C_SET_TIMINGR_PRESC(n) (((n) & \
|
|
+ (I2C_TIMINGR_PRESC_MAX - 1)) << 28)
|
|
+
|
|
+/* Bit definition for I2C_TIMEOUTR register */
|
|
+#define I2C_TIMEOUTR_TIMEOUTA GENMASK_32(11, 0)
|
|
+#define I2C_TIMEOUTR_TIDLE BIT(12)
|
|
+#define I2C_TIMEOUTR_TIMOUTEN BIT(15)
|
|
+#define I2C_TIMEOUTR_TIMEOUTB GENMASK_32(27, 16)
|
|
+#define I2C_TIMEOUTR_TEXTEN BIT(31)
|
|
+
|
|
+/* Bit definition for I2C_ISR register */
|
|
+#define I2C_ISR_TXE BIT(0)
|
|
+#define I2C_ISR_TXIS BIT(1)
|
|
+#define I2C_ISR_RXNE BIT(2)
|
|
+#define I2C_ISR_ADDR BIT(3)
|
|
+#define I2C_ISR_NACKF BIT(4)
|
|
+#define I2C_ISR_STOPF BIT(5)
|
|
+#define I2C_ISR_TC BIT(6)
|
|
+#define I2C_ISR_TCR BIT(7)
|
|
+#define I2C_ISR_BERR BIT(8)
|
|
+#define I2C_ISR_ARLO BIT(9)
|
|
+#define I2C_ISR_OVR BIT(10)
|
|
+#define I2C_ISR_PECERR BIT(11)
|
|
+#define I2C_ISR_TIMEOUT BIT(12)
|
|
+#define I2C_ISR_ALERT BIT(13)
|
|
+#define I2C_ISR_BUSY BIT(15)
|
|
+#define I2C_ISR_DIR BIT(16)
|
|
+#define I2C_ISR_ADDCODE GENMASK_32(23, 17)
|
|
+
|
|
+/* Bit definition for I2C_ICR register */
|
|
+#define I2C_ICR_ADDRCF BIT(3)
|
|
+#define I2C_ICR_NACKCF BIT(4)
|
|
+#define I2C_ICR_STOPCF BIT(5)
|
|
+#define I2C_ICR_BERRCF BIT(8)
|
|
+#define I2C_ICR_ARLOCF BIT(9)
|
|
+#define I2C_ICR_OVRCF BIT(10)
|
|
+#define I2C_ICR_PECCF BIT(11)
|
|
+#define I2C_ICR_TIMOUTCF BIT(12)
|
|
+#define I2C_ICR_ALERTCF BIT(13)
|
|
+
|
|
+enum i2c_speed_e {
|
|
+ I2C_SPEED_STANDARD, /* 100 kHz */
|
|
+ I2C_SPEED_FAST, /* 400 kHz */
|
|
+ I2C_SPEED_FAST_PLUS, /* 1 MHz */
|
|
+};
|
|
+
|
|
+#define STANDARD_RATE 100000
|
|
+#define FAST_RATE 400000
|
|
+#define FAST_PLUS_RATE 1000000
|
|
+
|
|
+struct stm32_i2c_init_s {
|
|
+ uint32_t own_address1; /*
|
|
+ * Specifies the first device own
|
|
+ * address. This parameter can be a
|
|
+ * 7-bit or 10-bit address.
|
|
+ */
|
|
+
|
|
+ uint32_t addressing_mode; /*
|
|
+ * Specifies if 7-bit or 10-bit
|
|
+ * addressing mode is selected.
|
|
+ * This parameter can be a value of
|
|
+ * @ref I2C_ADDRESSING_MODE.
|
|
+ */
|
|
+
|
|
+ uint32_t dual_address_mode; /*
|
|
+ * Specifies if dual addressing mode is
|
|
+ * selected.
|
|
+ * This parameter can be a value of @ref
|
|
+ * I2C_DUAL_ADDRESSING_MODE.
|
|
+ */
|
|
+
|
|
+ uint32_t own_address2; /*
|
|
+ * Specifies the second device own
|
|
+ * address if dual addressing mode is
|
|
+ * selected. This parameter can be a
|
|
+ * 7-bit address.
|
|
+ */
|
|
+
|
|
+ uint32_t own_address2_masks; /*
|
|
+ * Specifies the acknowledge mask
|
|
+ * address second device own address
|
|
+ * if dual addressing mode is selected
|
|
+ * This parameter can be a value of @ref
|
|
+ * I2C_OWN_ADDRESS2_MASKS.
|
|
+ */
|
|
+
|
|
+ uint32_t general_call_mode; /*
|
|
+ * Specifies if general call mode is
|
|
+ * selected.
|
|
+ * This parameter can be a value of @ref
|
|
+ * I2C_GENERAL_CALL_ADDRESSING_MODE.
|
|
+ */
|
|
+
|
|
+ uint32_t no_stretch_mode; /*
|
|
+ * Specifies if nostretch mode is
|
|
+ * selected.
|
|
+ * This parameter can be a value of @ref
|
|
+ * I2C_NOSTRETCH_MODE.
|
|
+ */
|
|
+
|
|
+ uint32_t rise_time; /*
|
|
+ * Specifies the SCL clock pin rising
|
|
+ * time in nanoseconds.
|
|
+ */
|
|
+
|
|
+ uint32_t fall_time; /*
|
|
+ * Specifies the SCL clock pin falling
|
|
+ * time in nanoseconds.
|
|
+ */
|
|
+
|
|
+ enum i2c_speed_e speed_mode; /*
|
|
+ * Specifies the I2C clock source
|
|
+ * frequency mode.
|
|
+ * This parameter can be a value of @ref
|
|
+ * i2c_speed_mode_e.
|
|
+ */
|
|
+
|
|
+ int analog_filter; /*
|
|
+ * Specifies if the I2C analog noise
|
|
+ * filter is selected.
|
|
+ * This parameter can be 0 (filter
|
|
+ * off), all other values mean filter
|
|
+ * on.
|
|
+ */
|
|
+
|
|
+ uint8_t digital_filter_coef; /*
|
|
+ * Specifies the I2C digital noise
|
|
+ * filter coefficient.
|
|
+ * This parameter can be a value
|
|
+ * between 0 and
|
|
+ * STM32_I2C_DIGITAL_FILTER_MAX.
|
|
+ */
|
|
+};
|
|
+
|
|
+enum i2c_state_e {
|
|
+ I2C_STATE_RESET = 0x00U, /* Not yet initialized */
|
|
+ I2C_STATE_READY = 0x20U, /* Ready for use */
|
|
+ I2C_STATE_BUSY = 0x24U, /* Internal process ongoing */
|
|
+ I2C_STATE_BUSY_TX = 0x21U, /* Data Transmission ongoing */
|
|
+ I2C_STATE_BUSY_RX = 0x22U, /* Data Reception ongoing */
|
|
+ I2C_STATE_SUSPENDED = 0xF0U, /* Bus is supended */
|
|
+};
|
|
+
|
|
+enum i2c_mode_e {
|
|
+ I2C_MODE_NONE = 0x00U, /* No active communication */
|
|
+ I2C_MODE_MASTER = 0x10U, /* Communication in Master Mode */
|
|
+ I2C_MODE_SLAVE = 0x20U, /* Communication in Slave Mode */
|
|
+ I2C_MODE_MEM = 0x40U /* Communication in Memory Mode */
|
|
+
|
|
+};
|
|
+
|
|
+#define I2C_ERROR_NONE 0x00000000U /* No error */
|
|
+#define I2C_ERROR_BERR 0x00000001U /* BERR error */
|
|
+#define I2C_ERROR_ARLO 0x00000002U /* ARLO error */
|
|
+#define I2C_ERROR_AF 0x00000004U /* ACKF error */
|
|
+#define I2C_ERROR_OVR 0x00000008U /* OVR error */
|
|
+#define I2C_ERROR_DMA 0x00000010U /* DMA transfer error */
|
|
+#define I2C_ERROR_TIMEOUT 0x00000020U /* Timeout error */
|
|
+#define I2C_ERROR_SIZE 0x00000040U /* Size Management error */
|
|
+
|
|
+#ifdef STM32_I2C_MAYBE_NON_SECURE
|
|
+struct i2c_cfg {
|
|
+ uint32_t timingr;
|
|
+ uint32_t oar1;
|
|
+ uint32_t oar2;
|
|
+ uint32_t cr1;
|
|
+ uint32_t cr2;
|
|
+};
|
|
+#endif
|
|
+
|
|
+struct i2c_handle_s {
|
|
+ uintptr_t pbase; /* Registers phys base address */
|
|
+ uintptr_t vbase; /* Registers virt base address */
|
|
+ unsigned int dt_status; /* DT nsec/sec status */
|
|
+ unsigned int clock; /* Clock reference */
|
|
+ uint8_t lock; /* Locking object (FIXME: really usefull?) */
|
|
+ enum i2c_state_e i2c_state; /* Communication state */
|
|
+ enum i2c_mode_e i2c_mode; /* Communication mode */
|
|
+ uint32_t i2c_err; /* Error code */
|
|
+ struct stm32_pinctrl *pinctrl; /* PINCTRLs configuration for the I2C PINs */
|
|
+ size_t pinctrl_count; /* Number of PINCTRLs elements */
|
|
+ struct i2c_cfg sec_cfg; /* Context for secure usage */
|
|
+#ifdef STM32_I2C_MAYBE_NON_SECURE
|
|
+ struct i2c_cfg alt_cfg; /* Alternate usage context */
|
|
+#endif
|
|
+};
|
|
+
|
|
+#define I2C_ADDRESSINGMODE_7BIT 0x00000001U
|
|
+#define I2C_ADDRESSINGMODE_10BIT 0x00000002U
|
|
+
|
|
+#define I2C_DUALADDRESS_DISABLE 0x00000000U
|
|
+#define I2C_DUALADDRESS_ENABLE I2C_OAR2_OA2EN
|
|
+
|
|
+#define I2C_GENERALCALL_DISABLE 0x00000000U
|
|
+#define I2C_GENERALCALL_ENABLE I2C_CR1_GCEN
|
|
+
|
|
+#define I2C_NOSTRETCH_DISABLE 0x00000000U
|
|
+#define I2C_NOSTRETCH_ENABLE I2C_CR1_NOSTRETCH
|
|
+
|
|
+#define I2C_MEMADD_SIZE_8BIT 0x00000001U
|
|
+#define I2C_MEMADD_SIZE_16BIT 0x00000002U
|
|
+
|
|
+#define I2C_RELOAD_MODE I2C_CR2_RELOAD
|
|
+#define I2C_AUTOEND_MODE I2C_CR2_AUTOEND
|
|
+#define I2C_SOFTEND_MODE 0x00000000U
|
|
+
|
|
+#define I2C_NO_STARTSTOP 0x00000000U
|
|
+#define I2C_GENERATE_STOP (BIT(31) | I2C_CR2_STOP)
|
|
+#define I2C_GENERATE_START_READ (BIT(31) | I2C_CR2_START | \
|
|
+ I2C_CR2_RD_WRN)
|
|
+#define I2C_GENERATE_START_WRITE (BIT(31) | I2C_CR2_START)
|
|
+
|
|
+#define I2C_FLAG_TXE I2C_ISR_TXE
|
|
+#define I2C_FLAG_TXIS I2C_ISR_TXIS
|
|
+#define I2C_FLAG_RXNE I2C_ISR_RXNE
|
|
+#define I2C_FLAG_ADDR I2C_ISR_ADDR
|
|
+#define I2C_FLAG_AF I2C_ISR_NACKF
|
|
+#define I2C_FLAG_STOPF I2C_ISR_STOPF
|
|
+#define I2C_FLAG_TC I2C_ISR_TC
|
|
+#define I2C_FLAG_TCR I2C_ISR_TCR
|
|
+#define I2C_FLAG_BERR I2C_ISR_BERR
|
|
+#define I2C_FLAG_ARLO I2C_ISR_ARLO
|
|
+#define I2C_FLAG_OVR I2C_ISR_OVR
|
|
+#define I2C_FLAG_PECERR I2C_ISR_PECERR
|
|
+#define I2C_FLAG_TIMEOUT I2C_ISR_TIMEOUT
|
|
+#define I2C_FLAG_ALERT I2C_ISR_ALERT
|
|
+#define I2C_FLAG_BUSY I2C_ISR_BUSY
|
|
+#define I2C_FLAG_DIR I2C_ISR_DIR
|
|
+
|
|
+#define I2C_RESET_CR2 (I2C_CR2_SADD | I2C_CR2_HEAD10R | \
|
|
+ I2C_CR2_NBYTES | I2C_CR2_RELOAD | \
|
|
+ I2C_CR2_RD_WRN)
|
|
+
|
|
+#define I2C_TIMEOUT_BUSY_MS 25U
|
|
+
|
|
+#define I2C_ANALOGFILTER_ENABLE 0x00000000U
|
|
+#define I2C_ANALOGFILTER_DISABLE I2C_CR1_ANFOFF
|
|
+
|
|
+/* STM32 specific defines */
|
|
+#define STM32_I2C_RISE_TIME_DEFAULT 25 /* ns */
|
|
+#define STM32_I2C_FALL_TIME_DEFAULT 10 /* ns */
|
|
+#define STM32_I2C_SPEED_DEFAULT I2C_SPEED_STANDARD
|
|
+#define STM32_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
|
|
+#define STM32_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
|
|
+#define STM32_I2C_DIGITAL_FILTER_MAX 16
|
|
+
|
|
+int stm32_i2c_get_setup_from_fdt(void *fdt, int node,
|
|
+ struct stm32_i2c_init_s *init,
|
|
+ struct stm32_pinctrl **pinctrl,
|
|
+ size_t *pinctrl_count);
|
|
+
|
|
+int stm32_i2c_init(struct i2c_handle_s *hi2c,
|
|
+ struct stm32_i2c_init_s *init_data);
|
|
+
|
|
+int stm32_i2c_mem_write(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint8_t *p_data, uint16_t size, uint32_t timeout_ms);
|
|
+
|
|
+int stm32_i2c_mem_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint16_t mem_addr, uint16_t mem_add_size,
|
|
+ uint8_t *p_data, uint16_t size, uint32_t timeout_ms);
|
|
+
|
|
+int stm32_i2c_master_transmit(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint8_t *p_data, uint16_t size,
|
|
+ uint32_t timeout_ms);
|
|
+
|
|
+int stm32_i2c_master_receive(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint8_t *p_data, uint16_t size,
|
|
+ uint32_t timeout_ms);
|
|
+
|
|
+bool stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ uint32_t trials, uint32_t timeout_ms);
|
|
+
|
|
+int stm32_i2c_read_write_membyte(struct i2c_handle_s *hi2c, uint16_t dev_addr,
|
|
+ unsigned int mem_addr, uint8_t *p_data,
|
|
+ bool write);
|
|
+
|
|
+void stm32_i2c_suspend(struct i2c_handle_s *hi2c);
|
|
+void stm32_i2c_resume(struct i2c_handle_s *hi2c);
|
|
+
|
|
+#endif /* __STM32_I2C_H */
|
|
diff --git a/core/include/drivers/stm32_iwdg.h b/core/include/drivers/stm32_iwdg.h
|
|
new file mode 100644
|
|
index 0000000..b649dee
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_iwdg.h
|
|
@@ -0,0 +1,17 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_IWDG_H__
|
|
+#define __STM32_IWDG_H__
|
|
+
|
|
+#include <stdint.h>
|
|
+
|
|
+#define IWDG_HW_ENABLED BIT(0)
|
|
+#define IWDG_ENABLE_ON_STOP BIT(1)
|
|
+#define IWDG_ENABLE_ON_STANDBY BIT(2)
|
|
+
|
|
+void stm32_iwdg_refresh(uint32_t instance);
|
|
+
|
|
+#endif /*__STM32_IWDG_H__*/
|
|
diff --git a/core/include/drivers/stm32_rng.h b/core/include/drivers/stm32_rng.h
|
|
new file mode 100644
|
|
index 0000000..1ba098f
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_rng.h
|
|
@@ -0,0 +1,14 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STM32_RNG_H__
|
|
+#define __STM32_RNG_H__
|
|
+
|
|
+#include <stdint.h>
|
|
+#include <stddef.h>
|
|
+
|
|
+int stm32_rng_read(uint8_t *out, size_t size);
|
|
+
|
|
+#endif /*__STM32_RNG__*/
|
|
diff --git a/core/include/drivers/stm32_rtc.h b/core/include/drivers/stm32_rtc.h
|
|
new file mode 100644
|
|
index 0000000..39a262c
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_rtc.h
|
|
@@ -0,0 +1,73 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
|
|
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
|
+ */
|
|
+
|
|
+#ifndef __PLAT_RTC_H__
|
|
+#define __PLAT_RTC_H__
|
|
+
|
|
+#include <stdbool.h>
|
|
+
|
|
+#define RTC_TR 0x00U
|
|
+#define RTC_DR 0x04U
|
|
+#define RTC_SSR 0x08U
|
|
+#define RTC_ICSR 0x0CU
|
|
+#define RTC_PRER 0x10U
|
|
+#define RTC_WUTR 0x14U
|
|
+#define RTC_CR 0x18U
|
|
+#define RTC_SMCR 0x20U
|
|
+#define RTC_WPR 0x24U
|
|
+#define RTC_CALR 0x28U
|
|
+#define RTC_SHIFTR 0x2CU
|
|
+#define RTC_TSTR 0x30U
|
|
+#define RTC_TSDR 0x34U
|
|
+#define RTC_TSSSR 0x38U
|
|
+#define RTC_ALRMAR 0x40U
|
|
+#define RTC_ALRMASSR 0x44U
|
|
+#define RTC_ALRMBR 0x48U
|
|
+#define RTC_ALRMBSSR 0x4CU
|
|
+#define RTC_SR 0x50U
|
|
+#define RTC_SCR 0x5CU
|
|
+#define RTC_OR 0x60U
|
|
+
|
|
+struct stm32_rtc_calendar {
|
|
+ uint32_t ssr;
|
|
+ uint32_t tr;
|
|
+ uint32_t dr;
|
|
+};
|
|
+
|
|
+enum months {
|
|
+ JANUARY = 1,
|
|
+ FEBRUARY,
|
|
+ MARCH,
|
|
+ APRIL,
|
|
+ MAY,
|
|
+ JUNE,
|
|
+ JULY,
|
|
+ AUGUST,
|
|
+ SEPTEMBER,
|
|
+ OCTOBER,
|
|
+ NOVEMBER,
|
|
+ DECEMBER,
|
|
+ NB_MONTHS = 12
|
|
+};
|
|
+
|
|
+struct stm32_rtc_time {
|
|
+ uint32_t hour;
|
|
+ uint32_t min;
|
|
+ uint32_t sec;
|
|
+ uint32_t wday;
|
|
+ uint32_t day;
|
|
+ enum months month;
|
|
+ uint32_t year;
|
|
+};
|
|
+
|
|
+void stm32_rtc_get_calendar(struct stm32_rtc_calendar *calendar);
|
|
+unsigned long long stm32_rtc_diff_calendar(struct stm32_rtc_calendar *current,
|
|
+ struct stm32_rtc_calendar *ref);
|
|
+void stm32_rtc_set_tamper_timestamp(void);
|
|
+bool stm32_rtc_is_timestamp_enable(void);
|
|
+void stm32_rtc_get_timestamp(struct stm32_rtc_time *tamp_ts);
|
|
+
|
|
+#endif /* __PLAT_RTC_H__ */
|
|
diff --git a/core/include/drivers/stm32_timer.h b/core/include/drivers/stm32_timer.h
|
|
new file mode 100644
|
|
index 0000000..b7e6751
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stm32_timer.h
|
|
@@ -0,0 +1,25 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef STM32_TIMER_H
|
|
+#define STM32_TIMER_H
|
|
+
|
|
+enum timer_cal {
|
|
+ HSI_CAL = 0,
|
|
+ CSI_CAL
|
|
+};
|
|
+
|
|
+unsigned long stm32_timer_hsi_freq(void);
|
|
+unsigned long stm32_timer_csi_freq(void);
|
|
+
|
|
+/*
|
|
+ * Get the timer frequence callback function for a target clock calibration
|
|
+ * @timer_freq_cb - Output callback function
|
|
+ * @type - Target clock calibration ID
|
|
+ */
|
|
+void stm32_timer_freq_func(unsigned long (**timer_freq_cb)(void),
|
|
+ enum timer_cal type);
|
|
+
|
|
+#endif /* STM32_TIMER_H */
|
|
diff --git a/core/include/drivers/stm32_uart.h b/core/include/drivers/stm32_uart.h
|
|
index 58c7077..6ddb8c4 100644
|
|
--- a/core/include/drivers/stm32_uart.h
|
|
+++ b/core/include/drivers/stm32_uart.h
|
|
@@ -7,12 +7,18 @@
|
|
#define __STM32_UART_H__
|
|
|
|
#include <drivers/serial.h>
|
|
+#include <drivers/stm32_gpio.h>
|
|
|
|
-struct console_pdata {
|
|
+struct stm32_uart_pdata {
|
|
struct io_pa_va base;
|
|
struct serial_chip chip;
|
|
+ bool secure;
|
|
+ struct stm32_pinctrl *pinctrl;
|
|
+ size_t pinctrl_count;
|
|
+ unsigned long clock;
|
|
};
|
|
|
|
-void stm32_uart_init(struct console_pdata *pd, vaddr_t base);
|
|
+void stm32_uart_init(struct stm32_uart_pdata *pd, vaddr_t base);
|
|
+struct stm32_uart_pdata *probe_uart_from_dt_node(void *fdt, int node);
|
|
|
|
#endif /*__STM32_UART_H__*/
|
|
diff --git a/core/include/drivers/stpmic1.h b/core/include/drivers/stpmic1.h
|
|
new file mode 100644
|
|
index 0000000..3073662
|
|
--- /dev/null
|
|
+++ b/core/include/drivers/stpmic1.h
|
|
@@ -0,0 +1,228 @@
|
|
+/* SPDX-License-Identifier: BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef __STPMIC1_H__
|
|
+#define __STPMIC1_H__
|
|
+
|
|
+#include <drivers/stm32_i2c.h>
|
|
+#include <util.h>
|
|
+
|
|
+#define TURN_ON_REG 0x1U
|
|
+#define TURN_OFF_REG 0x2U
|
|
+#define ICC_LDO_TURN_OFF_REG 0x3U
|
|
+#define ICC_BUCK_TURN_OFF_REG 0x4U
|
|
+#define RESET_STATUS_REG 0x5U
|
|
+#define VERSION_STATUS_REG 0x6U
|
|
+#define MAIN_CONTROL_REG 0x10U
|
|
+#define PADS_PULL_REG 0x11U
|
|
+#define BUCK_PULL_DOWN_REG 0x12U
|
|
+#define LDO14_PULL_DOWN_REG 0x13U
|
|
+#define LDO56_PULL_DOWN_REG 0x14U
|
|
+#define VIN_CONTROL_REG 0x15U
|
|
+#define PONKEY_TIMER_REG 0x16U
|
|
+#define MASK_RANK_BUCK_REG 0x17U
|
|
+#define MASK_RESET_BUCK_REG 0x18U
|
|
+#define MASK_RANK_LDO_REG 0x19U
|
|
+#define MASK_RESET_LDO_REG 0x1AU
|
|
+#define WATCHDOG_CONTROL_REG 0x1BU
|
|
+#define WATCHDOG_TIMER_REG 0x1CU
|
|
+#define BUCK_ICC_TURNOFF_REG 0x1DU
|
|
+#define LDO_ICC_TURNOFF_REG 0x1EU
|
|
+#define BUCK_APM_CONTROL_REG 0x1FU
|
|
+#define BUCK1_CONTROL_REG 0x20U
|
|
+#define BUCK2_CONTROL_REG 0x21U
|
|
+#define BUCK3_CONTROL_REG 0x22U
|
|
+#define BUCK4_CONTROL_REG 0x23U
|
|
+#define VREF_DDR_CONTROL_REG 0x24U
|
|
+#define LDO1_CONTROL_REG 0x25U
|
|
+#define LDO2_CONTROL_REG 0x26U
|
|
+#define LDO3_CONTROL_REG 0x27U
|
|
+#define LDO4_CONTROL_REG 0x28U
|
|
+#define LDO5_CONTROL_REG 0x29U
|
|
+#define LDO6_CONTROL_REG 0x2AU
|
|
+#define BUCK1_PWRCTRL_REG 0x30U
|
|
+#define BUCK2_PWRCTRL_REG 0x31U
|
|
+#define BUCK3_PWRCTRL_REG 0x32U
|
|
+#define BUCK4_PWRCTRL_REG 0x33U
|
|
+#define VREF_DDR_PWRCTRL_REG 0x34U
|
|
+#define LDO1_PWRCTRL_REG 0x35U
|
|
+#define LDO2_PWRCTRL_REG 0x36U
|
|
+#define LDO3_PWRCTRL_REG 0x37U
|
|
+#define LDO4_PWRCTRL_REG 0x38U
|
|
+#define LDO5_PWRCTRL_REG 0x39U
|
|
+#define LDO6_PWRCTRL_REG 0x3AU
|
|
+#define FREQUENCY_SPREADING_REG 0x3BU
|
|
+#define USB_CONTROL_REG 0x40U
|
|
+#define ITLATCH1_REG 0x50U
|
|
+#define ITLATCH2_REG 0x51U
|
|
+#define ITLATCH3_REG 0x52U
|
|
+#define ITLATCH4_REG 0x53U
|
|
+#define ITSETLATCH1_REG 0x60U
|
|
+#define ITSETLATCH2_REG 0x61U
|
|
+#define ITSETLATCH3_REG 0x62U
|
|
+#define ITSETLATCH4_REG 0x63U
|
|
+#define ITCLEARLATCH1_REG 0x70U
|
|
+#define ITCLEARLATCH2_REG 0x71U
|
|
+#define ITCLEARLATCH3_REG 0x72U
|
|
+#define ITCLEARLATCH4_REG 0x73U
|
|
+#define ITMASK1_REG 0x80U
|
|
+#define ITMASK2_REG 0x81U
|
|
+#define ITMASK3_REG 0x82U
|
|
+#define ITMASK4_REG 0x83U
|
|
+#define ITSETMASK1_REG 0x90U
|
|
+#define ITSETMASK2_REG 0x91U
|
|
+#define ITSETMASK3_REG 0x92U
|
|
+#define ITSETMASK4_REG 0x93U
|
|
+#define ITCLEARMASK1_REG 0xA0U
|
|
+#define ITCLEARMASK2_REG 0xA1U
|
|
+#define ITCLEARMASK3_REG 0xA2U
|
|
+#define ITCLEARMASK4_REG 0xA3U
|
|
+#define ITSOURCE1_REG 0xB0U
|
|
+#define ITSOURCE2_REG 0xB1U
|
|
+#define ITSOURCE3_REG 0xB2U
|
|
+#define ITSOURCE4_REG 0xB3U
|
|
+
|
|
+/* Registers masks */
|
|
+#define LDO_VOLTAGE_MASK 0x7CU
|
|
+#define BUCK_VOLTAGE_MASK 0xFCU
|
|
+#define LDO_BUCK_VOLTAGE_SHIFT 2
|
|
+#define LDO_BUCK_ENABLE_MASK 0x01U
|
|
+#define LDO_BUCK_HPLP_ENABLE_MASK 0x02U
|
|
+#define LDO_BUCK_HPLP_SHIFT 1
|
|
+#define LDO_BUCK_RANK_MASK 0x01U
|
|
+#define LDO_BUCK_RESET_MASK 0x01U
|
|
+#define LDO_BUCK_PULL_DOWN_MASK 0x03U
|
|
+
|
|
+/* Pull down register */
|
|
+#define BUCK1_PULL_DOWN_SHIFT 0
|
|
+#define BUCK2_PULL_DOWN_SHIFT 2
|
|
+#define BUCK3_PULL_DOWN_SHIFT 4
|
|
+#define BUCK4_PULL_DOWN_SHIFT 6
|
|
+#define VREF_DDR_PULL_DOWN_SHIFT 4
|
|
+
|
|
+/* Buck Mask reset register */
|
|
+#define BUCK1_MASK_RESET_SHIFT 0
|
|
+#define BUCK2_MASK_RESET_SHIFT 1
|
|
+#define BUCK3_MASK_RESET_SHIFT 2
|
|
+#define BUCK4_MASK_RESET_SHIFT 3
|
|
+
|
|
+/* LDO Mask reset register */
|
|
+#define LDO1_MASK_RESET_SHIFT 0
|
|
+#define LDO2_MASK_RESET_SHIFT 1
|
|
+#define LDO3_MASK_RESET_SHIFT 2
|
|
+#define LDO4_MASK_RESET_SHIFT 3
|
|
+#define LDO5_MASK_RESET_SHIFT 4
|
|
+#define LDO6_MASK_RESET_SHIFT 5
|
|
+#define VREF_DDR_MASK_RESET_SHIFT 6
|
|
+
|
|
+/* Main PMIC Control Register (MAIN_CONTROL_REG) */
|
|
+#define ICC_EVENT_ENABLED BIT(4)
|
|
+#define PWRCTRL_POLARITY_HIGH BIT(3)
|
|
+#define PWRCTRL_PIN_VALID BIT(2)
|
|
+#define RESTART_REQUEST_ENABLED BIT(1)
|
|
+#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0)
|
|
+
|
|
+/* Main PMIC PADS Control Register (PADS_PULL_REG) */
|
|
+#define WAKEUP_DETECTOR_DISABLED BIT(4)
|
|
+#define PWRCTRL_PD_ACTIVE BIT(3)
|
|
+#define PWRCTRL_PU_ACTIVE BIT(2)
|
|
+#define WAKEUP_PD_ACTIVE BIT(1)
|
|
+#define PONKEY_PU_ACTIVE BIT(0)
|
|
+
|
|
+/* Main PMIC VINLOW Control Register (VIN_CONTROL_REGC DMSC) */
|
|
+#define SWIN_DETECTOR_ENABLED BIT(7)
|
|
+#define SWOUT_DETECTOR_ENABLED BIT(6)
|
|
+#define VINLOW_HYST_MASK 0x3
|
|
+#define VINLOW_HYST_SHIFT 4
|
|
+#define VINLOW_THRESHOLD_MASK 0x7
|
|
+#define VINLOW_THRESHOLD_SHIFT 1
|
|
+#define VINLOW_ENABLED 0x01
|
|
+#define VINLOW_CTRL_REG_MASK 0xFF
|
|
+
|
|
+/* USB Control Register */
|
|
+#define BOOST_OVP_DISABLED BIT(7)
|
|
+#define VBUS_OTG_DETECTION_DISABLED BIT(6)
|
|
+#define OCP_LIMIT_HIGH BIT(3)
|
|
+#define SWIN_SWOUT_ENABLED BIT(2)
|
|
+#define USBSW_OTG_SWITCH_ENABLED BIT(1)
|
|
+
|
|
+int stpmic1_powerctrl_on(void);
|
|
+int stpmic1_switch_off(void);
|
|
+
|
|
+int stpmic1_register_read(uint8_t register_id, uint8_t *value);
|
|
+int stpmic1_register_write(uint8_t register_id, uint8_t value);
|
|
+int stpmic1_register_update(uint8_t register_id, uint8_t value, uint8_t mask);
|
|
+
|
|
+int stpmic1_regulator_enable(const char *name);
|
|
+int stpmic1_regulator_disable(const char *name);
|
|
+uint8_t stpmic1_is_regulator_enabled(const char *name);
|
|
+
|
|
+int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts);
|
|
+int stpmic1_regulator_voltage_get(const char *name);
|
|
+int stpmic1_regulator_mask_reset_set(const char *name);
|
|
+
|
|
+int stpmic1_lp_copy_reg(const char *name);
|
|
+int stpmic1_lp_reg_on_off(const char *name, uint8_t enable);
|
|
+int stpmic1_lp_set_mode(const char *name, uint8_t hplp);
|
|
+int stpmic1_lp_set_voltage(const char *name, uint16_t millivolts);
|
|
+
|
|
+/*
|
|
+ * The STPMIC1 is accessed during low power sequence in unpaged
|
|
+ * execution context. To prevent adding an unpaged constraint on
|
|
+ * STPMIC1 regulator definitions, conversion tables and device tree
|
|
+ * content, the regulators configurations are read from device tree
|
|
+ * at boot time and saved in memory for being applied at runtime
|
|
+ * without needing pager support.
|
|
+ *
|
|
+ * There are 2 types of regulator configuration loaded during such
|
|
+ * low power and unpaged sequences: boot-on (bo) configuration and
|
|
+ * low power (lp) configuration.
|
|
+ */
|
|
+struct stpmic1_bo_cfg {
|
|
+ uint8_t ctrl_reg;
|
|
+ uint8_t value;
|
|
+ uint8_t mask;
|
|
+ uint8_t pd_reg;
|
|
+ uint8_t pd_value;
|
|
+ uint8_t pd_mask;
|
|
+ uint8_t mrst_reg;
|
|
+ uint8_t mrst_value;
|
|
+ uint8_t mrst_mask;
|
|
+};
|
|
+
|
|
+struct stpmic1_lp_cfg {
|
|
+ uint8_t ctrl_reg;
|
|
+ uint8_t lp_reg;
|
|
+ uint8_t value;
|
|
+ uint8_t mask;
|
|
+};
|
|
+
|
|
+int stpmic1_bo_enable_unpg(struct stpmic1_bo_cfg *cfg);
|
|
+int stpmic1_bo_voltage_cfg(const char *name, uint16_t millivolts,
|
|
+ struct stpmic1_bo_cfg *cfg);
|
|
+int stpmic1_bo_voltage_unpg(struct stpmic1_bo_cfg *cfg);
|
|
+
|
|
+int stpmic1_bo_pull_down_cfg(const char *name,
|
|
+ struct stpmic1_bo_cfg *cfg);
|
|
+int stpmic1_bo_pull_down_unpg(struct stpmic1_bo_cfg *cfg);
|
|
+
|
|
+int stpmic1_bo_mask_reset_cfg(const char *name, struct stpmic1_bo_cfg *cfg);
|
|
+int stpmic1_bo_mask_reset_unpg(struct stpmic1_bo_cfg *cfg);
|
|
+
|
|
+int stpmic1_lp_cfg(const char *name, struct stpmic1_lp_cfg *cfg);
|
|
+int stpmic1_lp_load_unpg(struct stpmic1_lp_cfg *cfg);
|
|
+int stpmic1_lp_on_off_unpg(struct stpmic1_lp_cfg *cfg, int enable);
|
|
+int stpmic1_lp_mode_unpg(struct stpmic1_lp_cfg *cfg,
|
|
+ unsigned int mode);
|
|
+int stpmic1_lp_voltage_cfg(const char *name, uint16_t millivolts,
|
|
+ struct stpmic1_lp_cfg *cfg);
|
|
+int stpmic1_lp_voltage_unpg(struct stpmic1_lp_cfg *cfg);
|
|
+
|
|
+void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr);
|
|
+
|
|
+int stpmic1_get_version(unsigned long *version);
|
|
+void stpmic1_dump_regulators(void);
|
|
+
|
|
+#endif /*__STPMIC1_H__*/
|
|
diff --git a/core/include/dt-bindings/clock/stm32mp1-clks.h b/core/include/dt-bindings/clock/stm32mp1-clks.h
|
|
new file mode 100644
|
|
index 0000000..f5254cf
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/clock/stm32mp1-clks.h
|
|
@@ -0,0 +1,252 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_STM32MP1_CLKS_H_
|
|
+#define _DT_BINDINGS_STM32MP1_CLKS_H_
|
|
+
|
|
+/* OSCILLATOR clocks */
|
|
+#define CK_HSE 0
|
|
+#define CK_CSI 1
|
|
+#define CK_LSI 2
|
|
+#define CK_LSE 3
|
|
+#define CK_HSI 4
|
|
+#define CK_HSE_DIV2 5
|
|
+
|
|
+/* Bus clocks */
|
|
+#define TIM2 6
|
|
+#define TIM3 7
|
|
+#define TIM4 8
|
|
+#define TIM5 9
|
|
+#define TIM6 10
|
|
+#define TIM7 11
|
|
+#define TIM12 12
|
|
+#define TIM13 13
|
|
+#define TIM14 14
|
|
+#define LPTIM1 15
|
|
+#define SPI2 16
|
|
+#define SPI3 17
|
|
+#define USART2 18
|
|
+#define USART3 19
|
|
+#define UART4 20
|
|
+#define UART5 21
|
|
+#define UART7 22
|
|
+#define UART8 23
|
|
+#define I2C1 24
|
|
+#define I2C2 25
|
|
+#define I2C3 26
|
|
+#define I2C5 27
|
|
+#define SPDIF 28
|
|
+#define CEC 29
|
|
+#define DAC12 30
|
|
+#define MDIO 31
|
|
+#define TIM1 32
|
|
+#define TIM8 33
|
|
+#define TIM15 34
|
|
+#define TIM16 35
|
|
+#define TIM17 36
|
|
+#define SPI1 37
|
|
+#define SPI4 38
|
|
+#define SPI5 39
|
|
+#define USART6 40
|
|
+#define SAI1 41
|
|
+#define SAI2 42
|
|
+#define SAI3 43
|
|
+#define DFSDM 44
|
|
+#define FDCAN 45
|
|
+#define LPTIM2 46
|
|
+#define LPTIM3 47
|
|
+#define LPTIM4 48
|
|
+#define LPTIM5 49
|
|
+#define SAI4 50
|
|
+#define SYSCFG 51
|
|
+#define VREF 52
|
|
+#define TMPSENS 53
|
|
+#define PMBCTRL 54
|
|
+#define HDP 55
|
|
+#define LTDC 56
|
|
+#define DSI 57
|
|
+#define IWDG2 58
|
|
+#define USBPHY 59
|
|
+#define STGENRO 60
|
|
+#define SPI6 61
|
|
+#define I2C4 62
|
|
+#define I2C6 63
|
|
+#define USART1 64
|
|
+#define RTCAPB 65
|
|
+#define TZC1 66
|
|
+#define TZPC 67
|
|
+#define IWDG1 68
|
|
+#define BSEC 69
|
|
+#define STGEN 70
|
|
+#define DMA1 71
|
|
+#define DMA2 72
|
|
+#define DMAMUX 73
|
|
+#define ADC12 74
|
|
+#define USBO 75
|
|
+#define SDMMC3 76
|
|
+#define DCMI 77
|
|
+#define CRYP2 78
|
|
+#define HASH2 79
|
|
+#define RNG2 80
|
|
+#define CRC2 81
|
|
+#define HSEM 82
|
|
+#define IPCC 83
|
|
+#define GPIOA 84
|
|
+#define GPIOB 85
|
|
+#define GPIOC 86
|
|
+#define GPIOD 87
|
|
+#define GPIOE 88
|
|
+#define GPIOF 89
|
|
+#define GPIOG 90
|
|
+#define GPIOH 91
|
|
+#define GPIOI 92
|
|
+#define GPIOJ 93
|
|
+#define GPIOK 94
|
|
+#define GPIOZ 95
|
|
+#define CRYP1 96
|
|
+#define HASH1 97
|
|
+#define RNG1 98
|
|
+#define BKPSRAM 99
|
|
+#define MDMA 100
|
|
+#define GPU 101
|
|
+#define ETHCK 102
|
|
+#define ETHTX 103
|
|
+#define ETHRX 104
|
|
+#define ETHMAC 105
|
|
+#define FMC 106
|
|
+#define QSPI 107
|
|
+#define SDMMC1 108
|
|
+#define SDMMC2 109
|
|
+#define CRC1 110
|
|
+#define USBH 111
|
|
+#define ETHSTP 112
|
|
+#define TZC2 113
|
|
+
|
|
+/* Kernel clocks */
|
|
+#define SDMMC1_K 118
|
|
+#define SDMMC2_K 119
|
|
+#define SDMMC3_K 120
|
|
+#define FMC_K 121
|
|
+#define QSPI_K 122
|
|
+#define ETHCK_K 123
|
|
+#define RNG1_K 124
|
|
+#define RNG2_K 125
|
|
+#define GPU_K 126
|
|
+#define USBPHY_K 127
|
|
+#define STGEN_K 128
|
|
+#define SPDIF_K 129
|
|
+#define SPI1_K 130
|
|
+#define SPI2_K 131
|
|
+#define SPI3_K 132
|
|
+#define SPI4_K 133
|
|
+#define SPI5_K 134
|
|
+#define SPI6_K 135
|
|
+#define CEC_K 136
|
|
+#define I2C1_K 137
|
|
+#define I2C2_K 138
|
|
+#define I2C3_K 139
|
|
+#define I2C4_K 140
|
|
+#define I2C5_K 141
|
|
+#define I2C6_K 142
|
|
+#define LPTIM1_K 143
|
|
+#define LPTIM2_K 144
|
|
+#define LPTIM3_K 145
|
|
+#define LPTIM4_K 146
|
|
+#define LPTIM5_K 147
|
|
+#define USART1_K 148
|
|
+#define USART2_K 149
|
|
+#define USART3_K 150
|
|
+#define UART4_K 151
|
|
+#define UART5_K 152
|
|
+#define USART6_K 153
|
|
+#define UART7_K 154
|
|
+#define UART8_K 155
|
|
+#define DFSDM_K 156
|
|
+#define FDCAN_K 157
|
|
+#define SAI1_K 158
|
|
+#define SAI2_K 159
|
|
+#define SAI3_K 160
|
|
+#define SAI4_K 161
|
|
+#define ADC12_K 162
|
|
+#define DSI_K 163
|
|
+#define DSI_PX 164
|
|
+#define ADFSDM_K 165
|
|
+#define USBO_K 166
|
|
+#define LTDC_PX 167
|
|
+#define DAC12_K 168
|
|
+#define ETHPTP_K 169
|
|
+
|
|
+/* PLL */
|
|
+#define PLL1 176
|
|
+#define PLL2 177
|
|
+#define PLL3 178
|
|
+#define PLL4 179
|
|
+
|
|
+/* ODF */
|
|
+#define PLL1_P 180
|
|
+#define PLL1_Q 181
|
|
+#define PLL1_R 182
|
|
+#define PLL2_P 183
|
|
+#define PLL2_Q 184
|
|
+#define PLL2_R 185
|
|
+#define PLL3_P 186
|
|
+#define PLL3_Q 187
|
|
+#define PLL3_R 188
|
|
+#define PLL4_P 189
|
|
+#define PLL4_Q 190
|
|
+#define PLL4_R 191
|
|
+
|
|
+/* AUX */
|
|
+#define RTC 192
|
|
+
|
|
+/* MCLK */
|
|
+#define CK_PER 193
|
|
+#define CK_MPU 194
|
|
+#define CK_AXI 195
|
|
+#define CK_MCU 196
|
|
+
|
|
+/* Time base */
|
|
+#define TIM2_K 197
|
|
+#define TIM3_K 198
|
|
+#define TIM4_K 199
|
|
+#define TIM5_K 200
|
|
+#define TIM6_K 201
|
|
+#define TIM7_K 202
|
|
+#define TIM12_K 203
|
|
+#define TIM13_K 204
|
|
+#define TIM14_K 205
|
|
+#define TIM1_K 206
|
|
+#define TIM8_K 207
|
|
+#define TIM15_K 208
|
|
+#define TIM16_K 209
|
|
+#define TIM17_K 210
|
|
+
|
|
+/* MCO clocks */
|
|
+#define CK_MCO1 211
|
|
+#define CK_MCO2 212
|
|
+
|
|
+/* TRACE & DEBUG clocks */
|
|
+#define DBG 213
|
|
+#define CK_DBG 214
|
|
+#define CK_TRACE 215
|
|
+
|
|
+/* DDR */
|
|
+#define DDRC1 220
|
|
+#define DDRC1LP 221
|
|
+#define DDRC2 222
|
|
+#define DDRC2LP 223
|
|
+#define DDRPHYC 224
|
|
+#define DDRPHYCLP 225
|
|
+#define DDRCAPB 226
|
|
+#define DDRCAPBLP 227
|
|
+#define AXIDCG 228
|
|
+#define DDRPHYCAPB 229
|
|
+#define DDRPHYCAPBLP 230
|
|
+#define DDRPERFM 231
|
|
+
|
|
+#define STM32MP1_LAST_CLK 232
|
|
+
|
|
+#endif /* _DT_BINDINGS_STM32MP1_CLKS_H_ */
|
|
diff --git a/core/include/dt-bindings/clock/stm32mp1-clksrc.h b/core/include/dt-bindings/clock/stm32mp1-clksrc.h
|
|
new file mode 100644
|
|
index 0000000..de7d160
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/clock/stm32mp1-clksrc.h
|
|
@@ -0,0 +1,284 @@
|
|
+/*
|
|
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_CLOCK_STM32MP1_CLKSRC_H_
|
|
+#define _DT_BINDINGS_CLOCK_STM32MP1_CLKSRC_H_
|
|
+
|
|
+/* PLL output is enable when x=1, with x=p,q or r */
|
|
+#define PQR(p, q, r) (((p) & 1) | (((q) & 1) << 1) | (((r) & 1) << 2))
|
|
+
|
|
+/* st,clksrc: mandatory clock source */
|
|
+
|
|
+#define CLK_MPU_HSI 0x00000200
|
|
+#define CLK_MPU_HSE 0x00000201
|
|
+#define CLK_MPU_PLL1P 0x00000202
|
|
+#define CLK_MPU_PLL1P_DIV 0x00000203
|
|
+
|
|
+#define CLK_AXI_HSI 0x00000240
|
|
+#define CLK_AXI_HSE 0x00000241
|
|
+#define CLK_AXI_PLL2P 0x00000242
|
|
+
|
|
+#define CLK_MCU_HSI 0x00000480
|
|
+#define CLK_MCU_HSE 0x00000481
|
|
+#define CLK_MCU_CSI 0x00000482
|
|
+#define CLK_MCU_PLL3P 0x00000483
|
|
+
|
|
+#define CLK_PLL12_HSI 0x00000280
|
|
+#define CLK_PLL12_HSE 0x00000281
|
|
+
|
|
+#define CLK_PLL3_HSI 0x00008200
|
|
+#define CLK_PLL3_HSE 0x00008201
|
|
+#define CLK_PLL3_CSI 0x00008202
|
|
+
|
|
+#define CLK_PLL4_HSI 0x00008240
|
|
+#define CLK_PLL4_HSE 0x00008241
|
|
+#define CLK_PLL4_CSI 0x00008242
|
|
+#define CLK_PLL4_I2SCKIN 0x00008243
|
|
+
|
|
+#define CLK_RTC_DISABLED 0x00001400
|
|
+#define CLK_RTC_LSE 0x00001401
|
|
+#define CLK_RTC_LSI 0x00001402
|
|
+#define CLK_RTC_HSE 0x00001403
|
|
+
|
|
+#define CLK_MCO1_HSI 0x00008000
|
|
+#define CLK_MCO1_HSE 0x00008001
|
|
+#define CLK_MCO1_CSI 0x00008002
|
|
+#define CLK_MCO1_LSI 0x00008003
|
|
+#define CLK_MCO1_LSE 0x00008004
|
|
+#define CLK_MCO1_DISABLED 0x0000800F
|
|
+
|
|
+#define CLK_MCO2_MPU 0x00008040
|
|
+#define CLK_MCO2_AXI 0x00008041
|
|
+#define CLK_MCO2_MCU 0x00008042
|
|
+#define CLK_MCO2_PLL4P 0x00008043
|
|
+#define CLK_MCO2_HSE 0x00008044
|
|
+#define CLK_MCO2_HSI 0x00008045
|
|
+#define CLK_MCO2_DISABLED 0x0000804F
|
|
+
|
|
+/* st,pkcs: peripheral kernel clock source */
|
|
+
|
|
+#define CLK_I2C12_PCLK1 0x00008C00
|
|
+#define CLK_I2C12_PLL4R 0x00008C01
|
|
+#define CLK_I2C12_HSI 0x00008C02
|
|
+#define CLK_I2C12_CSI 0x00008C03
|
|
+#define CLK_I2C12_DISABLED 0x00008C07
|
|
+
|
|
+#define CLK_I2C35_PCLK1 0x00008C40
|
|
+#define CLK_I2C35_PLL4R 0x00008C41
|
|
+#define CLK_I2C35_HSI 0x00008C42
|
|
+#define CLK_I2C35_CSI 0x00008C43
|
|
+#define CLK_I2C35_DISABLED 0x00008C47
|
|
+
|
|
+#define CLK_I2C46_PCLK5 0x00000C00
|
|
+#define CLK_I2C46_PLL3Q 0x00000C01
|
|
+#define CLK_I2C46_HSI 0x00000C02
|
|
+#define CLK_I2C46_CSI 0x00000C03
|
|
+#define CLK_I2C46_DISABLED 0x00000C07
|
|
+
|
|
+#define CLK_SAI1_PLL4Q 0x00008C80
|
|
+#define CLK_SAI1_PLL3Q 0x00008C81
|
|
+#define CLK_SAI1_I2SCKIN 0x00008C82
|
|
+#define CLK_SAI1_CKPER 0x00008C83
|
|
+#define CLK_SAI1_PLL3R 0x00008C84
|
|
+#define CLK_SAI1_DISABLED 0x00008C87
|
|
+
|
|
+#define CLK_SAI2_PLL4Q 0x00008CC0
|
|
+#define CLK_SAI2_PLL3Q 0x00008CC1
|
|
+#define CLK_SAI2_I2SCKIN 0x00008CC2
|
|
+#define CLK_SAI2_CKPER 0x00008CC3
|
|
+#define CLK_SAI2_SPDIF 0x00008CC4
|
|
+#define CLK_SAI2_PLL3R 0x00008CC5
|
|
+#define CLK_SAI2_DISABLED 0x00008CC7
|
|
+
|
|
+#define CLK_SAI3_PLL4Q 0x00008D00
|
|
+#define CLK_SAI3_PLL3Q 0x00008D01
|
|
+#define CLK_SAI3_I2SCKIN 0x00008D02
|
|
+#define CLK_SAI3_CKPER 0x00008D03
|
|
+#define CLK_SAI3_PLL3R 0x00008D04
|
|
+#define CLK_SAI3_DISABLED 0x00008D07
|
|
+
|
|
+#define CLK_SAI4_PLL4Q 0x00008D40
|
|
+#define CLK_SAI4_PLL3Q 0x00008D41
|
|
+#define CLK_SAI4_I2SCKIN 0x00008D42
|
|
+#define CLK_SAI4_CKPER 0x00008D43
|
|
+#define CLK_SAI4_PLL3R 0x00008D44
|
|
+#define CLK_SAI4_DISABLED 0x00008D47
|
|
+
|
|
+#define CLK_SPI2S1_PLL4P 0x00008D80
|
|
+#define CLK_SPI2S1_PLL3Q 0x00008D81
|
|
+#define CLK_SPI2S1_I2SCKIN 0x00008D82
|
|
+#define CLK_SPI2S1_CKPER 0x00008D83
|
|
+#define CLK_SPI2S1_PLL3R 0x00008D84
|
|
+#define CLK_SPI2S1_DISABLED 0x00008D87
|
|
+
|
|
+#define CLK_SPI2S23_PLL4P 0x00008DC0
|
|
+#define CLK_SPI2S23_PLL3Q 0x00008DC1
|
|
+#define CLK_SPI2S23_I2SCKIN 0x00008DC2
|
|
+#define CLK_SPI2S23_CKPER 0x00008DC3
|
|
+#define CLK_SPI2S23_PLL3R 0x00008DC4
|
|
+#define CLK_SPI2S23_DISABLED 0x00008DC7
|
|
+
|
|
+#define CLK_SPI45_PCLK2 0x00008E00
|
|
+#define CLK_SPI45_PLL4Q 0x00008E01
|
|
+#define CLK_SPI45_HSI 0x00008E02
|
|
+#define CLK_SPI45_CSI 0x00008E03
|
|
+#define CLK_SPI45_HSE 0x00008E04
|
|
+#define CLK_SPI45_DISABLED 0x00008E07
|
|
+
|
|
+#define CLK_SPI6_PCLK5 0x00000C40
|
|
+#define CLK_SPI6_PLL4Q 0x00000C41
|
|
+#define CLK_SPI6_HSI 0x00000C42
|
|
+#define CLK_SPI6_CSI 0x00000C43
|
|
+#define CLK_SPI6_HSE 0x00000C44
|
|
+#define CLK_SPI6_PLL3Q 0x00000C45
|
|
+#define CLK_SPI6_DISABLED 0x00000C47
|
|
+
|
|
+#define CLK_UART6_PCLK2 0x00008E40
|
|
+#define CLK_UART6_PLL4Q 0x00008E41
|
|
+#define CLK_UART6_HSI 0x00008E42
|
|
+#define CLK_UART6_CSI 0x00008E43
|
|
+#define CLK_UART6_HSE 0x00008E44
|
|
+#define CLK_UART6_DISABLED 0x00008E47
|
|
+
|
|
+#define CLK_UART24_PCLK1 0x00008E80
|
|
+#define CLK_UART24_PLL4Q 0x00008E81
|
|
+#define CLK_UART24_HSI 0x00008E82
|
|
+#define CLK_UART24_CSI 0x00008E83
|
|
+#define CLK_UART24_HSE 0x00008E84
|
|
+#define CLK_UART24_DISABLED 0x00008E87
|
|
+
|
|
+#define CLK_UART35_PCLK1 0x00008EC0
|
|
+#define CLK_UART35_PLL4Q 0x00008EC1
|
|
+#define CLK_UART35_HSI 0x00008EC2
|
|
+#define CLK_UART35_CSI 0x00008EC3
|
|
+#define CLK_UART35_HSE 0x00008EC4
|
|
+#define CLK_UART35_DISABLED 0x00008EC7
|
|
+
|
|
+#define CLK_UART78_PCLK1 0x00008F00
|
|
+#define CLK_UART78_PLL4Q 0x00008F01
|
|
+#define CLK_UART78_HSI 0x00008F02
|
|
+#define CLK_UART78_CSI 0x00008F03
|
|
+#define CLK_UART78_HSE 0x00008F04
|
|
+#define CLK_UART78_DISABLED 0x00008F07
|
|
+
|
|
+#define CLK_UART1_PCLK5 0x00000C80
|
|
+#define CLK_UART1_PLL3Q 0x00000C81
|
|
+#define CLK_UART1_HSI 0x00000C82
|
|
+#define CLK_UART1_CSI 0x00000C83
|
|
+#define CLK_UART1_PLL4Q 0x00000C84
|
|
+#define CLK_UART1_HSE 0x00000C85
|
|
+#define CLK_UART1_DISABLED 0x00000C87
|
|
+
|
|
+#define CLK_SDMMC12_HCLK6 0x00008F40
|
|
+#define CLK_SDMMC12_PLL3R 0x00008F41
|
|
+#define CLK_SDMMC12_PLL4P 0x00008F42
|
|
+#define CLK_SDMMC12_HSI 0x00008F43
|
|
+#define CLK_SDMMC12_DISABLED 0x00008F47
|
|
+
|
|
+#define CLK_SDMMC3_HCLK2 0x00008F80
|
|
+#define CLK_SDMMC3_PLL3R 0x00008F81
|
|
+#define CLK_SDMMC3_PLL4P 0x00008F82
|
|
+#define CLK_SDMMC3_HSI 0x00008F83
|
|
+#define CLK_SDMMC3_DISABLED 0x00008F87
|
|
+
|
|
+#define CLK_ETH_PLL4P 0x00008FC0
|
|
+#define CLK_ETH_PLL3Q 0x00008FC1
|
|
+#define CLK_ETH_DISABLED 0x00008FC3
|
|
+
|
|
+#define CLK_QSPI_ACLK 0x00009000
|
|
+#define CLK_QSPI_PLL3R 0x00009001
|
|
+#define CLK_QSPI_PLL4P 0x00009002
|
|
+#define CLK_QSPI_CKPER 0x00009003
|
|
+
|
|
+#define CLK_FMC_ACLK 0x00009040
|
|
+#define CLK_FMC_PLL3R 0x00009041
|
|
+#define CLK_FMC_PLL4P 0x00009042
|
|
+#define CLK_FMC_CKPER 0x00009043
|
|
+
|
|
+#define CLK_FDCAN_HSE 0x000090C0
|
|
+#define CLK_FDCAN_PLL3Q 0x000090C1
|
|
+#define CLK_FDCAN_PLL4Q 0x000090C2
|
|
+#define CLK_FDCAN_PLL4R 0x000090C3
|
|
+
|
|
+#define CLK_SPDIF_PLL4P 0x00009140
|
|
+#define CLK_SPDIF_PLL3Q 0x00009141
|
|
+#define CLK_SPDIF_HSI 0x00009142
|
|
+#define CLK_SPDIF_DISABLED 0x00009143
|
|
+
|
|
+#define CLK_CEC_LSE 0x00009180
|
|
+#define CLK_CEC_LSI 0x00009181
|
|
+#define CLK_CEC_CSI_DIV122 0x00009182
|
|
+#define CLK_CEC_DISABLED 0x00009183
|
|
+
|
|
+#define CLK_USBPHY_HSE 0x000091C0
|
|
+#define CLK_USBPHY_PLL4R 0x000091C1
|
|
+#define CLK_USBPHY_HSE_DIV2 0x000091C2
|
|
+#define CLK_USBPHY_DISABLED 0x000091C3
|
|
+
|
|
+#define CLK_USBO_PLL4R 0x800091C0
|
|
+#define CLK_USBO_USBPHY 0x800091C1
|
|
+
|
|
+#define CLK_RNG1_CSI 0x00000CC0
|
|
+#define CLK_RNG1_PLL4R 0x00000CC1
|
|
+#define CLK_RNG1_LSE 0x00000CC2
|
|
+#define CLK_RNG1_LSI 0x00000CC3
|
|
+
|
|
+#define CLK_RNG2_CSI 0x00009200
|
|
+#define CLK_RNG2_PLL4R 0x00009201
|
|
+#define CLK_RNG2_LSE 0x00009202
|
|
+#define CLK_RNG2_LSI 0x00009203
|
|
+
|
|
+#define CLK_CKPER_HSI 0x00000D00
|
|
+#define CLK_CKPER_CSI 0x00000D01
|
|
+#define CLK_CKPER_HSE 0x00000D02
|
|
+#define CLK_CKPER_DISABLED 0x00000D03
|
|
+
|
|
+#define CLK_STGEN_HSI 0x00000D40
|
|
+#define CLK_STGEN_HSE 0x00000D41
|
|
+#define CLK_STGEN_DISABLED 0x00000D43
|
|
+
|
|
+#define CLK_DSI_DSIPLL 0x00009240
|
|
+#define CLK_DSI_PLL4P 0x00009241
|
|
+
|
|
+#define CLK_ADC_PLL4R 0x00009280
|
|
+#define CLK_ADC_CKPER 0x00009281
|
|
+#define CLK_ADC_PLL3Q 0x00009282
|
|
+#define CLK_ADC_DISABLED 0x00009283
|
|
+
|
|
+#define CLK_LPTIM45_PCLK3 0x000092C0
|
|
+#define CLK_LPTIM45_PLL4P 0x000092C1
|
|
+#define CLK_LPTIM45_PLL3Q 0x000092C2
|
|
+#define CLK_LPTIM45_LSE 0x000092C3
|
|
+#define CLK_LPTIM45_LSI 0x000092C4
|
|
+#define CLK_LPTIM45_CKPER 0x000092C5
|
|
+#define CLK_LPTIM45_DISABLED 0x000092C7
|
|
+
|
|
+#define CLK_LPTIM23_PCLK3 0x00009300
|
|
+#define CLK_LPTIM23_PLL4Q 0x00009301
|
|
+#define CLK_LPTIM23_CKPER 0x00009302
|
|
+#define CLK_LPTIM23_LSE 0x00009303
|
|
+#define CLK_LPTIM23_LSI 0x00009304
|
|
+#define CLK_LPTIM23_DISABLED 0x00009307
|
|
+
|
|
+#define CLK_LPTIM1_PCLK1 0x00009340
|
|
+#define CLK_LPTIM1_PLL4P 0x00009341
|
|
+#define CLK_LPTIM1_PLL3Q 0x00009342
|
|
+#define CLK_LPTIM1_LSE 0x00009343
|
|
+#define CLK_LPTIM1_LSI 0x00009344
|
|
+#define CLK_LPTIM1_CKPER 0x00009345
|
|
+#define CLK_LPTIM1_DISABLED 0x00009347
|
|
+
|
|
+/* define for st,pll /csg */
|
|
+#define SSCG_MODE_CENTER_SPREAD 0
|
|
+#define SSCG_MODE_DOWN_SPREAD 1
|
|
+
|
|
+/* define for st,drive */
|
|
+#define LSEDRV_LOWEST 0
|
|
+#define LSEDRV_MEDIUM_LOW 1
|
|
+#define LSEDRV_MEDIUM_HIGH 2
|
|
+#define LSEDRV_HIGHEST 3
|
|
+
|
|
+#endif
|
|
diff --git a/core/include/dt-bindings/etzpc/stm32-etzpc.h b/core/include/dt-bindings/etzpc/stm32-etzpc.h
|
|
new file mode 100644
|
|
index 0000000..f498651
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/etzpc/stm32-etzpc.h
|
|
@@ -0,0 +1,108 @@
|
|
+/*
|
|
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_STM32_ETZPC_H
|
|
+#define _DT_BINDINGS_STM32_ETZPC_H
|
|
+
|
|
+/* define DECPROT modes */
|
|
+#define DECPROT_S_RW 0x0
|
|
+#define DECPROT_NS_R_S_W 0x1
|
|
+#define DECPROT_MCU_ISOLATION 0x2
|
|
+#define DECPROT_NS_RW 0x3
|
|
+
|
|
+/* define DECPROT lock */
|
|
+#define DECPROT_UNLOCK 0x0
|
|
+#define DECPROT_LOCK 0x1
|
|
+
|
|
+/* define ETZPC ID */
|
|
+#define STM32MP1_ETZPC_STGENC_ID 0
|
|
+#define STM32MP1_ETZPC_BKPSRAM_ID 1
|
|
+#define STM32MP1_ETZPC_IWDG1_ID 2
|
|
+#define STM32MP1_ETZPC_USART1_ID 3
|
|
+#define STM32MP1_ETZPC_SPI6_ID 4
|
|
+#define STM32MP1_ETZPC_I2C4_ID 5
|
|
+#define STM32MP1_ETZPC_GPIOZ_ID 6
|
|
+#define STM32MP1_ETZPC_RNG1_ID 7
|
|
+#define STM32MP1_ETZPC_HASH1_ID 8
|
|
+#define STM32MP1_ETZPC_CRYP1_ID 9
|
|
+#define STM32MP1_ETZPC_DDRCTRL_ID 10
|
|
+#define STM32MP1_ETZPC_DDRPHYC_ID 11
|
|
+#define STM32MP1_ETZPC_I2C6_ID 12
|
|
+#define STM32MP1_ETZPC_TIM2_ID 16
|
|
+#define STM32MP1_ETZPC_TIM3_ID 17
|
|
+#define STM32MP1_ETZPC_TIM4_ID 18
|
|
+#define STM32MP1_ETZPC_TIM5_ID 19
|
|
+#define STM32MP1_ETZPC_TIM6_ID 20
|
|
+#define STM32MP1_ETZPC_TIM7_ID 21
|
|
+#define STM32MP1_ETZPC_TIM12_ID 22
|
|
+#define STM32MP1_ETZPC_TIM13_ID 23
|
|
+#define STM32MP1_ETZPC_TIM14_ID 24
|
|
+#define STM32MP1_ETZPC_LPTIM1_ID 25
|
|
+#define STM32MP1_ETZPC_WWDG1_ID 26
|
|
+#define STM32MP1_ETZPC_SPI2_ID 27
|
|
+#define STM32MP1_ETZPC_SPI3_ID 28
|
|
+#define STM32MP1_ETZPC_SPDIFRX_ID 29
|
|
+#define STM32MP1_ETZPC_USART2_ID 30
|
|
+#define STM32MP1_ETZPC_USART3_ID 31
|
|
+#define STM32MP1_ETZPC_UART4_ID 32
|
|
+#define STM32MP1_ETZPC_UART5_ID 33
|
|
+#define STM32MP1_ETZPC_I2C1_ID 34
|
|
+#define STM32MP1_ETZPC_I2C2_ID 35
|
|
+#define STM32MP1_ETZPC_I2C3_ID 36
|
|
+#define STM32MP1_ETZPC_I2C5_ID 37
|
|
+#define STM32MP1_ETZPC_CEC_ID 38
|
|
+#define STM32MP1_ETZPC_DAC_ID 39
|
|
+#define STM32MP1_ETZPC_UART7_ID 40
|
|
+#define STM32MP1_ETZPC_UART8_ID 41
|
|
+#define STM32MP1_ETZPC_MDIOS_ID 44
|
|
+#define STM32MP1_ETZPC_TIM1_ID 48
|
|
+#define STM32MP1_ETZPC_TIM8_ID 49
|
|
+#define STM32MP1_ETZPC_USART6_ID 51
|
|
+#define STM32MP1_ETZPC_SPI1_ID 52
|
|
+#define STM32MP1_ETZPC_SPI4_ID 53
|
|
+#define STM32MP1_ETZPC_TIM15_ID 54
|
|
+#define STM32MP1_ETZPC_TIM16_ID 55
|
|
+#define STM32MP1_ETZPC_TIM17_ID 56
|
|
+#define STM32MP1_ETZPC_SPI5_ID 57
|
|
+#define STM32MP1_ETZPC_SAI1_ID 58
|
|
+#define STM32MP1_ETZPC_SAI2_ID 59
|
|
+#define STM32MP1_ETZPC_SAI3_ID 60
|
|
+#define STM32MP1_ETZPC_DFSDM_ID 61
|
|
+#define STM32MP1_ETZPC_TT_FDCAN_ID 62
|
|
+#define STM32MP1_ETZPC_LPTIM2_ID 64
|
|
+#define STM32MP1_ETZPC_LPTIM3_ID 65
|
|
+#define STM32MP1_ETZPC_LPTIM4_ID 66
|
|
+#define STM32MP1_ETZPC_LPTIM5_ID 67
|
|
+#define STM32MP1_ETZPC_SAI4_ID 68
|
|
+#define STM32MP1_ETZPC_VREFBUF_ID 69
|
|
+#define STM32MP1_ETZPC_DCMI_ID 70
|
|
+#define STM32MP1_ETZPC_CRC2_ID 71
|
|
+#define STM32MP1_ETZPC_ADC_ID 72
|
|
+#define STM32MP1_ETZPC_HASH2_ID 73
|
|
+#define STM32MP1_ETZPC_RNG2_ID 74
|
|
+#define STM32MP1_ETZPC_CRYP2_ID 75
|
|
+#define STM32MP1_ETZPC_SRAM1_ID 80
|
|
+#define STM32MP1_ETZPC_SRAM2_ID 81
|
|
+#define STM32MP1_ETZPC_SRAM3_ID 82
|
|
+#define STM32MP1_ETZPC_SRAM4_ID 83
|
|
+#define STM32MP1_ETZPC_RETRAM_ID 84
|
|
+#define STM32MP1_ETZPC_OTG_ID 85
|
|
+#define STM32MP1_ETZPC_SDMMC3_ID 86
|
|
+#define STM32MP1_ETZPC_DLYBSD3_ID 87
|
|
+#define STM32MP1_ETZPC_DMA1_ID 88
|
|
+#define STM32MP1_ETZPC_DMA2_ID 89
|
|
+#define STM32MP1_ETZPC_DMAMUX_ID 90
|
|
+#define STM32MP1_ETZPC_FMC_ID 91
|
|
+#define STM32MP1_ETZPC_QSPI_ID 92
|
|
+#define STM32MP1_ETZPC_DLYBQ_ID 93
|
|
+#define STM32MP1_ETZPC_ETH_ID 94
|
|
+
|
|
+#define STM32MP1_ETZPC_MAX_ID 96
|
|
+
|
|
+#define DECPROT(ip_id, mode, lock) ((ip_id << 16) | (mode << 8) | lock)
|
|
+
|
|
+#endif /* _DT_BINDINGS_STM32_ETZPC_H */
|
|
+
|
|
diff --git a/core/include/dt-bindings/interrupt-controller/arm-gic.h b/core/include/dt-bindings/interrupt-controller/arm-gic.h
|
|
new file mode 100644
|
|
index 0000000..6863d5e
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/interrupt-controller/arm-gic.h
|
|
@@ -0,0 +1,21 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_ARM_GIC_H
|
|
+#define _DT_BINDINGS_INTERRUPT_CONTROLLER_ARM_GIC_H
|
|
+
|
|
+/* Interrupt specifier cell 0 */
|
|
+
|
|
+#define GIC_SPI 0
|
|
+#define GIC_PPI 1
|
|
+
|
|
+#define IRQ_TYPE_NONE 0
|
|
+#define IRQ_TYPE_EDGE_RISING 1
|
|
+#define IRQ_TYPE_EDGE_FALLING 2
|
|
+#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
|
|
+#define IRQ_TYPE_LEVEL_HIGH 4
|
|
+#define IRQ_TYPE_LEVEL_LOW 8
|
|
+
|
|
+#endif
|
|
diff --git a/core/include/dt-bindings/pinctrl/stm32-pinfunc.h b/core/include/dt-bindings/pinctrl/stm32-pinfunc.h
|
|
new file mode 100644
|
|
index 0000000..7f6e4b9
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/pinctrl/stm32-pinfunc.h
|
|
@@ -0,0 +1,41 @@
|
|
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
|
|
+ * Author: Torgue Alexandre <alexandre.torgue@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_STM32_PINFUNC_H
|
|
+#define _DT_BINDINGS_STM32_PINFUNC_H
|
|
+
|
|
+/* define PIN modes */
|
|
+#define GPIO 0x0
|
|
+#define AF0 0x1
|
|
+#define AF1 0x2
|
|
+#define AF2 0x3
|
|
+#define AF3 0x4
|
|
+#define AF4 0x5
|
|
+#define AF5 0x6
|
|
+#define AF6 0x7
|
|
+#define AF7 0x8
|
|
+#define AF8 0x9
|
|
+#define AF9 0xa
|
|
+#define AF10 0xb
|
|
+#define AF11 0xc
|
|
+#define AF12 0xd
|
|
+#define AF13 0xe
|
|
+#define AF14 0xf
|
|
+#define AF15 0x10
|
|
+#define ANALOG 0x11
|
|
+
|
|
+/* define Pins number*/
|
|
+#define PIN_NO(port, line) (((port) - 'A') * 0x10 + (line))
|
|
+
|
|
+#define STM32_PINMUX(port, line, mode) (((PIN_NO(port, line)) << 8) | (mode))
|
|
+
|
|
+/* package information */
|
|
+#define STM32MP157CAA 0x1
|
|
+#define STM32MP157CAB 0x2
|
|
+#define STM32MP157CAC 0x4
|
|
+#define STM32MP157CAD 0x8
|
|
+
|
|
+#endif /* _DT_BINDINGS_STM32_PINFUNC_H */
|
|
diff --git a/core/include/dt-bindings/power/stm32mp1-power.h b/core/include/dt-bindings/power/stm32mp1-power.h
|
|
new file mode 100644
|
|
index 0000000..bfb7f78
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/power/stm32mp1-power.h
|
|
@@ -0,0 +1,19 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Yann Gautier <yann.gautier@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#ifndef DT_BINDINGS_STM32MP1_POWER_H
|
|
+#define DT_BINDINGS_STM32MP1_POWER_H
|
|
+
|
|
+#define STM32_PM_CSLEEP_RUN 0
|
|
+#define STM32_PM_CSTOP_ALLOW_STOP 1
|
|
+#define STM32_PM_CSTOP_ALLOW_LP_STOP 2
|
|
+#define STM32_PM_CSTOP_ALLOW_LPLV_STOP 3
|
|
+#define STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR 4
|
|
+#define STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF 5
|
|
+#define STM32_PM_SHUTDOWN 6
|
|
+#define STM32_PM_MAX_SOC_MODE 7
|
|
+
|
|
+#endif /* DT_BINDINGS_STM32MP1_POWER_H */
|
|
diff --git a/core/include/dt-bindings/reset/stm32mp1-resets.h b/core/include/dt-bindings/reset/stm32mp1-resets.h
|
|
new file mode 100644
|
|
index 0000000..f0c3aae
|
|
--- /dev/null
|
|
+++ b/core/include/dt-bindings/reset/stm32mp1-resets.h
|
|
@@ -0,0 +1,108 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_STM32MP1_RESET_H_
|
|
+#define _DT_BINDINGS_STM32MP1_RESET_H_
|
|
+
|
|
+#define LTDC_R 3072
|
|
+#define DSI_R 3076
|
|
+#define DDRPERFM_R 3080
|
|
+#define USBPHY_R 3088
|
|
+#define SPI6_R 3136
|
|
+#define I2C4_R 3138
|
|
+#define I2C6_R 3139
|
|
+#define USART1_R 3140
|
|
+#define STGEN_R 3156
|
|
+#define GPIOZ_R 3200
|
|
+#define CRYP1_R 3204
|
|
+#define HASH1_R 3205
|
|
+#define RNG1_R 3206
|
|
+#define AXIM_R 3216
|
|
+#define GPU_R 3269
|
|
+#define ETHMAC_R 3274
|
|
+#define FMC_R 3276
|
|
+#define QSPI_R 3278
|
|
+#define SDMMC1_R 3280
|
|
+#define SDMMC2_R 3281
|
|
+#define CRC1_R 3284
|
|
+#define USBH_R 3288
|
|
+#define MDMA_R 3328
|
|
+#define MCU_R 8225
|
|
+#define TIM2_R 19456
|
|
+#define TIM3_R 19457
|
|
+#define TIM4_R 19458
|
|
+#define TIM5_R 19459
|
|
+#define TIM6_R 19460
|
|
+#define TIM7_R 19461
|
|
+#define TIM12_R 16462
|
|
+#define TIM13_R 16463
|
|
+#define TIM14_R 16464
|
|
+#define LPTIM1_R 19465
|
|
+#define SPI2_R 19467
|
|
+#define SPI3_R 19468
|
|
+#define USART2_R 19470
|
|
+#define USART3_R 19471
|
|
+#define UART4_R 19472
|
|
+#define UART5_R 19473
|
|
+#define UART7_R 19474
|
|
+#define UART8_R 19475
|
|
+#define I2C1_R 19477
|
|
+#define I2C2_R 19478
|
|
+#define I2C3_R 19479
|
|
+#define I2C5_R 19480
|
|
+#define SPDIF_R 19482
|
|
+#define CEC_R 19483
|
|
+#define DAC12_R 19485
|
|
+#define MDIO_R 19847
|
|
+#define TIM1_R 19520
|
|
+#define TIM8_R 19521
|
|
+#define TIM15_R 19522
|
|
+#define TIM16_R 19523
|
|
+#define TIM17_R 19524
|
|
+#define SPI1_R 19528
|
|
+#define SPI4_R 19529
|
|
+#define SPI5_R 19530
|
|
+#define USART6_R 19533
|
|
+#define SAI1_R 19536
|
|
+#define SAI2_R 19537
|
|
+#define SAI3_R 19538
|
|
+#define DFSDM_R 19540
|
|
+#define FDCAN_R 19544
|
|
+#define LPTIM2_R 19584
|
|
+#define LPTIM3_R 19585
|
|
+#define LPTIM4_R 19586
|
|
+#define LPTIM5_R 19587
|
|
+#define SAI4_R 19592
|
|
+#define SYSCFG_R 19595
|
|
+#define VREF_R 19597
|
|
+#define TMPSENS_R 19600
|
|
+#define PMBCTRL_R 19601
|
|
+#define DMA1_R 19648
|
|
+#define DMA2_R 19649
|
|
+#define DMAMUX_R 19650
|
|
+#define ADC12_R 19653
|
|
+#define USBO_R 19656
|
|
+#define SDMMC3_R 19664
|
|
+#define CAMITF_R 19712
|
|
+#define CRYP2_R 19716
|
|
+#define HASH2_R 19717
|
|
+#define RNG2_R 19718
|
|
+#define CRC2_R 19719
|
|
+#define HSEM_R 19723
|
|
+#define MBOX_R 19724
|
|
+#define GPIOA_R 19776
|
|
+#define GPIOB_R 19777
|
|
+#define GPIOC_R 19778
|
|
+#define GPIOD_R 19779
|
|
+#define GPIOE_R 19780
|
|
+#define GPIOF_R 19781
|
|
+#define GPIOG_R 19782
|
|
+#define GPIOH_R 19783
|
|
+#define GPIOI_R 19784
|
|
+#define GPIOJ_R 19785
|
|
+#define GPIOK_R 19786
|
|
+
|
|
+#endif /* _DT_BINDINGS_STM32MP1_RESET_H_ */
|
|
diff --git a/core/include/kernel/interrupt.h b/core/include/kernel/interrupt.h
|
|
index d7b8abd..5a7e18d 100644
|
|
--- a/core/include/kernel/interrupt.h
|
|
+++ b/core/include/kernel/interrupt.h
|
|
@@ -23,6 +23,11 @@ struct itr_ops {
|
|
uint8_t cpu_mask);
|
|
void (*set_affinity)(struct itr_chip *chip, size_t it,
|
|
uint8_t cpu_mask);
|
|
+#if !defined(CFG_ARM_GICV3)
|
|
+ uint8_t (*set_pmr)(struct itr_chip *chip, uint8_t mask);
|
|
+ uint8_t (*set_ipriority)(struct itr_chip *chip, size_t it,
|
|
+ uint8_t mask);
|
|
+#endif
|
|
};
|
|
|
|
enum itr_return {
|
|
@@ -57,4 +62,14 @@ void itr_raise_sgi(size_t it, uint8_t cpu_mask);
|
|
*/
|
|
void itr_set_affinity(size_t it, uint8_t cpu_mask);
|
|
|
|
+/*
|
|
+ * Set the Priority Mask Regarding and return its previous value
|
|
+ */
|
|
+uint8_t itr_set_pmr(uint8_t mask);
|
|
+
|
|
+/*
|
|
+ * Set the targe tinterrupt priority mask and return its previous value
|
|
+ */
|
|
+uint8_t itr_set_ipriority(size_t it, uint8_t mask);
|
|
+
|
|
#endif /*__KERNEL_INTERRUPT_H*/
|
|
diff --git a/core/kernel/console.c b/core/kernel/console.c
|
|
index 1f11503..98fde7c 100644
|
|
--- a/core/kernel/console.c
|
|
+++ b/core/kernel/console.c
|
|
@@ -65,8 +65,13 @@ void configure_console_from_dt(void)
|
|
return;
|
|
|
|
offs = fdt_path_offset(fdt, "/secure-chosen");
|
|
- if (offs < 0)
|
|
- return;
|
|
+ if (offs < 0) {
|
|
+ /* Fallback to node /chosen */
|
|
+ offs = fdt_path_offset(fdt, "/chosen");
|
|
+ if (offs < 0) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
|
|
if (!prop) {
|
|
/*
|
|
diff --git a/core/kernel/interrupt.c b/core/kernel/interrupt.c
|
|
index cff1f80..ce27aca 100644
|
|
--- a/core/kernel/interrupt.c
|
|
+++ b/core/kernel/interrupt.c
|
|
@@ -78,3 +78,13 @@ void itr_set_affinity(size_t it, uint8_t cpu_mask)
|
|
{
|
|
itr_chip->ops->set_affinity(itr_chip, it, cpu_mask);
|
|
}
|
|
+
|
|
+uint8_t itr_set_pmr(uint8_t mask)
|
|
+{
|
|
+ return itr_chip->ops->set_pmr(itr_chip, mask);
|
|
+}
|
|
+
|
|
+uint8_t itr_set_ipriority(size_t it, uint8_t mask)
|
|
+{
|
|
+ return itr_chip->ops->set_ipriority(itr_chip, it, mask);
|
|
+}
|
|
diff --git a/core/lib/libfdt/fdt_ro.c b/core/lib/libfdt/fdt_ro.c
|
|
index 34110da..79b1b6d 100644
|
|
--- a/core/lib/libfdt/fdt_ro.c
|
|
+++ b/core/lib/libfdt/fdt_ro.c
|
|
@@ -89,6 +89,32 @@ static int _fdt_string_eq(const void *fdt, int stroffset,
|
|
return (strlen(p) == len) && (memcmp(p, s, len) == 0);
|
|
}
|
|
|
|
+uint32_t fdt_get_max_phandle(const void *fdt)
|
|
+{
|
|
+ uint32_t max_phandle = 0;
|
|
+ int offset;
|
|
+
|
|
+ for (offset = fdt_next_node(fdt, -1, NULL);;
|
|
+ offset = fdt_next_node(fdt, offset, NULL)) {
|
|
+ uint32_t phandle;
|
|
+
|
|
+ if (offset == -FDT_ERR_NOTFOUND)
|
|
+ return max_phandle;
|
|
+
|
|
+ if (offset < 0)
|
|
+ return (uint32_t)-1;
|
|
+
|
|
+ phandle = fdt_get_phandle(fdt, offset);
|
|
+ if (phandle == (uint32_t)-1)
|
|
+ continue;
|
|
+
|
|
+ if (phandle > max_phandle)
|
|
+ max_phandle = phandle;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
|
|
{
|
|
FDT_CHECK_HEADER(fdt);
|
|
@@ -155,9 +181,9 @@ int fdt_subnode_offset(const void *fdt, int parentoffset,
|
|
return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
|
|
}
|
|
|
|
-int fdt_path_offset(const void *fdt, const char *path)
|
|
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
|
|
{
|
|
- const char *end = path + strlen(path);
|
|
+ const char *end = path + namelen;
|
|
const char *p = path;
|
|
int offset = 0;
|
|
|
|
@@ -165,7 +191,7 @@ int fdt_path_offset(const void *fdt, const char *path)
|
|
|
|
/* see if we have an alias */
|
|
if (*path != '/') {
|
|
- const char *q = strchr(path, '/');
|
|
+ const char *q = memchr(path, '/', end - p);
|
|
|
|
if (!q)
|
|
q = end;
|
|
@@ -178,14 +204,15 @@ int fdt_path_offset(const void *fdt, const char *path)
|
|
p = q;
|
|
}
|
|
|
|
- while (*p) {
|
|
+ while (p < end) {
|
|
const char *q;
|
|
|
|
- while (*p == '/')
|
|
+ while (*p == '/') {
|
|
p++;
|
|
- if (! *p)
|
|
- return offset;
|
|
- q = strchr(p, '/');
|
|
+ if (p == end)
|
|
+ return offset;
|
|
+ }
|
|
+ q = memchr(p, '/', end - p);
|
|
if (! q)
|
|
q = end;
|
|
|
|
@@ -199,6 +226,11 @@ int fdt_path_offset(const void *fdt, const char *path)
|
|
return offset;
|
|
}
|
|
|
|
+int fdt_path_offset(const void *fdt, const char *path)
|
|
+{
|
|
+ return fdt_path_offset_namelen(fdt, path, strlen(path));
|
|
+}
|
|
+
|
|
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
|
|
{
|
|
const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
|
|
@@ -533,6 +565,106 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
|
|
return 0;
|
|
}
|
|
|
|
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
|
|
+{
|
|
+ const char *list, *end;
|
|
+ int length, count = 0;
|
|
+
|
|
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
|
|
+ if (!list)
|
|
+ return -length;
|
|
+
|
|
+ end = list + length;
|
|
+
|
|
+ while (list < end) {
|
|
+ length = strnlen(list, end - list) + 1;
|
|
+
|
|
+ /* Abort if the last string isn't properly NUL-terminated. */
|
|
+ if (list + length > end)
|
|
+ return -FDT_ERR_BADVALUE;
|
|
+
|
|
+ list += length;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
|
|
+ const char *string)
|
|
+{
|
|
+ int length, len, idx = 0;
|
|
+ const char *list, *end;
|
|
+
|
|
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
|
|
+ if (!list)
|
|
+ return -length;
|
|
+
|
|
+ len = strlen(string) + 1;
|
|
+ end = list + length;
|
|
+
|
|
+ while (list < end) {
|
|
+ length = strnlen(list, end - list) + 1;
|
|
+
|
|
+ /* Abort if the last string isn't properly NUL-terminated. */
|
|
+ if (list + length > end)
|
|
+ return -FDT_ERR_BADVALUE;
|
|
+
|
|
+ if (length == len && memcmp(list, string, length) == 0)
|
|
+ return idx;
|
|
+
|
|
+ list += length;
|
|
+ idx++;
|
|
+ }
|
|
+
|
|
+ return -FDT_ERR_NOTFOUND;
|
|
+}
|
|
+
|
|
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
|
|
+ const char *property, int idx,
|
|
+ int *lenp)
|
|
+{
|
|
+ const char *list, *end;
|
|
+ int length;
|
|
+
|
|
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
|
|
+ if (!list) {
|
|
+ if (lenp)
|
|
+ *lenp = length;
|
|
+
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ end = list + length;
|
|
+
|
|
+ while (list < end) {
|
|
+ length = strnlen(list, end - list) + 1;
|
|
+
|
|
+ /* Abort if the last string isn't properly NUL-terminated. */
|
|
+ if (list + length > end) {
|
|
+ if (lenp)
|
|
+ *lenp = -FDT_ERR_BADVALUE;
|
|
+
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (idx == 0) {
|
|
+ if (lenp)
|
|
+ *lenp = length - 1;
|
|
+
|
|
+ return list;
|
|
+ }
|
|
+
|
|
+ list += length;
|
|
+ idx--;
|
|
+ }
|
|
+
|
|
+ if (lenp)
|
|
+ *lenp = -FDT_ERR_NOTFOUND;
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
|
const char *compatible)
|
|
{
|
|
@@ -542,10 +674,8 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
|
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
|
|
if (!prop)
|
|
return len;
|
|
- if (fdt_stringlist_contains(prop, len, compatible))
|
|
- return 0;
|
|
- else
|
|
- return 1;
|
|
+
|
|
+ return !fdt_stringlist_contains(prop, len, compatible);
|
|
}
|
|
|
|
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
|
diff --git a/core/lib/libfdt/fdt_rw.c b/core/lib/libfdt/fdt_rw.c
|
|
index 1785a51..15897b7 100644
|
|
--- a/core/lib/libfdt/fdt_rw.c
|
|
+++ b/core/lib/libfdt/fdt_rw.c
|
|
@@ -102,6 +102,8 @@ static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
|
|
|
|
if (((p + oldlen) < p) || ((p + oldlen) > end))
|
|
return -FDT_ERR_BADOFFSET;
|
|
+ if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
|
|
+ return -FDT_ERR_BADOFFSET;
|
|
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
|
|
return -FDT_ERR_NOSPACE;
|
|
memmove(p + newlen, p + oldlen, end - p - oldlen);
|
|
@@ -190,17 +192,13 @@ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
|
|
int fdt_del_mem_rsv(void *fdt, int n)
|
|
{
|
|
struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
|
|
- int err;
|
|
|
|
FDT_RW_CHECK_HEADER(fdt);
|
|
|
|
if (n >= fdt_num_mem_rsv(fdt))
|
|
return -FDT_ERR_NOTFOUND;
|
|
|
|
- err = _fdt_splice_mem_rsv(fdt, re, 1, 0);
|
|
- if (err)
|
|
- return err;
|
|
- return 0;
|
|
+ return _fdt_splice_mem_rsv(fdt, re, 1, 0);
|
|
}
|
|
|
|
static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
|
|
@@ -286,8 +284,7 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name,
|
|
if (err)
|
|
return err;
|
|
|
|
- if (len)
|
|
- memcpy(prop->data, val, len);
|
|
+ memcpy(prop->data, val, len);
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/core/lib/libfdt/fdt_wip.c b/core/lib/libfdt/fdt_wip.c
|
|
index c97c5f7..ee9df5c 100644
|
|
--- a/core/lib/libfdt/fdt_wip.c
|
|
+++ b/core/lib/libfdt/fdt_wip.c
|
|
@@ -56,21 +56,42 @@
|
|
|
|
#include "libfdt_internal.h"
|
|
|
|
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
|
|
+ const char *name, int namelen,
|
|
+ uint32_t idx, const void *val,
|
|
+ int len)
|
|
+{
|
|
+ void *propval;
|
|
+ int proplen;
|
|
+
|
|
+ propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
|
|
+ &proplen);
|
|
+ if (!propval)
|
|
+ return proplen;
|
|
+
|
|
+ if (proplen < (len + idx))
|
|
+ return -FDT_ERR_NOSPACE;
|
|
+
|
|
+ memcpy((char *)propval + idx, val, len);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
|
|
const void *val, int len)
|
|
{
|
|
- void *propval;
|
|
+ const void *propval;
|
|
int proplen;
|
|
|
|
- propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen);
|
|
+ propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
|
|
if (! propval)
|
|
return proplen;
|
|
|
|
if (proplen != len)
|
|
return -FDT_ERR_NOSPACE;
|
|
|
|
- memcpy(propval, val, len);
|
|
- return 0;
|
|
+ return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
|
|
+ strlen(name), 0,
|
|
+ val, len);
|
|
}
|
|
|
|
static void _fdt_nop_region(void *start, int len)
|
|
diff --git a/core/lib/libfdt/include/fdt.h b/core/lib/libfdt/include/fdt.h
|
|
index 520bdcf..faa8046 100644
|
|
--- a/core/lib/libfdt/include/fdt.h
|
|
+++ b/core/lib/libfdt/include/fdt.h
|
|
@@ -53,8 +53,16 @@
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
+/*
|
|
+ * Portions copyright (c) 2016-2017, ARM Limited and Contributors.
|
|
+ * All rights reserved.
|
|
+ */
|
|
+
|
|
#ifndef __ASSEMBLY__
|
|
|
|
+#include <libfdt_env.h>
|
|
+
|
|
+
|
|
struct fdt_header {
|
|
fdt32_t magic; /* magic word FDT_MAGIC */
|
|
fdt32_t totalsize; /* total size of DT block */
|
|
diff --git a/core/lib/libfdt/include/libfdt.h b/core/lib/libfdt/include/libfdt.h
|
|
index 01a9065..5ce9bfb 100644
|
|
--- a/core/lib/libfdt/include/libfdt.h
|
|
+++ b/core/lib/libfdt/include/libfdt.h
|
|
@@ -52,6 +52,11 @@
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
+/*
|
|
+ * Portions copyright (c) 2016-2017, ARM Limited and Contributors.
|
|
+ * All rights reserved.
|
|
+ */
|
|
+
|
|
#include <libfdt_env.h>
|
|
#include <fdt.h>
|
|
|
|
@@ -122,7 +127,12 @@
|
|
/* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
|
|
* or similar property with a bad format or value */
|
|
|
|
-#define FDT_ERR_MAX 14
|
|
+#define FDT_ERR_BADVALUE 15
|
|
+ /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
|
|
+ * value. For example: a property expected to contain a string list
|
|
+ * is not NUL-terminated within the length of its value. */
|
|
+
|
|
+#define FDT_ERR_MAX 15
|
|
|
|
/**********************************************************************/
|
|
/* Low-level functions (you probably don't need these) */
|
|
@@ -164,27 +174,55 @@ int fdt_first_subnode(const void *fdt, int offset);
|
|
*/
|
|
int fdt_next_subnode(const void *fdt, int offset);
|
|
|
|
+/**
|
|
+ * fdt_for_each_subnode - iterate over all subnodes of a parent
|
|
+ *
|
|
+ * @node: child node (int, lvalue)
|
|
+ * @fdt: FDT blob (const void *)
|
|
+ * @parent: parent node (int)
|
|
+ *
|
|
+ * This is actually a wrapper around a for loop and would be used like so:
|
|
+ *
|
|
+ * fdt_for_each_subnode(node, fdt, parent) {
|
|
+ * Use node
|
|
+ * ...
|
|
+ * }
|
|
+ *
|
|
+ * if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) {
|
|
+ * Error handling
|
|
+ * }
|
|
+ *
|
|
+ * Note that this is implemented as a macro and @node is used as
|
|
+ * iterator in the loop. The parent variable be constant or even a
|
|
+ * literal.
|
|
+ *
|
|
+ */
|
|
+#define fdt_for_each_subnode(node, fdt, parent) \
|
|
+ for (node = fdt_first_subnode(fdt, parent); \
|
|
+ node >= 0; \
|
|
+ node = fdt_next_subnode(fdt, node))
|
|
+
|
|
/**********************************************************************/
|
|
/* General functions */
|
|
/**********************************************************************/
|
|
|
|
#define fdt_get_header(fdt, field) \
|
|
(fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
|
|
-#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
|
|
+#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
|
|
#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
|
|
#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
|
|
#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
|
|
#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
|
|
#define fdt_version(fdt) (fdt_get_header(fdt, version))
|
|
-#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
|
|
-#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
|
|
-#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
|
|
+#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
|
|
+#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
|
|
+#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
|
|
#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
|
|
|
|
#define __fdt_set_hdr(name) \
|
|
static inline void fdt_set_##name(void *fdt, uint32_t val) \
|
|
{ \
|
|
- struct fdt_header *fdth = (struct fdt_header*)fdt; \
|
|
+ struct fdt_header *fdth = (struct fdt_header *)fdt; \
|
|
fdth->name = cpu_to_fdt32(val); \
|
|
}
|
|
__fdt_set_hdr(magic)
|
|
@@ -255,6 +293,21 @@ int fdt_move(const void *fdt, void *buf, int bufsize);
|
|
const char *fdt_string(const void *fdt, int stroffset);
|
|
|
|
/**
|
|
+ * fdt_get_max_phandle - retrieves the highest phandle in a tree
|
|
+ * @fdt: pointer to the device tree blob
|
|
+ *
|
|
+ * fdt_get_max_phandle retrieves the highest phandle in the given
|
|
+ * device tree. This will ignore badly formatted phandles, or phandles
|
|
+ * with a value of 0 or -1.
|
|
+ *
|
|
+ * returns:
|
|
+ * the highest phandle on success
|
|
+ * 0, if no phandle was found in the device tree
|
|
+ * -1, if an error occurred
|
|
+ */
|
|
+uint32_t fdt_get_max_phandle(const void *fdt);
|
|
+
|
|
+/**
|
|
* fdt_num_mem_rsv - retrieve the number of memory reserve map entries
|
|
* @fdt: pointer to the device tree blob
|
|
*
|
|
@@ -314,8 +367,9 @@ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
|
|
* returns:
|
|
* structure block offset of the requested subnode (>=0), on success
|
|
* -FDT_ERR_NOTFOUND, if the requested subnode does not exist
|
|
- * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
|
|
- * -FDT_ERR_BADMAGIC,
|
|
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
|
|
+ * tag
|
|
+ * -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
* -FDT_ERR_BADSTRUCTURE,
|
|
@@ -324,6 +378,17 @@ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
|
|
int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
|
|
|
|
/**
|
|
+ * fdt_path_offset_namelen - find a tree node by its full path
|
|
+ * @fdt: pointer to the device tree blob
|
|
+ * @path: full path of the node to locate
|
|
+ * @namelen: number of characters of path to consider
|
|
+ *
|
|
+ * Identical to fdt_path_offset(), but only consider the first namelen
|
|
+ * characters of path as the path name.
|
|
+ */
|
|
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
|
|
+
|
|
+/**
|
|
* fdt_path_offset - find a tree node by its full path
|
|
* @fdt: pointer to the device tree blob
|
|
* @path: full path of the node to locate
|
|
@@ -336,7 +401,8 @@ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
|
|
* address).
|
|
*
|
|
* returns:
|
|
- * structure block offset of the node with the requested path (>=0), on success
|
|
+ * structure block offset of the node with the requested path (>=0), on
|
|
+ * success
|
|
* -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
|
|
* -FDT_ERR_NOTFOUND, if the requested node does not exist
|
|
* -FDT_ERR_BADMAGIC,
|
|
@@ -360,10 +426,12 @@ int fdt_path_offset(const void *fdt, const char *path);
|
|
*
|
|
* returns:
|
|
* pointer to the node's name, on success
|
|
- * If lenp is non-NULL, *lenp contains the length of that name (>=0)
|
|
+ * If lenp is non-NULL, *lenp contains the length of that name
|
|
+ * (>=0)
|
|
* NULL, on error
|
|
* if lenp is non-NULL *lenp contains an error code (<0):
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
|
|
+ * tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE, standard meanings
|
|
@@ -412,6 +480,33 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset);
|
|
int fdt_next_property_offset(const void *fdt, int offset);
|
|
|
|
/**
|
|
+ * fdt_for_each_property_offset - iterate over all properties of a node
|
|
+ *
|
|
+ * @property_offset: property offset (int, lvalue)
|
|
+ * @fdt: FDT blob (const void *)
|
|
+ * @node: node offset (int)
|
|
+ *
|
|
+ * This is actually a wrapper around a for loop and would be used like so:
|
|
+ *
|
|
+ * fdt_for_each_property_offset(property, fdt, node) {
|
|
+ * Use property
|
|
+ * ...
|
|
+ * }
|
|
+ *
|
|
+ * if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) {
|
|
+ * Error handling
|
|
+ * }
|
|
+ *
|
|
+ * Note that this is implemented as a macro and property is used as
|
|
+ * iterator in the loop. The node variable can be constant or even a
|
|
+ * literal.
|
|
+ */
|
|
+#define fdt_for_each_property_offset(property, fdt, node) \
|
|
+ for (property = fdt_first_property_offset(fdt, node); \
|
|
+ property >= 0; \
|
|
+ property = fdt_next_property_offset(fdt, property))
|
|
+
|
|
+/**
|
|
* fdt_get_property_by_offset - retrieve the property at a given offset
|
|
* @fdt: pointer to the device tree blob
|
|
* @offset: offset of the property to retrieve
|
|
@@ -447,8 +542,8 @@ const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
|
|
* @namelen: number of characters of name to consider
|
|
* @lenp: pointer to an integer variable (will be overwritten) or NULL
|
|
*
|
|
- * Identical to fdt_get_property_namelen(), but only examine the first
|
|
- * namelen characters of name for matching the property name.
|
|
+ * Identical to fdt_get_property(), but only examine the first namelen
|
|
+ * characters of name for matching the property name.
|
|
*/
|
|
const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
|
int nodeoffset,
|
|
@@ -475,7 +570,8 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
|
* NULL, on error
|
|
* if lenp is non-NULL, *lenp contains an error code (<0):
|
|
* -FDT_ERR_NOTFOUND, node does not have named property
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
|
|
+ * tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -539,6 +635,13 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
|
|
*/
|
|
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
|
const char *name, int namelen, int *lenp);
|
|
+static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
|
|
+ const char *name, int namelen,
|
|
+ int *lenp)
|
|
+{
|
|
+ return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
|
|
+ namelen, lenp);
|
|
+}
|
|
|
|
/**
|
|
* fdt_getprop - retrieve the value of a given property
|
|
@@ -560,7 +663,8 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
|
* NULL, on error
|
|
* if lenp is non-NULL, *lenp contains an error code (<0):
|
|
* -FDT_ERR_NOTFOUND, node does not have named property
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
|
|
+ * tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -632,7 +736,7 @@ const char *fdt_get_alias(const void *fdt, const char *name);
|
|
* 0, on success
|
|
* buf contains the absolute path of the node at
|
|
* nodeoffset, as a NUL-terminated string.
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
* -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
|
|
* characters and will not fit in the given buffer.
|
|
* -FDT_ERR_BADMAGIC,
|
|
@@ -662,11 +766,11 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
|
|
* structure from the start to nodeoffset.
|
|
*
|
|
* returns:
|
|
-
|
|
* structure block offset of the node at node offset's ancestor
|
|
* of depth supernodedepth (>=0), on success
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
-* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
|
|
+ * nodeoffset
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -688,7 +792,7 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
|
|
*
|
|
* returns:
|
|
* depth of the node at nodeoffset (>=0), on success
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -711,7 +815,7 @@ int fdt_node_depth(const void *fdt, int nodeoffset);
|
|
* returns:
|
|
* structure block offset of the parent of the node at nodeoffset
|
|
* (>=0), on success
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -751,7 +855,7 @@ int fdt_parent_offset(const void *fdt, int nodeoffset);
|
|
* on success
|
|
* -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
|
|
* tree after startoffset
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -798,7 +902,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
|
|
* 1, if the node has a 'compatible' property, but it does not list
|
|
* the given string
|
|
* -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
|
|
- * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -835,7 +939,7 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
|
* on success
|
|
* -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
|
|
* tree after startoffset
|
|
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -858,6 +962,68 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
|
*/
|
|
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
|
|
|
|
+/**
|
|
+ * fdt_stringlist_count - count the number of strings in a string list
|
|
+ * @fdt: pointer to the device tree blob
|
|
+ * @nodeoffset: offset of a tree node
|
|
+ * @property: name of the property containing the string list
|
|
+ * @return:
|
|
+ * the number of strings in the given property
|
|
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
|
|
+ * -FDT_ERR_NOTFOUND if the property does not exist
|
|
+ */
|
|
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
|
|
+
|
|
+/**
|
|
+ * fdt_stringlist_search - find a string in a string list and return its index
|
|
+ * @fdt: pointer to the device tree blob
|
|
+ * @nodeoffset: offset of a tree node
|
|
+ * @property: name of the property containing the string list
|
|
+ * @string: string to look up in the string list
|
|
+ *
|
|
+ * Note that it is possible for this function to succeed on property values
|
|
+ * that are not NUL-terminated. That's because the function will stop after
|
|
+ * finding the first occurrence of @string. This can for example happen with
|
|
+ * small-valued cell properties, such as #address-cells, when searching for
|
|
+ * the empty string.
|
|
+ *
|
|
+ * @return:
|
|
+ * the index of the string in the list of strings
|
|
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
|
|
+ * -FDT_ERR_NOTFOUND if the property does not exist or does not contain
|
|
+ * the given string
|
|
+ */
|
|
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
|
|
+ const char *string);
|
|
+
|
|
+/**
|
|
+ * fdt_stringlist_get() - obtain the string at a given index in a string list
|
|
+ * @fdt: pointer to the device tree blob
|
|
+ * @nodeoffset: offset of a tree node
|
|
+ * @property: name of the property containing the string list
|
|
+ * @index: index of the string to return
|
|
+ * @lenp: return location for the string length or an error code on failure
|
|
+ *
|
|
+ * Note that this will successfully extract strings from properties with
|
|
+ * non-NUL-terminated values. For example on small-valued cell properties
|
|
+ * this function will return the empty string.
|
|
+ *
|
|
+ * If non-NULL, the length of the string (on success) or a negative error-code
|
|
+ * (on failure) will be stored in the integer pointer to by lenp.
|
|
+ *
|
|
+ * @return:
|
|
+ * A pointer to the string at the given index in the string list or NULL on
|
|
+ * failure. On success the length of the string will be stored in the memory
|
|
+ * location pointed to by the lenp parameter, if non-NULL. On failure one of
|
|
+ * the following negative error codes will be returned in the lenp parameter
|
|
+ * (if non-NULL):
|
|
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
|
|
+ * -FDT_ERR_NOTFOUND if the property does not exist
|
|
+ */
|
|
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
|
|
+ const char *property, int index,
|
|
+ int *lenp);
|
|
+
|
|
/**********************************************************************/
|
|
/* Read-only functions (addressing related) */
|
|
/**********************************************************************/
|
|
@@ -883,7 +1049,8 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
|
|
* returns:
|
|
* 0 <= n < FDT_MAX_NCELLS, on success
|
|
* 2, if the node has no #address-cells property
|
|
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid #address-cells property
|
|
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
|
|
+ * #address-cells property
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -903,7 +1070,8 @@ int fdt_address_cells(const void *fdt, int nodeoffset);
|
|
* returns:
|
|
* 0 <= n < FDT_MAX_NCELLS, on success
|
|
* 2, if the node has no #address-cells property
|
|
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid #size-cells property
|
|
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
|
|
+ * #size-cells property
|
|
* -FDT_ERR_BADMAGIC,
|
|
* -FDT_ERR_BADVERSION,
|
|
* -FDT_ERR_BADSTATE,
|
|
@@ -918,6 +1086,27 @@ int fdt_size_cells(const void *fdt, int nodeoffset);
|
|
/**********************************************************************/
|
|
|
|
/**
|
|
+ * fdt_setprop_inplace_namelen_partial - change a property's value,
|
|
+ * but not its size
|
|
+ * @fdt: pointer to the device tree blob
|
|
+ * @nodeoffset: offset of the node whose property to change
|
|
+ * @name: name of the property to change
|
|
+ * @namelen: number of characters of name to consider
|
|
+ * @idx: index of the property to change in the array
|
|
+ * @val: pointer to data to replace the property value with
|
|
+ * @len: length of the property value
|
|
+ *
|
|
+ * Identical to fdt_setprop_inplace(), but modifies the given property
|
|
+ * starting from the given index, and using only the first characters
|
|
+ * of the name. It is useful when you want to manipulate only one value of
|
|
+ * an array and you have a string that doesn't end with \0.
|
|
+ */
|
|
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
|
|
+ const char *name, int namelen,
|
|
+ uint32_t idx, const void *val,
|
|
+ int len);
|
|
+
|
|
+/**
|
|
* fdt_setprop_inplace - change a property's value, but not its size
|
|
* @fdt: pointer to the device tree blob
|
|
* @nodeoffset: offset of the node whose property to change
|
|
@@ -1527,9 +1716,11 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
|
|
* change the offsets of some existing nodes.
|
|
|
|
* returns:
|
|
- * structure block offset of the created nodeequested subnode (>=0), on success
|
|
+ * structure block offset of the created nodeequested subnode (>=0), on
|
|
+ * success
|
|
* -FDT_ERR_NOTFOUND, if the requested subnode does not exist
|
|
- * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
|
|
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
|
|
+ * tag
|
|
* -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
|
|
* the given name
|
|
* -FDT_ERR_NOSPACE, if there is insufficient free space in the
|
|
diff --git a/core/secure_dt.mk b/core/secure_dt.mk
|
|
new file mode 100644
|
|
index 0000000..16ea741
|
|
--- /dev/null
|
|
+++ b/core/secure_dt.mk
|
|
@@ -0,0 +1,107 @@
|
|
+#
|
|
+# Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
|
|
+#
|
|
+# SPDX-License-Identifier: BSD-3-Clause
|
|
+#
|
|
+
|
|
+DTC_FLAGS += -I dts -O dtb
|
|
+DTC := dtc
|
|
+
|
|
+define MAKE_PREREQ_DIR
|
|
+ifneq (${1},${2})
|
|
+${1} :
|
|
+ @mkdir -p "${1}"
|
|
+
|
|
+endif
|
|
+endef
|
|
+
|
|
+# Convert device tree source file names to matching blobs
|
|
+# $(1) = input dts
|
|
+define SOURCES_TO_DTBS
|
|
+ $(notdir $(patsubst %.dts,%.dtb,$(filter %.dts,$(1))))
|
|
+endef
|
|
+
|
|
+# MAKE_FDT_DIRS macro creates the prerequisite directories that host the
|
|
+# FDT binaries
|
|
+# $(1) = output directory
|
|
+# $(2) = input dts
|
|
+define MAKE_FDT_DIRS
|
|
+ $(eval DTBS := $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2))))
|
|
+ $(eval TEMP_DTB_DIRS := $(sort $(dir ${DTBS})))
|
|
+ # The $(dir ) function leaves a trailing / on the directory names
|
|
+ # Rip off the / to match directory names with make rule targets.
|
|
+ $(eval DTB_DIRS := $(patsubst %/,%,$(TEMP_DTB_DIRS)))
|
|
+
|
|
+$(eval $(foreach objd,${DTB_DIRS},$(call MAKE_PREREQ_DIR,${objd},${out-dir})))
|
|
+
|
|
+fdt_dirs: ${DTB_DIRS}
|
|
+endef
|
|
+
|
|
+# MAKE_DTB generate the Flattened device tree binary
|
|
+# $(1) = output directory
|
|
+# $(2) = input dts
|
|
+define MAKE_DTB
|
|
+
|
|
+# List of DTB file(s) to generate, based on DTS file basename list
|
|
+$(eval DTBOBJ := $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2))))
|
|
+# List of the pre-compiled DTS file(s)
|
|
+$(eval DTSPRE := $(addprefix $(1)/,$(patsubst %.dts,%.pre.dts,$(notdir $(2)))))
|
|
+# Dependencies of the pre-compiled DTS file(s) on its source and included files
|
|
+$(eval DTSDEP := $(patsubst %.dtb,%.o.d,$(DTBOBJ)))
|
|
+# Dependencies of the DT compilation on its pre-compiled DTS
|
|
+$(eval DTBDEP := $(patsubst %.dtb,%.d,$(DTBOBJ)))
|
|
+
|
|
+$(DTBOBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | fdt_dirs
|
|
+ @echo " CPP $$<"
|
|
+ $(eval DTBS := $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2))))
|
|
+ @$(CPP$(sm)) $$(CPPFLAGS) -Icore/include/ -x assembler-with-cpp \
|
|
+ -E -ffreestanding -MT $(DTBS) -MMD -MF $(DTSDEP) -o $(DTSPRE) $$<
|
|
+ @echo " DTC $$<"
|
|
+ @$(DTC) $$(DTC_FLAGS) -d $(DTBDEP) -o $$@ $(DTSPRE)
|
|
+
|
|
+-include $(DTBDEP)
|
|
+-include $(DTSDEP)
|
|
+endef
|
|
+
|
|
+# MAKE_DTBS builds flattened device tree sources
|
|
+# $(1) = output directory
|
|
+# $(2) = list of flattened device tree source files
|
|
+define MAKE_DTBS
|
|
+ $(eval DTBOBJS := $(filter %.dts,$(2)))
|
|
+ $(eval REMAIN := $(filter-out %.dts,$(2)))
|
|
+ $(and $(REMAIN),$(error FDT_SOURCES contain non-DTS files: $(REMAIN)))
|
|
+ $(eval $(foreach obj,$(DTBOBJS),$(call MAKE_DTB,$(1),$(obj))))
|
|
+ $(eval $(call MAKE_FDT_DIRS,$(1),$(2)))
|
|
+
|
|
+dtbs: $(DTBS)
|
|
+all: dtbs
|
|
+endef
|
|
+
|
|
+# Generating the DTB from the DTS and integrating into OP-TEE core.
|
|
+#
|
|
+# CFG_SECURE_DT provides the DTS base name. It is looked up as file
|
|
+# name as $(CFG_SECURE_DT).dts from the platform sub directory fdts/.
|
|
+#
|
|
+# Build precompiles $(CFG_SECURE_DT).dts to resolve pre compilation features
|
|
+# (build directive, file inclusion, ...) then generates $(CFG_SECURE_DT).dtb.
|
|
+#
|
|
+# If CFG_STATIC_SECURE_DT is enabled, $(CFG_SECURE_DT).dtb content is wrapped
|
|
+# into C source file builtin_secure_dtb.c that defines the DTB byte array
|
|
+# CFG_STATIC_SECURE_DT expects.
|
|
+
|
|
+DTB_FILE_NAME := $(CFG_SECURE_DT).dtb
|
|
+FDT_SOURCES := $(addprefix $(arch-dir)/fdts/, $(CFG_SECURE_DT).dts)
|
|
+
|
|
+DTC_FLAGS += -Wno-unit_address_vs_reg
|
|
+
|
|
+$(eval $(call MAKE_DTBS,$(out-dir)/core/fdts,$(FDT_SOURCES)))
|
|
+
|
|
+ifeq ($(CFG_STATIC_SECURE_DT),y)
|
|
+gensrcs-y += builtin_secure_dtb
|
|
+produce-builtin_secure_dtb = fdts/builtin_secure_dtb.c
|
|
+depends-builtin_secure_dtb = $(DTBOBJ) scripts/ta_bin_to_c.py
|
|
+recipe-builtin_secure_dtb = scripts/bin_to_c.py --dtb $(DTBOBJ) \
|
|
+ --label static_secure_dtb \
|
|
+ --out $(out-dir)/core/fdts/builtin_secure_dtb.c
|
|
+cleanfiles += $(out-dir)/core/fdts/builtin_secure_dtb.c
|
|
+endif
|
|
diff --git a/core/sub.mk b/core/sub.mk
|
|
index 9aee1b4..3119b58 100644
|
|
--- a/core/sub.mk
|
|
+++ b/core/sub.mk
|
|
@@ -26,3 +26,10 @@ $(foreach f, $(EARLY_TA_PATHS), $(eval $(call process_early_ta,$(f))))
|
|
$(foreach f, $(CFG_IN_TREE_EARLY_TAS), $(eval $(call \
|
|
process_early_ta,$(out-dir)/ta/$(f).stripped.elf)))
|
|
endif
|
|
+
|
|
+#
|
|
+# Secure device tree support
|
|
+#
|
|
+ifneq ($(CFG_SECURE_DT),)
|
|
+include core/secure_dt.mk
|
|
+endif
|
|
diff --git a/lib/libutils/ext/include/util.h b/lib/libutils/ext/include/util.h
|
|
index 4561758..2509b07 100644
|
|
--- a/lib/libutils/ext/include/util.h
|
|
+++ b/lib/libutils/ext/include/util.h
|
|
@@ -49,9 +49,16 @@
|
|
|
|
/* Round down the even multiple of size, size has to be a multiple of 2 */
|
|
#define ROUNDDOWN(v, size) ((v) & ~((__typeof__(v))(size) - 1))
|
|
+
|
|
+/* Unsigned integer division with nearest rounding variant */
|
|
+#define UDIV_ROUND_NEAREST(x, y) \
|
|
+ (__extension__ ({ __typeof__(x) _x = (x); \
|
|
+ __typeof__(y) _y = (y); \
|
|
+ (_x + (_y / 2)) / _y; }))
|
|
#else
|
|
#define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y))
|
|
#define ROUNDDOWN(x, y) (((x) / (y)) * (y))
|
|
+#define UDIV_ROUND_NEAREST(x, y) (((x) + ((y) / 2)) / (y))
|
|
#endif
|
|
|
|
/* x has to be of an unsigned type */
|
|
diff --git a/mk/config.mk b/mk/config.mk
|
|
index e0c050a..21ba469 100644
|
|
--- a/mk/config.mk
|
|
+++ b/mk/config.mk
|
|
@@ -236,10 +236,25 @@ CFG_CORE_SANITIZE_UNDEFINED ?= n
|
|
CFG_CORE_SANITIZE_KADDRESS ?= n
|
|
|
|
# Device Tree support
|
|
-# When enabled, the TEE _start function expects to find the address of a
|
|
-# Device Tree Blob (DTB) in register r2. The DT parsing code relies on
|
|
-# libfdt. Currently only used to add the optee node and a reserved-memory
|
|
-# node for shared memory.
|
|
+#
|
|
+# - When CFG_DT is enabled, the libfdt is embedded in the core. It allows
|
|
+# to parse a device tree.
|
|
+# - When CFG_STATIC_SECURE_DT is enabled, TEE core embeds a statically linked
|
|
+# device tree blob.
|
|
+# - When CFG_SECURE_DT is defined, it defines the base name of the device tree
|
|
+# source file from which a device tree blob is generated at core build.
|
|
+#
|
|
+# If CFG_DT is enabled and CFG_STATIC_SECURE_DT is disabled, the TEE _start
|
|
+# function expects to find the address of a Device Tree Blob (DTB) in
|
|
+# register r2 and is currently only used to add the optee node and a
|
|
+# reserved-memory node for shared memory.
|
|
+ifneq ($(CFG_SECURE_DT),)
|
|
+$(call force,CFG_STATIC_SECURE_DT,y)
|
|
+endif
|
|
+CFG_STATIC_SECURE_DT ?= n
|
|
+ifeq ($(CFG_STATIC_SECURE_DT),y)
|
|
+$(call force,CFG_DT,y)
|
|
+endif
|
|
CFG_DT ?= n
|
|
|
|
# Maximum size of the Device Tree Blob, has to be large enough to allow
|
|
diff --git a/scripts/bin_to_c.py b/scripts/bin_to_c.py
|
|
new file mode 100755
|
|
index 0000000..6fe7772
|
|
--- /dev/null
|
|
+++ b/scripts/bin_to_c.py
|
|
@@ -0,0 +1,58 @@
|
|
+#!/usr/bin/env python
|
|
+# SPDX-License-Identifier: BSD-2-Clause
|
|
+#
|
|
+# Copyright (c) 2018, Linaro Limited
|
|
+#
|
|
+
|
|
+import argparse
|
|
+import array
|
|
+import os
|
|
+import re
|
|
+
|
|
+def get_args():
|
|
+
|
|
+ parser = argparse.ArgumentParser(description='Converts a binary '
|
|
+ 'file into C source file defining binary data as a'
|
|
+ 'constant byte array.')
|
|
+
|
|
+ parser.add_argument('--dtb', required=True,
|
|
+ help='Path to the input binary file')
|
|
+
|
|
+ parser.add_argument('--label', required=True,
|
|
+ help='Label for the generated table in the C source file.')
|
|
+
|
|
+ parser.add_argument('--out', required=True,
|
|
+ help='Path of the output C file')
|
|
+
|
|
+ return parser.parse_args()
|
|
+
|
|
+def main():
|
|
+
|
|
+ args = get_args();
|
|
+
|
|
+ with open(args.dtb, 'rb') as indata:
|
|
+ bytes = indata.read()
|
|
+ size = len(bytes)
|
|
+
|
|
+ f = open(args.out, 'w')
|
|
+ f.write('/* Generated from ' + args.dtb + ' by ' +
|
|
+ os.path.basename(__file__) + ' */\n\n')
|
|
+ f.write('#include <compiler.h>\n');
|
|
+ f.write('#include <stdint.h>\n');
|
|
+ f.write('__extension__ const uint8_t ' + args.label + '[] ' +
|
|
+ ' __aligned(__alignof__(uint64_t)) = {\n')
|
|
+ i = 0
|
|
+ while i < size:
|
|
+ if i % 8 == 0:
|
|
+ f.write('\t\t');
|
|
+ f.write('0x' + '{:02x}'.format(ord(bytes[i])) + ',')
|
|
+ i = i + 1
|
|
+ if i % 8 == 0 or i == size:
|
|
+ f.write('\n')
|
|
+ else:
|
|
+ f.write(' ')
|
|
+ f.write('};\n');
|
|
+ f.close()
|
|
+
|
|
+if __name__ == "__main__":
|
|
+ main()
|
|
--
|
|
2.7.4
|
|
|