From da84a97b7083d65144ed658e0f0def1232b7852c Mon Sep 17 00:00:00 2001 From: christophe montaud 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 = ; + st,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 for STMicroelectronics. + */ +#include + +/ { + 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 = , /* QSPI_BK1_IO0 */ + , /* QSPI_BK1_IO1 */ + , /* QSPI_BK1_IO2 */ + ; /* QSPI_BK1_IO3 */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + pins2 { + pinmux = ; /* QSPI_BK1_NCS */ + bias-pull-up; + drive-push-pull; + slew-rate = <1>; + }; + }; + + qspi_bk2_pins_a: qspi-bk2-0 { + pins1 { + pinmux = , /* QSPI_BK2_IO0 */ + , /* QSPI_BK2_IO1 */ + , /* QSPI_BK2_IO2 */ + ; /* QSPI_BK2_IO3 */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + pins2 { + pinmux = ; /* QSPI_BK2_NCS */ + bias-pull-up; + drive-push-pull; + slew-rate = <1>; + }; + }; + + qspi_clk_pins_a: qspi-clk-0 { + pins { + pinmux = ; /* QSPI_CLK */ + bias-disable; + drive-push-pull; + slew-rate = <3>; + }; + }; + + sdmmc1_b4_pins_a: sdmmc1-b4-0 { + pins1 { + pinmux = , /* SDMMC1_D0 */ + , /* SDMMC1_D1 */ + , /* SDMMC1_D2 */ + , /* SDMMC1_D3 */ + ; /* SDMMC1_CMD */ + slew-rate = <1>; + drive-push-pull; + bias-disable; + }; + pins2 { + pinmux = ; /* SDMMC1_CK */ + slew-rate = <2>; + drive-push-pull; + bias-disable; + }; + }; + + sdmmc1_dir_pins_a: sdmmc1-dir-0 { + pins1 { + pinmux = , /* SDMMC1_D0DIR */ + , /* SDMMC1_D123DIR */ + ; /* SDMMC1_CDIR */ + slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + pins2{ + pinmux = ; /* SDMMC1_CKIN */ + bias-pull-up; + }; + }; + + sdmmc1_dir_pins_b: sdmmc1-dir-1 { + pins1 { + pinmux = , /* SDMMC1_D0DIR */ + , /* SDMMC1_D123DIR */ + ; /* SDMMC1_CDIR */ + slew-rate = <3>; + drive-push-pull; + bias-pull-up; + }; + pins2 { + pinmux = ; /* SDMMC1_CKIN */ + bias-pull-up; + }; + }; + + sdmmc2_b4_pins_a: sdmmc2-b4-0 { + pins1 { + pinmux = , /* SDMMC2_D0 */ + , /* SDMMC2_D1 */ + , /* SDMMC2_D2 */ + , /* SDMMC2_D3 */ + ; /* SDMMC2_CMD */ + slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + pins2 { + pinmux = ; /* SDMMC2_CK */ + slew-rate = <2>; + drive-push-pull; + bias-pull-up; + }; + }; + + sdmmc2_d47_pins_a: sdmmc2-d47-0 { + pins { + pinmux = , /* SDMMC2_D4 */ + , /* SDMMC2_D5 */ + , /* SDMMC2_D6 */ + ; /* SDMMC2_D7 */ + slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + }; + + uart4_pins_a: uart4-0 { + pins1 { + pinmux = ; /* UART4_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART4_RX */ + bias-disable; + }; + }; + + uart7_pins_a: uart7-0 { + pins1 { + pinmux = ; /* USART7_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* USART7_RX */ + bias-disable; + }; + }; + + usart3_pins_a: usart3-0 { + pins1 { + pinmux = , /* USART3_TX */ + ; /* USART3_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART3_RX */ + ; /* USART3_CTS_NSS */ + bias-disable; + }; + }; + + usart3_pins_b: usart3-1 { + pins1 { + pinmux = , /* USART3_TX */ + ; /* USART3_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART3_RX */ + ; /* 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 = , /* I2C4_SCL */ + ; /* 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 . + */ + +/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 +#include +#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 = ; +}; + +&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 . + */ + +/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 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 +#include +#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 = ; +}; + +&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 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 + +/ { + soc { + iwdg1: iwdg@5C003000 { + compatible = "st,stm32mp1-iwdg"; + reg = <0x5C003000 0x400>; + clocks = <&rcc IWDG1>, <&rcc CK_LSI>; + clock-names = "pclk", "lsi"; + interrupts = ; + 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 = ; +}; + +&rcc { + secure-interrupts = ; + interrupt-names = "wakeup"; +}; + +&sdmmc1 { + compatible = "st,stm32-sdmmc2"; +}; + +&sdmmc2 { + compatible = "st,stm32-sdmmc2"; +}; + +&tamp { + compatible = "st,stm32-tamp"; + clocks = <&rcc RTCAPB>; + interrupts = ; + 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 for STMicroelectronics. + */ +#include +#include +#include + +/ { + #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 = ; + }; + + 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 = ; + clocks = <&rcc CRYP1>; + resets = <&rcc CRYP1_R>; + status = "disabled"; + }; + + hash1: hash@54002000 { + compatible = "st,stm32f756-hash"; + reg = <0x54002000 0x400>; + interrupts = ; + 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 = ; + 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 + */ + +#include "stm32mp157-pinctrl.dtsi" +/ { + soc { + pinctrl: pin-controller@50002000 { + st,package = ; + + 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 = ; + + 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 + */ + +#include "stm32mp157-pinctrl.dtsi" +/ { + soc { + pinctrl: pin-controller@50002000 { + st,package = ; + + 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 + */ + +#include "stm32mp157-pinctrl.dtsi" +/ { + soc { + pinctrl: pin-controller@50002000 { + st,package = ; + + 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 = ; + + 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 + */ + +#include "stm32mp157-pinctrl.dtsi" +/ { + soc { + pinctrl: pin-controller@50002000 { + st,package = ; + + 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 #include #include #include @@ -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 #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 +#include + 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 #include +#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 +#include +#include +#include +#include +#include + +#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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CFG_DT +#include +#include +#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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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 +#include +#include + +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 + +#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 +#include +#include +#include +#include + +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 +#include + +#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_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_PLLCR 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_PLLCFGR1 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_PLLCFGR2 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_PLLFRACR 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_PLLCSGR 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 #include #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include +#include +#include #include #include +#include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include -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 +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#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 + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +#ifdef CFG_DT +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(' +#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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/******************************************************************************* + * 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 +#include + +#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 +#include +#include + +#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 #include #include +#include #include #include #include +#include #include -#include #include +#include /* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CFG_DT +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CFG_DT +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CFG_DT +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include #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 +#include +#include +#include +#include +#include +#include + +#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 or ldo (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 or ldo (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 or ldo (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 or ldo (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 or ldo (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 #include +/* 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 +#include + +#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 +#include + +#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 +#include +#include + +#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 +#include +#include +#include + +/* + * 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 + +#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 +#include + +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 + +#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 +#include -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 +#include + +#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 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 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 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 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 + + 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 #include @@ -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 \n'); + f.write('#include \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