From 423ad55f481adefb98fdd57f391e185213733ec6 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Mon, 15 Mar 2021 17:31:45 +0100 Subject: [PATCH] st update v2.4-r1.0.0 --- .editorconfig | 6 +- CONTRIBUTING.md | 30 + Makefile | 10 +- bl1/aarch32/bl1_entrypoint.S | 14 +- bl2/aarch32/bl2_el3_entrypoint.S | 5 +- bl2/aarch32/bl2_el3_exceptions.S | 9 + bl2/aarch32/bl2_entrypoint.S | 9 + bl2/bl2_image_load_v2.c | 14 +- bl2u/aarch32/bl2u_entrypoint.S | 9 + bl32/sp_min/aarch32/entrypoint.S | 22 +- bl32/sp_min/sp_min.ld.S | 10 +- common/aarch32/debug.S | 52 +- common/bl_common.c | 2 +- common/fdt_fixup.c | 44 +- docs/devicetree/bindings/arm/secure.txt | 53 + .../bindings/clock/st,stm32mp1-rcc.txt | 496 +++++ docs/devicetree/bindings/i2c/i2c-stm32.txt | 54 + .../memory-controllers/st,stm32mp1-ddr.txt | 301 +++ docs/devicetree/bindings/mmc/mmci.txt | 72 + .../bindings/mmc/st,stm32-sdmmc2.txt | 22 + .../bindings/power/st,stm32mp1-pwr.txt | 43 + docs/devicetree/bindings/power/st,stpmic1.txt | 94 + .../bindings/reset/st,stm32mp1-rcc.txt | 6 + docs/devicetree/bindings/rng/st,stm32-rng.txt | 23 + .../bindings/serial/st,stm32-usart.txt | 88 + .../bindings/soc/st,stm32-etzpc.txt | 56 + .../bindings/soc/st,stm32-romem.txt | 74 + .../bindings/soc/st,stm32-stgen.txt | 18 + .../devicetree/bindings/soc/st,stm32-tamp.txt | 22 + .../bindings/watchdog/st,stm32-iwdg.txt | 28 + docs/getting_started/porting-guide.rst | 25 + docs/plat/stm32mp1.rst | 169 +- drivers/arm/tzc/tzc400.c | 135 +- drivers/arm/tzc/tzc_common_private.h | 21 + drivers/auth/auth_mod.c | 14 + drivers/auth/mbedtls/mbedtls_common.mk | 2 +- drivers/auth/mbedtls/mbedtls_x509_parser.c | 4 +- drivers/auth/tbbr/tbbr_cot_bl1.c | 15 - drivers/auth/tbbr/tbbr_cot_bl2.c | 1 + drivers/auth/tbbr/tbbr_cot_common.c | 15 + drivers/clk/clk.c | 64 + drivers/io/io_mtd.c | 68 +- drivers/mmc/mmc.c | 67 +- drivers/mtd/nand/core.c | 43 +- drivers/mtd/nor/spi_nor.c | 20 +- drivers/st/bsec/{bsec.c => bsec2.c} | 534 +++-- drivers/st/clk/stm32mp1_calib.c | 536 +++++ drivers/st/clk/stm32mp1_clk.c | 1829 ++++++++++++++--- drivers/st/clk/stm32mp_clkfunc.c | 220 +- drivers/st/crypto/stm32_hash.c | 19 +- drivers/st/ddr/stm32mp1_ddr.c | 331 ++- drivers/st/ddr/stm32mp1_ddr_helpers.c | 593 +++++- drivers/st/ddr/stm32mp1_ram.c | 120 +- drivers/st/etzpc/etzpc.c | 50 +- drivers/st/fmc/stm32_fmc2_nand.c | 5 +- drivers/st/gpio/stm32_gpio.c | 9 +- drivers/st/i2c/stm32_i2c.c | 460 ++++- drivers/st/io/io_stm32image.c | 196 +- drivers/st/iwdg/stm32_iwdg.c | 151 +- drivers/st/mmc/stm32_sdmmc2.c | 125 +- drivers/st/pmic/stm32mp_pmic.c | 484 ++++- drivers/st/pmic/stpmic1.c | 116 +- .../st/regulator/stm32mp_dummy_regulator.c | 27 + drivers/st/regulator/stm32mp_regulator.c | 38 + drivers/st/reset/stm32mp1_reset.c | 31 +- drivers/st/rng/stm32_rng.c | 193 ++ drivers/st/rtc/stm32_rtc.c | 480 +++++ drivers/st/spi/stm32_qspi.c | 5 +- drivers/st/tamper/stm32_tamp.c | 379 ++++ drivers/st/timer/stm32_timer.c | 323 +++ drivers/st/uart/aarch32/stm32_console.S | 23 +- drivers/st/uart/stm32_uart.c | 405 ++++ drivers/st/usb_dwc2/usb_dwc2.c | 1094 ++++++++++ fdts/stm32mp15-bl2.dtsi | 116 ++ fdts/stm32mp15-bl32.dtsi | 42 + fdts/stm32mp15-ddr-1g-fw-config.dts | 63 + fdts/stm32mp15-ddr-512m-fw-config.dts | 63 + fdts/stm32mp15-ddr.dtsi | 12 + fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi | 4 +- fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi | 4 +- fdts/stm32mp15-pinctrl.dtsi | 87 +- fdts/stm32mp15-ssp-bl2.dtsi | 125 ++ fdts/stm32mp151.dtsi | 95 +- fdts/stm32mp153.dtsi | 1 + fdts/stm32mp157a-avenger96-fw-config.dts | 6 + fdts/stm32mp157a-avenger96.dts | 20 + fdts/stm32mp157a-dk1-fw-config.dts | 6 + fdts/stm32mp157a-dk1.dts | 18 + fdts/stm32mp157a-ed1-fw-config.dts | 6 + fdts/stm32mp157a-ed1.dts | 38 + fdts/stm32mp157a-ev1-fw-config.dts | 6 + fdts/stm32mp157a-ev1.dts | 24 + fdts/stm32mp157c-dk2-fw-config.dts | 6 + fdts/stm32mp157c-dk2.dts | 18 + fdts/stm32mp157c-ed1-fw-config.dts | 6 + fdts/stm32mp157c-ed1.dts | 329 +-- fdts/stm32mp157c-ev1-fw-config.dts | 6 + fdts/stm32mp157c-ev1.dts | 47 +- fdts/stm32mp157d-dk1-fw-config.dts | 6 + fdts/stm32mp157d-dk1.dts | 45 + fdts/stm32mp157d-ed1-fw-config.dts | 6 + fdts/stm32mp157d-ed1.dts | 39 + fdts/stm32mp157d-ev1-fw-config.dts | 6 + fdts/stm32mp157d-ev1.dts | 23 + fdts/stm32mp157f-dk2-fw-config.dts | 6 + fdts/stm32mp157f-dk2.dts | 51 + fdts/stm32mp157f-ed1-fw-config.dts | 6 + fdts/stm32mp157f-ed1.dts | 43 + fdts/stm32mp157f-ev1-fw-config.dts | 6 + fdts/stm32mp157f-ev1.dts | 23 + fdts/stm32mp15xa.dtsi | 13 + fdts/stm32mp15xc.dtsi | 3 + fdts/stm32mp15xd.dtsi | 19 + fdts/stm32mp15xf.dtsi | 21 + fdts/stm32mp15xx-dkx.dtsi | 180 +- fdts/stm32mp15xx-edx.dtsi | 518 +++++ fdts/stm32mp15xx-evx.dtsi | 73 + fdts/stm32mp15xxaa-pinctrl.dtsi | 3 +- fdts/stm32mp15xxab-pinctrl.dtsi | 2 +- fdts/stm32mp15xxac-pinctrl.dtsi | 3 +- fdts/stm32mp15xxad-pinctrl.dtsi | 2 +- include/arch/aarch32/arch.h | 18 +- include/arch/aarch32/arch_helpers.h | 4 + include/arch/aarch32/el3_common_macros.S | 42 +- include/arch/aarch64/arch.h | 6 +- include/common/bl_common.ld.h | 9 + include/common/tbbr/cot_def.h | 2 +- include/drivers/arm/tzc400.h | 44 +- include/drivers/auth/tbbr_cot_common.h | 1 + include/drivers/clk.h | 28 + include/drivers/io/io_mtd.h | 13 +- include/drivers/mmc.h | 27 +- include/drivers/nand.h | 12 +- include/drivers/st/bsec.h | 159 +- include/drivers/st/bsec2_reg.h | 98 + include/drivers/st/stm32_i2c.h | 41 +- include/drivers/st/stm32_iwdg.h | 1 + include/drivers/st/stm32_rng.h | 13 + include/drivers/st/stm32_rtc.h | 78 + include/drivers/st/stm32_sdmmc2.h | 2 + include/drivers/st/stm32_tamp.h | 163 ++ include/drivers/st/stm32_timer.h | 21 + include/drivers/st/stm32_uart.h | 170 ++ include/drivers/st/stm32mp1_calib.h | 20 + include/drivers/st/stm32mp1_clk.h | 52 +- include/drivers/st/stm32mp1_ddr.h | 13 +- include/drivers/st/stm32mp1_ddr_helpers.h | 20 +- include/drivers/st/stm32mp1_ddr_regs.h | 14 +- include/drivers/st/stm32mp1_pwr.h | 21 +- include/drivers/st/stm32mp1_ram.h | 3 +- include/drivers/st/stm32mp1_rcc.h | 32 +- include/drivers/st/stm32mp_clkfunc.h | 19 +- include/drivers/st/stm32mp_dummy_regulator.h | 14 + include/drivers/st/stm32mp_pmic.h | 41 +- include/drivers/st/stm32mp_regulator.h | 31 + include/drivers/st/stm32mp_reset.h | 14 +- include/drivers/st/stpmic1.h | 29 +- include/drivers/st/usb_dwc2.h | 19 + include/dt-bindings/clock/stm32mp1-clks.h | 6 + include/dt-bindings/power/stm32mp1-power.h | 19 + include/dt-bindings/reset/stm32mp1-resets.h | 2 + include/dt-bindings/soc/st,stm32-etzpc.h | 86 + include/dt-bindings/soc/stm32mp1-tzc400.h | 37 + include/lib/optee_utils.h | 1 + include/lib/psci/psci.h | 1 + include/lib/usb/usb_core.h | 277 +++ include/lib/usb/usb_st_dfu.h | 85 + include/lib/utils_def.h | 10 + include/plat/common/platform.h | 9 +- lib/aarch32/misc_helpers.S | 126 ++ lib/optee/optee_utils.c | 37 +- lib/psci/psci_private.h | 1 - lib/usb/usb_core.c | 838 ++++++++ lib/usb/usb_st_dfu.c | 537 +++++ make_helpers/defaults.mk | 8 +- plat/common/aarch32/platform_helpers.S | 34 + plat/common/plat_bl_common.c | 2 +- plat/st/common/bl2_io_storage.c | 506 ++--- plat/st/common/bl2_stm32_io_storage.c | 769 +++++++ plat/st/common/include/stm32cubeprogrammer.h | 67 + plat/st/common/include/stm32mp_auth.h | 19 - plat/st/common/include/stm32mp_common.h | 53 +- plat/st/common/include/stm32mp_dt.h | 16 +- plat/st/common/include/stm32mp_fconf_getter.h | 29 + plat/st/common/include/stm32mp_io_storage.h | 23 + .../st/common/include/stm32mp_shres_helpers.h | 65 +- plat/st/common/stm32_gic.c | 223 ++ plat/st/common/stm32cubeprogrammer_uart.c | 690 +++++++ plat/st/common/stm32cubeprogrammer_usb.c | 352 ++++ plat/st/common/stm32mp_auth.c | 90 - plat/st/common/stm32mp_common.c | 158 +- plat/st/common/stm32mp_cot.c | 114 + plat/st/common/stm32mp_crypto_lib.c | 447 ++++ plat/st/common/stm32mp_dt.c | 347 +++- plat/st/common/stm32mp_fconf_io.c | 136 ++ plat/st/common/stm32mp_img_parser_lib.c | 75 + plat/st/common/stm32mp_shres_helpers.c | 63 + plat/st/common/stm32mp_trusted_boot.c | 143 ++ plat/st/stm32mp1/bl2_plat_setup.c | 516 ++++- plat/st/stm32mp1/include/boot_api.h | 552 ++++- plat/st/stm32mp1/include/platform_def.h | 40 +- .../include/stm32mp15_mbedtls_config.h | 119 ++ plat/st/stm32mp1/include/stm32mp1_context.h | 29 +- .../stm32mp1/include/stm32mp1_critic_power.h | 22 + plat/st/stm32mp1/include/stm32mp1_low_power.h | 23 + .../stm32mp1/include/stm32mp1_power_config.h | 29 + plat/st/stm32mp1/include/stm32mp1_private.h | 33 +- .../include/stm32mp1_shared_resources.h | 17 + plat/st/stm32mp1/include/stm32mp1_smc.h | 87 +- plat/st/stm32mp1/plat_bl2_mem_params_desc.c | 71 +- .../stm32mp1/plat_bl2_stm32_mem_params_desc.c | 111 + plat/st/stm32mp1/plat_image_load.c | 35 +- plat/st/stm32mp1/platform.mk | 258 ++- plat/st/stm32mp1/services/bsec_svc.c | 455 +++- plat/st/stm32mp1/services/low_power_svc.c | 22 + plat/st/stm32mp1/services/low_power_svc.h | 14 + plat/st/stm32mp1/services/pwr_svc.c | 102 + plat/st/stm32mp1/services/pwr_svc.h | 12 + plat/st/stm32mp1/services/rcc_svc.c | 169 ++ plat/st/stm32mp1/services/rcc_svc.h | 14 + .../st/stm32mp1/services/stm32mp1_svc_setup.c | 34 +- plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk | 30 +- plat/st/stm32mp1/sp_min/sp_min_setup.c | 487 ++++- plat/st/stm32mp1/stm32mp1.S | 4 +- plat/st/stm32mp1/stm32mp1.ld.S | 14 +- plat/st/stm32mp1/stm32mp1_boot_device.c | 6 +- plat/st/stm32mp1/stm32mp1_context.c | 483 ++++- plat/st/stm32mp1/stm32mp1_critic_power.c | 91 + .../stm32mp1/stm32mp1_critic_power_wrapper.S | 62 + plat/st/stm32mp1/stm32mp1_dbgmcu.c | 48 +- plat/st/stm32mp1/stm32mp1_def.h | 438 ++-- plat/st/stm32mp1/stm32mp1_fconf_firewall.c | 124 ++ plat/st/stm32mp1/stm32mp1_gic.c | 92 - plat/st/stm32mp1/stm32mp1_helper.S | 135 +- plat/st/stm32mp1/stm32mp1_low_power.c | 455 ++++ plat/st/stm32mp1/stm32mp1_pm.c | 107 +- plat/st/stm32mp1/stm32mp1_power_config.c | 223 ++ plat/st/stm32mp1/stm32mp1_private.c | 625 +++++- plat/st/stm32mp1/stm32mp1_scmi.c | 131 +- plat/st/stm32mp1/stm32mp1_security.c | 119 +- plat/st/stm32mp1/stm32mp1_shared_resources.c | 211 +- plat/st/stm32mp1/stm32mp1_ssp.c | 1039 ++++++++++ plat/st/stm32mp1/stm32mp1_ssp.mk | 84 + plat/st/stm32mp1/stm32mp1_syscfg.c | 51 +- plat/st/stm32mp1/stm32mp1_usb.c | 491 +++++ tools/cert_create/Makefile | 8 +- tools/cert_create/include/key.h | 6 +- tools/cert_create/src/key.c | 20 +- tools/cert_create/src/main.c | 3 +- tools/encrypt_fw/Makefile | 8 +- tools/fiptool/Makefile | 8 +- tools/stm32image/stm32image.c | 46 +- 252 files changed, 26563 insertions(+), 3015 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 docs/devicetree/bindings/arm/secure.txt create mode 100644 docs/devicetree/bindings/clock/st,stm32mp1-rcc.txt create mode 100644 docs/devicetree/bindings/i2c/i2c-stm32.txt create mode 100644 docs/devicetree/bindings/memory-controllers/st,stm32mp1-ddr.txt create mode 100644 docs/devicetree/bindings/mmc/mmci.txt create mode 100644 docs/devicetree/bindings/mmc/st,stm32-sdmmc2.txt create mode 100644 docs/devicetree/bindings/power/st,stm32mp1-pwr.txt create mode 100644 docs/devicetree/bindings/power/st,stpmic1.txt create mode 100644 docs/devicetree/bindings/reset/st,stm32mp1-rcc.txt create mode 100644 docs/devicetree/bindings/rng/st,stm32-rng.txt create mode 100644 docs/devicetree/bindings/serial/st,stm32-usart.txt create mode 100644 docs/devicetree/bindings/soc/st,stm32-etzpc.txt create mode 100644 docs/devicetree/bindings/soc/st,stm32-romem.txt create mode 100644 docs/devicetree/bindings/soc/st,stm32-stgen.txt create mode 100644 docs/devicetree/bindings/soc/st,stm32-tamp.txt create mode 100644 docs/devicetree/bindings/watchdog/st,stm32-iwdg.txt create mode 100644 drivers/clk/clk.c rename drivers/st/bsec/{bsec.c => bsec2.c} (62%) create mode 100644 drivers/st/clk/stm32mp1_calib.c create mode 100644 drivers/st/regulator/stm32mp_dummy_regulator.c create mode 100644 drivers/st/regulator/stm32mp_regulator.c create mode 100644 drivers/st/rng/stm32_rng.c create mode 100644 drivers/st/rtc/stm32_rtc.c create mode 100644 drivers/st/tamper/stm32_tamp.c create mode 100644 drivers/st/timer/stm32_timer.c create mode 100644 drivers/st/uart/stm32_uart.c create mode 100644 drivers/st/usb_dwc2/usb_dwc2.c create mode 100644 fdts/stm32mp15-bl2.dtsi create mode 100644 fdts/stm32mp15-bl32.dtsi create mode 100644 fdts/stm32mp15-ddr-1g-fw-config.dts create mode 100644 fdts/stm32mp15-ddr-512m-fw-config.dts create mode 100644 fdts/stm32mp15-ssp-bl2.dtsi create mode 100644 fdts/stm32mp157a-avenger96-fw-config.dts create mode 100644 fdts/stm32mp157a-dk1-fw-config.dts create mode 100644 fdts/stm32mp157a-ed1-fw-config.dts create mode 100644 fdts/stm32mp157a-ed1.dts create mode 100644 fdts/stm32mp157a-ev1-fw-config.dts create mode 100644 fdts/stm32mp157a-ev1.dts create mode 100644 fdts/stm32mp157c-dk2-fw-config.dts create mode 100644 fdts/stm32mp157c-ed1-fw-config.dts create mode 100644 fdts/stm32mp157c-ev1-fw-config.dts create mode 100644 fdts/stm32mp157d-dk1-fw-config.dts create mode 100644 fdts/stm32mp157d-dk1.dts create mode 100644 fdts/stm32mp157d-ed1-fw-config.dts create mode 100644 fdts/stm32mp157d-ed1.dts create mode 100644 fdts/stm32mp157d-ev1-fw-config.dts create mode 100644 fdts/stm32mp157d-ev1.dts create mode 100644 fdts/stm32mp157f-dk2-fw-config.dts create mode 100644 fdts/stm32mp157f-dk2.dts create mode 100644 fdts/stm32mp157f-ed1-fw-config.dts create mode 100644 fdts/stm32mp157f-ed1.dts create mode 100644 fdts/stm32mp157f-ev1-fw-config.dts create mode 100644 fdts/stm32mp157f-ev1.dts create mode 100644 fdts/stm32mp15xa.dtsi create mode 100644 fdts/stm32mp15xd.dtsi create mode 100644 fdts/stm32mp15xf.dtsi create mode 100644 fdts/stm32mp15xx-edx.dtsi create mode 100644 fdts/stm32mp15xx-evx.dtsi create mode 100644 include/drivers/clk.h create mode 100644 include/drivers/st/bsec2_reg.h create mode 100644 include/drivers/st/stm32_rng.h create mode 100644 include/drivers/st/stm32_rtc.h create mode 100644 include/drivers/st/stm32_tamp.h create mode 100644 include/drivers/st/stm32_timer.h create mode 100644 include/drivers/st/stm32_uart.h create mode 100644 include/drivers/st/stm32mp1_calib.h create mode 100644 include/drivers/st/stm32mp_dummy_regulator.h create mode 100644 include/drivers/st/stm32mp_regulator.h create mode 100644 include/drivers/st/usb_dwc2.h create mode 100644 include/dt-bindings/power/stm32mp1-power.h create mode 100644 include/dt-bindings/soc/stm32mp1-tzc400.h create mode 100644 include/lib/usb/usb_core.h create mode 100644 include/lib/usb/usb_st_dfu.h create mode 100644 lib/usb/usb_core.c create mode 100644 lib/usb/usb_st_dfu.c create mode 100644 plat/st/common/bl2_stm32_io_storage.c create mode 100644 plat/st/common/include/stm32cubeprogrammer.h delete mode 100644 plat/st/common/include/stm32mp_auth.h create mode 100644 plat/st/common/include/stm32mp_fconf_getter.h create mode 100644 plat/st/common/include/stm32mp_io_storage.h create mode 100644 plat/st/common/stm32_gic.c create mode 100644 plat/st/common/stm32cubeprogrammer_uart.c create mode 100644 plat/st/common/stm32cubeprogrammer_usb.c delete mode 100644 plat/st/common/stm32mp_auth.c create mode 100644 plat/st/common/stm32mp_cot.c create mode 100644 plat/st/common/stm32mp_crypto_lib.c create mode 100644 plat/st/common/stm32mp_fconf_io.c create mode 100644 plat/st/common/stm32mp_img_parser_lib.c create mode 100644 plat/st/common/stm32mp_shres_helpers.c create mode 100644 plat/st/common/stm32mp_trusted_boot.c create mode 100644 plat/st/stm32mp1/include/stm32mp15_mbedtls_config.h create mode 100644 plat/st/stm32mp1/include/stm32mp1_critic_power.h create mode 100644 plat/st/stm32mp1/include/stm32mp1_low_power.h create mode 100644 plat/st/stm32mp1/include/stm32mp1_power_config.h create mode 100644 plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c create mode 100644 plat/st/stm32mp1/services/low_power_svc.c create mode 100644 plat/st/stm32mp1/services/low_power_svc.h create mode 100644 plat/st/stm32mp1/services/pwr_svc.c create mode 100644 plat/st/stm32mp1/services/pwr_svc.h create mode 100644 plat/st/stm32mp1/services/rcc_svc.c create mode 100644 plat/st/stm32mp1/services/rcc_svc.h create mode 100644 plat/st/stm32mp1/stm32mp1_critic_power.c create mode 100644 plat/st/stm32mp1/stm32mp1_critic_power_wrapper.S create mode 100644 plat/st/stm32mp1/stm32mp1_fconf_firewall.c delete mode 100644 plat/st/stm32mp1/stm32mp1_gic.c create mode 100644 plat/st/stm32mp1/stm32mp1_low_power.c create mode 100644 plat/st/stm32mp1/stm32mp1_power_config.c create mode 100644 plat/st/stm32mp1/stm32mp1_ssp.c create mode 100644 plat/st/stm32mp1/stm32mp1_ssp.mk create mode 100644 plat/st/stm32mp1/stm32mp1_usb.c diff --git a/.editorconfig b/.editorconfig index f523ca19da..12f786de5a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -38,10 +38,10 @@ indent_style = tab insert_final_newline = true # [LCS] Chapter 2: Breaking long lines and strings -# "The limit on the length of lines is 80 columns" +# "The limit on the length of lines is 100 columns" # This is a "soft" requirement for Arm-TF, and should not be the sole # reason for changes. -max_line_length = 80 +max_line_length = 100 # [LCS] Chapter 1: Indentation # "Tabs are 8 characters" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..3d1bacd78a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing guide + +This document serves as a checklist before contributing to this repository. It includes links to read up on if topics are unclear to you. + +This guide mainly focuses on the proper use of Git. + +## 1. Issues + +STM32MPU projects do not activate "Github issues" feature for the time being. If you need to report an issue or question about this project deliverables, you can report them using [ ST Support Center ](https://my.st.com/ols#/ols/newrequest) or [ ST Community MPU Forum ](https://community.st.com/s/topic/0TO0X0000003u2AWAQ/stm32-mpus). + +## 2. Pull Requests + +STMicrolectronics is happy to receive contributions from the community, based on an initial Contributor License Agreement (CLA) procedure. + +* If you are an individual writing original source code and you are sure **you own the intellectual property**, then you need to sign an Individual CLA (https://cla.st.com). +* If you work for a company that wants also to allow you to contribute with your work, your company needs to provide a Corporate CLA (https://cla.st.com) mentioning your GitHub account name. +* If you are not sure that a CLA (Individual or Corporate) has been signed for your GitHub account you can check here (https://cla.st.com). + +Please note that: +* The Corporate CLA will always take precedence over the Individual CLA. +* One CLA submission is sufficient, for any project proposed by STMicroelectronics. + +__How to proceed__ + +* We recommend to fork the project in your GitHub account to further develop your contribution. Please use the latest commit version. +* Please, submit one Pull Request for one new feature or proposal. This will ease the analysis and final merge if accepted. + +__Note__ + +Merge will not be done directly in GitHub but it will need first to follow internal integration process before public deliver in a standard release. The Pull request will stay open until it is merged and delivered. diff --git a/Makefile b/Makefile index 5c9186ece3..db60400b8b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -548,11 +548,9 @@ endif endif BL31_CFLAGS += -fpie BL31_LDFLAGS += $(PIE_LDFLAGS) -ifeq ($(ARCH),aarch64) BL32_CFLAGS += -fpie BL32_LDFLAGS += $(PIE_LDFLAGS) endif -endif ifeq (${ARCH},aarch64) BL1_CPPFLAGS += -DIMAGE_AT_EL3 @@ -918,6 +916,7 @@ $(eval $(call assert_booleans,\ RAS_TRAP_LOWER_EL_ERR_ACCESS \ COT_DESC_IN_DTB \ USE_SP804_TIMER \ + AARCH32_EXCEPTION_DEBUG \ ))) $(eval $(call assert_numerics,\ @@ -1007,6 +1006,7 @@ $(eval $(call add_defines,\ RAS_TRAP_LOWER_EL_ERR_ACCESS \ COT_DESC_IN_DTB \ USE_SP804_TIMER \ + AARCH32_EXCEPTION_DEBUG \ ))) ifeq (${SANITIZE_UB},trap) @@ -1224,7 +1224,7 @@ certtool: ${CRTTOOL} .PHONY: ${CRTTOOL} ${CRTTOOL}: - ${Q}${MAKE} PLAT=${PLAT} USE_TBBR_DEFS=${USE_TBBR_DEFS} COT=${COT} OPENSSL_DIR=${OPENSSL_DIR} CRTTOOL=${CRTTOOL} --no-print-directory -C ${CRTTOOLPATH} + ${Q}${MAKE} PLAT=${PLAT} USE_TBBR_DEFS=${USE_TBBR_DEFS} COT=${COT} CRTTOOL=${CRTTOOL} --no-print-directory -C ${CRTTOOLPATH} @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @${ECHO_BLANK_LINE} @@ -1299,7 +1299,7 @@ enctool: ${ENCTOOL} .PHONY: ${ENCTOOL} ${ENCTOOL}: - ${Q}${MAKE} PLAT=${PLAT} BUILD_INFO=0 OPENSSL_DIR=${OPENSSL_DIR} ENCTOOL=${ENCTOOL} --no-print-directory -C ${ENCTOOLPATH} + ${Q}${MAKE} PLAT=${PLAT} BUILD_INFO=0 ENCTOOL=${ENCTOOL} --no-print-directory -C ${ENCTOOLPATH} @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @${ECHO_BLANK_LINE} diff --git a/bl1/aarch32/bl1_entrypoint.S b/bl1/aarch32/bl1_entrypoint.S index 6a155660b6..09ad3b035e 100644 --- a/bl1/aarch32/bl1_entrypoint.S +++ b/bl1/aarch32/bl1_entrypoint.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,10 +21,19 @@ */ vector_base bl1_vector_table b bl1_entrypoint +#if AARCH32_EXCEPTION_DEBUG + b report_undef_inst /* Undef */ +#else b report_exception /* Undef */ +#endif b bl1_aarch32_smc_handler /* SMC call */ +#if AARCH32_EXCEPTION_DEBUG + b report_prefetch_abort /* Prefetch abort */ + b report_data_abort /* Data abort */ +#else b report_exception /* Prefetch abort */ b report_exception /* Data abort */ +#endif b report_exception /* Reserved */ b report_exception /* IRQ */ b report_exception /* FIQ */ @@ -49,7 +58,8 @@ func bl1_entrypoint _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ - _exception_vectors=bl1_vector_table + _exception_vectors=bl1_vector_table \ + _pie_fixup_size=0 /* ----------------------------------------------------- * Perform BL1 setup diff --git a/bl2/aarch32/bl2_el3_entrypoint.S b/bl2/aarch32/bl2_el3_entrypoint.S index 2e851e61a4..49d48980c0 100644 --- a/bl2/aarch32/bl2_el3_entrypoint.S +++ b/bl2/aarch32/bl2_el3_entrypoint.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,7 +26,8 @@ func bl2_entrypoint _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ - _exception_vectors=bl2_vector_table + _exception_vectors=bl2_vector_table \ + _pie_fixup_size=0 /* * Restore parameters of boot rom diff --git a/bl2/aarch32/bl2_el3_exceptions.S b/bl2/aarch32/bl2_el3_exceptions.S index 087b6656dc..dff4e36a4a 100644 --- a/bl2/aarch32/bl2_el3_exceptions.S +++ b/bl2/aarch32/bl2_el3_exceptions.S @@ -12,10 +12,19 @@ vector_base bl2_vector_table b bl2_entrypoint +#if AARCH32_EXCEPTION_DEBUG + b report_undef_inst /* Undef */ +#else b report_exception /* Undef */ +#endif b report_exception /* SVC call */ +#if AARCH32_EXCEPTION_DEBUG + b report_prefetch_abort /* Prefetch abort */ + b report_data_abort /* Data abort */ +#else b report_exception /* Prefetch abort */ b report_exception /* Data abort */ +#endif b report_exception /* Reserved */ b report_exception /* IRQ */ b report_exception /* FIQ */ diff --git a/bl2/aarch32/bl2_entrypoint.S b/bl2/aarch32/bl2_entrypoint.S index 102fd2f514..bfd721ca1d 100644 --- a/bl2/aarch32/bl2_entrypoint.S +++ b/bl2/aarch32/bl2_entrypoint.S @@ -14,10 +14,19 @@ vector_base bl2_vector_table b bl2_entrypoint +#if AARCH32_EXCEPTION_DEBUG + b report_undef_inst /* Undef */ +#else b report_exception /* Undef */ +#endif b report_exception /* SVC call */ +#if AARCH32_EXCEPTION_DEBUG + b report_prefetch_abort /* Prefetch abort */ + b report_data_abort /* Data abort */ +#else b report_exception /* Prefetch abort */ b report_exception /* Data abort */ +#endif b report_exception /* Reserved */ b report_exception /* IRQ */ b report_exception /* FIQ */ diff --git a/bl2/bl2_image_load_v2.c b/bl2/bl2_image_load_v2.c index 48c9beca6c..4fb0e030d0 100644 --- a/bl2/bl2_image_load_v2.c +++ b/bl2/bl2_image_load_v2.c @@ -74,17 +74,17 @@ struct entry_point_info *bl2_load_images(void) bl2_node_info->image_id, err); plat_error_handler(err); } + + /* Allow platform to handle image information. */ + err = bl2_plat_handle_post_image_load(bl2_node_info->image_id); + if (err != 0) { + ERROR("BL2: Failure in post image load handling (%i)\n", err); + plat_error_handler(err); + } } else { INFO("BL2: Skip loading image id %d\n", bl2_node_info->image_id); } - /* Allow platform to handle image information. */ - err = bl2_plat_handle_post_image_load(bl2_node_info->image_id); - if (err != 0) { - ERROR("BL2: Failure in post image load handling (%i)\n", err); - plat_error_handler(err); - } - /* Go to next image */ bl2_node_info = bl2_node_info->next_load_info; } diff --git a/bl2u/aarch32/bl2u_entrypoint.S b/bl2u/aarch32/bl2u_entrypoint.S index 6391f537cd..426176d98e 100644 --- a/bl2u/aarch32/bl2u_entrypoint.S +++ b/bl2u/aarch32/bl2u_entrypoint.S @@ -14,10 +14,19 @@ vector_base bl2u_vector_table b bl2u_entrypoint +#if AARCH32_EXCEPTION_DEBUG + b report_undef_inst /* Undef */ +#else b report_exception /* Undef */ +#endif b report_exception /* SVC call */ +#if AARCH32_EXCEPTION_DEBUG + b report_prefetch_abort /* Prefetch abort */ + b report_data_abort /* Data abort */ +#else b report_exception /* Prefetch abort */ b report_exception /* Data abort */ +#endif b report_exception /* Reserved */ b report_exception /* IRQ */ b report_exception /* FIQ */ diff --git a/bl32/sp_min/aarch32/entrypoint.S b/bl32/sp_min/aarch32/entrypoint.S index f3a1e440b7..b73943aca8 100644 --- a/bl32/sp_min/aarch32/entrypoint.S +++ b/bl32/sp_min/aarch32/entrypoint.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,6 +23,8 @@ .globl sp_min_handle_smc .globl sp_min_handle_fiq +#define FIXUP_SIZE ((BL32_LIMIT) - (BL32_BASE)) + .macro route_fiq_to_sp_min reg /* ----------------------------------------------------- * FIQs are secure interrupts trapped by Monitor and non @@ -47,10 +49,19 @@ vector_base sp_min_vector_table b sp_min_entrypoint +#if AARCH32_EXCEPTION_DEBUG + b report_undef_inst /* Undef */ +#else b plat_panic_handler /* Undef */ +#endif b sp_min_handle_smc /* Syscall */ +#if AARCH32_EXCEPTION_DEBUG + b report_prefetch_abort /* Prefetch abort */ + b report_data_abort /* Data abort */ +#else b plat_panic_handler /* Prefetch abort */ b plat_panic_handler /* Data abort */ +#endif b plat_panic_handler /* Reserved */ b plat_panic_handler /* IRQ */ b sp_min_handle_fiq /* FIQ */ @@ -87,7 +98,8 @@ func sp_min_entrypoint _secondary_cold_boot=0 \ _init_memory=0 \ _init_c_runtime=1 \ - _exception_vectors=sp_min_vector_table + _exception_vectors=sp_min_vector_table \ + _pie_fixup_size=FIXUP_SIZE /* --------------------------------------------------------------------- * Relay the previous bootloader's arguments to the platform layer @@ -106,7 +118,8 @@ func sp_min_entrypoint _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ - _exception_vectors=sp_min_vector_table + _exception_vectors=sp_min_vector_table \ + _pie_fixup_size=FIXUP_SIZE /* --------------------------------------------------------------------- * For RESET_TO_SP_MIN systems, BL32 (SP_MIN) is the first bootloader @@ -306,7 +319,8 @@ func sp_min_warm_entrypoint _secondary_cold_boot=0 \ _init_memory=0 \ _init_c_runtime=0 \ - _exception_vectors=sp_min_vector_table + _exception_vectors=sp_min_vector_table \ + _pie_fixup_size=0 /* * We're about to enable MMU and participate in PSCI state coordination. diff --git a/bl32/sp_min/sp_min.ld.S b/bl32/sp_min/sp_min.ld.S index f202c7ada8..175305aa07 100644 --- a/bl32/sp_min/sp_min.ld.S +++ b/bl32/sp_min/sp_min.ld.S @@ -92,6 +92,7 @@ SECTIONS __RW_START__ = . ; DATA_SECTION >RAM + RELA_SECTION >RAM #ifdef BL32_PROGBITS_LIMIT ASSERT(. <= BL32_PROGBITS_LIMIT, "BL32 progbits has exceeded its limit.") @@ -101,8 +102,6 @@ SECTIONS BSS_SECTION >RAM XLAT_TABLE_SECTION >RAM - __BSS_SIZE__ = SIZEOF(.bss); - #if USE_COHERENT_MEM /* * The base address of the coherent memory section must be page-aligned (4K) @@ -128,9 +127,6 @@ SECTIONS . = ALIGN(PAGE_SIZE); __COHERENT_RAM_END__ = .; } >RAM - - __COHERENT_RAM_UNALIGNED_SIZE__ = - __COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__; #endif /* @@ -141,5 +137,9 @@ SECTIONS __BL32_END__ = .; + /DISCARD/ : { + *(.dynsym .dynstr .hash .gnu.hash) + } + ASSERT(. <= BL32_LIMIT, "BL32 image has exceeded its limit.") } diff --git a/common/aarch32/debug.S b/common/aarch32/debug.S index 9d410df07e..69b1ed4ed5 100644 --- a/common/aarch32/debug.S +++ b/common/aarch32/debug.S @@ -14,6 +14,11 @@ .globl asm_assert .globl do_panic .globl report_exception +#if AARCH32_EXCEPTION_DEBUG + .globl report_undef_inst + .globl report_prefetch_abort + .globl report_data_abort +#endif /* Since the max decimal input number is 65536 */ #define MAX_DEC_DIVISOR 10000 @@ -113,10 +118,10 @@ endfunc asm_assert /* * This function prints a string from address in r4 - * Clobber: lr, r0 - r4 + * Clobber: lr, r0 - r4, r7 */ func asm_print_str - mov r3, lr + mov r7, lr 1: ldrb r0, [r4], #0x1 cmp r0, #0 @@ -124,20 +129,20 @@ func asm_print_str bl plat_crash_console_putc b 1b 2: - bx r3 + bx r7 endfunc asm_print_str /* * This function prints a hexadecimal number in r4. * In: r4 = the hexadecimal to print. - * Clobber: lr, r0 - r3, r5 + * Clobber: lr, r0 - r3, r5, r7 */ func asm_print_hex mov r5, #32 /* No of bits to convert to ascii */ /* Convert to ascii number of bits in r5 */ asm_print_hex_bits: - mov r3, lr + mov r7, lr 1: sub r5, r5, #4 lsr r0, r4, r5 @@ -153,7 +158,7 @@ asm_print_hex_bits: bl plat_crash_console_putc cmp r5, #0 bne 1b - bx r3 + bx r7 endfunc asm_print_hex /*********************************************************** @@ -205,3 +210,38 @@ func report_exception bl plat_report_exception no_ret plat_panic_handler endfunc report_exception + +#if AARCH32_EXCEPTION_DEBUG + /*********************************************************** + * This function is called from the vector table for + * undefined instruction. The lr_und is given as an + * argument to platform handler. + ***********************************************************/ +func report_undef_inst + mrs r0, lr_und + bl plat_report_undef_inst + no_ret plat_panic_handler +endfunc report_undef_inst + + /*********************************************************** + * This function is called from the vector table for + * unhandled exceptions. The lr_abt is given as an + * argument to platform handler. + ***********************************************************/ +func report_prefetch_abort + mrs r0, lr_abt + bl plat_report_prefetch_abort + no_ret plat_panic_handler +endfunc report_prefetch_abort + + /*********************************************************** + * This function is called from the vector table for + * unhandled exceptions. The lr_abt is given as an + * argument to platform handler. + ***********************************************************/ +func report_data_abort + mrs r0, lr_abt + bl plat_report_data_abort + no_ret plat_panic_handler +endfunc report_data_abort +#endif diff --git a/common/bl_common.c b/common/bl_common.c index f17afcb115..22a0daf18c 100644 --- a/common/bl_common.c +++ b/common/bl_common.c @@ -241,7 +241,7 @@ int load_auth_image(unsigned int image_id, image_info_t *image_data) do { err = load_auth_image_internal(image_id, image_data); - } while ((err != 0) && (plat_try_next_boot_source() != 0)); + } while ((err != 0) && (plat_try_next_boot_source(image_id) != 0)); return err; } diff --git a/common/fdt_fixup.c b/common/fdt_fixup.c index e88a550080..f6d5963890 100644 --- a/common/fdt_fixup.c +++ b/common/fdt_fixup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -195,23 +195,51 @@ int fdt_add_reserved_memory(void *dtb, const char *node_name, uintptr_t base, size_t size) { int offs = fdt_path_offset(dtb, "/reserved-memory"); - uint32_t addresses[3]; + uint32_t addr_len = sizeof(base) / sizeof(uint32_t); + uint32_t size_len = sizeof(size) / sizeof(uint32_t); + uint32_t addresses[4]; if (offs < 0) { /* create if not existing yet */ offs = fdt_add_subnode(dtb, 0, "reserved-memory"); if (offs < 0) return offs; - fdt_setprop_u32(dtb, offs, "#address-cells", 2); - fdt_setprop_u32(dtb, offs, "#size-cells", 1); + + fdt_setprop_u32(dtb, offs, "#address-cells", addr_len); + fdt_setprop_u32(dtb, offs, "#size-cells", size_len); fdt_setprop(dtb, offs, "ranges", NULL, 0); + } else { + const fdt32_t *prop; + int len; + + prop = fdt_getprop(dtb, offs, "#address-cells", &len); + if ((prop == NULL) || (fdt32_to_cpu(*prop) != addr_len)) { + return -1; + } + + prop = fdt_getprop(dtb, offs, "#size-cells", &len); + if ((prop == NULL) || (fdt32_to_cpu(*prop) != size_len)) { + return -1; + } + } + + if (addr_len == 1U) { + addresses[0] = cpu_to_fdt32(base & 0xffffffff); + } else { + addresses[0] = cpu_to_fdt32(HIGH_BITS(base)); + addresses[1] = cpu_to_fdt32(base & 0xffffffff); + } + + if (size_len == 1U) { + addresses[addr_len] = cpu_to_fdt32(size & 0xffffffff); + } else { + addresses[addr_len] = cpu_to_fdt32(HIGH_BITS(size)); + addresses[addr_len + 1U] = cpu_to_fdt32(size & 0xffffffff); } - addresses[0] = cpu_to_fdt32(HIGH_BITS(base)); - addresses[1] = cpu_to_fdt32(base & 0xffffffff); - addresses[2] = cpu_to_fdt32(size & 0xffffffff); offs = fdt_add_subnode(dtb, offs, node_name); fdt_setprop(dtb, offs, "no-map", NULL, 0); - fdt_setprop(dtb, offs, "reg", addresses, 12); + fdt_setprop(dtb, offs, "reg", addresses, + (addr_len + size_len) * sizeof(uint32_t)); return 0; } diff --git a/docs/devicetree/bindings/arm/secure.txt b/docs/devicetree/bindings/arm/secure.txt new file mode 100644 index 0000000000..e31303fb23 --- /dev/null +++ b/docs/devicetree/bindings/arm/secure.txt @@ -0,0 +1,53 @@ +* ARM Secure world bindings + +ARM CPUs with TrustZone support have two distinct address spaces, +"Normal" and "Secure". Most devicetree consumers (including the Linux +kernel) are not TrustZone aware and run entirely in either the Normal +world or the Secure world. However some devicetree consumers are +TrustZone aware and need to be able to determine whether devices are +visible only in the Secure address space, only in the Normal address +space, or visible in both. (One example of that situation would be a +virtual machine which boots Secure firmware and wants to tell the +firmware about the layout of the machine via devicetree.) + +The general principle of the naming scheme for Secure world bindings +is that any property that needs a different value in the Secure world +can be supported by prefixing the property name with "secure-". So for +instance "secure-foo" would override "foo". For property names with +a vendor prefix, the Secure variant of "vendor,foo" would be +"vendor,secure-foo". If there is no "secure-" property then the Secure +world value is the same as specified for the Normal world by the +non-prefixed property. However, only the properties listed below may +validly have "secure-" versions; this list will be enlarged on a +case-by-case basis. + +Defining the bindings in this way means that a device tree which has +been annotated to indicate the presence of Secure-only devices can +still be processed unmodified by existing Non-secure software (and in +particular by the kernel). + +Note that it is still valid for bindings intended for purely Secure +world consumers (like kernels that run entirely in Secure) to simply +describe the view of Secure world using the standard bindings. These +secure- bindings only need to be used where both the Secure and Normal +world views need to be described in a single device tree. + +Valid Secure world properties: + +- secure-status : specifies whether the device is present and usable + in the secure world. The combination of this with "status" allows + the various possible combinations of device visibility to be + specified. If "secure-status" is not specified it defaults to the + same value as "status"; if "status" is not specified either then + both default to "okay". This means the following combinations are + possible: + + /* Neither specified: default to visible in both S and NS */ + secure-status = "okay"; /* visible in both */ + status = "okay"; /* visible in both */ + status = "okay"; secure-status = "okay"; /* visible in both */ + secure-status = "disabled"; /* NS-only */ + status = "okay"; secure-status = "disabled"; /* NS-only */ + status = "disabled"; secure-status = "okay"; /* S-only */ + status = "disabled"; /* disabled in both */ + status = "disabled"; secure-status = "disabled"; /* disabled in both */ diff --git a/docs/devicetree/bindings/clock/st,stm32mp1-rcc.txt b/docs/devicetree/bindings/clock/st,stm32mp1-rcc.txt new file mode 100644 index 0000000000..7d2b5be9d7 --- /dev/null +++ b/docs/devicetree/bindings/clock/st,stm32mp1-rcc.txt @@ -0,0 +1,496 @@ +STMicroelectronics STM32 Peripheral Reset Clock Controller +========================================================== + +The RCC IP is both a reset and a clock controller. + +RCC makes also power management (resume/supend and wakeup interrupt). + +Please also refer to reset.txt for common reset controller binding usage. + +Please also refer to clock-bindings.txt for common clock controller +binding usage. + + +Required properties: +- compatible: "st,stm32mp1-rcc", "syscon" +- reg: should be register base and length as documented in the datasheet +- #clock-cells: 1, device nodes should specify the clock in their + "clocks" property, containing a phandle to the clock device node, + an index specifying the clock to use. +- #reset-cells: Shall be 1 +- interrupts: Should contain a general interrupt line. +- secure-interrupts: Should contain a interrupt line to the wake-up of + processor (CSTOP). +- secure-status: Relates to RCC TZ_ENABLE configuration to restrict RCC access. + +Example: + rcc: rcc@50000000 { + compatible = "st,stm32mp1-rcc", "syscon"; + reg = <0x50000000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + interrupts = ; + secure-interrupts = ; + }; + +Specifying clocks +================= + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/stm32mp1-clks.h header and can be used in device +tree sources. + +Specifying softreset control of devices +======================================= + +Device nodes should specify the reset channel required in their "resets" +property, containing a phandle to the reset device node and an index specifying +which channel to use. +The index is the bit number within the RCC registers bank, starting from RCC +base address. +It is calculated as: index = register_offset / 4 * 32 + bit_offset. +Where bit_offset is the bit offset within the register. + +For example on STM32MP1, for LTDC reset: + ltdc = APB4_RSTSETR_offset / 4 * 32 + LTDC_bit_offset + = 0x180 / 4 * 32 + 0 = 3072 + +The list of valid indices for STM32MP1 is available in: +include/dt-bindings/reset-controller/stm32mp1-resets.h + +This file implements defines like: +#define LTDC_R 3072 + + +Defining clock source distribution with property st,clksrc +========================================================== + +- st,clksrc : The clock sources configuration array in a platform specific + order. + + Property can be used to configure the clock distribution tree. + When used, it shall describe the whole distribution tree. + + For the STM32MP15x family there are 9 clock sources selector which are + configured in the following order: + MPU AXI MCU PLL12 PLL3 PLL4 RTC MCO1 MCO2 + + Clock source configuration values are defined by macros CLK__ + from dt-bindings/clock/stm32mp1-clksrc.h. + + Example: + st,clksrc = < + CLK_MPU_PLL1P + CLK_AXI_PLL2P + CLK_MCU_PLL3P + CLK_PLL12_HSE + CLK_PLL3_HSE + CLK_PLL4_HSE + CLK_RTC_LSE + CLK_MCO1_DISABLED + CLK_MCO2_DISABLED + >; + +Defining clock dividers with property st,clkdiv +=============================================== + +- st,clkdiv : The clock main dividers value specified in an array + in a platform specific order. + + When used, it shall describe the whole clock dividers tree. + + Property can be used to configure the clock main dividers value. + When used, it shall describe the whole clock dividers tree. + + For the STM32MP15x family there are 11 dividers values expected. + They shall be configured in the following order: + MPU AXI MCU APB1 APB2 APB3 APB4 APB5 RTC MCO1 MCO2 + + The each divider value uses the DIV coding defined in RCC associated + register RCC_xxxDIVR. In most cases, it is: + 0x0: not divided + 0x1: division by 2 + 0x2: division by 4 + 0x3: division by 8 + ... + + Note that for RTC MCO1 MCO2, the coding is different: + 0x0: not divided + 0x1: division by 2 + 0x2: division by 3 + 0x3: division by 4 + ... + + Example: + st,clkdiv = < + 1 /*MPU*/ + 0 /*AXI*/ + 0 /*MCU*/ + 1 /*APB1*/ + 1 /*APB2*/ + 1 /*APB3*/ + 1 /*APB4*/ + 2 /*APB5*/ + 23 /*RTC*/ + 0 /*MCO1*/ + 0 /*MCO2*/ + >; + +Optional Properties: +Defining peripherals kernel clock tree distribution with property st,pkcs +========================================================================= + +- st,pkcs : used to configure the peripherals kernel clock selection. + + The property is a list of peripheral kernel clock source identifiers defined + by macros CLK__ as defined by header file + dt-bindings/clock/stm32mp1-clksrc.h. + + st,pkcs may not list all the kernel clocks and has no ordering requirements. + + Example: + st,pkcs = < + CLK_STGEN_HSE + CLK_CKPER_HSI + CLK_USBPHY_PLL2P + CLK_DSI_PLL2Q + CLK_I2C46_HSI + CLK_UART1_HSI + CLK_UART24_HSI + >; + +Defining peripheral PLL frequencies +======================================================== + +- children for a PLL static configuration with "st,stm32mp1-pll" compatible + + Each PLL children nodes for PLL1 to PLL4 (see ref manual for details) + are listed with associated reg 0 to 3. + + PLL2, PLL3 or PLL4 are off when their associated nodes are absent or + deactivated. + + The configuration of PLL1, the source clock of Cortex-A7 core, with st,pll@0 + node, is optional as TF-A automatically selects the most suitable operating + point for the platform. + The node st,pll@0 node should be absent; it is only used if you want to + override the PLL1 properties computed by TF-A (clock spreading for example). + + Here are the available properties for each PLL node: + - compatible: should be "st,stm32mp1-pll" + + - reg: index of the pll instance + + - cfg: The parameters for PLL configuration in the following order: + DIVM DIVN DIVP DIVQ DIVR Output. + + DIVx values are defined as in RCC spec: + 0x0: bypass (division by 1) + 0x1: division by 2 + 0x2: division by 3 + 0x3: division by 4 + ... + + Output contains a bitfield for each output value (1:ON/0:OFF) + BIT(0) => output P : DIVPEN + BIT(1) => output Q : DIVQEN + BIT(2) => output R : DIVREN + NB: macro PQR(p,q,r) can be used to build this value + with p,q,r = 0 or 1. + + - frac: Fractional part of the multiplication factor + (optional, PLL is in integer mode when absent). + + - csg: Clock Spreading Generator (optional) with parameters in the + following order: MOD_PER INC_STEP SSCG_MODE. + + MOD_PER: Modulation Period Adjustment + INC_STEP: Modulation Depth Adjustment + SSCG_MODE: Spread spectrum clock generator mode, with associated + defined from stm32mp1-clksrc.h: + - SSCG_MODE_CENTER_SPREAD = 0 + - SSCG_MODE_DOWN_SPREAD = 1 + + Example: + st,pll@0 { + compatible = "st,stm32mp1-pll"; + reg = <0>; + cfg = <1 53 0 0 0 1>; + frac = <0x810>; + }; + st,pll@1 { + compatible = "st,stm32mp1-pll"; + reg = <1>; + cfg = <1 43 1 0 0 PQR(0,1,1)>; + csg = <10 20 1>; + }; + st,pll@2 { + compatible = "st,stm32mp1-pll"; + reg = <2>; + cfg = <2 85 3 13 3 0>; + csg = <10 20 SSCG_MODE_CENTER_SPREAD>; + }; + st,pll@3 { + compatible = "st,stm32mp1-pll"; + reg = <3>; + cfg = <2 78 4 7 9 3>; + }; + +Fixed clocks description +======================== + +The clock tree is also based on 5 fixed-clock in clocks node +used to define the state of associated ST32MP1 oscillators: + - clk-lsi + - clk-lse + - clk-hsi + - clk-hse + - clk-csi + +At boot the clock tree initialization will + - enable oscillators present in device tree and not disabled + (node with status="disabled"), + - disable HSI oscillator if the node is absent (always activated by bootrom) + and not disabled (node with status="disabled"). + +Optional properties : + +a) for external oscillator: "clk-lse", "clk-hse" + + 4 optional fields are managed + - "st,bypass" configures the oscillator bypass mode (HSEBYP, LSEBYP) + - "st,digbypass" configures the bypass mode as full-swing digital + signal (DIGBYP) + - "st,css" activates the clock security system (HSECSSON, LSECSSON) + - "st,drive" (only for LSE) contains the value of the drive for the + oscillator (see LSEDRV_ defined in the file + dt-bindings/clock/stm32mp1-clksrc.h) + + Example board file: + / { + clocks { + clk_hse: clk-hse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <64000000>; + st,bypass; + }; + + clk_lse: clk-lse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; + st,css; + st,drive = ; + }; + }; + +b) for internal oscillator: "clk-hsi" + + Internally HSI clock is fixed to 64MHz for STM32MP157 SoC. + In device tree, clk-hsi is the clock after HSIDIV (clk_hsi in RCC + doc). So this clock frequency is used to compute the expected HSI_DIV + for the clock tree initialization. + + Example with HSIDIV = /1: + / { + clocks { + clk_hsi: clk-hsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <64000000>; + }; + }; + + Example with HSIDIV = /2 + / { + clocks { + clk_hsi: clk-hsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32000000>; + }; + }; + +HSI & CSI calibration +======================== + +Calibration is an optional feature that may be enabled from device tree. It +allows to request calibration of the HSI or the CSI clocks from several means: + - SiP SMC service + - Periodic calibration every X seconds + - Interrupt raised by the MCU + +This feature requires that a HW timer is assigned to the calibration sequence. + +Dedicated secure interrupt must be defined using "mcu_sev" name to start a +calibration on detection of an interrupt raised by MCU. + +- st,hsi-cal: used to enable HSI clock calibration feature. + +- st,csi-cal; used to enable CSI clock calibration feature. + +- st,cal-sec: used to enable periodic calibration every specified seconds from + secure monitor. Time must be given in seconds. If not specified, calibration + is processed for each incoming request. + +Example: + &rcc { + st,hsi-cal; + st,csi-cal; + st,cal-sec = <15>; + secure-interrupts = , + ; + secure-interrupt-names = "mcu_sev", "wakeup"; + }; + + +Example of clock tree initialization +==================================== + +/ { + clocks { + clk_hse: clk-hse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24000000>; + st,digbypass; + }; + + 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>; + }; + }; + + soc { + + rcc: rcc@50000000 { + compatible = "st,stm32mp1-rcc", "syscon"; + reg = <0x50000000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + #reset-cells = <1>; + interrupts = ; + secure-interrupts = ; + secure-interrupt-names = "wakeup"; + secure-status = "okay"; + + st,clksrc = < + CLK_MPU_PLL1P + CLK_AXI_PLL2P + CLK_MCU_PLL3P + CLK_PLL12_HSE + CLK_PLL3_HSE + CLK_PLL4_HSE + CLK_RTC_LSE + CLK_MCO1_DISABLED + CLK_MCO2_DISABLED + >; + + st,clkdiv = < + 1 /*MPU*/ + 0 /*AXI*/ + 0 /*MCU*/ + 1 /*APB1*/ + 1 /*APB2*/ + 1 /*APB3*/ + 1 /*APB4*/ + 2 /*APB5*/ + 23 /*RTC*/ + 0 /*MCO1*/ + 0 /*MCO2*/ + >; + + st,pkcs = < + CLK_CKPER_HSE + CLK_FMC_ACLK + CLK_QSPI_ACLK + CLK_ETH_DISABLED + CLK_SDMMC12_PLL4P + CLK_DSI_DSIPLL + CLK_STGEN_HSE + CLK_USBPHY_HSE + CLK_SPI2S1_PLL3Q + CLK_SPI2S23_PLL3Q + CLK_SPI45_HSI + CLK_SPI6_HSI + CLK_I2C46_HSI + CLK_SDMMC3_PLL4P + CLK_USBO_USBPHY + CLK_ADC_CKPER + CLK_CEC_LSE + CLK_I2C12_HSI + CLK_I2C35_HSI + CLK_UART1_HSI + CLK_UART24_HSI + CLK_UART35_HSI + CLK_UART6_HSI + CLK_UART78_HSI + CLK_SPDIF_PLL4P + CLK_FDCAN_PLL4Q + CLK_SAI1_PLL3Q + CLK_SAI2_PLL3Q + CLK_SAI3_PLL3Q + CLK_SAI4_PLL3Q + CLK_RNG1_LSI + CLK_RNG2_LSI + CLK_LPTIM1_PCLK1 + CLK_LPTIM23_PCLK3 + CLK_LPTIM45_LSE + >; + + /* VCO = 1300.0 MHz => P = 650 (CPU) */ + pll1: st,pll@0 { + compatible = "st,stm32mp1-pll"; + reg = <0>; + cfg = <2 80 0 0 0 PQR(1,0,0)>; + frac = <0x800>; + }; + + /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), + R = 533 (DDR) */ + pll2: st,pll@1 { + compatible = "st,stm32mp1-pll"; + reg = <1>; + cfg = <2 65 1 0 0 PQR(1,1,1)>; + frac = <0x1400>; + }; + + /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ + pll3: st,pll@2 { + compatible = "st,stm32mp1-pll"; + reg = <2>; + cfg = <1 33 1 16 36 PQR(1,1,1)>; + frac = <0x1a04>; + }; + + /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ + pll4: st,pll@3 { + compatible = "st,stm32mp1-pll"; + reg = <3>; + cfg = <3 98 5 7 7 PQR(1,1,1)>; + }; + }; + }; +}; diff --git a/docs/devicetree/bindings/i2c/i2c-stm32.txt b/docs/devicetree/bindings/i2c/i2c-stm32.txt new file mode 100644 index 0000000000..68aefa6dc4 --- /dev/null +++ b/docs/devicetree/bindings/i2c/i2c-stm32.txt @@ -0,0 +1,54 @@ +* I2C controller embedded in STMicroelectronics STM32 I2C platform + +Required properties : +- compatible : Must be one of the following + - "st,stm32f7-i2c" +- reg : Offset and length of the register set for the device +- resets: Must contain the phandle to the reset controller. +- clocks: Must contain the input clock of the I2C instance. +- A pinctrl state named "default" must be defined to set pins in mode of + operation for I2C transfer. An optional pinctrl state named "sleep" has to + be defined as well as to put I2C in low power mode in suspend mode. +- #address-cells = <1>; +- #size-cells = <0>; + +Optional properties : +- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified, + the default 100 kHz frequency will be used. +- i2c-scl-rising-time-ns : Only for STM32F7, I2C SCL Rising time for the board + (default: 25) +- i2c-scl-falling-time-ns : Only for STM32F7, I2C SCL Falling time for the board + (default: 10) + I2C Timings are derived from these 2 values +- st,syscfg-fmp: Only for STM32F7, use to set Fast Mode Plus bit within SYSCFG + whether Fast Mode Plus speed is selected by slave. + 1st cell : phandle to syscfg + 2nd cell : register offset within SYSCFG + 3rd cell : register bitmask for FMP bit + +Example : + + i2c@40005400 { + compatible = "st,stm32f4-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40005400 0x400>; + resets = <&rcc 277>; + clocks = <&rcc 0 149>; + pinctrl-0 = <&i2c1_sda_pin>, <&i2c1_scl_pin>; + pinctrl-names = "default"; + }; + + i2c@40005400 { + compatible = "st,stm32f7-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40005400 0x400>; + resets = <&rcc STM32F7_APB1_RESET(I2C1)>; + clocks = <&rcc 1 CLK_I2C1>; + pinctrl-0 = <&i2c1_sda_pin>, <&i2c1_scl_pin>; + pinctrl-1 = <&i2c1_sda_pin_sleep>, <&i2c1_scl_pin_sleep>; + pinctrl-names = "default", "sleep"; + st,syscfg-fmp = <&syscfg 0x4 0x1>; + }; + diff --git a/docs/devicetree/bindings/memory-controllers/st,stm32mp1-ddr.txt b/docs/devicetree/bindings/memory-controllers/st,stm32mp1-ddr.txt new file mode 100644 index 0000000000..ac6a7df432 --- /dev/null +++ b/docs/devicetree/bindings/memory-controllers/st,stm32mp1-ddr.txt @@ -0,0 +1,301 @@ +ST,stm32mp1 DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL and DDRPHYC) + +-------------------- +Required properties: +-------------------- +- compatible : Should be "st,stm32mp1-ddr" +- reg : controleur (DDRCTRL) and phy (DDRPHYC) base address +- clocks : controller clocks handle +- clock-names : associated controller clock names + the "ddrphyc" clock is used to check the DDR frequency + at phy level according the expected value in "mem-speed" field + +the next attributes are DDR parameters, they are generated by DDR tools +included in STM32 Cube tool + +info attributes: +---------------- +- st,mem-name : name for DDR configuration, simple string for information +- st,mem-speed : DDR expected speed for the setting in kHz +- st,mem-size : DDR mem size in byte + + +controlleur attributes: +----------------------- +- st,ctl-reg : controleur values depending of the DDR type + (DDR3/LPDDR2/LPDDR3) + for STM32MP15x: 25 values are requested in this order + MSTR + MRCTRL0 + MRCTRL1 + DERATEEN + DERATEINT + PWRCTL + PWRTMG + HWLPCTL + RFSHCTL0 + RFSHCTL3 + CRCPARCTL0 + ZQCTL0 + DFITMG0 + DFITMG1 + DFILPCFG0 + DFIUPD0 + DFIUPD1 + DFIUPD2 + DFIPHYMSTR + ODTMAP + DBG0 + DBG1 + DBGCMD + POISONCFG + PCCFG + +- st,ctl-timing : controleur values depending of frequency and timing parameter + of DDR + for STM32MP15x: 12 values are requested in this order + RFSHTMG + DRAMTMG0 + DRAMTMG1 + DRAMTMG2 + DRAMTMG3 + DRAMTMG4 + DRAMTMG5 + DRAMTMG6 + DRAMTMG7 + DRAMTMG8 + DRAMTMG14 + ODTCFG + +- st,ctl-map : controleur values depending of address mapping + for STM32MP15x: 9 values are requested in this order + ADDRMAP1 + ADDRMAP2 + ADDRMAP3 + ADDRMAP4 + ADDRMAP5 + ADDRMAP6 + ADDRMAP9 + ADDRMAP10 + ADDRMAP11 + +- st,ctl-perf : controleur values depending of performance and scheduling + for STM32MP15x: 17 values are requested in this order + SCHED + SCHED1 + PERFHPR1 + PERFLPR1 + PERFWR1 + PCFGR_0 + PCFGW_0 + PCFGQOS0_0 + PCFGQOS1_0 + PCFGWQOS0_0 + PCFGWQOS1_0 + PCFGR_1 + PCFGW_1 + PCFGQOS0_1 + PCFGQOS1_1 + PCFGWQOS0_1 + PCFGWQOS1_1 + +phyc attributes: +---------------- +- st,phy-reg : phy values depending of the DDR type (DDR3/LPDDR2/LPDDR3) + for STM32MP15x: 11 values are requested in this order + PGCR + ACIOCR + DXCCR + DSGCR + DCR + ODTCR + ZQ0CR1 + DX0GCR + DX1GCR + DX2GCR + DX3GCR + +- st,phy-timing : phy values depending of frequency and timing parameter of DDR + for STM32MP15x: 10 values are requested in this order + PTR0 + PTR1 + PTR2 + DTPR0 + DTPR1 + DTPR2 + MR0 + MR1 + MR2 + MR3 + +- st,phy-cal : phy cal depending of calibration or tuning of DDR + This parameter is optional; when it is absent the built-in PHY + calibration is done. + for STM32MP15x: 12 values are requested in this order + DX0DLLCR + DX0DQTR + DX0DQSTR + DX1DLLCR + DX1DQTR + DX1DQSTR + DX2DLLCR + DX2DQTR + DX2DQSTR + DX3DLLCR + DX3DQTR + DX3DQSTR + +Example: + +/ { + soc { + u-boot,dm-spl; + + ddr: ddr@0x5A003000{ + u-boot,dm-spl; + u-boot,dm-pre-reloc; + + compatible = "st,stm32mp1-ddr"; + + reg = <0x5A003000 0x550 + 0x5A004000 0x234>; + + clocks = <&rcc_clk AXIDCG>, + <&rcc_clk DDRC1>, + <&rcc_clk DDRC2>, + <&rcc_clk DDRPHYC>, + <&rcc_clk DDRCAPB>, + <&rcc_clk DDRPHYCAPB>; + + clock-names = "axidcg", + "ddrc1", + "ddrc2", + "ddrphyc", + "ddrcapb", + "ddrphycapb"; + + st,mem-name = "DDR3 2x4Gb 533MHz"; + st,mem-speed = <533000>; + st,mem-size = <0x40000000>; + + st,ctl-reg = < + 0x00040401 /*MSTR*/ + 0x00000010 /*MRCTRL0*/ + 0x00000000 /*MRCTRL1*/ + 0x00000000 /*DERATEEN*/ + 0x00800000 /*DERATEINT*/ + 0x00000000 /*PWRCTL*/ + 0x00400010 /*PWRTMG*/ + 0x00000000 /*HWLPCTL*/ + 0x00210000 /*RFSHCTL0*/ + 0x00000000 /*RFSHCTL3*/ + 0x00000000 /*CRCPARCTL0*/ + 0xC2000040 /*ZQCTL0*/ + 0x02050105 /*DFITMG0*/ + 0x00000202 /*DFITMG1*/ + 0x07000000 /*DFILPCFG0*/ + 0xC0400003 /*DFIUPD0*/ + 0x00000000 /*DFIUPD1*/ + 0x00000000 /*DFIUPD2*/ + 0x00000000 /*DFIPHYMSTR*/ + 0x00000001 /*ODTMAP*/ + 0x00000000 /*DBG0*/ + 0x00000000 /*DBG1*/ + 0x00000000 /*DBGCMD*/ + 0x00000000 /*POISONCFG*/ + 0x00000010 /*PCCFG*/ + >; + + st,ctl-timing = < + 0x0080008A /*RFSHTMG*/ + 0x121B2414 /*DRAMTMG0*/ + 0x000D041B /*DRAMTMG1*/ + 0x0607080E /*DRAMTMG2*/ + 0x0050400C /*DRAMTMG3*/ + 0x07040407 /*DRAMTMG4*/ + 0x06060303 /*DRAMTMG5*/ + 0x02020002 /*DRAMTMG6*/ + 0x00000202 /*DRAMTMG7*/ + 0x00001005 /*DRAMTMG8*/ + 0x000D041B /*DRAMTMG1*/4 + 0x06000600 /*ODTCFG*/ + >; + + st,ctl-map = < + 0x00080808 /*ADDRMAP1*/ + 0x00000000 /*ADDRMAP2*/ + 0x00000000 /*ADDRMAP3*/ + 0x00001F1F /*ADDRMAP4*/ + 0x07070707 /*ADDRMAP5*/ + 0x0F070707 /*ADDRMAP6*/ + 0x00000000 /*ADDRMAP9*/ + 0x00000000 /*ADDRMAP10*/ + 0x00000000 /*ADDRMAP11*/ + >; + + st,ctl-perf = < + 0x00001201 /*SCHED*/ + 0x00001201 /*SCHED*/1 + 0x01000001 /*PERFHPR1*/ + 0x08000200 /*PERFLPR1*/ + 0x08000400 /*PERFWR1*/ + 0x00010000 /*PCFGR_0*/ + 0x00000000 /*PCFGW_0*/ + 0x02100B03 /*PCFGQOS0_0*/ + 0x00800100 /*PCFGQOS1_0*/ + 0x01100B03 /*PCFGWQOS0_0*/ + 0x01000200 /*PCFGWQOS1_0*/ + 0x00010000 /*PCFGR_1*/ + 0x00000000 /*PCFGW_1*/ + 0x02100B03 /*PCFGQOS0_1*/ + 0x00800000 /*PCFGQOS1_1*/ + 0x01100B03 /*PCFGWQOS0_1*/ + 0x01000200 /*PCFGWQOS1_1*/ + >; + + st,phy-reg = < + 0x01442E02 /*PGCR*/ + 0x10400812 /*ACIOCR*/ + 0x00000C40 /*DXCCR*/ + 0xF200001F /*DSGCR*/ + 0x0000000B /*DCR*/ + 0x00010000 /*ODTCR*/ + 0x0000007B /*ZQ0CR1*/ + 0x0000CE81 /*DX0GCR*/ + 0x0000CE81 /*DX1GCR*/ + 0x0000CE81 /*DX2GCR*/ + 0x0000CE81 /*DX3GCR*/ + >; + + st,phy-timing = < + 0x0022A41B /*PTR0*/ + 0x047C0740 /*PTR1*/ + 0x042D9C80 /*PTR2*/ + 0x369477D0 /*DTPR0*/ + 0x098A00D8 /*DTPR1*/ + 0x10023600 /*DTPR2*/ + 0x00000830 /*MR0*/ + 0x00000000 /*MR1*/ + 0x00000208 /*MR2*/ + 0x00000000 /*MR3*/ + >; + + st,phy-cal = < + 0x40000000 /*DX0DLLCR*/ + 0xFFFFFFFF /*DX0DQTR*/ + 0x3DB02000 /*DX0DQSTR*/ + 0x40000000 /*DX1DLLCR*/ + 0xFFFFFFFF /*DX1DQTR*/ + 0x3DB02000 /*DX1DQSTR*/ + 0x40000000 /*DX2DLLCR*/ + 0xFFFFFFFF /*DX2DQTR*/ + 0x3DB02000 /*DX2DQSTR*/ + 0x40000000 /*DX3DLLCR*/ + 0xFFFFFFFF /*DX3DQTR*/ + 0x3DB02000 /*DX3DQSTR*/ + >; + + status = "okay"; + }; + }; +}; diff --git a/docs/devicetree/bindings/mmc/mmci.txt b/docs/devicetree/bindings/mmc/mmci.txt new file mode 100644 index 0000000000..6d3c626e01 --- /dev/null +++ b/docs/devicetree/bindings/mmc/mmci.txt @@ -0,0 +1,72 @@ +* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1 + +The ARM PrimeCell MMCI PL180 and PL181 provides an interface for +reading and writing to MultiMedia and SD cards alike. + +This file documents differences between the core properties described +by mmc.txt and the properties used by the mmci driver. Using "st" as +the prefix for a property, indicates support by the ST Micro variant. + +Required properties: +- compatible : contains "arm,pl18x", "arm,primecell". +- vmmc-supply : phandle to the regulator device tree node, mentioned + as the VCC/VDD supply in the eMMC/SD specs. + +Optional properties: +- arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides + the ID provided by the HW +- resets : phandle to internal reset line. + Should be defined for sdmmc variant. +- vqmmc-supply : phandle to the regulator device tree node, mentioned + as the VCCQ/VDD_IO supply in the eMMC/SD specs. +specific for ux500 variant: +- st,sig-dir-dat0 : bus signal direction pin used for DAT[0]. +- st,sig-dir-dat2 : bus signal direction pin used for DAT[2]. +- st,sig-dir-dat31 : bus signal direction pin used for DAT[3] and DAT[1]. +- st,sig-dir-dat74 : bus signal direction pin used for DAT[4] to DAT[7]. +- st,sig-dir-cmd : cmd signal direction pin used for CMD. +- st,sig-pin-fbclk : feedback clock signal pin used. + +specific for sdmmc variant: +- st,sig-dir : signal direction polarity used for cmd, dat0 dat123. +- st,neg-edge : data & command phase relation, generated on + sd clock falling edge. +- st,use-ckin : use ckin pin from an external driver to sample + the receive data (example: with voltage + switch transceiver). + +Deprecated properties: +- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable. +- mmc-cap-sd-highspeed : indicates whether SD is high speed capable. + +Example: + +sdi0_per1@80126000 { + compatible = "arm,pl18x", "arm,primecell"; + reg = <0x80126000 0x1000>; + interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>; + + dmas = <&dma 29 0 0x2>, /* Logical - DevToMem */ + <&dma 29 0 0x0>; /* Logical - MemToDev */ + dma-names = "rx", "tx"; + + clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>; + clock-names = "sdi", "apb_pclk"; + + max-frequency = <100000000>; + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + cd-gpios = <&gpio2 31 0x4>; // 95 + st,sig-dir-dat0; + st,sig-dir-dat2; + st,sig-dir-cmd; + st,sig-pin-fbclk; + + vmmc-supply = <&ab8500_ldo_aux3_reg>; + vqmmc-supply = <&vmmci>; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sdi0_default_mode>; + pinctrl-1 = <&sdi0_sleep_mode>; +}; diff --git a/docs/devicetree/bindings/mmc/st,stm32-sdmmc2.txt b/docs/devicetree/bindings/mmc/st,stm32-sdmmc2.txt new file mode 100644 index 0000000000..51576a3844 --- /dev/null +++ b/docs/devicetree/bindings/mmc/st,stm32-sdmmc2.txt @@ -0,0 +1,22 @@ +* STMicroelectronics STM32 SDMMC2 controller + +The highspeed MMC host controller on STM32 soc family +provides an interface for MMC, SD and SDIO types of memory cards. + +This file documents differences between the core properties described +by mmci.txt and the properties used by the sdmmc2 driver. + +Required properties: + - compatible: should be one of: + "st,stm32-sdmmc2" + +Example: + sdmmc1: sdmmc@0x58005000 { + compatible = "st,stm32-sdmmc2"; + reg = <0x58005000 0x1000>; + clocks = <&rcc SDMMC1_K>; + resets = <&rcc SDMMC1_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + status = "disabled"; + }; diff --git a/docs/devicetree/bindings/power/st,stm32mp1-pwr.txt b/docs/devicetree/bindings/power/st,stm32mp1-pwr.txt new file mode 100644 index 0000000000..22779b05af --- /dev/null +++ b/docs/devicetree/bindings/power/st,stm32mp1-pwr.txt @@ -0,0 +1,43 @@ +STMicroelectronics STM32MP1 Power Management Controller +======================================================= + +The PWR IP is responsible for handling the power related resources such as +clocks, power supplies and resets. It provides 6 wake-up pins that are handled +by an interrupt-controller. Wake-up pin can be used to wake-up from STANDBY SoC +state. + +Required properties: +- compatible should be: "st,stm32mp1-pwr", "st,stm32-pwr" +- reg: should be register base and length as documented in the + datasheet + +Optional Properties: +- Nodes corresponding to PSCI commands issued by kernel: + - system_suspend_supported_soc_modes: list of supported SoC modes in suspend + - system_off_soc_mode: SoC mode for shutdown + - st,retram-enabled-in-standby-ddr-sr: enable retram during standby-ddr-sr + +The list of SoC modes is in include/dt-bindings/power/stm32mp1-power.h: + - modes for system_suspend + 1 -> STM32_PM_CSTOP_ALLOW_STOP + 2 -> STM32_PM_CSTOP_ALLOW_LP_STOP + 3 -> STM32_PM_CSTOP_ALLOW_LPLV_STOP + 4 -> STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR + - modes for system_off + 6 -> STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF + 7 -> STM32_PM_SHUTDOWN + +Example: + +pwr: pwr@50001000 { + compatible = "st,stm32mp1-pwr", "st,stm32-pwr", "syscon", "simple-mfd"; + reg = <0x50001000 0x400>; + + 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 = ; +}; diff --git a/docs/devicetree/bindings/power/st,stpmic1.txt b/docs/devicetree/bindings/power/st,stpmic1.txt new file mode 100644 index 0000000000..83307d23b4 --- /dev/null +++ b/docs/devicetree/bindings/power/st,stpmic1.txt @@ -0,0 +1,94 @@ +* STMicroelectronics STPMIC1 Power Management IC + +Required parent device properties: +- compatible: "st,stpmic1" +- reg: The I2C slave address for the STPMIC1 chip. +- interrupts: The interrupt lines the device is connected to. + The second interrupt is used for wake-up. +- #interrupt-cells: Should be 2. +- interrupt-controller: Describes the STPMIC1 as an interrupt + controller (has its own domain). Interrupt number are the following: + /* Interrupt Register 1 (0x50 for latch) */ + IT_SWOUT_R=0 + IT_SWOUT_F=1 + IT_VBUS_OTG_R=2 + IT_VBUS_OTG_F=3 + IT_WAKEUP_R=4 + IT_WAKEUP_F=5 + IT_PONKEY_R=6 + IT_PONKEY_F=7 + /* Interrupt Register 2 (0x51 for latch) */ + IT_OVP_BOOST=8 + IT_OCP_BOOST=9 + IT_OCP_SWOUT=10 + IT_OCP_OTG=11 + IT_CURLIM_BUCK4=12 + IT_CURLIM_BUCK3=13 + IT_CURLIM_BUCK2=14 + IT_CURLIM_BUCK1=15 + /* Interrupt Register 3 (0x52 for latch) */ + IT_SHORT_SWOUT=16 + IT_SHORT_SWOTG=17 + IT_CURLIM_LDO6=18 + IT_CURLIM_LDO5=19 + IT_CURLIM_LDO4=20 + IT_CURLIM_LDO3=21 + IT_CURLIM_LDO2=22 + IT_CURLIM_LDO1=23 + /* Interrupt Register 3 (0x52 for latch) */ + IT_SWIN_R=24 + IT_SWIN_F=25 + IT_RESERVED_1=26 + IT_RESERVED_2=27 + IT_VINLOW_R=28 + IT_VINLOW_F=29 + IT_TWARN_R=30 + IT_TWARN_F=31 + +STPMIC1 consists in a varied group of sub-devices. +Each sub-device binding is be described in own documentation file. + +Device Description +------ ------------ +st,stpmic1-onkey : Power on key, see ../input/st,stpmic1-onkey.txt +st,stpmic1-regulators : Regulators, see ../regulator/st,stpmic1-regulator.txt +st,stpmic1-wdt : Watchdog, see ../watchdog/st,stpmic1-wdt.txt + +Example: + +pmic: pmic@33 { + compatible = "st,stpmic1"; + reg = <0x33>; + interrupt-parent = <&gpioa>; + interrupts = <0 2>; + interrupt-controller; + #interrupt-cells = <2>; + + onkey { + compatible = "st,stpmic1-onkey"; + interrupts = ,; + interrupt-names = "onkey-falling", "onkey-rising"; + power-off-time-sec = <10>; + }; + + watchdog { + compatible = "st,stpmic1-wdt"; + }; + + regulators { + compatible = "st,stpmic1-regulators"; + + vdd_core: buck1 { + regulator-name = "vdd_core"; + regulator-boot-on; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1200000>; + }; + vdd: buck3 { + regulator-name = "vdd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-pull-down; + }; + }; diff --git a/docs/devicetree/bindings/reset/st,stm32mp1-rcc.txt b/docs/devicetree/bindings/reset/st,stm32mp1-rcc.txt new file mode 100644 index 0000000000..b4edaf7c7f --- /dev/null +++ b/docs/devicetree/bindings/reset/st,stm32mp1-rcc.txt @@ -0,0 +1,6 @@ +STMicroelectronics STM32MP1 Peripheral Reset Controller +======================================================= + +The RCC IP is both a reset and a clock controller. + +Please see Documentation/devicetree/bindings/clock/st,stm32mp1-rcc.txt diff --git a/docs/devicetree/bindings/rng/st,stm32-rng.txt b/docs/devicetree/bindings/rng/st,stm32-rng.txt new file mode 100644 index 0000000000..3c613d7914 --- /dev/null +++ b/docs/devicetree/bindings/rng/st,stm32-rng.txt @@ -0,0 +1,23 @@ +STMicroelectronics STM32 HW RNG +=============================== + +The STM32 hardware random number generator is a simple fixed purpose IP and +is fully separated from other crypto functions. + +Required properties: + +- compatible : Should be "st,stm32-rng" +- reg : Should be register base and length as documented in the datasheet +- clocks : The clock needed to enable the RNG + +Optional properties: +- resets : The reset to properly start RNG +- clock-error-detect : Enable the clock detection management + +Example: + + rng: rng@50060800 { + compatible = "st,stm32-rng"; + reg = <0x50060800 0x400>; + clocks = <&rcc 0 38>; + }; diff --git a/docs/devicetree/bindings/serial/st,stm32-usart.txt b/docs/devicetree/bindings/serial/st,stm32-usart.txt new file mode 100644 index 0000000000..08b499045a --- /dev/null +++ b/docs/devicetree/bindings/serial/st,stm32-usart.txt @@ -0,0 +1,88 @@ +* STMicroelectronics STM32 USART + +Required properties: +- compatible: can be either: + - "st,stm32-uart", + - "st,stm32f7-uart", + - "st,stm32h7-uart". + depending is compatible with stm32(f4), stm32f7 or stm32h7. +- reg: The address and length of the peripheral registers space +- interrupts: + - The interrupt line for the USART instance, + - An optional wake-up interrupt. +- interrupt-names: Contains "event" for the USART interrupt line. +- clocks: The input clock of the USART instance + +Optional properties: +- resets: Must contain the phandle to the reset controller. +- pinctrl-names: Set to "default". An additional "sleep" state can be defined + to set pins in sleep state when in low power. In case the device is used as + a wakeup source, "idle" state is defined in order to keep RX pin active. + For a console device, an optional state "no_console_suspend" can be defined + to enable console messages during suspend. Typically, "no_console_suspend" and + "default" states can refer to the same pin configuration. +- pinctrl-n: Phandle(s) pointing to pin configuration nodes. + For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt +- st,hw-flow-ctrl: bool flag to enable hardware flow control. +- rs485-rts-delay, rs485-rx-during-tx, rs485-rts-active-low, + linux,rs485-enabled-at-boot-time: see rs485.txt. +- dmas: phandle(s) to DMA controller node(s). Refer to stm32-dma.txt +- dma-names: "rx" and/or "tx" +- wakeup-source: bool flag to indicate this device has wakeup capabilities +- interrupt-names : Should contain "wakeup" if optional wake-up interrupt is + used. + +Note for dma using: +- "tx" dma can be used without any constraint since it uses single +dma transfers. +- "rx" dma using requires some attention: + 1) if you cannot anticipate the length of your received packets + and if your usart device embeds an internal fifo, then DON'T use + dma mode. + 2) if you enable dma mode WITHOUT mdma intermediate copy (cf. + stm32-dma.txt), then the availability of the received data will + depend on the dma driver policy and it may be delayed until dma + internal fifo is full. The usart driver will see this checking + the dma residue when rx interrupt (RXNE or RTO) occurs. + 3) if you enable dma mode WITH mdma intermediate copy (cf. + stm32-dma.txt) then the usart driver will never see the dma + residue becoming smaller than RX_BUF_P but it will get its + rx dma complete callback called when the cyclic transfer period + (RX_BUF_P) is reached. +The three possibilities above are ordered from the most cpu time +consuming one to the least one. The counterpart of this optimisation +is the reception granularity achievable by the usart driver, from +one byte up to RX_BUF_P. + +Examples: +usart4: serial@40004c00 { + compatible = "st,stm32-uart"; + reg = <0x40004c00 0x400>; + interrupts = <52>; + clocks = <&clk_pclk1>; + pinctrl-names = "default", "sleep", "idle", "no_console_suspend"; + pinctrl-0 = <&pinctrl_usart4>; + pinctrl-1 = <&pinctrl_usart4_sleep>; + pinctrl-2 = <&pinctrl_usart4_idle>; + pinctrl-3 = <&pinctrl_usart4>; +}; + +usart2: serial@40004400 { + compatible = "st,stm32-uart"; + reg = <0x40004400 0x400>; + interrupts = <38>; + clocks = <&clk_pclk1>; + st,hw-flow-ctrl; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usart2 &pinctrl_usart2_rtscts>; +}; + +usart1: serial@40011000 { + compatible = "st,stm32-uart"; + reg = <0x40011000 0x400>; + interrupts = <37>; + clocks = <&rcc 0 164>; + dmas = <&dma2 2 4 0x414 0x0>, + <&dma2 7 4 0x414 0x0>; + dma-names = "rx", "tx"; +}; diff --git a/docs/devicetree/bindings/soc/st,stm32-etzpc.txt b/docs/devicetree/bindings/soc/st,stm32-etzpc.txt new file mode 100644 index 0000000000..a2ac263cec --- /dev/null +++ b/docs/devicetree/bindings/soc/st,stm32-etzpc.txt @@ -0,0 +1,56 @@ +STM32 ETZPC +--------------------------------- + +Required properties: +- compatible: should be "st,stm32-etzpc" +- reg: physical base address and length of the registers set for the device +- clocks: reference to the clock entry + +Optional property: +- st,decprot: Configure option to properly set firewall for IPs. + +Examples: +etzpc: etzpc@5C007000 { + compatible = "st,stm32-etzpc"; + reg = <0x5C007000 0x400>; + clocks = <&rcc TZPC>; + status = "disabled"; + secure-status = "okay"; + }; + +Firewall specifications +======================= + +DECPROT macro must be used to properly configure IP firewalling. It must +specify ID, domain and locking register status. + +The macro is defined in the binding header file [1]. + +Example: + ... { + st,decprot = < + DECPROT(STM32MP1_ETZPC_I2C4_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) + DECPROT(STM32MP1_ETZPC_GPIOZ_ID, DECPROT_NS_RW, DECPROT_UNLOCK)>; + }; + +Specify Peripheral IDs +======================= + +Each peripheral is identified with a specific ID. Each platform defines the +identifiers relevant to that platform. Peripheral IDs are defined in [1]. + +Specify domain +============== +Firewall controls peripherals in specific domains: + +DECPROT_S_RW 0x0 -> Read/write Secure +DECPROT_NS_R_S_W 0x1 -> Non secure read / Read/write Secure +DECPROT_MCU_ISOLATION 0x2 -> MCU access only +DECPROT_NS_RW 0x3 -> Non secure read/write + + +[1] include/dt-bindings/soc/st,stm32-etzpc.h + diff --git a/docs/devicetree/bindings/soc/st,stm32-romem.txt b/docs/devicetree/bindings/soc/st,stm32-romem.txt new file mode 100644 index 0000000000..c430fb84d7 --- /dev/null +++ b/docs/devicetree/bindings/soc/st,stm32-romem.txt @@ -0,0 +1,74 @@ +STMicroelectronics STM32 Factory-programmed data device tree bindings + +This represents STM32 Factory-programmed read only non-volatile area: locked +flash, OTP, read-only HW regs... This contains various information such as: +analog calibration data for temperature sensor (e.g. TS_CAL1, TS_CAL2), +internal vref (VREFIN_CAL), unique device ID... + +Required properties: +- compatible: Should be one of: + "st,stm32-romem" + "st,stm32mp15-bsec" +- reg: Offset and length of factory-programmed area. +- #address-cells: Should be '<1>'. +- #size-cells: Should be '<1>'. + +Optional Data cells: +- Must be child nodes as described in nvmem.txt. + +Optional-properties: +- "st,non-secure-otp" specifies that the OTP can be accessed by non-secure + world through secure world services. Only useful for upper OTPs. This + property mandates 32-bit granularity of the related nvmem area, that is + offset and length are both multiple of 4. + +Example on stm32f4: + romem: nvmem@1fff7800 { + compatible = "st,stm32-romem"; + reg = <0x1fff7800 0x400>; + #address-cells = <1>; + #size-cells = <1>; + + /* Data cells: ts_cal1 at 0x1fff7a2c */ + ts_cal1: calib@22c { + reg = <0x22c 0x2>; + }; + ... + }; + +Example on stm32mp1: + bsec: nvmem@5c005000 { + ... + mac_addr: mac_addr@e4 { + reg = <0xe4 0x8>; + st,non-secure-otp; + }; + ... + }; + +The nvmem_layout node gathers all nvmem platform-dependent layout information, +including OTP names and phandles, in order to allow easy accesses for data +consumers, using pre-defined string in nvmem-cell-names property. + +Required properties: +- compatible: "st,stm32-nvmem-layout" +- nvmem-cells and nvmem-cell-names, as described in nvmem.txt. + +Example on stm32mp1: + nvmem_layout: nvmem_layout@0 { + compatible = "st,stm32-nvmem-layout"; + nvmem-cells = <&part_number_otp>, + ... + ; + nvmem-cell-names = "part_number_otp", + ... + ; + }; + + bsec: nvmem@5c005000 { + ... + part_number_otp: part_number_otp@4 { + reg = <0x4 0x1>; + }; + ... + }; diff --git a/docs/devicetree/bindings/soc/st,stm32-stgen.txt b/docs/devicetree/bindings/soc/st,stm32-stgen.txt new file mode 100644 index 0000000000..dbd962ebc4 --- /dev/null +++ b/docs/devicetree/bindings/soc/st,stm32-stgen.txt @@ -0,0 +1,18 @@ +STMicroelectronics STM32 STGEN +=============================== + +The STM32 System Generic Counter generate a time count value. This +is a 64 bits wide counter. + +Required properties: + +- compatible : Should be "st,stm32-stgen" +- reg : Should be register base and length as documented in the datasheet + +Example: + + stgen: stgen@5C008000 { + compatible = "st,stm32-stgen"; + reg = <0x5C008000 0x1000>; + status = "okay"; + }; diff --git a/docs/devicetree/bindings/soc/st,stm32-tamp.txt b/docs/devicetree/bindings/soc/st,stm32-tamp.txt new file mode 100644 index 0000000000..4d21c6b8a3 --- /dev/null +++ b/docs/devicetree/bindings/soc/st,stm32-tamp.txt @@ -0,0 +1,22 @@ +STM32 TAMPER +--------------------------------- + +Required properties: +- compatible: should be "st,stm32-tamp" +- reg: physical base address and length of the registers set for the device +- clocks: reference to the clock entry +- secure-status: Required to properly disable/enable secure IP + +Optional property: +- st,out3-pc13: Configure option register to map OUT3 on PC13 +- wakeup-source : Configure tamp as wakeup-src + +Examples: +tamp: tamp@5C00A000 { + compatible = "st,stm32-tamp"; + reg = <0x5C00A000 0x100>; + clocks = <&rcc_clk RTCAPB>; + st,out3-pc13; + wakeup-source; + secure-status = "okay"; +}; diff --git a/docs/devicetree/bindings/watchdog/st,stm32-iwdg.txt b/docs/devicetree/bindings/watchdog/st,stm32-iwdg.txt new file mode 100644 index 0000000000..2453603a17 --- /dev/null +++ b/docs/devicetree/bindings/watchdog/st,stm32-iwdg.txt @@ -0,0 +1,28 @@ +STM32 Independent WatchDoG (IWDG) +--------------------------------- + +Required properties: +- compatible: should be "st,stm32mp1-iwdg". +- reg: physical base address and length of the registers set for the device. +- clocks: reference to the clock entry lsi. Additional pclk clock entry. + is required only for st,stm32mp1-iwdg. +- clock-names: name of the clocks used. + "pclk", "lsi" for st,stm32mp1-iwdg. + +Optional properties: +- timeout-sec: Watchdog timeout value in seconds. +- secure-timeout-sec: Watchdog early timeout management in seconds. +- stm32,enable-on-stop: Keep watchdog enable during stop. +- stm32,enable-on-standby: Keep watchdog enable durung standby. +- secure-status: Required to properly enable/disable secure IP. + +Examples: + +iwdg2: iwdg@5a002000 { + compatible = "st,stm32mp1-iwdg"; + reg = <0x5a002000 0x400>; + clocks = <&rcc IWDG2>, <&clk_lsi>; + clock-names = "pclk", "lsi"; + instance = <2>; + timeout-sec = <30>; +}; diff --git a/docs/getting_started/porting-guide.rst b/docs/getting_started/porting-guide.rst index 19e26e4ea1..ba816fae58 100644 --- a/docs/getting_started/porting-guide.rst +++ b/docs/getting_started/porting-guide.rst @@ -796,6 +796,31 @@ The function returns 0 on success. Any other value means the counter value either could not be updated or the authentication image descriptor indicates that it is not allowed to be updated. +Function: plat_get_hashed_pk() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : void *, unsigned int, void **, unsigned int * + Return : int + +This function is optional when Trusted Board Boot is enabled, and only +used if the platform saves a hash of the ROTPK. +First argument is the BER ROTPK. +Second argument is its size. +Third argument is used to return a pointer to a buffer, which hash should +be the one saved in OTP +Fourth argument is a pointer to return its size + +Most platforms save the hash of the BER ROTPK, but some may save the hash of +a non encapsulated public key or a platform specific encapsulated ROT public +key. Defining this function allows to transform the BER ROTPK used to verify +the signature to the buffer (a platform specific encapsulated public key) which +hash is saved in OTP. + +The function returns 0 on success. Any other value means the expected +public key buffer cannot be extracted from the BER public key. + Common mandatory function modifications --------------------------------------- diff --git a/docs/plat/stm32mp1.rst b/docs/plat/stm32mp1.rst index f597460db2..fb7afdbbb0 100644 --- a/docs/plat/stm32mp1.rst +++ b/docs/plat/stm32mp1.rst @@ -37,6 +37,15 @@ The TF-A image must be properly formatted with a STM32 header structure for ROM code is able to load this image. Tool stm32image can be used to prepend this header to the generated TF-A binary. +Boot with FIP +~~~~~~~~~~~~~ +The use of FIP is now the recommended way to boot STM32MP1 platform. +Only BL2 (with STM32 header) is loaded by ROM code. The other binaries are +inside the FIP binary: BL32 (SP_min or OP-TEE), U-Boot and their respective +device tree blobs. + +STM32IMAGE bootchain (deprecated) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ At compilation step, BL2, BL32 and DTB file are linked together in a single binary. The stm32image tool is also generated and the header is added to TF-A binary. This binary file with header is named tf-a-stm32mp157c-ev1.stm32. @@ -55,15 +64,17 @@ Memory mapping | ... | | | 0x2FFC0000 +-----------------+ \ - | | | + | BL32 DTB | | + 0x2FFC5000 +-----------------+ | + | BL32 | | + 0x2FFDF000 +-----------------+ | | ... | | - | | | - 0x2FFD8000 +-----------------+ | - | TF-A DTB | | Embedded SRAM - 0x2FFDC000 +-----------------+ | + 0x2FFE3000 +-----------------+ | + | BL2 DTB | | Embedded SRAM + 0x2FFEA000 +-----------------+ | | BL2 | | - 0x2FFEF000 +-----------------+ | - | BL32 | | + 0x2FFFF000 +-----------------+ | + | SCMI mailbox | | 0x30000000 +-----------------+ / | | | ... | @@ -95,41 +106,169 @@ Build Instructions ------------------ Boot media(s) supported by BL2 must be specified in the build command. Available storage medias are: + - ``STM32MP_SDMMC`` - ``STM32MP_EMMC`` - ``STM32MP_RAW_NAND`` - ``STM32MP_SPI_NAND`` - ``STM32MP_SPI_NOR`` -To build with SP_min and support for all bootable devices: +Boot with FIP +~~~~~~~~~~~~~ +You need to build BL2, BL32 (SP_min or OP-TEE) and BL33 (U-Boot) before building FIP binary. + +U-Boot +______ .. code:: bash - make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1 - STM32MP_SPI_NOR=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb cd make stm32mp15_trusted_defconfig make DEVICE_TREE=stm32mp157c-ev1 all -To build TF-A with OP-TEE support for all bootable devices: +OP-TEE (optional) +_________________ + .. code:: bash - make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=optee STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1 STM32MP_SPI_NOR=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb cd - make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-ev1.dts + make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm PLATFORM=stm32mp1 \ + CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-ev1.dts + + +TF-A BL32 (SP_min) +__________________ +If you choose not to use OP-TEE, you can use TF-A SP_min. +To build TF-A BL32, and its device tree file: + +.. code:: bash + + make CROSS_COMPILE=arm-none-eabi- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ + AARCH32_SP=sp_min DTB_FILE_NAME=stm32mp157c-ev1.dtb bl32 dtbs + +TF-A BL2 +________ +To build TF-A BL2 with its STM32 header for SD-card boot: + +.. code:: bash + + make CROSS_COMPILE=arm-none-eabi- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ + DTB_FILE_NAME=stm32mp157c-ev1.dtb + +This BL2 is independent of the BL32 used (SP_min or OP-TEE) + + +FIP +___ +With BL32 SP_min: + +.. code:: bash + + make CROSS_COMPILE=arm-none-eabi- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ + AARCH32_SP=sp_min \ + DTB_FILE_NAME=stm32mp157c-ev1.dtb \ + BL33=/u-boot-nodtb.bin \ + BL33_CFG=/u-boot.dtb \ + fip + +With OP-TEE: + +.. code:: bash + + make CROSS_COMPILE=arm-none-eabi- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ + DTB_FILE_NAME=stm32mp157c-ev1.dtb \ + BL33=/u-boot-nodtb.bin \ + BL33_CFG=/u-boot.dtb \ + BL32=/tee-header_v2.bin \ + BL32_EXTRA1=/tee-pager_v2.bin + BL32_EXTRA2=/tee-pageable_v2.bin + fip + +Trusted Boot Board +__________________ + +.. code:: shell + + tools/cert_create/cert_create -n --rot-key "build/stm32mp1/debug/rot_key.pem" \ + --tfw-nvctr 0 \ + --ntfw-nvctr 0 \ + --key-alg ecdsa --hash-alg sha256 \ + --trusted-key-cert build/stm32mp1/cert_images/trusted-key-cert.key-crt \ + --tb-fw=build/stm32mp1/debug/bl2.bin \ + --tb-fw-cert build/stm32mp1/cert_images/trusted-boot-fw.key-crt\ + --tos-fw /tee-header_v2.bin \ + --tos-fw-cert build/stm32mp1/cert_images/tee-header_v2.bin.crt \ + --tos-fw-key-cert build/stm32mp1/cert_images/tee-header_v2.bin.key-crt \ + --tos-fw-extra1 /tee-pager_v2.bin \ + --tos-fw-extra2 /tee-pageable_v2.bin \ + --nt-fw /u-boot-nodtb.bin \ + --nt-fw-cert build/stm32mp1/cert_images/u-boot.bin.crt \ + --nt-fw-key-cert build/stm32mp1/cert_images/u-boot.bin.key-crt \ + --hw-config /u-boot.dtb + --fw-config build/stm32mp1/debug/fdts/fw-config.dtb + + tools/fiptool/fiptool create --tos-fw /tee-header_v2.bin \ + --tos-fw-extra1 /tee-pager_v2.bin \ + --tos-fw-extra2 /tee-pageable_v2.bin \ + --nt-fw /u-boot-nodtb.bin \ + --hw-config /uboot-nodtb.dtb \ + --tos-fw-cert build/stm32mp1/cert_images/tee-header_v2.bin.crt \ + --tos-fw-key-cert build/stm32mp1/cert_images/tee-header_v2.bin.key-crt \ + --nt-fw-cert build/stm32mp1/cert_images/u-boot.bin.crt \ + --nt-fw-key-cert build/stm32mp1/cert_images/u-boot.bin.key-crt \ + --trusted-key-cert build/stm32mp1/cert_images/trusted-key-cert.key-crt \ + --tb-fw-cert build/stm32mp1/cert_images/trusted-boot-fw.key-crt stm32mp1.fip + + +STM32IMAGE bootchain (deprecated) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You need to add the following flag to the make command: +``STM32MP_USE_STM32IMAGE=1`` + +To build with SP_min and support for SD-card boot: + +.. code:: bash + + make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ + AARCH32_SP=sp_min STM32MP_SDMMC=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb \ + STM32MP_USE_STM32IMAGE=1 + cd make stm32mp15_trusted_defconfig make DEVICE_TREE=stm32mp157c-ev1 all +To build TF-A with OP-TEE support for SD-card boot: -The following build options are supported: +.. code:: bash -- ``ENABLE_STACK_PROTECTOR``: To enable the stack protection. + make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ + AARCH32_SP=optee STM32MP_SDMMC=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb \ + STM32MP_USE_STM32IMAGE=1 + + cd + make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm PLATFORM=stm32mp1 \ + CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-ev1.dts + + cd + make stm32mp15_trusted_defconfig + make DEVICE_TREE=stm32mp157c-ev1 all Populate SD-card ---------------- +Boot with FIP +~~~~~~~~~~~~~ +The SD-card has to be formated with GPT. +It should contain at least those partitions: + +- fsbl: to copy the tf-a-stm32mp157c-ev1.stm32 binary (BL2) +- fip: which contains the FIP binary + +Usually, two copies of fsbl are used (fsbl1 and fsbl2) instead of one partition fsbl. + +STM32IMAGE bootchain (deprecated) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The SD-card has to be formated with GPT. It should contain at least those partitions: diff --git a/drivers/arm/tzc/tzc400.c b/drivers/arm/tzc/tzc400.c index 95a5e7f77f..fa32a695e0 100644 --- a/drivers/arm/tzc/tzc400.c +++ b/drivers/arm/tzc/tzc400.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "tzc_common_private.h" @@ -60,18 +61,54 @@ static inline void _tzc400_write_gate_keeper(uintptr_t base, unsigned int val) GATE_KEEPER_OS_SHIFT) & \ GATE_KEEPER_OS_MASK) - /* Define common core functions used across different TZC peripherals. */ DEFINE_TZC_COMMON_WRITE_ACTION(400, 400) DEFINE_TZC_COMMON_WRITE_REGION_BASE(400, 400) DEFINE_TZC_COMMON_WRITE_REGION_TOP(400, 400) DEFINE_TZC_COMMON_WRITE_REGION_ATTRIBUTES(400, 400) DEFINE_TZC_COMMON_WRITE_REGION_ID_ACCESS(400, 400) +DEFINE_TZC_COMMON_UPDATE_FILTERS(400, 400) DEFINE_TZC_COMMON_CONFIGURE_REGION0(400) DEFINE_TZC_COMMON_CONFIGURE_REGION(400) +static inline void tzc400_clear_it(long base, uint32_t filter) +{ + mmio_write_32(base + INT_CLEAR, BIT(filter)); +} + +static inline uint32_t tzc400_get_int_by_filter(long base, uint32_t filter) +{ + return (mmio_read_32(base + INT_STATUS) & BIT(filter)); +} + +#if DEBUG +static unsigned long tzc400_get_fail_address(long base, uint32_t filter) +{ + unsigned long fail_address; + + fail_address = mmio_read_32(base + FAIL_ADDRESS_LOW_OFF + + (filter * FILTER_OFFSET)); +#ifdef __aarch64__ + fail_address += mmio_read_32(base + FAIL_ADDRESS_HIGH_OFF + + (filter * FILTER_OFFSET)) << 32; +#endif + + return fail_address; +} + +static inline uint32_t tzc400_get_fail_id(long base, uint32_t filter) +{ + return mmio_read_32(base + FAIL_ID + (filter * FILTER_OFFSET)); +} + +static inline uint32_t tzc400_get_fail_control(long base, uint32_t filter) +{ + return mmio_read_32(base + FAIL_CONTROL_OFF + (filter * FILTER_OFFSET)); +} +#endif + static unsigned int _tzc400_get_gate_keeper(uintptr_t base, - unsigned int filter) + unsigned int filter) { unsigned int open_status; @@ -81,9 +118,8 @@ static unsigned int _tzc400_get_gate_keeper(uintptr_t base, } /* This function is not MP safe. */ -static void _tzc400_set_gate_keeper(uintptr_t base, - unsigned int filter, - int val) +static void _tzc400_set_gate_keeper(uintptr_t base, unsigned int filter, + int val) { unsigned int open_status; @@ -151,7 +187,7 @@ void tzc400_init(uintptr_t base) * changed. This function only changes the access permissions. */ void tzc400_configure_region0(unsigned int sec_attr, - unsigned int ns_device_access) + unsigned int ns_device_access) { assert(tzc400.base != 0U); assert(sec_attr <= TZC_REGION_S_RDWR); @@ -168,11 +204,11 @@ void tzc400_configure_region0(unsigned int sec_attr, * for this region (see comment for that function). */ void tzc400_configure_region(unsigned int filters, - unsigned int region, - unsigned long long region_base, - unsigned long long region_top, - unsigned int sec_attr, - unsigned int nsaid_permissions) + unsigned int region, + unsigned long long region_base, + unsigned long long region_top, + unsigned int sec_attr, + unsigned int nsaid_permissions) { assert(tzc400.base != 0U); @@ -185,7 +221,7 @@ void tzc400_configure_region(unsigned int filters, * the max and expected case. */ assert((region_top <= (UINT64_MAX >> (64U - tzc400.addr_width))) && - (region_base < region_top)); + (region_base < region_top)); /* region_base and (region_top + 1) must be 4KB aligned */ assert(((region_base | (region_top + 1U)) & (4096U - 1U)) == 0U); @@ -193,8 +229,16 @@ void tzc400_configure_region(unsigned int filters, assert(sec_attr <= TZC_REGION_S_RDWR); _tzc400_configure_region(tzc400.base, filters, region, region_base, - region_top, - sec_attr, nsaid_permissions); + region_top, sec_attr, nsaid_permissions); +} + +void tzc400_update_filters(unsigned int region, unsigned int filters) +{ + /* Do range checks on filters and regions. */ + assert(((filters >> tzc400.num_filters) == 0U) && + (region < tzc400.num_regions)); + + _tzc400_update_filters(tzc400.base, region, tzc400.num_filters, filters); } void tzc400_enable_filters(void) @@ -217,8 +261,8 @@ void tzc400_enable_filters(void) * See the 'ARM (R) CoreLink TM TZC-400 TrustZone (R) * Address Space Controller' Technical Reference Manual. */ - ERROR("TZC-400 : Filter %d Gatekeeper already" - " enabled.\n", filter); + ERROR("TZC-400: Filter %d Gatekeeper already enabled\n", + filter); panic(); } _tzc400_set_gate_keeper(tzc400.base, filter, 1); @@ -238,3 +282,62 @@ void tzc400_disable_filters(void) for (filter = 0; filter < tzc400.num_filters; filter++) _tzc400_set_gate_keeper(tzc400.base, filter, 0); } + +void tzc400_it_handler(void) +{ + uint32_t filter; + uint32_t filter_it_pending = tzc400.num_filters; +#if DEBUG + uint32_t control_fail; + uint32_t fail_id; + unsigned long address_fail; +#endif + + assert(tzc400.base != 0U); + + /* first display information conerning the fault access */ + for (filter = 0U; (filter < tzc400.num_filters) && + (filter_it_pending == tzc400.num_filters); filter++) { + if (tzc400_get_int_by_filter(tzc400.base, filter)) { + filter_it_pending = filter; + } + } + + if (filter_it_pending == tzc400.num_filters) { + ERROR("Error no IT pending!"); + panic(); + } + +#if DEBUG + address_fail = tzc400_get_fail_address(tzc400.base, filter_it_pending); + ERROR("Illegal access to 0x%lx:\n", address_fail); + + fail_id = tzc400_get_fail_id(tzc400.base, filter_it_pending); + ERROR("\tFAIL_ID = 0x%x\n", fail_id); + + control_fail = tzc400_get_fail_control(tzc400.base, filter_it_pending); + + if (((control_fail & BIT(FAIL_CONTROL_NS_SHIFT)) >> FAIL_CONTROL_NS_SHIFT) == + FAIL_CONTROL_NS_NONSECURE) { + ERROR("\tNon-Secure\n"); + } else { + ERROR("\tSecure\n"); + } + + if (((control_fail & BIT(FAIL_CONTROL_PRIV_SHIFT)) >> FAIL_CONTROL_PRIV_SHIFT) == + FAIL_CONTROL_PRIV_PRIV) { + ERROR("\tPrivilege\n"); + } else { + ERROR("\tUnprivilege\n"); + } + + if (((control_fail & BIT(FAIL_CONTROL_DIR_SHIFT)) >> FAIL_CONTROL_DIR_SHIFT) == + FAIL_CONTROL_DIR_WRITE) { + ERROR("\tWrite\n"); + } else { + ERROR("\tRead\n"); + } +#endif + + tzc400_clear_it(tzc400.base, filter_it_pending); +} diff --git a/drivers/arm/tzc/tzc_common_private.h b/drivers/arm/tzc/tzc_common_private.h index 1d99077ad1..b2af140fb7 100644 --- a/drivers/arm/tzc/tzc_common_private.h +++ b/drivers/arm/tzc/tzc_common_private.h @@ -89,6 +89,27 @@ val); \ } +/* + * It is used to modify the filters status for a defined region. + */ +#define DEFINE_TZC_COMMON_UPDATE_FILTERS(fn_name, macro_name) \ + static inline void _tzc##fn_name##_update_filters( \ + uintptr_t base, \ + unsigned int region_no, \ + unsigned int nbfilters, \ + unsigned int filters) \ + { \ + uint32_t filters_mask = GENMASK(nbfilters - 1U, 0); \ + \ + mmio_clrsetbits_32(base + \ + TZC_REGION_OFFSET( \ + TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_ATTR_0_OFFSET, \ + filters_mask << TZC_REGION_ATTR_F_EN_SHIFT, \ + filters << TZC_REGION_ATTR_F_EN_SHIFT); \ + } + /* * It is used to program region 0 ATTRIBUTES and ACCESS register. */ diff --git a/drivers/auth/auth_mod.c b/drivers/auth/auth_mod.c index 91ee1bea97..a84787fc39 100644 --- a/drivers/auth/auth_mod.c +++ b/drivers/auth/auth_mod.c @@ -30,6 +30,7 @@ } while (0) #pragma weak plat_set_nv_ctr2 +#pragma weak plat_get_hashed_pk static int cmp_auth_param_type_desc(const auth_param_type_desc_t *a, @@ -201,6 +202,10 @@ static int auth_signature(const auth_method_param_sig_t *param, NOTICE("ROTPK is not deployed on platform. " "Skipping ROTPK verification.\n"); } else { + /* platform may store the hash of a prefixed, suffixed or modified pk */ + rc = plat_get_hashed_pk(pk_ptr, pk_len, &pk_ptr, &pk_len); + return_if_error(rc); + /* Ask the crypto-module to verify the key hash */ rc = crypto_mod_verify_hash(pk_ptr, pk_len, pk_hash_ptr, pk_hash_len); @@ -292,6 +297,15 @@ int plat_set_nv_ctr2(void *cookie, const auth_img_desc_t *img_desc __unused, return plat_set_nv_ctr(cookie, nv_ctr); } +int plat_get_hashed_pk(void *full_pk_ptr, unsigned int full_pk_len, + void **hashed_pk_ptr, unsigned int *hashed_pk_len) +{ + *hashed_pk_ptr = full_pk_ptr; + *hashed_pk_len = full_pk_len; + + return 0; +} + /* * Return the parent id in the output parameter '*parent_id' * diff --git a/drivers/auth/mbedtls/mbedtls_common.mk b/drivers/auth/mbedtls/mbedtls_common.mk index 53ebe30b63..ace4312cf3 100644 --- a/drivers/auth/mbedtls/mbedtls_common.mk +++ b/drivers/auth/mbedtls/mbedtls_common.mk @@ -16,7 +16,7 @@ endif MBEDTLS_INC = -I${MBEDTLS_DIR}/include # Specify mbed TLS configuration file -MBEDTLS_CONFIG_FILE := "" +MBEDTLS_CONFIG_FILE ?= "" $(eval $(call add_define,MBEDTLS_CONFIG_FILE)) MBEDTLS_SOURCES += drivers/auth/mbedtls/mbedtls_common.c diff --git a/drivers/auth/mbedtls/mbedtls_x509_parser.c b/drivers/auth/mbedtls/mbedtls_x509_parser.c index 129566bd69..7b3ecd1a31 100644 --- a/drivers/auth/mbedtls/mbedtls_x509_parser.c +++ b/drivers/auth/mbedtls/mbedtls_x509_parser.c @@ -114,10 +114,10 @@ static int get_ext(const char *oid, void **ext, unsigned int *ext_len) oid_len = mbedtls_oid_get_numeric_string(oid_str, MAX_OID_STR_LEN, &extn_oid); - if (oid_len == MBEDTLS_ERR_OID_BUF_TOO_SMALL) { + if ((oid_len == MBEDTLS_ERR_OID_BUF_TOO_SMALL) || (oid_len < 0)) { return IMG_PARSER_ERR; } - if ((oid_len == strlen(oid_str)) && !strcmp(oid, oid_str)) { + if (((size_t)oid_len == strlen(oid_str)) && !strcmp(oid, oid_str)) { *ext = (void *)p; *ext_len = (unsigned int)len; return IMG_PARSER_OK; diff --git a/drivers/auth/tbbr/tbbr_cot_bl1.c b/drivers/auth/tbbr/tbbr_cot_bl1.c index e4c92213ae..15a35431d6 100644 --- a/drivers/auth/tbbr/tbbr_cot_bl1.c +++ b/drivers/auth/tbbr/tbbr_cot_bl1.c @@ -150,21 +150,6 @@ static const auth_img_desc_t tb_fw_config = { } }; -static const auth_img_desc_t fw_config = { - .img_id = FW_CONFIG_ID, - .img_type = IMG_RAW, - .parent = &trusted_boot_fw_cert, - .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { - [0] = { - .type = AUTH_METHOD_HASH, - .param.hash = { - .data = &raw_data, - .hash = &fw_config_hash - } - } - } -}; - /* * TBBR Chain of trust definition */ diff --git a/drivers/auth/tbbr/tbbr_cot_bl2.c b/drivers/auth/tbbr/tbbr_cot_bl2.c index 65a0478abf..de7ad8f902 100644 --- a/drivers/auth/tbbr/tbbr_cot_bl2.c +++ b/drivers/auth/tbbr/tbbr_cot_bl2.c @@ -671,6 +671,7 @@ static const auth_img_desc_t * const cot_desc[] = { [NON_TRUSTED_FW_CONTENT_CERT_ID] = &non_trusted_fw_content_cert, [BL33_IMAGE_ID] = &bl33_image, [NT_FW_CONFIG_ID] = &nt_fw_config, + [FW_CONFIG_ID] = &fw_config, #if defined(SPD_spmd) [SIP_SP_CONTENT_CERT_ID] = &sip_sp_content_cert, [SP_PKG1_ID] = &sp_pkg1, diff --git a/drivers/auth/tbbr/tbbr_cot_common.c b/drivers/auth/tbbr/tbbr_cot_common.c index ff3f22de15..279f30e2e2 100644 --- a/drivers/auth/tbbr/tbbr_cot_common.c +++ b/drivers/auth/tbbr/tbbr_cot_common.c @@ -124,3 +124,18 @@ const auth_img_desc_t hw_config = { } } }; + +const auth_img_desc_t fw_config = { + .img_id = FW_CONFIG_ID, + .img_type = IMG_RAW, + .parent = &trusted_boot_fw_cert, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_HASH, + .param.hash = { + .data = &raw_data, + .hash = &fw_config_hash + } + } + } +}; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c new file mode 100644 index 0000000000..9336b88b4f --- /dev/null +++ b/drivers/clk/clk.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * Author(s): Ludovic Barre, for STMicroelectronics. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +static const clk_ops_t *ops; + +int clk_enable(unsigned long id) +{ + assert((ops != NULL) && (ops->enable != NULL)); + + return ops->enable(id); +} + +void clk_disable(unsigned long id) +{ + assert((ops != NULL) && (ops->disable != NULL)); + + ops->disable(id); +} + +unsigned long clk_get_rate(unsigned long id) +{ + assert((ops != NULL) && (ops->get_rate != NULL)); + + return ops->get_rate(id); +} + +int clk_get_parent(unsigned long id) +{ + assert((ops != NULL) && (ops->get_parent != NULL)); + + return ops->get_parent(id); +} + +bool clk_is_enabled(unsigned long id) +{ + assert((ops != NULL) && (ops->is_enabled != NULL)); + + return ops->is_enabled(id); +} + +/* + * Initialize the clk. The fields in the provided clk + * ops pointer must be valid. + */ +void clk_register(const clk_ops_t *ops_ptr) +{ + assert((ops_ptr != NULL) && (ops_ptr->enable != NULL) && + (ops_ptr->disable != NULL) && + (ops_ptr->get_rate != NULL) && + (ops_ptr->get_parent != NULL) && + (ops_ptr->is_enabled != NULL)); + + ops = ops_ptr; +} diff --git a/drivers/io/io_mtd.c b/drivers/io/io_mtd.c index 7575fa2503..a9f41a18c6 100644 --- a/drivers/io/io_mtd.c +++ b/drivers/io/io_mtd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,8 +18,9 @@ typedef struct { io_mtd_dev_spec_t *dev_spec; uintptr_t base; - unsigned long long offset; /* Offset in bytes */ - unsigned long long size; /* Size of device in bytes */ + unsigned long long pos; /* Offset in bytes */ + unsigned long long size; /* Size of device in bytes */ + unsigned long long extra_offset; /* Extra offset in bytes */ } mtd_dev_state_t; io_type_t device_type_mtd(void); @@ -110,16 +111,47 @@ static int free_dev_info(io_dev_info_t *dev_info) return 0; } +static int mtd_add_extra_offset(mtd_dev_state_t *cur, size_t *extra_offset) +{ + io_mtd_ops_t *ops = &cur->dev_spec->ops; + int ret; + + if (ops->seek == NULL) { + return 0; + } + + ret = ops->seek(cur->base, cur->pos, extra_offset); + if (ret != 0) { + ERROR("%s: Seek error %d\n", __func__, ret); + return ret; + } + + return 0; +} + static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity) { mtd_dev_state_t *cur; + io_block_spec_t *region; + size_t extra_offset = 0U; + int ret; assert((dev_info->info != 0UL) && (entity->info == 0UL)); + region = (io_block_spec_t *)spec; cur = (mtd_dev_state_t *)dev_info->info; entity->info = (uintptr_t)cur; - cur->offset = 0U; + cur->base = region->offset; + cur->pos = 0U; + cur->extra_offset = 0U; + + ret = mtd_add_extra_offset(cur, &extra_offset); + if (ret != 0) { + return ret; + } + + cur->base += extra_offset; return 0; } @@ -128,6 +160,8 @@ static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, static int mtd_seek(io_entity_t *entity, int mode, signed long long offset) { mtd_dev_state_t *cur; + size_t extra_offset = 0U; + int ret; assert((entity->info != (uintptr_t)NULL) && (offset >= 0)); @@ -140,22 +174,29 @@ static int mtd_seek(io_entity_t *entity, int mode, signed long long offset) return -EINVAL; } - cur->offset = offset; + cur->pos = offset; break; case IO_SEEK_CUR: - if (((cur->offset + (unsigned long long)offset) >= + if (((cur->base + cur->pos + (unsigned long long)offset) >= cur->size) || - ((cur->offset + (unsigned long long)offset) < - cur->offset)) { + ((cur->base + cur->pos + (unsigned long long)offset) < + cur->base + cur->pos)) { return -EINVAL; } - cur->offset += (unsigned long long)offset; + cur->pos += (unsigned long long)offset; break; default: return -EINVAL; } + ret = mtd_add_extra_offset(cur, &extra_offset); + if (ret != 0) { + return ret; + } + + cur->extra_offset = extra_offset; + return 0; } @@ -174,18 +215,19 @@ static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, assert(ops->read != NULL); VERBOSE("Read at %llx into %lx, length %zi\n", - cur->offset, buffer, length); - if ((cur->offset + length) > cur->dev_spec->device_size) { + cur->base + cur->pos, buffer, length); + if ((cur->base + cur->pos + length) > cur->dev_spec->device_size) { return -EINVAL; } - ret = ops->read(cur->offset, buffer, length, out_length); + ret = ops->read(cur->base + cur->pos + cur->extra_offset, buffer, + length, out_length); if (ret < 0) { return ret; } assert(*out_length == length); - cur->offset += *out_length; + cur->pos += *out_length; return 0; } diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b5f6a10d38..42243ea096 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -25,6 +25,7 @@ static const struct mmc_ops *ops; static unsigned int mmc_ocr_value; static struct mmc_csd_emmc mmc_csd; +static struct sd_switch_status sd_switch_func_status; static unsigned char mmc_ext_csd[512] __aligned(16); static unsigned int mmc_flags; static struct mmc_device_info *mmc_dev_info; @@ -44,6 +45,11 @@ static bool is_cmd23_enabled(void) return ((mmc_flags & MMC_FLAG_CMD23) != 0U); } +static bool is_sd_cmd6_enabled(void) +{ + return ((mmc_flags & MMC_FLAG_SD_CMD6) != 0U); +} + static int mmc_send_cmd(unsigned int idx, unsigned int arg, unsigned int r_type, unsigned int *r_data) { @@ -327,6 +333,33 @@ static int mmc_fill_device_info(void) return 0; } +static int sd_switch(unsigned char mode, unsigned char group, + unsigned char func) +{ + unsigned int group_shift = (group - 1U) * 4U; + unsigned int group_mask = GENMASK(group_shift + 3U, group_shift); + unsigned int arg; + int ret = 0; + + ret = ops->prepare(0, (uintptr_t)&sd_switch_func_status, + sizeof(sd_switch_func_status)); + if (ret != 0) { + return ret; + } + + /* MMC CMD6: SWITCH_FUNC */ + arg = (mode << 31) | GENMASK(23, 0); + arg &= ~group_mask; + arg |= func << group_shift; + ret = mmc_send_cmd(MMC_CMD(6), arg, MMC_RESPONSE_R1, NULL); + if (ret != 0) { + return ret; + } + + return ops->read(0, (uintptr_t)&sd_switch_func_status, + sizeof(sd_switch_func_status)); +} + static int sd_send_op_cond(void) { int n; @@ -494,7 +527,39 @@ static int mmc_enumerate(unsigned int clk, unsigned int bus_width) return ret; } - return mmc_fill_device_info(); + ret = mmc_fill_device_info(); + if (ret != 0) { + return ret; + } + + if (is_sd_cmd6_enabled() && + (mmc_dev_info->mmc_dev_type == MMC_IS_SD_HC)) { + /* Try to switch to High Speed Mode */ + ret = sd_switch(SD_SWITCH_FUNC_CHECK, 1U, 1U); + if (ret != 0) { + return ret; + } + + if ((sd_switch_func_status.support_g1 & BIT(9)) == 0U) { + /* High speed not supported, keep default speed */ + return 0; + } + + ret = sd_switch(SD_SWITCH_FUNC_SWITCH, 1U, 1U); + if (ret != 0) { + return ret; + } + + if ((sd_switch_func_status.sel_g2_g1 & 0x1U) == 0U) { + /* Cannot switch to high speed, keep default speed */ + return 0; + } + + mmc_dev_info->max_bus_freq = 50000000U; + ret = ops->set_ios(clk, bus_width); + } + + return ret; } size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size) diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c index 44b001e35b..d961c24630 100644 --- a/drivers/mtd/nand/core.c +++ b/drivers/mtd/nand/core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2019-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -112,6 +112,47 @@ int nand_read(unsigned int offset, uintptr_t buffer, size_t length, return 0; } +int nand_seek_bb(uintptr_t base, unsigned int offset, size_t *extra_offset) +{ + unsigned int block; + unsigned int offset_block; + unsigned int max_block; + int is_bad; + size_t count_bb = 0U; + + block = base / nand_dev.block_size; + + if (offset != 0U) { + offset_block = (base + offset - 1U) / nand_dev.block_size; + } else { + offset_block = block; + } + + max_block = nand_dev.size / nand_dev.block_size; + + while (block <= offset_block) { + if (offset_block >= max_block) { + return -EIO; + } + + is_bad = nand_dev.mtd_block_is_bad(block); + if (is_bad < 0) { + return is_bad; + } + + if (is_bad == 1) { + count_bb++; + offset_block++; + } + + block++; + } + + *extra_offset = count_bb * nand_dev.block_size; + + return 0; +} + struct nand_device *get_nand_device(void) { return &nand_dev; diff --git a/drivers/mtd/nor/spi_nor.c b/drivers/mtd/nor/spi_nor.c index 108f893d3f..2ed562a677 100644 --- a/drivers/mtd/nor/spi_nor.c +++ b/drivers/mtd/nor/spi_nor.c @@ -103,7 +103,7 @@ static int spi_nor_ready(void) 0 : 1; } - return (((sr & SR_WIP) != 0U) ? 1 : 0); + return (((sr & SR_WIP) == 0U) ? 0 : 1); } static int spi_nor_wait_ready(void) @@ -131,7 +131,7 @@ static int spi_nor_macronix_quad_enable(void) return ret; } - if ((sr & SR_QUAD_EN_MX) == 0U) { + if ((sr & SR_QUAD_EN_MX) != 0U) { return 0; } @@ -141,7 +141,7 @@ static int spi_nor_macronix_quad_enable(void) } sr |= SR_QUAD_EN_MX; - ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1, SPI_MEM_DATA_OUT); + ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1U, SPI_MEM_DATA_OUT); if (ret != 0) { return ret; } @@ -168,7 +168,7 @@ static int spi_nor_write_sr_cr(uint8_t *sr_cr) return ret; } - ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2, SPI_MEM_DATA_OUT); + ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2U, SPI_MEM_DATA_OUT); if (ret != 0) { return -EINVAL; } @@ -230,7 +230,7 @@ static int spi_nor_clean_bar(void) } return spi_nor_reg(nor_dev.bank_write_cmd, &nor_dev.selected_bank, - 1, SPI_MEM_DATA_OUT); + 1U, SPI_MEM_DATA_OUT); } static int spi_nor_write_bar(uint32_t offset) @@ -248,7 +248,7 @@ static int spi_nor_write_bar(uint32_t offset) } ret = spi_nor_reg(nor_dev.bank_write_cmd, &selected_bank, - 1, SPI_MEM_DATA_OUT); + 1U, SPI_MEM_DATA_OUT); if (ret != 0) { return ret; } @@ -260,11 +260,11 @@ static int spi_nor_write_bar(uint32_t offset) static int spi_nor_read_bar(void) { - uint8_t selected_bank = 0; + uint8_t selected_bank = 0U; int ret; ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank, - 1, SPI_MEM_DATA_IN); + 1U, SPI_MEM_DATA_IN); if (ret != 0) { return ret; } @@ -280,7 +280,7 @@ int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length, size_t remain_len; int ret; - *length_read = 0; + *length_read = 0U; nor_dev.read_op.addr.val = offset; nor_dev.read_op.data.buf = (void *)buffer; @@ -339,7 +339,7 @@ int spi_nor_init(unsigned long long *size, unsigned int *erase_size) return -EINVAL; } - assert(nor_dev.size != 0); + assert(nor_dev.size != 0U); if (nor_dev.size > BANK_SIZE) { nor_dev.flags |= SPI_NOR_USE_BANK; diff --git a/drivers/st/bsec/bsec.c b/drivers/st/bsec/bsec2.c similarity index 62% rename from drivers/st/bsec/bsec.c rename to drivers/st/bsec/bsec2.c index 01c369edcd..de00a65812 100644 --- a/drivers/st/bsec/bsec.c +++ b/drivers/st/bsec/bsec2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,11 +14,13 @@ #include #include #include +#include #include #include -#define BSEC_IP_VERSION_1_0 0x10 -#define BSEC_COMPAT "st,stm32mp15-bsec" +#define BSEC_IP_VERSION_1_1 U(0x11) +#define BSEC_IP_VERSION_2_0 U(0x20) +#define BSEC_IP_ID_2 U(0x100032) #define OTP_ACCESS_SIZE (round_up(OTP_MAX_SIZE, __WORD_BIT) / __WORD_BIT) @@ -44,11 +46,12 @@ static void bsec_unlock(void) } } +#if defined(IMAGE_BL32) static int bsec_get_dt_node(struct dt_node_info *info) { int node; - node = dt_get_node(info, -1, BSEC_COMPAT); + node = dt_get_node(info, -1, DT_BSEC_COMPAT); if (node < 0) { return -FDT_ERR_NOTFOUND; } @@ -56,7 +59,6 @@ static int bsec_get_dt_node(struct dt_node_info *info) return node; } -#if defined(IMAGE_BL32) static void enable_non_secure_access(uint32_t otp) { otp_nsec_access[otp / __WORD_BIT] |= BIT(otp % __WORD_BIT); @@ -78,39 +80,85 @@ static int bsec_dt_otp_nsec_access(void *fdt, int bsec_node) fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) { const fdt32_t *cuint; - uint32_t reg; + uint32_t otp; uint32_t i; uint32_t size; - uint8_t status; + uint32_t offset; + uint32_t length; 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; + offset = fdt32_to_cpu(*cuint); + cuint++; + length = fdt32_to_cpu(*cuint); + + otp = offset / sizeof(uint32_t); + + if (otp < STM32MP1_UPPER_OTP_START) { + unsigned int otp_end = round_up(offset + length, + sizeof(uint32_t)) / + sizeof(uint32_t); + + if (otp_end > STM32MP1_UPPER_OTP_START) { + /* + * OTP crosses Lower/Upper boundary, consider + * only the upper part. + */ + otp = STM32MP1_UPPER_OTP_START; + length -= (STM32MP1_UPPER_OTP_START * + sizeof(uint32_t)) - offset; + offset = STM32MP1_UPPER_OTP_START * + sizeof(uint32_t); + + WARN("OTP crosses Lower/Upper boundary\n"); + } else { + continue; + } } - status = fdt_get_status(bsec_subnode); - if ((status & DT_NON_SECURE) == 0U) { + if ((fdt_getprop(fdt, bsec_subnode, + "st,non-secure-otp", NULL)) == NULL) { continue; } - size = fdt32_to_cpu(*(cuint + 1)) / sizeof(uint32_t); - - if ((fdt32_to_cpu(*(cuint + 1)) % sizeof(uint32_t)) != 0) { - size++; + if (((offset % sizeof(uint32_t)) != 0) || + ((length % sizeof(uint32_t)) != 0)) { + ERROR("Unaligned non-secure OTP\n"); + panic(); } - for (i = reg; i < (reg + size); i++) { + size = length / sizeof(uint32_t); + + for (i = otp; i < (otp + size); i++) { enable_non_secure_access(i); } } return 0; } + +static void bsec_late_init(void) +{ + void *fdt; + int node; + struct dt_node_info bsec_info; + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + node = bsec_get_dt_node(&bsec_info); + if (node < 0) { + panic(); + } + + assert(bsec_base == bsec_info.base); + + bsec_dt_otp_nsec_access(fdt, node); +} #endif static uint32_t otp_bank_offset(uint32_t otp) @@ -121,19 +169,30 @@ static uint32_t otp_bank_offset(uint32_t otp) sizeof(uint32_t); } -static uint32_t bsec_check_error(uint32_t otp) +/* + * bsec_check_error: check BSEC error status. + * otp: OTP number. + * check_disturbed: check only error (false), + * or error and disturbed status (true). + * return value: BSEC_OK if no error. + */ +static uint32_t bsec_check_error(uint32_t otp, bool check_disturbed) { uint32_t bit = BIT(otp & BSEC_OTP_MASK); uint32_t bank = otp_bank_offset(otp); - if ((mmio_read_32(bsec_base + BSEC_DISTURBED_OFF + bank) & bit) != 0U) { - return BSEC_DISTURBED; - } - if ((mmio_read_32(bsec_base + BSEC_ERROR_OFF + bank) & bit) != 0U) { return BSEC_ERROR; } + if (!check_disturbed) { + return BSEC_OK; + } + + if ((mmio_read_32(bsec_base + BSEC_DISTURBED_OFF + bank) & bit) != 0U) { + return BSEC_DISTURBED; + } + return BSEC_OK; } @@ -143,23 +202,16 @@ static uint32_t bsec_check_error(uint32_t otp) */ uint32_t bsec_probe(void) { - void *fdt; - int node; - struct dt_node_info bsec_info; - - if (fdt_get_address(&fdt) == 0) { - panic(); - } + bsec_base = BSEC_BASE; - node = bsec_get_dt_node(&bsec_info); - if (node < 0) { + if ((((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_1_1) && + ((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_2_0)) || + (bsec_get_id() != BSEC_IP_ID_2)) { panic(); } - bsec_base = bsec_info.base; - #if defined(IMAGE_BL32) - bsec_dt_otp_nsec_access(fdt, node); + bsec_late_init(); #endif return BSEC_OK; } @@ -251,6 +303,79 @@ uint32_t bsec_get_config(struct bsec_config *cfg) return BSEC_OK; } +/* + * bsec_find_otp_name_in_dt: get OTP ID and length in DT. + * name: sub-node name to look up. + * otp: pointer to read OTP number or NULL. + * otp_len: pointer to read OTP length in bits or NULL. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_find_otp_name_in_dt(const char *name, uint32_t *otp, + uint32_t *otp_len) +{ + void *fdt; + int node; + int index, len; + const fdt32_t *cuint; + + if ((name == NULL) || (otp == NULL)) { + return BSEC_INVALID_PARAM; + } + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_NVMEM_LAYOUT_COMPAT); + if (node < 0) { + return BSEC_ERROR; + } + + index = fdt_stringlist_search(fdt, node, "nvmem-cell-names", name); + if (index < 0) { + return BSEC_ERROR; + } + + cuint = fdt_getprop(fdt, node, "nvmem-cells", &len); + if (cuint == NULL) { + return BSEC_ERROR; + } + + if ((index * (int)sizeof(uint32_t)) > len) { + return BSEC_ERROR; + } + + cuint += index; + + node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (node < 0) { + ERROR("Malformed nvmem_layout node: ignored\n"); + return BSEC_ERROR; + } + + cuint = fdt_getprop(fdt, node, "reg", &len); + if ((cuint == NULL) || (len != (2 * (int)sizeof(uint32_t)))) { + ERROR("Malformed nvmem_layout node: ignored\n"); + return BSEC_ERROR; + } + + if (fdt32_to_cpu(*cuint) % sizeof(uint32_t)) { + ERROR("Misaligned nvmem_layout element: ignored\n"); + return BSEC_ERROR; + } + + if (otp != NULL) { + *otp = fdt32_to_cpu(*cuint) / sizeof(uint32_t); + } + + if (otp_len != NULL) { + cuint++; + *otp_len = fdt32_to_cpu(*cuint) * CHAR_BIT; + } + + return BSEC_OK; +} + /* * bsec_shadow_register: copy SAFMEM OTP to BSEC data. * otp: OTP number. @@ -259,14 +384,16 @@ uint32_t bsec_get_config(struct bsec_config *cfg) uint32_t bsec_shadow_register(uint32_t otp) { uint32_t result; + bool value; bool power_up = false; - if (otp > STM32MP1_OTP_MAX_ID) { - return BSEC_INVALID_PARAM; + result = bsec_read_sr_lock(otp, &value); + if (result != BSEC_OK) { + ERROR("BSEC: %u Sticky-read bit read Error %i\n", otp, result); + return result; } - /* Check if shadowing of OTP is locked */ - if (bsec_read_sr_lock(otp)) { + if (value) { VERBOSE("BSEC: OTP %i is locked and will not be refreshed\n", otp); } @@ -283,14 +410,13 @@ uint32_t bsec_shadow_register(uint32_t otp) bsec_lock(); - /* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */ mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_READ); while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { ; } - result = bsec_check_error(otp); + result = bsec_check_error(otp, true); bsec_unlock(); @@ -311,22 +437,14 @@ uint32_t bsec_shadow_register(uint32_t otp) */ uint32_t bsec_read_otp(uint32_t *val, uint32_t otp) { - uint32_t result; - if (otp > STM32MP1_OTP_MAX_ID) { return BSEC_INVALID_PARAM; } - bsec_lock(); - *val = mmio_read_32(bsec_base + BSEC_OTP_DATA_OFF + (otp * sizeof(uint32_t))); - result = bsec_check_error(otp); - - bsec_unlock(); - - return result; + return BSEC_OK; } /* @@ -338,24 +456,25 @@ uint32_t bsec_read_otp(uint32_t *val, uint32_t otp) uint32_t bsec_write_otp(uint32_t val, uint32_t otp) { uint32_t result; + bool value; - if (otp > STM32MP1_OTP_MAX_ID) { - return BSEC_INVALID_PARAM; + result = bsec_read_sw_lock(otp, &value); + if (result != BSEC_OK) { + ERROR("BSEC: %u Sticky-write bit read Error %i\n", otp, result); + return result; } - /* Check if programming of OTP is locked */ - if (bsec_read_sw_lock(otp)) { + if (value) { VERBOSE("BSEC: OTP %i is locked and write will be ignored\n", otp); } + /* Ensure integrity of each register access sequence */ bsec_lock(); mmio_write_32(bsec_base + BSEC_OTP_DATA_OFF + (otp * sizeof(uint32_t)), val); - result = bsec_check_error(otp); - bsec_unlock(); return result; @@ -372,14 +491,23 @@ uint32_t bsec_program_otp(uint32_t val, uint32_t otp) { uint32_t result; bool power_up = false; + bool sp_lock, perm_lock; - if (otp > STM32MP1_OTP_MAX_ID) { - return BSEC_INVALID_PARAM; + result = bsec_read_sp_lock(otp, &sp_lock); + if (result != BSEC_OK) { + ERROR("BSEC: %u Sticky-prog bit read Error %i\n", otp, result); + return result; + } + + result = bsec_read_permanent_lock(otp, &perm_lock); + if (result != BSEC_OK) { + ERROR("BSEC: %u permanent bit read Error %i\n", otp, result); + return result; } - /* Check if programming of OTP is locked */ - if (bsec_read_sp_lock(otp)) { + if (sp_lock || perm_lock) { WARN("BSEC: OTP locked, prog will be ignored\n"); + return BSEC_PROG_FAIL; } if ((mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF) & @@ -399,10 +527,8 @@ uint32_t bsec_program_otp(uint32_t val, uint32_t otp) bsec_lock(); - /* Set value in write register */ mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, val); - /* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */ mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_WRITE); while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { @@ -412,7 +538,7 @@ uint32_t bsec_program_otp(uint32_t val, uint32_t otp) if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) { result = BSEC_PROG_FAIL; } else { - result = bsec_check_error(otp); + result = bsec_check_error(otp, true); } bsec_unlock(); @@ -464,10 +590,8 @@ uint32_t bsec_permanent_lock_otp(uint32_t otp) bsec_lock(); - /* Set value in write register */ mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, data); - /* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */ mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK); @@ -478,7 +602,7 @@ uint32_t bsec_permanent_lock_otp(uint32_t otp) if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) { result = BSEC_PROG_FAIL; } else { - result = bsec_check_error(otp); + result = bsec_check_error(otp, false); } bsec_unlock(); @@ -493,37 +617,46 @@ uint32_t bsec_permanent_lock_otp(uint32_t otp) } /* - * bsec_write_debug_conf: write value in debug feature + * 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. + * return value: none. */ -uint32_t bsec_write_debug_conf(uint32_t val) +void bsec_write_debug_conf(uint32_t val) { - uint32_t result = BSEC_ERROR; - uint32_t masked_val = val & BSEC_DEN_ALL_MSK; - bsec_lock(); - - mmio_write_32(bsec_base + BSEC_DEN_OFF, masked_val); - - if ((mmio_read_32(bsec_base + BSEC_DEN_OFF) ^ masked_val) == 0U) { - result = BSEC_OK; - } - + mmio_write_32(bsec_base + BSEC_DEN_OFF, val & BSEC_DEN_ALL_MSK); bsec_unlock(); - - return result; } /* - * bsec_read_debug_conf: read debug configuration. + * bsec_read_debug_conf: return debug configuration register value. */ uint32_t bsec_read_debug_conf(void) { return mmio_read_32(bsec_base + BSEC_DEN_OFF); } +/* + * bsec_write_scratch: write value in scratch register. + * val: value to write. + * return value: none. + */ +void bsec_write_scratch(uint32_t val) +{ + bsec_lock(); + mmio_write_32(bsec_base + BSEC_SCRATCH_OFF, val); + bsec_unlock(); +} + +/* + * bsec_read_scratch: return scratch register value. + */ +uint32_t bsec_read_scratch(void) +{ + return mmio_read_32(bsec_base + BSEC_SCRATCH_OFF); +} + /* * bsec_get_status: return status register value. */ @@ -533,7 +666,7 @@ uint32_t bsec_get_status(void) } /* - * bsec_get_hw_conf: return hardware configuration. + * bsec_get_hw_conf: return hardware configuration register value. */ uint32_t bsec_get_hw_conf(void) { @@ -541,7 +674,7 @@ uint32_t bsec_get_hw_conf(void) } /* - * bsec_get_version: return BSEC version. + * bsec_get_version: return BSEC version register value. */ uint32_t bsec_get_version(void) { @@ -549,7 +682,7 @@ uint32_t bsec_get_version(void) } /* - * bsec_get_id: return BSEC ID. + * bsec_get_id: return BSEC ID register value. */ uint32_t bsec_get_id(void) { @@ -557,7 +690,7 @@ uint32_t bsec_get_id(void) } /* - * bsec_get_magic_id: return BSEC magic number. + * bsec_get_magic_id: return BSEC magic number register value. */ uint32_t bsec_get_magic_id(void) { @@ -565,229 +698,178 @@ uint32_t bsec_get_magic_id(void) } /* - * bsec_write_sr_lock: write shadow-read lock. + * bsec_set_sr_lock: set 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. + * return value: BSEC_OK if no error. */ -bool bsec_write_sr_lock(uint32_t otp, uint32_t value) +uint32_t bsec_set_sr_lock(uint32_t otp) { - bool result = false; uint32_t bank = otp_bank_offset(otp); - uint32_t bank_value; uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); - bsec_lock(); - - bank_value = mmio_read_32(bsec_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. - */ - mmio_write_32(bsec_base + BSEC_SRLOCK_OFF + bank, bank_value); - result = true; + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; } + bsec_lock(); + mmio_write_32(bsec_base + BSEC_SRLOCK_OFF + bank, otp_mask); bsec_unlock(); - return result; + return BSEC_OK; } /* * bsec_read_sr_lock: read shadow-read lock. * otp: OTP number. - * return: true if otp is locked, else false. + * value: read value (true or false). + * return value: BSEC_OK if no error. */ -bool bsec_read_sr_lock(uint32_t otp) +uint32_t bsec_read_sr_lock(uint32_t otp, bool *value) { uint32_t bank = otp_bank_offset(otp); uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); - uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank); + uint32_t bank_value; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank); + + *value = ((bank_value & otp_mask) != 0U); - return (bank_value & otp_mask) != 0U; + return BSEC_OK; } /* - * bsec_write_sw_lock: write shadow-write lock. + * bsec_set_sw_lock: set 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. + * return value: BSEC_OK if no error. */ -bool bsec_write_sw_lock(uint32_t otp, uint32_t value) +uint32_t bsec_set_sw_lock(uint32_t otp) { - bool result = false; uint32_t bank = otp_bank_offset(otp); uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); - uint32_t bank_value; - - bsec_lock(); - bank_value = mmio_read_32(bsec_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. - */ - mmio_write_32(bsec_base + BSEC_SWLOCK_OFF + bank, bank_value); - result = true; + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; } + bsec_lock(); + mmio_write_32(bsec_base + BSEC_SWLOCK_OFF + bank, otp_mask); bsec_unlock(); - return result; + return BSEC_OK; } /* * bsec_read_sw_lock: read shadow-write lock. * otp: OTP number. - * return: true if OTP is locked, else false. + * value: read value (true or false). + * return value: BSEC_OK if no error. */ -bool bsec_read_sw_lock(uint32_t otp) +uint32_t bsec_read_sw_lock(uint32_t otp, bool *value) { uint32_t bank = otp_bank_offset(otp); uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); - uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank); + uint32_t bank_value; - return (bank_value & otp_mask) != 0U; + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank); + + *value = ((bank_value & otp_mask) != 0U); + + return BSEC_OK; } /* - * bsec_write_sp_lock: write shadow-program lock. + * bsec_set_sp_lock: set 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. + * return value: BSEC_OK if no error. */ -bool bsec_write_sp_lock(uint32_t otp, uint32_t value) +uint32_t bsec_set_sp_lock(uint32_t otp) { - bool result = false; uint32_t bank = otp_bank_offset(otp); - uint32_t bank_value; uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); - bsec_lock(); - - bank_value = mmio_read_32(bsec_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. - */ - mmio_write_32(bsec_base + BSEC_SPLOCK_OFF + bank, bank_value); - result = true; + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; } + bsec_lock(); + mmio_write_32(bsec_base + BSEC_SPLOCK_OFF + bank, otp_mask); bsec_unlock(); - return result; + return BSEC_OK; } /* * bsec_read_sp_lock: read shadow-program lock. * otp: OTP number. - * return: true if OTP is locked, else false. + * value: read value (true or false). + * return value: BSEC_OK if no error. */ -bool bsec_read_sp_lock(uint32_t otp) +uint32_t bsec_read_sp_lock(uint32_t otp, bool *value) { uint32_t bank = otp_bank_offset(otp); uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); - uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank); + uint32_t bank_value; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank); + + *value = ((bank_value & otp_mask) != 0U); - return (bank_value & otp_mask) != 0U; + return BSEC_OK; } /* - * bsec_wr_lock: Read permanent lock status. + * bsec_read_permanent_lock: Read permanent lock status. * otp: OTP number. - * return: true if OTP is locked, else false. + * value: read value (true or false). + * return value: BSEC_OK if no error. */ -bool bsec_wr_lock(uint32_t otp) +uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value) { uint32_t bank = otp_bank_offset(otp); - uint32_t lock_bit = BIT(otp & BSEC_OTP_MASK); + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t bank_value; - if ((mmio_read_32(bsec_base + BSEC_WRLOCK_OFF + bank) & - lock_bit) != 0U) { - /* - * In case of write don't need to write, - * the lock is already set. - */ - return true; + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; } - return false; + bank_value = mmio_read_32(bsec_base + BSEC_WRLOCK_OFF + bank); + + *value = ((bank_value & otp_mask) != 0U); + + return BSEC_OK; } /* - * 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. + * bsec_otp_lock: Lock Upper OTP or Global Programming or Debug Enable. + * service: Service to lock, see header file. + * return value: BSEC_OK if no error. */ -uint32_t bsec_otp_lock(uint32_t service, uint32_t value) +uint32_t bsec_otp_lock(uint32_t service) { uintptr_t reg = bsec_base + BSEC_OTP_LOCK_OFF; switch (service) { case BSEC_LOCK_UPPER_OTP: - mmio_write_32(reg, value << BSEC_LOCK_UPPER_OTP); + mmio_write_32(reg, BIT(BSEC_LOCK_UPPER_OTP)); break; case BSEC_LOCK_DEBUG: - mmio_write_32(reg, value << BSEC_LOCK_DEBUG); + mmio_write_32(reg, BIT(BSEC_LOCK_DEBUG)); break; case BSEC_LOCK_PROGRAM: - mmio_write_32(reg, value << BSEC_LOCK_PROGRAM); + mmio_write_32(reg, BIT(BSEC_LOCK_PROGRAM)); break; default: return BSEC_INVALID_PARAM; @@ -799,7 +881,7 @@ uint32_t bsec_otp_lock(uint32_t service, uint32_t value) /* * bsec_power_safmem: Activate or deactivate SAFMEM power. * power: true to power up, false to power down. - * return: BSEC_OK if succeed. + * return value: BSEC_OK if no error. */ static uint32_t bsec_power_safmem(bool power) { @@ -818,7 +900,6 @@ static uint32_t bsec_power_safmem(bool power) mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, register_val); - /* Waiting loop */ if (power) { while (((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) && (timeout != 0U)) { @@ -841,7 +922,7 @@ static uint32_t bsec_power_safmem(bool power) } /* - * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value + * 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. @@ -867,7 +948,7 @@ uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word) /* * bsec_check_nsec_access_rights: check non-secure access rights to target OTP. * otp: OTP number. - * return: BSEC_OK if authorized access. + * return value: BSEC_OK if authorized access. */ uint32_t bsec_check_nsec_access_rights(uint32_t otp) { @@ -877,11 +958,8 @@ uint32_t bsec_check_nsec_access_rights(uint32_t otp) } if (otp >= STM32MP1_UPPER_OTP_START) { - /* Check if BSEC is in OTP-SECURED closed_device state. */ - if (stm32mp_is_closed_device()) { - if (!non_secure_can_access(otp)) { - return BSEC_ERROR; - } + if (!non_secure_can_access(otp)) { + return BSEC_ERROR; } } #endif diff --git a/drivers/st/clk/stm32mp1_calib.c b/drivers/st/clk/stm32mp1_calib.c new file mode 100644 index 0000000000..11a334303c --- /dev/null +++ b/drivers/st/clk/stm32mp1_calib.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_10MS 10000 +#define CALIB_TIMEOUT TIMEOUT_10MS + +struct stm32mp1_trim_boundary_t { + /* Max boundary trim value around forbidden value */ + unsigned int x1; + /* Min boundary trim value around forbidden value */ + unsigned int x2; +}; + +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]; +}; + +/* RCC Wakeup status */ +static bool rcc_wakeup; + +/* List of forbiden values for HSI */ +static uint16_t fbv_hsi[] = { + 512, + 480, + 448, + 416, + 384, + 352, + 320, + 288, + 256, + 224, + 192, + 160, + 128, + 96, + 64, + 32, + 0 +}; + +/* List of forbiden values for CSI */ +static uint16_t fbv_csi[] = { + 256, + 240, + 224, + 208, + 192, + 176, + 160, + 144, + 128, + 112, + 96, + 80, + 64, + 48, + 32, + 16, + 0 +}; + +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 stm32mp1_clk_cal_hsi = { + .fbv = fbv_hsi, + .trim_max = 63, + .trim_min = -64, + .ref_freq = 0, + .freq_margin = 5, + .set_trim = hsi_set_trim, + .get_trim = hsi_get_trimed_cal, +}; + +static struct stm32mp1_clk_cal stm32mp1_clk_cal_csi = { + .fbv = fbv_csi, + .trim_max = 15, + .trim_min = -16, + .ref_freq = 0, + .freq_margin = 8, + .set_trim = csi_set_trim, + .get_trim = csi_get_trimed_cal, +}; + +static uint32_t timer_val; + +/* + * HSI Calibration part + */ +static int get_signed_value(uint8_t val) +{ + return ((int8_t)(val << 1)) >> 1; +} + +static void hsi_set_trim(unsigned int cal) +{ + int clk_trim = (int)cal - (int)stm32mp1_clk_cal_hsi.cal_ref; + uint32_t trim = ((uint32_t)clk_trim << RCC_HSICFGR_HSITRIM_SHIFT) & + RCC_HSICFGR_HSITRIM_MASK; + + mmio_clrsetbits_32(stm32mp_rcc_base() + RCC_HSICFGR, + RCC_HSICFGR_HSITRIM_MASK, trim); +} + +static unsigned int hsi_get_trimed_cal(void) +{ + uint32_t utrim = (mmio_read_32(stm32mp_rcc_base() + RCC_HSICFGR) & + RCC_HSICFGR_HSITRIM_MASK) >> + RCC_HSICFGR_HSITRIM_SHIFT; + + int trim = get_signed_value((uint8_t)utrim); + + if (trim + (int)stm32mp1_clk_cal_hsi.cal_ref < 0) { + return 0; + } + + return stm32mp1_clk_cal_hsi.cal_ref + trim; +} + +static void csi_set_trim(unsigned int cal) +{ + int clk_trim = (int)cal - (int)stm32mp1_clk_cal_csi.cal_ref + + stm32mp1_clk_cal_csi.trim_max + 1; + uint32_t trim = ((uint32_t)clk_trim << RCC_CSICFGR_CSITRIM_SHIFT) & + RCC_CSICFGR_CSITRIM_MASK; + + mmio_clrsetbits_32(stm32mp_rcc_base() + RCC_CSICFGR, + RCC_CSICFGR_CSITRIM_MASK, trim); +} + +static unsigned int csi_get_trimed_cal(void) +{ + uint32_t trim = (mmio_read_32(stm32mp_rcc_base() + RCC_CSICFGR) & + RCC_CSICFGR_CSITRIM_MASK) >> + RCC_CSICFGR_CSITRIM_SHIFT; + + return (int)trim - stm32mp1_clk_cal_csi.trim_max + + (int)stm32mp1_clk_cal_csi.cal_ref - 1; +} + +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 freq = clk_cal->get_freq(); + unsigned long min = clk_cal->ref_freq - + ((clk_cal->ref_freq * clk_cal->freq_margin) / 1000); + unsigned long max = clk_cal->ref_freq + + ((clk_cal->ref_freq * clk_cal->freq_margin) / 1000); + int trim, new_trim; + unsigned long conv; + unsigned long min_conv = ULONG_MAX; + uint64_t start; + + if ((freq >= min) && (freq <= max)) { + return; + } + + trim = clk_cal->get_trim(); + start = timeout_init_us(CALIB_TIMEOUT); + do { + if (freq < clk_cal->ref_freq) { + new_trim = trim_increase(clk_cal, trim); + } else { + new_trim = trim_decrease(clk_cal, trim); + } + + clk_cal->set_trim(new_trim); + freq = clk_cal->get_freq(); + if (freq == 0U) { + /* Calibration will be stopped */ + clk_cal->ref_freq = 0U; + return; + } + conv = (clk_cal->ref_freq < freq) ? + freq - clk_cal->ref_freq : clk_cal->ref_freq - freq; + if (conv < min_conv) { + min_conv = conv; + trim = new_trim; + } + + if (timeout_elapsed(start)) { + break; + } + + } while (conv == min_conv); + + clk_cal->set_trim(trim); + freq = clk_cal->get_freq(); + + if ((freq < min) || (freq > max)) { + ERROR("%s Calibration : Freq %lu, trim %i\n", + (clk_cal->set_trim == hsi_set_trim) ? "HSI" : "CSI", + freq, trim); +#if DEBUG + /* + * Show the steps around the selected trim value + * to correct the margin if needed + */ + new_trim = trim_decrease(clk_cal, trim); + clk_cal->set_trim(new_trim); + ERROR("%s Calibration : Freq %lu, trim %i\n", + (clk_cal->set_trim == hsi_set_trim) ? + "HSI" : "CSI", clk_cal->get_freq(), new_trim); + + new_trim = trim_increase(clk_cal, trim); + clk_cal->set_trim(new_trim); + ERROR("%s Calibration : Freq %lu, trim %i\n", + (clk_cal->set_trim == hsi_set_trim) ? + "HSI" : "CSI", clk_cal->get_freq(), new_trim); +#endif + } +} + +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; +} + +bool stm32mp1_calib_get_wakeup(void) +{ + return rcc_wakeup; +} + +void stm32mp1_calib_set_wakeup(bool state) +{ + rcc_wakeup = state; +} + +void stm32mp1_calib_it_handler(uint32_t id) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + switch (id) { + case STM32MP1_IRQ_RCC_WAKEUP: + plat_ic_set_priority_mask(GIC_HIGHEST_NS_PRIORITY); + mmio_setbits_32(rcc_base + RCC_MP_CIFR, RCC_MP_CIFR_WKUPF); + stm32mp1_calib_set_wakeup(true); + return; + + case STM32MP1_IRQ_MCU_SEV: + stm32mp1_calib_set_wakeup(false); + if ((mmio_read_32(EXTI_BASE + EXTI_RPR3) & + EXTI_RPR3_RPIF65) != 0U) { + mmio_setbits_32(EXTI_BASE + EXTI_RPR3, + EXTI_RPR3_RPIF65); + } + + if ((mmio_read_32(EXTI_BASE + EXTI_FPR3) & + EXTI_FPR3_FPIF65) != 0U) { + mmio_setbits_32(EXTI_BASE + EXTI_FPR3, + EXTI_FPR3_FPIF65); + } + + break; + + case ARM_IRQ_SEC_PHY_TIMER: + default: + break; + } + + if (stm32mp1_clk_cal_hsi.ref_freq != 0U) { + rcc_calibration(&stm32mp1_clk_cal_hsi); + } + + if (stm32mp1_clk_cal_csi.ref_freq != 0U) { + rcc_calibration(&stm32mp1_clk_cal_csi); + } + + if (timer_val != 0U) { + write_cntp_tval(timer_val); + } +} + +int stm32mp1_calib_start_hsi_cal(void) +{ + if (stm32mp1_clk_cal_hsi.ref_freq == 0U) { + return -ENOENT; + } + + rcc_calibration(&stm32mp1_clk_cal_hsi); + return 0; +} + +int stm32mp1_calib_start_csi_cal(void) +{ + if (stm32mp1_clk_cal_csi.ref_freq == 0U) { + return -ENOENT; + } + + rcc_calibration(&stm32mp1_clk_cal_csi); + return 0; +} + +static void init_hsi_cal(void) +{ + int len; + + if (fdt_rcc_read_prop("st,hsi-cal", &len) == NULL) { + return; + } + + stm32_timer_freq_func(&stm32mp1_clk_cal_hsi.get_freq, HSI_CAL); + if (stm32mp1_clk_cal_hsi.get_freq == NULL) { + return; + } + + stm32mp1_clk_cal_hsi.ref_freq = clk_get_rate(CK_HSI); + + /* Read initial value */ + stm32mp1_clk_cal_hsi.cal_ref = + ((mmio_read_32(stm32mp_rcc_base() + RCC_HSICFGR) + & RCC_HSICFGR_HSICAL_MASK) >> RCC_HSICFGR_HSICAL_SHIFT); + + trim_table_init(&stm32mp1_clk_cal_hsi); + + stm32mp1_clk_cal_hsi.set_trim(stm32mp1_clk_cal_hsi.cal_ref); + + rcc_calibration(&stm32mp1_clk_cal_hsi); +} + +static void init_csi_cal(void) +{ + int len; + + if (fdt_rcc_read_prop("st,csi-cal", &len) == NULL) { + return; + } + + stm32_timer_freq_func(&stm32mp1_clk_cal_csi.get_freq, CSI_CAL); + if (stm32mp1_clk_cal_csi.get_freq == NULL) { + return; + } + + stm32mp1_clk_cal_csi.ref_freq = clk_get_rate(CK_CSI); + + /* Read initial value */ + stm32mp1_clk_cal_csi.cal_ref = + ((mmio_read_32(stm32mp_rcc_base() + RCC_CSICFGR) & + RCC_CSICFGR_CSICAL_MASK) >> RCC_CSICFGR_CSICAL_SHIFT); + + trim_table_init(&stm32mp1_clk_cal_csi); + + stm32mp1_clk_cal_csi.set_trim(stm32mp1_clk_cal_csi.cal_ref); + + rcc_calibration(&stm32mp1_clk_cal_csi); +} + +void stm32mp1_calib_init(void) +{ + init_hsi_cal(); + init_csi_cal(); + + timer_val = fdt_rcc_read_uint32_default("st,cal-sec", 0) * + plat_get_syscnt_freq2(); + + if (timer_val > INT32_MAX) { + timer_val = INT32_MAX; + } + + if (timer_val != 0U) { + /* Load & enable timer */ + INFO("Set calibration timer to %u sec\n", + timer_val / plat_get_syscnt_freq2()); + write_cntp_tval(timer_val); + write_cntp_ctl(BIT(0)); + }; + + if (fdt_rcc_enable_it("mcu_sev") < 0) { + VERBOSE("No MCU calibration\n"); + } +} diff --git a/drivers/st/clk/stm32mp1_clk.c b/drivers/st/clk/stm32mp1_clk.c index 564bd87989..00954cb2e8 100644 --- a/drivers/st/clk/stm32mp1_clk.c +++ b/drivers/st/clk/stm32mp1_clk.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -17,8 +18,9 @@ #include #include #include +#include #include -#include +#include #include #include #include @@ -49,6 +51,19 @@ const char *stm32mp_osc_node_label[NB_OSC] = { [_I2S_CKIN] = "i2s_ckin", }; +/* PLL settings computation related definitions */ +#define POST_DIVM_MIN 8000000 +#define POST_DIVM_MAX 16000000 +#define DIVM_MIN 0 +#define DIVM_MAX 63 +#define DIVN_MIN 24 +#define DIVN_MAX 99 +#define DIVP_MIN 0 +#define DIVP_MAX 127 +#define FRAC_MAX 8192 +#define VCO_MIN 800000000 +#define VCO_MAX 1600000000 + enum stm32mp1_parent_id { /* Oscillators are defined in enum stm32mp_osc_id */ @@ -239,6 +254,7 @@ struct stm32mp1_clk_gate { uint8_t bit; uint8_t index; uint8_t set_clr; + uint8_t secure; uint8_t sel; /* Relates to enum stm32mp1_parent_sel */ uint8_t fixed; /* Relates to enum stm32mp1_parent_id */ }; @@ -263,46 +279,59 @@ struct stm32mp1_clk_pll { enum stm32mp_osc_id refclk[REFCLK_SIZE]; }; +/* Compact structure of 32bit cells, copied raw when suspending */ +struct stm32mp1_pll_settings { + uint32_t valid_id; + uint32_t freq[PLAT_MAX_OPP_NB]; + uint32_t volt[PLAT_MAX_OPP_NB]; + uint32_t cfg[PLAT_MAX_OPP_NB][PLAT_MAX_PLLCFG_NB]; + uint32_t frac[PLAT_MAX_OPP_NB]; +}; + /* Clocks with selectable source and non set/clr register access */ -#define _CLK_SELEC(off, b, idx, s) \ +#define _CLK_SELEC(sec, off, b, idx, s) \ { \ .offset = (off), \ .bit = (b), \ .index = (idx), \ .set_clr = 0, \ + .secure = (sec), \ .sel = (s), \ .fixed = _UNKNOWN_ID, \ } /* Clocks with fixed source and non set/clr register access */ -#define _CLK_FIXED(off, b, idx, f) \ +#define _CLK_FIXED(sec, off, b, idx, f) \ { \ .offset = (off), \ .bit = (b), \ .index = (idx), \ .set_clr = 0, \ + .secure = (sec), \ .sel = _UNKNOWN_SEL, \ .fixed = (f), \ } /* Clocks with selectable source and set/clr register access */ -#define _CLK_SC_SELEC(off, b, idx, s) \ +#define _CLK_SC_SELEC(sec, off, b, idx, s) \ { \ .offset = (off), \ .bit = (b), \ .index = (idx), \ .set_clr = 1, \ + .secure = (sec), \ .sel = (s), \ .fixed = _UNKNOWN_ID, \ } /* Clocks with fixed source and set/clr register access */ -#define _CLK_SC_FIXED(off, b, idx, f) \ +#define _CLK_SC_FIXED(sec, off, b, idx, f) \ { \ .offset = (off), \ .bit = (b), \ .index = (idx), \ .set_clr = 1, \ + .secure = (sec), \ .sel = _UNKNOWN_SEL, \ .fixed = (f), \ } @@ -336,81 +365,105 @@ struct stm32mp1_clk_pll { #define NB_GATES ARRAY_SIZE(stm32mp1_clk_gate) +#define SEC 1 +#define N_S 0 + 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_SC_FIXED(RCC_MP_APB1ENSETR, 6, TIM12_K, _PCLK1), - _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_APB1ENSETR, 21, I2C1_K, _I2C12_SEL), - _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 22, I2C2_K, _I2C12_SEL), - _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 23, I2C3_K, _I2C35_SEL), - _CLK_SC_SELEC(RCC_MP_APB1ENSETR, 24, I2C5_K, _I2C35_SEL), - - _CLK_SC_FIXED(RCC_MP_APB2ENSETR, 2, TIM15_K, _PCLK2), - _CLK_SC_SELEC(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL), - - _CLK_SC_FIXED(RCC_MP_APB3ENSETR, 11, SYSCFG, _UNKNOWN_ID), - - _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_APB5ENSETR, 0, SPI6_K, _SPI6_SEL), - _CLK_SC_SELEC(RCC_MP_APB5ENSETR, 2, I2C4_K, _I2C46_SEL), - _CLK_SC_SELEC(RCC_MP_APB5ENSETR, 3, I2C6_K, _I2C46_SEL), - _CLK_SC_SELEC(RCC_MP_APB5ENSETR, 4, USART1_K, _UART1_SEL), - _CLK_SC_FIXED(RCC_MP_APB5ENSETR, 8, RTCAPB, _PCLK5), - _CLK_SC_FIXED(RCC_MP_APB5ENSETR, 11, TZC1, _PCLK5), - _CLK_SC_FIXED(RCC_MP_APB5ENSETR, 12, TZC2, _PCLK5), - _CLK_SC_FIXED(RCC_MP_APB5ENSETR, 13, TZPC, _PCLK5), - _CLK_SC_FIXED(RCC_MP_APB5ENSETR, 15, IWDG1, _PCLK5), - _CLK_SC_FIXED(RCC_MP_APB5ENSETR, 16, BSEC, _PCLK5), - _CLK_SC_SELEC(RCC_MP_APB5ENSETR, 20, STGEN_K, _STGEN_SEL), - - _CLK_SC_SELEC(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL), - _CLK_SC_SELEC(RCC_MP_AHB2ENSETR, 16, SDMMC3_K, _SDMMC3_SEL), - - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 3, GPIOD, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 4, GPIOE, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 5, GPIOF, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 6, GPIOG, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 7, GPIOH, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 8, GPIOI, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 9, GPIOJ, _UNKNOWN_SEL), - _CLK_SC_SELEC(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_SEL), - - _CLK_SC_FIXED(RCC_MP_AHB5ENSETR, 0, GPIOZ, _PCLK5), - _CLK_SC_FIXED(RCC_MP_AHB5ENSETR, 4, CRYP1, _PCLK5), - _CLK_SC_FIXED(RCC_MP_AHB5ENSETR, 5, HASH1, _PCLK5), - _CLK_SC_SELEC(RCC_MP_AHB5ENSETR, 6, RNG1_K, _RNG1_SEL), - _CLK_SC_FIXED(RCC_MP_AHB5ENSETR, 8, BKPSRAM, _PCLK5), - - _CLK_SC_SELEC(RCC_MP_AHB6ENSETR, 12, FMC_K, _FMC_SEL), - _CLK_SC_SELEC(RCC_MP_AHB6ENSETR, 14, QSPI_K, _QSPI_SEL), - _CLK_SC_SELEC(RCC_MP_AHB6ENSETR, 16, SDMMC1_K, _SDMMC12_SEL), - _CLK_SC_SELEC(RCC_MP_AHB6ENSETR, 17, SDMMC2_K, _SDMMC12_SEL), - _CLK_SC_SELEC(RCC_MP_AHB6ENSETR, 24, USBH, _UNKNOWN_SEL), - - _CLK_SELEC(RCC_BDCR, 20, RTC, _RTC_SEL), - _CLK_SELEC(RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL), + _CLK_FIXED(SEC, RCC_DDRITFCR, 0, DDRC1, _ACLK), + _CLK_FIXED(SEC, RCC_DDRITFCR, 1, DDRC1LP, _ACLK), + _CLK_FIXED(SEC, RCC_DDRITFCR, 2, DDRC2, _ACLK), + _CLK_FIXED(SEC, RCC_DDRITFCR, 3, DDRC2LP, _ACLK), + _CLK_FIXED(SEC, RCC_DDRITFCR, 4, DDRPHYC, _PLL2_R), + _CLK_FIXED(SEC, RCC_DDRITFCR, 5, DDRPHYCLP, _PLL2_R), + _CLK_FIXED(SEC, RCC_DDRITFCR, 6, DDRCAPB, _PCLK4), + _CLK_FIXED(SEC, RCC_DDRITFCR, 7, DDRCAPBLP, _PCLK4), + _CLK_FIXED(SEC, RCC_DDRITFCR, 8, AXIDCG, _ACLK), + _CLK_FIXED(SEC, RCC_DDRITFCR, 9, DDRPHYCAPB, _PCLK4), + _CLK_FIXED(SEC, RCC_DDRITFCR, 10, DDRPHYCAPBLP, _PCLK4), + +#if defined(IMAGE_BL32) + _CLK_SC_FIXED(N_S, RCC_MP_APB1ENSETR, 6, TIM12_K, _PCLK1), +#endif +#if defined(IMAGE_BL2) + _CLK_SC_SELEC(N_S, RCC_MP_APB1ENSETR, 14, USART2_K, _UART24_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB1ENSETR, 15, USART3_K, _UART35_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB1ENSETR, 16, UART4_K, _UART24_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB1ENSETR, 17, UART5_K, _UART35_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB1ENSETR, 18, UART7_K, _UART78_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB1ENSETR, 19, UART8_K, _UART78_SEL), +#endif + +#if defined(IMAGE_BL32) + _CLK_SC_FIXED(N_S, RCC_MP_APB2ENSETR, 2, TIM15_K, _PCLK2), +#endif +#if defined(IMAGE_BL2) + _CLK_SC_SELEC(N_S, RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL), +#endif + + _CLK_SC_FIXED(N_S, RCC_MP_APB3ENSETR, 11, SYSCFG, _UNKNOWN_ID), + +#if defined(IMAGE_BL32) + _CLK_SC_SELEC(N_S, RCC_MP_APB4ENSETR, 0, LTDC_PX, _UNKNOWN_SEL), +#endif + _CLK_SC_SELEC(N_S, RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL), + + _CLK_SC_SELEC(SEC, RCC_MP_APB5ENSETR, 0, SPI6_K, _SPI6_SEL), + _CLK_SC_SELEC(SEC, RCC_MP_APB5ENSETR, 2, I2C4_K, _I2C46_SEL), + _CLK_SC_SELEC(SEC, RCC_MP_APB5ENSETR, 3, I2C6_K, _I2C46_SEL), + _CLK_SC_SELEC(SEC, RCC_MP_APB5ENSETR, 4, USART1_K, _UART1_SEL), + _CLK_SC_FIXED(SEC, RCC_MP_APB5ENSETR, 8, RTCAPB, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_APB5ENSETR, 11, TZC1, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_APB5ENSETR, 12, TZC2, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_APB5ENSETR, 13, TZPC, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_APB5ENSETR, 15, IWDG1, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_APB5ENSETR, 16, BSEC, _PCLK5), + _CLK_SC_SELEC(SEC, RCC_MP_APB5ENSETR, 20, STGEN_K, _STGEN_SEL), + + _CLK_SELEC(SEC, RCC_BDCR, 20, RTC, _RTC_SEL), + +#if defined(IMAGE_BL32) + _CLK_SC_SELEC(N_S, RCC_MP_AHB2ENSETR, 0, DMA1, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB2ENSETR, 1, DMA2, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB2ENSETR, 16, SDMMC3_K, _SDMMC3_SEL), +#endif + + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 3, GPIOD, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 4, GPIOE, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 5, GPIOF, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 6, GPIOG, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 7, GPIOH, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 8, GPIOI, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 9, GPIOJ, _UNKNOWN_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_SEL), + + _CLK_SC_FIXED(SEC, RCC_MP_AHB5ENSETR, 0, GPIOZ, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_AHB5ENSETR, 4, CRYP1, _PCLK5), + _CLK_SC_FIXED(SEC, RCC_MP_AHB5ENSETR, 5, HASH1, _PCLK5), + _CLK_SC_SELEC(SEC, RCC_MP_AHB5ENSETR, 6, RNG1_K, _RNG1_SEL), + _CLK_SC_FIXED(SEC, RCC_MP_AHB5ENSETR, 8, BKPSRAM, _PCLK5), + +#if defined(IMAGE_BL32) + _CLK_SC_FIXED(SEC, RCC_MP_TZAHB6ENSETR, 0, MDMA, _ACLK), + _CLK_SC_SELEC(N_S, RCC_MP_AHB6ENSETR, 5, GPU, _UNKNOWN_SEL), + _CLK_SC_FIXED(N_S, RCC_MP_AHB6ENSETR, 10, ETHMAC, _ACLK), +#endif +#if defined(IMAGE_BL2) + _CLK_SC_SELEC(N_S, RCC_MP_AHB6ENSETR, 12, FMC_K, _FMC_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB6ENSETR, 14, QSPI_K, _QSPI_SEL), +#endif + _CLK_SC_SELEC(N_S, RCC_MP_AHB6ENSETR, 16, SDMMC1_K, _SDMMC12_SEL), + _CLK_SC_SELEC(N_S, RCC_MP_AHB6ENSETR, 17, SDMMC2_K, _SDMMC12_SEL), +#if defined(IMAGE_BL32) + _CLK_SC_SELEC(N_S, RCC_MP_AHB6ENSETR, 24, USBH, _UNKNOWN_SEL), +#endif + + _CLK_SELEC(N_S, RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL), }; static const uint8_t i2c12_parents[] = { @@ -465,12 +518,12 @@ static const uint8_t fmc_parents[] = { _ACLK, _PLL3_R, _PLL4_P, _CK_PER }; -static const uint8_t ass_parents[] = { - _HSI, _HSE, _PLL2 +static const uint8_t axiss_parents[] = { + _HSI, _HSE, _PLL2_P }; -static const uint8_t mss_parents[] = { - _HSI, _HSE, _CSI, _PLL3 +static const uint8_t mcuss_parents[] = { + _HSI, _HSE, _CSI, _PLL3_P }; static const uint8_t usbphy_parents[] = { @@ -512,14 +565,13 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { _CLK_PARENT_SEL(SDMMC3, RCC_SDMMC3CKSELR, sdmmc3_parents), _CLK_PARENT_SEL(QSPI, RCC_QSPICKSELR, qspi_parents), _CLK_PARENT_SEL(FMC, RCC_FMCCKSELR, fmc_parents), - _CLK_PARENT_SEL(AXIS, RCC_ASSCKSELR, ass_parents), - _CLK_PARENT_SEL(MCUS, RCC_MSSCKSELR, mss_parents), + _CLK_PARENT_SEL(AXIS, RCC_ASSCKSELR, axiss_parents), + _CLK_PARENT_SEL(MCUS, RCC_MSSCKSELR, mcuss_parents), _CLK_PARENT_SEL(USBPHY, RCC_USBCKSELR, usbphy_parents), _CLK_PARENT_SEL(USBO, RCC_USBCKSELR, usbo_parents), }; /* Define characteristic of PLL according type */ -#define DIVN_MIN 24 static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { [PLL_800] = { .refclk_min = 4, @@ -614,17 +666,55 @@ static const char * const stm32mp1_clk_parent_name[_PARENT_NB] __unused = { [_USB_PHY_48] = "USB_PHY_48", }; +static const char * +const stm32mp1_clk_parent_sel_name[_PARENT_SEL_NB] __unused = { + [_I2C12_SEL] = "I2C12", + [_I2C35_SEL] = "I2C35", + [_STGEN_SEL] = "STGEN", + [_I2C46_SEL] = "I2C46", + [_SPI6_SEL] = "SPI6", + [_UART1_SEL] = "USART1", + [_RNG1_SEL] = "RNG1", + [_UART6_SEL] = "UART6", + [_UART24_SEL] = "UART24", + [_UART35_SEL] = "UART35", + [_UART78_SEL] = "UART78", + [_SDMMC12_SEL] = "SDMMC12", + [_SDMMC3_SEL] = "SDMMC3", + [_QSPI_SEL] = "QSPI", + [_FMC_SEL] = "FMC", + [_AXIS_SEL] = "AXISS", + [_MCUS_SEL] = "MCUSS", + [_USBPHY_SEL] = "USBPHY", + [_USBO_SEL] = "USBO", +}; + /* RCC clock device driver private */ static unsigned long stm32mp1_osc[NB_OSC]; static struct spinlock reg_lock; static unsigned int gate_refcounts[NB_GATES]; static struct spinlock refcount_lock; +static struct stm32mp1_pll_settings pll1_settings; +static uint32_t current_opp_khz; +static uint32_t pll3cr; +static uint32_t pll4cr; +static uint32_t mssckselr; +static uint32_t mcudivr; +#if STM32MP_SP_MIN_IN_DDR +static uint32_t mpapb_iwdg1; +static uint32_t mpapb_iwdg2; +#endif static const struct stm32mp1_clk_gate *gate_ref(unsigned int idx) { return &stm32mp1_clk_gate[idx]; } +static bool gate_is_non_secure(const struct stm32mp1_clk_gate *gate) +{ + return gate->secure == N_S; +} + static const struct stm32mp1_clk_sel *clk_sel_ref(unsigned int idx) { return &stm32mp1_clk_sel[idx]; @@ -747,6 +837,12 @@ static int stm32mp1_clk_get_parent(unsigned long id) p_sel = (mmio_read_32(rcc_base + sel->offset) & (sel->msk << sel->src)) >> sel->src; if (p_sel < sel->nb_parent) { +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + VERBOSE("%s: %s clock is the parent %s of clk id %ld\n", + __func__, + stm32mp1_clk_parent_name[sel->parent[p_sel]], + stm32mp1_clk_parent_sel_name[s], id); +#endif return (int)sel->parent[p_sel]; } @@ -847,9 +943,7 @@ static unsigned long get_clock_rate(int p) reg = mmio_read_32(rcc_base + RCC_MPCKDIVR); clkdiv = reg & RCC_MPUDIV_MASK; - if (clkdiv != 0U) { - clock /= stm32mp1_mpu_div[clkdiv]; - } + clock >>= stm32mp1_mpu_div[clkdiv]; break; default: break; @@ -1057,17 +1151,6 @@ static bool __clk_is_enabled(struct stm32mp1_clk_gate const *gate) return mmio_read_32(rcc_base + gate->offset) & BIT(gate->bit); } -unsigned int stm32mp1_clk_get_refcount(unsigned long id) -{ - int i = stm32mp1_clk_get_gated_id(id); - - if (i < 0) { - panic(); - } - - return gate_refcounts[i]; -} - /* Oscillators and PLLs are not gated at runtime */ static bool clock_is_always_on(unsigned long id) { @@ -1086,17 +1169,19 @@ static bool clock_is_always_on(unsigned long id) case PLL3_P: case PLL3_Q: case PLL3_R: + case CK_AXI: + case CK_MPU: + case CK_MCU: return true; default: return false; } } -void __stm32mp1_clk_enable(unsigned long id, bool secure) +static void __stm32mp1_clk_enable(unsigned long id, bool with_refcnt) { const struct stm32mp1_clk_gate *gate; int i; - unsigned int *refcnt; if (clock_is_always_on(id)) { return; @@ -1109,22 +1194,38 @@ void __stm32mp1_clk_enable(unsigned long id, bool secure) } gate = gate_ref(i); - refcnt = &gate_refcounts[i]; + + if (!with_refcnt) { + __clk_enable(gate); + return; + } + +#if defined(IMAGE_BL32) + if (gate_is_non_secure(gate)) { + /* Enable non-secure clock w/o any refcounting */ + __clk_enable(gate); + return; + } +#endif stm32mp1_clk_lock(&refcount_lock); - if (stm32mp_incr_shrefcnt(refcnt, secure) != 0) { + if (gate_refcounts[i] == 0) { __clk_enable(gate); } + gate_refcounts[i]++; + if (gate_refcounts[i] == UINT_MAX) { + panic(); + } + stm32mp1_clk_unlock(&refcount_lock); } -void __stm32mp1_clk_disable(unsigned long id, bool secure) +static void __stm32mp1_clk_disable(unsigned long id, bool with_refcnt) { const struct stm32mp1_clk_gate *gate; int i; - unsigned int *refcnt; if (clock_is_always_on(id)) { return; @@ -1137,28 +1238,56 @@ void __stm32mp1_clk_disable(unsigned long id, bool secure) } gate = gate_ref(i); - refcnt = &gate_refcounts[i]; + + if (!with_refcnt) { + __clk_disable(gate); + return; + } + +#if defined(IMAGE_BL32) + if (gate_is_non_secure(gate)) { + /* Don't disable non-secure clocks */ + return; + } +#endif stm32mp1_clk_lock(&refcount_lock); - if (stm32mp_decr_shrefcnt(refcnt, secure) != 0) { + if (gate_refcounts[i] == 0) { + panic(); + } + gate_refcounts[i]--; + + if (gate_refcounts[i] == 0) { __clk_disable(gate); } stm32mp1_clk_unlock(&refcount_lock); } -void stm32mp_clk_enable(unsigned long id) +static int stm32mp_clk_enable(unsigned long id) { __stm32mp1_clk_enable(id, true); + + return 0; } -void stm32mp_clk_disable(unsigned long id) +static void stm32mp_clk_disable(unsigned long id) { __stm32mp1_clk_disable(id, true); } -bool stm32mp_clk_is_enabled(unsigned long id) +void stm32mp1_clk_force_enable(unsigned long id) +{ + __stm32mp1_clk_enable(id, false); +} + +void stm32mp1_clk_force_disable(unsigned long id) +{ + __stm32mp1_clk_disable(id, false); +} + +static bool stm32mp_clk_is_enabled(unsigned long id) { int i; @@ -1174,15 +1303,55 @@ bool stm32mp_clk_is_enabled(unsigned long id) return __clk_is_enabled(gate_ref(i)); } -unsigned long stm32mp_clk_get_rate(unsigned long id) +static unsigned long stm32mp_clk_get_rate(unsigned long id) { + uintptr_t rcc_base = stm32mp_rcc_base(); int p = stm32mp1_clk_get_parent(id); + uint32_t prescaler, timpre; + unsigned long parent_rate; if (p < 0) { return 0; } - return get_clock_rate(p); + parent_rate = get_clock_rate(p); + + switch (id) { + case TIM2_K: + case TIM3_K: + case TIM4_K: + case TIM5_K: + case TIM6_K: + case TIM7_K: + case TIM12_K: + case TIM13_K: + case TIM14_K: + prescaler = mmio_read_32(rcc_base + RCC_APB1DIVR) & + RCC_APBXDIV_MASK; + timpre = mmio_read_32(rcc_base + RCC_TIMG1PRER) & + RCC_TIMGXPRER_TIMGXPRE; + break; + + case TIM1_K: + case TIM8_K: + case TIM15_K: + case TIM16_K: + case TIM17_K: + prescaler = mmio_read_32(rcc_base + RCC_APB2DIVR) & + RCC_APBXDIV_MASK; + timpre = mmio_read_32(rcc_base + RCC_TIMG2PRER) & + RCC_TIMGXPRER_TIMGXPRE; + break; + + default: + return parent_rate; + } + + if (prescaler == 0U) { + return parent_rate; + } + + return parent_rate * (timpre + 1U) * 2U; } static void stm32mp1_ls_osc_set(bool enable, uint32_t offset, uint32_t mask_on) @@ -1299,6 +1468,13 @@ static void stm32mp1_hse_enable(bool bypass, bool digbyp, bool css) if (css) { mmio_write_32(rcc_base + RCC_OCENSETR, RCC_OCENR_HSECSSON); } + +#if defined(STM32MP_USB) || defined(STM32MP_UART) + if ((mmio_read_32(rcc_base + RCC_OCENSETR) & RCC_OCENR_HSEBYP) && + (!(digbyp || bypass))) { + panic(); + } +#endif } static void stm32mp1_csi_set(bool enable) @@ -1498,11 +1674,8 @@ static int stm32mp1_pll_stop(enum stm32mp1_pll_id pll_id) return 0; } -static void stm32mp1_pll_config_output(enum stm32mp1_pll_id pll_id, - uint32_t *pllcfg) +static uint32_t stm32mp1_pll_compute_pllxcfgr2(uint32_t *pllcfg) { - const struct stm32mp1_clk_pll *pll = pll_ref(pll_id); - uintptr_t rcc_base = stm32mp_rcc_base(); uint32_t value; value = (pllcfg[PLLCFG_P] << RCC_PLLNCFGR2_DIVP_SHIFT) & @@ -1511,21 +1684,33 @@ static void stm32mp1_pll_config_output(enum stm32mp1_pll_id pll_id, RCC_PLLNCFGR2_DIVQ_MASK; value |= (pllcfg[PLLCFG_R] << RCC_PLLNCFGR2_DIVR_SHIFT) & RCC_PLLNCFGR2_DIVR_MASK; - mmio_write_32(rcc_base + pll->pllxcfgr2, value); + + return value; } -static int stm32mp1_pll_config(enum stm32mp1_pll_id pll_id, - uint32_t *pllcfg, uint32_t fracv) +static void stm32mp1_pll_config_output(enum stm32mp1_pll_id pll_id, + uint32_t *pllcfg) { const struct stm32mp1_clk_pll *pll = pll_ref(pll_id); + uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t value; + + value = stm32mp1_pll_compute_pllxcfgr2(pllcfg); + + mmio_write_32(rcc_base + pll->pllxcfgr2, value); +} + +static int stm32mp1_pll_compute_pllxcfgr1(const struct stm32mp1_clk_pll *pll, + uint32_t *pllcfg, uint32_t *cfgr1) +{ uintptr_t rcc_base = stm32mp_rcc_base(); enum stm32mp1_plltype type = pll->plltype; unsigned long refclk; uint32_t ifrge = 0; - uint32_t src, value; + uint32_t src; src = mmio_read_32(rcc_base + pll->rckxselr) & - RCC_SELR_REFCLK_SRC_MASK; + RCC_SELR_REFCLK_SRC_MASK; refclk = stm32mp1_clk_get_fixed(pll->refclk[src]) / (pllcfg[PLLCFG_M] + 1U); @@ -1539,23 +1724,39 @@ static int stm32mp1_pll_config(enum stm32mp1_pll_id pll_id, ifrge = 1U; } - value = (pllcfg[PLLCFG_N] << RCC_PLLNCFGR1_DIVN_SHIFT) & - RCC_PLLNCFGR1_DIVN_MASK; - value |= (pllcfg[PLLCFG_M] << RCC_PLLNCFGR1_DIVM_SHIFT) & - RCC_PLLNCFGR1_DIVM_MASK; - value |= (ifrge << RCC_PLLNCFGR1_IFRGE_SHIFT) & - RCC_PLLNCFGR1_IFRGE_MASK; + *cfgr1 = (pllcfg[PLLCFG_N] << RCC_PLLNCFGR1_DIVN_SHIFT) & + RCC_PLLNCFGR1_DIVN_MASK; + *cfgr1 |= (pllcfg[PLLCFG_M] << RCC_PLLNCFGR1_DIVM_SHIFT) & + RCC_PLLNCFGR1_DIVM_MASK; + *cfgr1 |= (ifrge << RCC_PLLNCFGR1_IFRGE_SHIFT) & + RCC_PLLNCFGR1_IFRGE_MASK; + + return 0; +} + +static int stm32mp1_pll_config(enum stm32mp1_pll_id pll_id, + uint32_t *pllcfg, uint32_t fracv) +{ + const struct stm32mp1_clk_pll *pll = pll_ref(pll_id); + uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t value; + int ret; + + ret = stm32mp1_pll_compute_pllxcfgr1(pll, pllcfg, &value); + if (ret != 0) { + return ret; + } + mmio_write_32(rcc_base + pll->pllxcfgr1, value); /* Fractional configuration */ value = 0; mmio_write_32(rcc_base + pll->pllxfracr, value); + /* Frac must be enabled only once its configuration is loaded */ value = fracv << RCC_PLLNFRACR_FRACV_SHIFT; mmio_write_32(rcc_base + pll->pllxfracr, value); - - value |= RCC_PLLNFRACR_FRACLE; - mmio_write_32(rcc_base + pll->pllxfracr, value); + mmio_setbits_32(rcc_base + pll->pllxfracr, RCC_PLLNFRACR_FRACLE); stm32mp1_pll_config_output(pll_id, pllcfg); @@ -1662,48 +1863,41 @@ static void stm32mp1_set_rtcsrc(unsigned int clksrc, bool lse_css) } } -static void stm32mp1_stgen_config(void) +/******************************************************************************* + * This function determines the number of needed RTC calendar read operations + * to get consistent values (1 or 2 depending on clock frequencies). + * If APB1 frequency is lower than 7 times the RTC one, the software has to + * read the calendar time and date registers twice. + * Returns true if read twice is needed, false else. + ******************************************************************************/ +bool stm32mp1_rtc_get_read_twice(void) { - uint32_t cntfid0; - unsigned long rate; - unsigned long long counter; - - cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF); - rate = get_clock_rate(stm32mp1_clk_get_parent(STGEN_K)); + unsigned long apb1_freq; + uint32_t rtc_freq; + uint32_t apb1_div; + uintptr_t rcc_base = stm32mp_rcc_base(); - if (cntfid0 == rate) { - return; + switch ((mmio_read_32(rcc_base + RCC_BDCR) & + RCC_BDCR_RTCSRC_MASK) >> RCC_BDCR_RTCSRC_SHIFT) { + case 1: + rtc_freq = stm32mp_clk_get_rate(CK_LSE); + break; + case 2: + rtc_freq = stm32mp_clk_get_rate(CK_LSI); + break; + case 3: + rtc_freq = stm32mp_clk_get_rate(CK_HSE); + rtc_freq /= (mmio_read_32(rcc_base + RCC_RTCDIVR) & + RCC_DIVR_DIV_MASK) + 1U; + break; + default: + panic(); } - mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); - counter = (unsigned long long)mmio_read_32(STGEN_BASE + CNTCVL_OFF); - counter |= ((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF)) << 32; - counter = (counter * rate / cntfid0); - - mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter); - mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32)); - mmio_write_32(STGEN_BASE + CNTFID_OFF, rate); - mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); - - write_cntfrq((u_register_t)rate); - - /* Need to update timer with new frequency */ - generic_delay_timer_init(); -} - -void stm32mp1_stgen_increment(unsigned long long offset_in_ms) -{ - unsigned long long cnt; - - cnt = ((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | - mmio_read_32(STGEN_BASE + CNTCVL_OFF); - - cnt += (offset_in_ms * mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U; + apb1_div = mmio_read_32(rcc_base + RCC_APB1DIVR) & RCC_APBXDIV_MASK; + apb1_freq = stm32mp_clk_get_rate(CK_MCU) >> apb1_div; - mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); - mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)cnt); - mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(cnt >> 32)); - mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); + return apb1_freq < (rtc_freq * 7U); } static void stm32mp1_pkcs_config(uint32_t pkcs) @@ -1720,152 +1914,740 @@ static void stm32mp1_pkcs_config(uint32_t pkcs) mmio_clrsetbits_32(address, mask, value); } -int stm32mp1_clk_init(void) +static bool clk_pll1_settings_are_valid(void) { - uintptr_t rcc_base = stm32mp_rcc_base(); - unsigned int clksrc[CLKSRC_NB]; - unsigned int clkdiv[CLKDIV_NB]; - unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; - int plloff[_PLL_NB]; - int ret, len; - enum stm32mp1_pll_id i; - bool lse_css = false; - bool pll3_preserve = false; - bool pll4_preserve = false; - bool pll4_bootrom = false; - const fdt32_t *pkcs_cell; - void *fdt; + return pll1_settings.valid_id == PLL1_SETTINGS_VALID_ID; +} - if (fdt_get_address(&fdt) == 0) { - return false; - } +int stm32mp1_round_opp_khz(uint32_t *freq_khz) +{ + unsigned int i; + uint32_t round_opp = 0U; + + if (!clk_pll1_settings_are_valid()) { + /* + * No OPP table in DT, or an error occurred during PLL1 + * settings computation, system can only work on current + * operating point, so return current CPU frequency. + */ + *freq_khz = current_opp_khz; - /* Check status field to disable security */ - if (!fdt_get_rcc_secure_status()) { - mmio_write_32(rcc_base + RCC_TZCR, 0); + return 0; } - ret = fdt_rcc_read_uint32_array("st,clksrc", (uint32_t)CLKSRC_NB, - clksrc); - if (ret < 0) { - return -FDT_ERR_NOTFOUND; + for (i = 0; i < PLAT_MAX_OPP_NB; i++) { + if ((pll1_settings.freq[i] <= *freq_khz) && + (pll1_settings.freq[i] > round_opp)) { + round_opp = pll1_settings.freq[i]; + } } - ret = fdt_rcc_read_uint32_array("st,clkdiv", (uint32_t)CLKDIV_NB, - clkdiv); - if (ret < 0) { - return -FDT_ERR_NOTFOUND; + *freq_khz = round_opp; + + return 0; +} + +/* + * Check if PLL1 can be configured on the fly. + * @result (-1) => config on the fly is not possible. + * (0) => config on the fly is possible. + * (+1) => same parameters, no need to reconfigure. + * Return value is 0 if no error. + */ +static int stm32mp1_is_pll_config_on_the_fly(enum stm32mp1_pll_id pll_id, + uint32_t *pllcfg, uint32_t fracv, + int *result) +{ + const struct stm32mp1_clk_pll *pll = pll_ref(pll_id); + uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t fracr; + uint32_t value; + int ret; + + ret = stm32mp1_pll_compute_pllxcfgr1(pll, pllcfg, &value); + if (ret != 0) { + return ret; } - for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { - char name[12]; + if (mmio_read_32(rcc_base + pll->pllxcfgr1) != value) { + /* Different DIVN/DIVM, can't config on the fly */ + *result = -1; + return 0; + } - snprintf(name, sizeof(name), "st,pll@%d", i); - plloff[i] = fdt_rcc_subnode_offset(name); + *result = true; - if (!fdt_check_node(plloff[i])) { - continue; - } + fracr = fracv << RCC_PLLNFRACR_FRACV_SHIFT; + fracr |= RCC_PLLNFRACR_FRACLE; + value = stm32mp1_pll_compute_pllxcfgr2(pllcfg); - ret = fdt_read_uint32_array(fdt, plloff[i], "cfg", - (int)PLLCFG_NB, pllcfg[i]); - if (ret < 0) { - return -FDT_ERR_NOTFOUND; - } + if ((mmio_read_32(rcc_base + pll->pllxfracr) == fracr) && + (mmio_read_32(rcc_base + pll->pllxcfgr2) == value)) { + /* Same parameters, no need to config */ + *result = 1; + } else { + *result = 0; } - stm32mp1_mco_csg(clksrc[CLKSRC_MCO1], clkdiv[CLKDIV_MCO1]); - stm32mp1_mco_csg(clksrc[CLKSRC_MCO2], clkdiv[CLKDIV_MCO2]); + return 0; +} - /* - * Switch ON oscillator found in device-tree. - * Note: HSI already ON after BootROM stage. - */ - if (stm32mp1_osc[_LSI] != 0U) { - stm32mp1_lsi_set(true); +static int stm32mp1_get_mpu_div(uint32_t freq_khz) +{ + unsigned long freq_pll1_p; + unsigned long div; + + freq_pll1_p = get_clock_rate(_PLL1_P) / 1000UL; + if ((freq_pll1_p % freq_khz) != 0U) { + return -1; } - if (stm32mp1_osc[_LSE] != 0U) { - bool bypass, digbyp; - uint32_t lsedrv; - bypass = fdt_osc_read_bool(_LSE, "st,bypass"); - digbyp = fdt_osc_read_bool(_LSE, "st,digbypass"); - lse_css = fdt_osc_read_bool(_LSE, "st,css"); - lsedrv = fdt_osc_read_uint32_default(_LSE, "st,drive", - LSEDRV_MEDIUM_HIGH); - stm32mp1_lse_enable(bypass, digbyp, lsedrv); + div = freq_pll1_p / freq_khz; + + switch (div) { + case 1UL: + case 2UL: + case 4UL: + case 8UL: + case 16UL: + return __builtin_ffs(div) - 1; + default: + return -1; } - if (stm32mp1_osc[_HSE] != 0U) { - bool bypass, digbyp, css; +} - bypass = fdt_osc_read_bool(_HSE, "st,bypass"); - digbyp = fdt_osc_read_bool(_HSE, "st,digbypass"); - css = fdt_osc_read_bool(_HSE, "st,css"); - stm32mp1_hse_enable(bypass, digbyp, css); +static int stm32mp1_pll1_config_from_opp_khz(uint32_t freq_khz) +{ + unsigned int i; + int ret; + int div; + int config_on_the_fly = -1; + + for (i = 0; i < PLAT_MAX_OPP_NB; i++) { + if (pll1_settings.freq[i] == freq_khz) { + break; + } } - /* - * CSI is mandatory for automatic I/O compensation (SYSCFG_CMPCR) - * => switch on CSI even if node is not present in device tree - */ - stm32mp1_csi_set(true); - /* Come back to HSI */ - ret = stm32mp1_set_clksrc(CLK_MPU_HSI); - if (ret != 0) { - return ret; + if (i == PLAT_MAX_OPP_NB) { + return -ENXIO; } - ret = stm32mp1_set_clksrc(CLK_AXI_HSI); - if (ret != 0) { + + div = stm32mp1_get_mpu_div(freq_khz); + + switch (div) { + case -1: + break; + case 0: + return stm32mp1_set_clksrc(CLK_MPU_PLL1P); + default: + ret = stm32mp1_set_clkdiv(div, stm32mp_rcc_base() + + RCC_MPCKDIVR); + if (ret == 0) { + ret = stm32mp1_set_clksrc(CLK_MPU_PLL1P_DIV); + } return ret; } - ret = stm32mp1_set_clksrc(CLK_MCU_HSI); + + ret = stm32mp1_is_pll_config_on_the_fly(_PLL1, &pll1_settings.cfg[i][0], + pll1_settings.frac[i], + &config_on_the_fly); if (ret != 0) { return ret; } - if ((mmio_read_32(rcc_base + RCC_MP_RSTSCLRR) & - RCC_MP_RSTSCLRR_MPUP0RSTF) != 0) { - pll3_preserve = stm32mp1_check_pll_conf(_PLL3, - clksrc[CLKSRC_PLL3], - pllcfg[_PLL3], - plloff[_PLL3]); - pll4_preserve = stm32mp1_check_pll_conf(_PLL4, - clksrc[CLKSRC_PLL4], - pllcfg[_PLL4], - plloff[_PLL4]); + if (config_on_the_fly == 1) { + /* No need to reconfigure, setup already OK */ + return 0; } - for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { - if (((i == _PLL3) && pll3_preserve) || - ((i == _PLL4) && pll4_preserve)) { - continue; - } - - ret = stm32mp1_pll_stop(i); + if (config_on_the_fly == -1) { + /* Switch to HSI and stop PLL1 before reconfiguration */ + ret = stm32mp1_set_clksrc(CLK_MPU_HSI); if (ret != 0) { return ret; } - } - /* Configure HSIDIV */ - if (stm32mp1_osc[_HSI] != 0U) { - ret = stm32mp1_hsidiv(stm32mp1_osc[_HSI]); + ret = stm32mp1_pll_stop(_PLL1); if (ret != 0) { return ret; } - stm32mp1_stgen_config(); } - /* Select DIV */ - /* No ready bit when MPUSRC != CLK_MPU_PLL1P_DIV, MPUDIV is disabled */ - mmio_write_32(rcc_base + RCC_MPCKDIVR, - clkdiv[CLKDIV_MPU] & RCC_DIVR_DIV_MASK); - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_AXI], rcc_base + RCC_AXIDIVR); + ret = stm32mp1_pll_config(_PLL1, &pll1_settings.cfg[i][0], + pll1_settings.frac[i]); if (ret != 0) { return ret; } - ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB4], rcc_base + RCC_APB4DIVR); - if (ret != 0) { + + if (config_on_the_fly == -1) { + /* Start PLL1 and switch back to after reconfiguration */ + stm32mp1_pll_start(_PLL1); + + ret = stm32mp1_pll_output(_PLL1, + pll1_settings.cfg[i][PLLCFG_O]); + if (ret != 0) { + return ret; + } + + ret = stm32mp1_set_clksrc(CLK_MPU_PLL1P); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +int stm32mp1_set_opp_khz(uint32_t freq_khz) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t mpu_src; + + if (freq_khz == current_opp_khz) { + /* OPP already set, nothing to do */ + return 0; + } + + if (!clk_pll1_settings_are_valid()) { + /* + * No OPP table in DT or an error occurred during PLL1 + * settings computation, system can only work on current + * operating point so return error. + */ + return -EACCES; + } + + /* Check that PLL1 is MPU clock source */ + mpu_src = mmio_read_32(rcc_base + RCC_MPCKSELR) & RCC_SELR_SRC_MASK; + if ((mpu_src != RCC_MPCKSELR_PLL) && + (mpu_src != RCC_MPCKSELR_PLL_MPUDIV)) { + return -EPERM; + } + + if (stm32mp1_pll1_config_from_opp_khz(freq_khz) != 0) { + /* Restore original value */ + if (stm32mp1_pll1_config_from_opp_khz(current_opp_khz) != 0) { + ERROR("No CPU operating point can be set\n"); + panic(); + } + + return -EIO; + } + + current_opp_khz = freq_khz; + + return 0; +} + +static int clk_get_pll_settings_from_dt(int plloff, unsigned int *pllcfg, + uint32_t *fracv, uint32_t *csg, + bool *csg_set) +{ + void *fdt; + int ret; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + ret = fdt_read_uint32_array(fdt, plloff, "cfg", (uint32_t)PLLCFG_NB, + pllcfg); + if (ret < 0) { + return -FDT_ERR_NOTFOUND; + } + + *fracv = fdt_read_uint32_default(fdt, plloff, "frac", 0); + + ret = fdt_read_uint32_array(fdt, plloff, "csg", (uint32_t)PLLCSG_NB, + csg); + + *csg_set = (ret == 0); + + if (ret == -FDT_ERR_NOTFOUND) { + ret = 0; + } + + return ret; +} + +static int clk_compute_pll1_settings(unsigned long input_freq, + uint32_t freq_khz, + uint32_t *pllcfg, uint32_t *fracv) +{ + unsigned long long output_freq = freq_khz * 1000U; + unsigned long long freq; + unsigned long long vco; + int divm; + int divn; + int divp; + int frac; + int i; + unsigned int diff; + unsigned int best_diff = UINT_MAX; + + /* Following parameters have always the same value */ + pllcfg[PLLCFG_Q] = 0; + pllcfg[PLLCFG_R] = 0; + pllcfg[PLLCFG_O] = PQR(1, 0, 0); + + for (divm = DIVM_MAX; divm >= DIVM_MIN; divm--) { + unsigned long post_divm = input_freq / + (unsigned long)(divm + 1); + + if ((post_divm < POST_DIVM_MIN) || + (post_divm > POST_DIVM_MAX)) { + continue; + } + + for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { + + freq = output_freq * (divm + 1) * (divp + 1); + + divn = (int)((freq / input_freq) - 1); + if ((divn < DIVN_MIN) || (divn > DIVN_MAX)) { + continue; + } + + frac = (int)(((freq * FRAC_MAX) / input_freq) - + ((divn + 1) * FRAC_MAX)); + + /* 2 loops to refine the fractional part */ + for (i = 2; i != 0; i--) { + if (frac > FRAC_MAX) { + break; + } + + vco = (post_divm * (divn + 1)) + + ((post_divm * (unsigned long long)frac) / + FRAC_MAX); + + if ((vco < (VCO_MIN / 2)) || + (vco > (VCO_MAX / 2))) { + frac++; + continue; + } + + freq = vco / (divp + 1); + if (output_freq < freq) { + diff = (unsigned int)(freq - + output_freq); + } else { + diff = (unsigned int)(output_freq - + freq); + } + + if (diff < best_diff) { + pllcfg[PLLCFG_M] = divm; + pllcfg[PLLCFG_N] = divn; + pllcfg[PLLCFG_P] = divp; + *fracv = frac; + + if (diff == 0) { + return 0; + } + + best_diff = diff; + } + + frac++; + } + } + } + + if (best_diff == UINT_MAX) { + return -1; + } + + return 0; +} + +static int clk_get_pll1_settings(uint32_t clksrc, uint32_t freq_khz, + uint32_t *pllcfg, uint32_t *fracv) +{ + unsigned int i; + + assert(pllcfg != NULL); + assert(fracv != NULL); + + for (i = 0; i < PLAT_MAX_OPP_NB; i++) { + if (pll1_settings.freq[i] == freq_khz) { + break; + } + } + + if (((i == PLAT_MAX_OPP_NB) && (pll1_settings.valid_id == 0U)) || + ((i < PLAT_MAX_OPP_NB) && + (pll1_settings.cfg[i][PLLCFG_O] == 0U))) { + unsigned long input_freq; + + /* + * Either PLL1 settings structure is completely empty, + * or these settings are not yet computed: do it. + */ + switch (clksrc) { + case CLK_PLL12_HSI: + input_freq = stm32mp_clk_get_rate(CK_HSI); + break; + case CLK_PLL12_HSE: + input_freq = stm32mp_clk_get_rate(CK_HSE); + break; + default: + panic(); + } + + return clk_compute_pll1_settings(input_freq, freq_khz, pllcfg, + fracv); + } + + if ((i < PLAT_MAX_OPP_NB) && + (pll1_settings.cfg[i][PLLCFG_O] != 0U)) { + /* + * Index is in range and PLL1 settings are computed: + * use content to answer to the request. + */ + memcpy(pllcfg, &pll1_settings.cfg[i][0], + sizeof(uint32_t) * PLAT_MAX_PLLCFG_NB); + *fracv = pll1_settings.frac[i]; + + return 0; + } + + return -1; +} + +int stm32mp1_clk_get_maxfreq_opp(uint32_t *freq_khz, + uint32_t *voltage_mv) +{ + unsigned int i; + uint32_t freq = 0U; + uint32_t voltage = 0U; + + assert(freq_khz != NULL); + assert(voltage_mv != NULL); + + if (!clk_pll1_settings_are_valid()) { + return -1; + } + + for (i = 0; i < PLAT_MAX_OPP_NB; i++) { + if (pll1_settings.freq[i] > freq) { + freq = pll1_settings.freq[i]; + voltage = pll1_settings.volt[i]; + } + } + + if ((freq == 0U) || (voltage == 0U)) { + return -1; + } + + *freq_khz = freq; + *voltage_mv = voltage; + + return 0; +} + +static int clk_save_current_pll1_settings(uint32_t buck1_voltage) +{ + const struct stm32mp1_clk_pll *pll = pll_ref(_PLL1); + uint32_t rcc_base = stm32mp_rcc_base(); + uint32_t freq; + unsigned int i; + + freq = udiv_round_nearest(stm32mp_clk_get_rate(CK_MPU), 1000L); + + for (i = 0; i < PLAT_MAX_OPP_NB; i++) { + if (pll1_settings.freq[i] == freq) { + break; + } + } + + if ((i == PLAT_MAX_OPP_NB) || + ((pll1_settings.volt[i] != buck1_voltage) && + (buck1_voltage != 0U))) { + return -1; + } + + pll1_settings.cfg[i][PLLCFG_M] = + (mmio_read_32(rcc_base + pll->pllxcfgr1) & + RCC_PLLNCFGR1_DIVM_MASK) >> RCC_PLLNCFGR1_DIVM_SHIFT; + + pll1_settings.cfg[i][PLLCFG_N] = + (mmio_read_32(rcc_base + pll->pllxcfgr1) & + RCC_PLLNCFGR1_DIVN_MASK) >> RCC_PLLNCFGR1_DIVN_SHIFT; + + pll1_settings.cfg[i][PLLCFG_P] = + (mmio_read_32(rcc_base + pll->pllxcfgr2) & + RCC_PLLNCFGR2_DIVP_MASK) >> RCC_PLLNCFGR2_DIVP_SHIFT; + + pll1_settings.cfg[i][PLLCFG_Q] = + (mmio_read_32(rcc_base + pll->pllxcfgr2) & + RCC_PLLNCFGR2_DIVQ_MASK) >> RCC_PLLNCFGR2_DIVQ_SHIFT; + + pll1_settings.cfg[i][PLLCFG_R] = + (mmio_read_32(rcc_base + pll->pllxcfgr2) & + RCC_PLLNCFGR2_DIVR_MASK) >> RCC_PLLNCFGR2_DIVR_SHIFT; + + pll1_settings.cfg[i][PLLCFG_O] = + mmio_read_32(rcc_base + pll->pllxcr) >> + RCC_PLLNCR_DIVEN_SHIFT; + + pll1_settings.frac[i] = + (mmio_read_32(rcc_base + pll->pllxfracr) & + RCC_PLLNFRACR_FRACV_MASK) >> RCC_PLLNFRACR_FRACV_SHIFT; + + return i; +} + +static uint32_t stm32mp1_clk_get_pll1_current_clksrc(void) +{ + uint32_t value; + const struct stm32mp1_clk_pll *pll = pll_ref(_PLL1); + uint32_t rcc_base = stm32mp_rcc_base(); + + value = mmio_read_32(rcc_base + pll->rckxselr); + + switch (value & RCC_SELR_REFCLK_SRC_MASK) { + case 0: + return CLK_PLL12_HSI; + case 1: + return CLK_PLL12_HSE; + default: + panic(); + } +} + +int stm32mp1_clk_compute_all_pll1_settings(uint32_t buck1_voltage) +{ + int i; + int ret; + int index; + uint32_t count = PLAT_MAX_OPP_NB; + uint32_t clksrc; + + ret = dt_get_all_opp_freqvolt(&count, pll1_settings.freq, + pll1_settings.volt); + switch (ret) { + case 0: + break; + case -FDT_ERR_NOTFOUND: + VERBOSE("Cannot find OPP table in DT, use default settings.\n"); + return 0; + default: + ERROR("Inconsistent OPP settings found in DT, ignored.\n"); + return 0; + } + + index = clk_save_current_pll1_settings(buck1_voltage); + + clksrc = stm32mp1_clk_get_pll1_current_clksrc(); + + for (i = 0; i < (int)count; i++) { + if (i == index) { + continue; + } + + ret = clk_get_pll1_settings(clksrc, pll1_settings.freq[i], + &pll1_settings.cfg[i][0], + &pll1_settings.frac[i]); + if (ret != 0) { + return ret; + } + } + + pll1_settings.valid_id = PLL1_SETTINGS_VALID_ID; + + return 0; +} + +void stm32mp1_clk_lp_save_opp_pll1_settings(uint8_t *data, size_t size) +{ + if (size != sizeof(pll1_settings) || !clk_pll1_settings_are_valid()) { + panic(); + } + + memcpy(data, &pll1_settings, size); +} + +void stm32mp1_clk_lp_load_opp_pll1_settings(uint8_t *data, size_t size) +{ + if (size != sizeof(pll1_settings)) { + panic(); + } + + memcpy(&pll1_settings, data, size); +} + +int stm32mp1_clk_init(uint32_t pll1_freq_khz) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t pllfracv[_PLL_NB]; + uint32_t pllcsg[_PLL_NB][PLLCSG_NB]; + unsigned int clksrc[CLKSRC_NB]; + unsigned int clkdiv[CLKDIV_NB]; + unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; + int plloff[_PLL_NB]; + int ret, len; + enum stm32mp1_pll_id i; + bool pllcsg_set[_PLL_NB]; + bool pllcfg_valid[_PLL_NB]; + bool lse_css = false; + bool pll3_preserve = false; + bool pll4_preserve = false; + bool pll4_bootrom = false; + const fdt32_t *pkcs_cell; + void *fdt; + int stgen_p = stm32mp1_clk_get_parent((int)STGEN_K); + int usbphy_p = stm32mp1_clk_get_parent((int)USBPHY_K); + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + ret = fdt_rcc_read_uint32_array("st,clksrc", (uint32_t)CLKSRC_NB, + clksrc); + if (ret < 0) { + return -FDT_ERR_NOTFOUND; + } + + ret = fdt_rcc_read_uint32_array("st,clkdiv", (uint32_t)CLKDIV_NB, + clkdiv); + if (ret < 0) { + return -FDT_ERR_NOTFOUND; + } + + for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { + char name[12]; + + snprintf(name, sizeof(name), "st,pll@%d", i); + plloff[i] = fdt_rcc_subnode_offset(name); + + pllcfg_valid[i] = fdt_check_node(plloff[i]); + if (pllcfg_valid[i]) { + ret = clk_get_pll_settings_from_dt(plloff[i], pllcfg[i], + &pllfracv[i], + pllcsg[i], + &pllcsg_set[i]); + if (ret != 0) { + return ret; + } + + continue; + } + + if ((i == _PLL1) && (pll1_freq_khz != 0U)) { + ret = clk_get_pll1_settings(clksrc[CLKSRC_PLL12], + pll1_freq_khz, + pllcfg[i], &pllfracv[i]); + if (ret != 0) { + return ret; + } + + pllcfg_valid[i] = true; + } + } + + stm32mp1_mco_csg(clksrc[CLKSRC_MCO1], clkdiv[CLKDIV_MCO1]); + stm32mp1_mco_csg(clksrc[CLKSRC_MCO2], clkdiv[CLKDIV_MCO2]); + + /* + * Switch ON oscillator found in device-tree. + * Note: HSI already ON after BootROM stage. + */ + if (stm32mp1_osc[_LSI] != 0U) { + stm32mp1_lsi_set(true); + } + if (stm32mp1_osc[_LSE] != 0U) { + const char *name = stm32mp_osc_node_label[_LSE]; + bool bypass, digbyp; + uint32_t lsedrv; + + bypass = fdt_clk_read_bool(name, "st,bypass"); + digbyp = fdt_clk_read_bool(name, "st,digbypass"); + lse_css = fdt_clk_read_bool(name, "st,css"); + lsedrv = fdt_clk_read_uint32_default(name, "st,drive", + LSEDRV_MEDIUM_HIGH); + stm32mp1_lse_enable(bypass, digbyp, lsedrv); + } + if (stm32mp1_osc[_HSE] != 0U) { + const char *name = stm32mp_osc_node_label[_HSE]; + bool bypass, digbyp, css; + + bypass = fdt_clk_read_bool(name, "st,bypass"); + digbyp = fdt_clk_read_bool(name, "st,digbypass"); + css = fdt_clk_read_bool(name, "st,css"); + stm32mp1_hse_enable(bypass, digbyp, css); + } + /* + * CSI is mandatory for automatic I/O compensation (SYSCFG_CMPCR) + * => switch on CSI even if node is not present in device tree + */ + stm32mp1_csi_set(true); + + /* Come back to HSI */ + ret = stm32mp1_set_clksrc(CLK_MPU_HSI); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clksrc(CLK_AXI_HSI); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clksrc(CLK_MCU_HSI); + if (ret != 0) { + return ret; + } + + if ((mmio_read_32(rcc_base + RCC_MP_RSTSCLRR) & + RCC_MP_RSTSCLRR_MPUP0RSTF) != 0) { + pll3_preserve = stm32mp1_check_pll_conf(_PLL3, + clksrc[CLKSRC_PLL3], + pllcfg[_PLL3], + plloff[_PLL3]); + pll4_preserve = stm32mp1_check_pll_conf(_PLL4, + clksrc[CLKSRC_PLL4], + pllcfg[_PLL4], + plloff[_PLL4]); + } + /* Don't initialize PLL4, when used by BOOTROM */ + if ((get_boot_device() == BOOT_DEVICE_USB) && + ((stgen_p == (int)_PLL4_R) || (usbphy_p == (int)_PLL4_R))) { + pll4_bootrom = true; + pll4_preserve = true; + } + + for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { + if (((i == _PLL3) && pll3_preserve) || + ((i == _PLL4) && pll4_preserve)) { + continue; + } + + ret = stm32mp1_pll_stop(i); + if (ret != 0) { + return ret; + } + } + + /* Configure HSIDIV */ + if (stm32mp1_osc[_HSI] != 0U) { + ret = stm32mp1_hsidiv(stm32mp1_osc[_HSI]); + if (ret != 0) { + return ret; + } + + stm32mp_stgen_config(stm32mp_clk_get_rate(STGEN_K)); + } + + /* Select DIV */ + /* No ready bit when MPUSRC != CLK_MPU_PLL1P_DIV, MPUDIV is disabled */ + mmio_write_32(rcc_base + RCC_MPCKDIVR, + clkdiv[CLKDIV_MPU] & RCC_DIVR_DIV_MASK); + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_AXI], rcc_base + RCC_AXIDIVR); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB4], rcc_base + RCC_APB4DIVR); + if (ret != 0) { return ret; } ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB5], rcc_base + RCC_APB5DIVR); @@ -1915,15 +2697,12 @@ int stm32mp1_clk_init(void) /* Configure and start PLLs */ for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { - uint32_t fracv; - uint32_t csg[PLLCSG_NB]; - if (((i == _PLL3) && pll3_preserve) || ((i == _PLL4) && pll4_preserve && !pll4_bootrom)) { continue; } - if (!fdt_check_node(plloff[i])) { + if (!pllcfg_valid[i]) { continue; } @@ -1933,25 +2712,20 @@ int stm32mp1_clk_init(void) continue; } - fracv = fdt_read_uint32_default(fdt, plloff[i], "frac", 0); - - ret = stm32mp1_pll_config(i, pllcfg[i], fracv); + ret = stm32mp1_pll_config(i, pllcfg[i], pllfracv[i]); if (ret != 0) { return ret; } - ret = fdt_read_uint32_array(fdt, plloff[i], "csg", - (uint32_t)PLLCSG_NB, csg); - if (ret == 0) { - stm32mp1_pll_csg(i, csg); - } else if (ret != -FDT_ERR_NOTFOUND) { - return ret; + + if (pllcsg_set[i]) { + stm32mp1_pll_csg(i, pllcsg[i]); } stm32mp1_pll_start(i); } /* Wait and start PLLs ouptut when ready */ for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { - if (!fdt_check_node(plloff[i])) { + if (!pllcfg_valid[i]) { continue; } @@ -1985,6 +2759,11 @@ int stm32mp1_clk_init(void) if (pkcs_cell != NULL) { bool ckper_disabled = false; uint32_t j; + uint32_t usbreg_bootrom = 0U; + + if (pll4_bootrom) { + usbreg_bootrom = mmio_read_32(rcc_base + RCC_USBCKSELR); + } for (j = 0; j < ((uint32_t)len / sizeof(uint32_t)); j++) { uint32_t pkcs = fdt32_to_cpu(pkcs_cell[j]); @@ -2005,13 +2784,33 @@ int stm32mp1_clk_init(void) if (ckper_disabled) { stm32mp1_pkcs_config(CLK_CKPER_DISABLED); } + + if (pll4_bootrom) { + uint32_t usbreg_value, usbreg_mask; + const struct stm32mp1_clk_sel *sel; + + sel = clk_sel_ref(_USBPHY_SEL); + usbreg_mask = (uint32_t)sel->msk << sel->src; + sel = clk_sel_ref(_USBO_SEL); + usbreg_mask |= (uint32_t)sel->msk << sel->src; + + usbreg_value = mmio_read_32(rcc_base + RCC_USBCKSELR) & + usbreg_mask; + usbreg_bootrom &= usbreg_mask; + if (usbreg_bootrom != usbreg_value) { + VERBOSE("forbidden new USB clk path\n"); + VERBOSE("vs bootrom on USB boot\n"); + return -FDT_ERR_BADVALUE; + } + } } /* Switch OFF HSI if not found in device-tree */ if (stm32mp1_osc[_HSI] == 0U) { stm32mp1_hsi_set(false); } - stm32mp1_stgen_config(); + + stm32mp_stgen_config(stm32mp_clk_get_rate(STGEN_K)); /* Software Self-Refresh mode (SSR) during DDR initilialization */ mmio_clrsetbits_32(rcc_base + RCC_DDRITFCR, @@ -2200,6 +2999,429 @@ void stm32mp1_register_clock_parents_secure(unsigned long clock_id) } #endif /* STM32MP_SHARED_RESOURCES */ +/* + * Sequence to save/restore the non-secure configuration. + * Restoring clocks and muxes need IPs to run on kernel clock + * hence on configuration is restored at resume, kernel clock + * should be disable: this mandates secure access. + * + * backup_mux*_cfg for the clock muxes. + * backup_clock_sc_cfg for the set/clear clock gating registers + * backup_clock_cfg for the regular full write registers + */ + +struct backup_mux_cfg { + uint16_t offset; + uint8_t value; + uint8_t bit_len; +}; + +#define MUXCFG(_offset, _bit_len) \ + { .offset = (_offset), .bit_len = (_bit_len) } + +static struct backup_mux_cfg backup_mux0_cfg[] = { + MUXCFG(RCC_SDMMC12CKSELR, 3), + MUXCFG(RCC_SPI2S23CKSELR, 3), + MUXCFG(RCC_SPI45CKSELR, 3), + MUXCFG(RCC_I2C12CKSELR, 3), + MUXCFG(RCC_I2C35CKSELR, 3), + MUXCFG(RCC_LPTIM23CKSELR, 3), + MUXCFG(RCC_LPTIM45CKSELR, 3), + MUXCFG(RCC_UART24CKSELR, 3), + MUXCFG(RCC_UART35CKSELR, 3), + MUXCFG(RCC_UART78CKSELR, 3), + MUXCFG(RCC_SAI1CKSELR, 3), + MUXCFG(RCC_ETHCKSELR, 2), + MUXCFG(RCC_I2C46CKSELR, 3), + MUXCFG(RCC_RNG2CKSELR, 2), + MUXCFG(RCC_SDMMC3CKSELR, 3), + MUXCFG(RCC_FMCCKSELR, 2), + MUXCFG(RCC_QSPICKSELR, 2), + MUXCFG(RCC_USBCKSELR, 2), + MUXCFG(RCC_SPDIFCKSELR, 2), + MUXCFG(RCC_SPI2S1CKSELR, 3), + MUXCFG(RCC_CECCKSELR, 2), + MUXCFG(RCC_LPTIM1CKSELR, 3), + MUXCFG(RCC_UART6CKSELR, 3), + MUXCFG(RCC_FDCANCKSELR, 2), + MUXCFG(RCC_SAI2CKSELR, 3), + MUXCFG(RCC_SAI3CKSELR, 3), + MUXCFG(RCC_SAI4CKSELR, 3), + MUXCFG(RCC_ADCCKSELR, 2), + MUXCFG(RCC_DSICKSELR, 1), + MUXCFG(RCC_CPERCKSELR, 2), + MUXCFG(RCC_RNG1CKSELR, 2), + MUXCFG(RCC_STGENCKSELR, 2), + MUXCFG(RCC_UART1CKSELR, 3), + MUXCFG(RCC_SPI6CKSELR, 3), +}; + +static struct backup_mux_cfg backup_mux4_cfg[] = { + MUXCFG(RCC_USBCKSELR, 1), +}; + +static void backup_mux_cfg(void) +{ + uintptr_t base = stm32mp_rcc_base(); + struct backup_mux_cfg *cfg; + size_t i; + + cfg = backup_mux0_cfg; + for (i = 0U; i < ARRAY_SIZE(backup_mux0_cfg); i++) { + cfg[i].value = mmio_read_32(base + cfg[i].offset) & + GENMASK_32(cfg[i].bit_len - 1U, 0U); + } + + cfg = backup_mux4_cfg; + for (i = 0U; i < ARRAY_SIZE(backup_mux4_cfg); i++) { + cfg[i].value = mmio_read_32(base + cfg[i].offset) & + GENMASK_32(4U + cfg[i].bit_len - 1U, 4U); + } +} + +static void restore_mux_cfg(void) +{ + uintptr_t base = stm32mp_rcc_base(); + struct backup_mux_cfg *cfg; + size_t i; + + cfg = backup_mux0_cfg; + for (i = 0U; i < ARRAY_SIZE(backup_mux0_cfg); i++) { + uint32_t mask = GENMASK_32(cfg[i].bit_len - 1U, 0U); + uint32_t value = cfg[i].value & mask; + + mmio_clrsetbits_32(base + cfg[i].offset, mask, value); + } + + cfg = backup_mux4_cfg; + for (i = 0U; i < ARRAY_SIZE(backup_mux4_cfg); i++) { + uint32_t mask = GENMASK_32(4U + cfg[i].bit_len - 1U, 4U); + uint32_t value = cfg[i].value & mask; + + mmio_clrsetbits_32(base + cfg[i].offset, mask, value); + } +} + +/* Structure is used for set/clear registers and for regular registers */ +struct backup_clock_cfg { + uint32_t offset; + uint32_t value; +}; + +static struct backup_clock_cfg backup_clock_sc_cfg[] = { + { .offset = RCC_MP_APB1ENSETR }, + { .offset = RCC_MP_APB2ENSETR }, + { .offset = RCC_MP_APB3ENSETR }, + { .offset = RCC_MP_APB4ENSETR }, + { .offset = RCC_MP_APB5ENSETR }, + { .offset = RCC_MP_AHB2ENSETR }, + { .offset = RCC_MP_AHB3ENSETR }, + { .offset = RCC_MP_AHB4ENSETR }, + { .offset = RCC_MP_AHB5ENSETR }, + { .offset = RCC_MP_AHB6ENSETR }, + { .offset = RCC_MP_MLAHBENSETR }, +}; + +static struct backup_clock_cfg backup_clock_cfg[] = { + { .offset = RCC_MCO1CFGR }, + { .offset = RCC_MCO2CFGR }, + { .offset = RCC_PLL3CR }, + { .offset = RCC_PLL4CR }, + { .offset = RCC_PLL4CFGR2 }, + { .offset = RCC_MCUDIVR }, + { .offset = RCC_MSSCKSELR }, +}; + +static void backup_sc_cfg(void) +{ + struct backup_clock_cfg *cfg = backup_clock_sc_cfg; + size_t count = ARRAY_SIZE(backup_clock_sc_cfg); + uintptr_t base = stm32mp_rcc_base(); + size_t i; + + for (i = 0U; i < count; i++) { + cfg[i].value = mmio_read_32(base + cfg[i].offset); + } +} + +static void restore_sc_cfg(void) +{ + struct backup_clock_cfg *cfg = backup_clock_sc_cfg; + size_t count = ARRAY_SIZE(backup_clock_sc_cfg); + uintptr_t base = stm32mp_rcc_base(); + size_t i; + + for (i = 0U; i < count; i++) { + mmio_write_32(base + cfg[i].offset, cfg[i].value); + mmio_write_32(base + cfg[i].offset + RCC_MP_ENCLRR_OFFSET, + ~cfg[i].value); + } +} + +static void backup_regular_cfg(void) +{ + struct backup_clock_cfg *cfg = backup_clock_cfg; + size_t count = ARRAY_SIZE(backup_clock_cfg); + uintptr_t base = stm32mp_rcc_base(); + size_t i; + + for (i = 0U; i < count; i++) { + cfg[i].value = mmio_read_32(base + cfg[i].offset); + } +} + +static void restore_regular_cfg(void) +{ + struct backup_clock_cfg *cfg = backup_clock_cfg; + size_t count = ARRAY_SIZE(backup_clock_cfg); + uintptr_t base = stm32mp_rcc_base(); + size_t i; + + for (i = 0U; i < count; i++) { + mmio_write_32(base + cfg[i].offset, cfg[i].value); + } +} + +static void disable_kernel_clocks(void) +{ + const uint32_t ker_mask = RCC_OCENR_HSIKERON | + RCC_OCENR_CSIKERON | + RCC_OCENR_HSEKERON; + + /* Disable all ck_xxx_ker clocks */ + mmio_write_32(stm32mp_rcc_base() + RCC_OCENCLRR, ker_mask); +} + +static void enable_kernel_clocks(void) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t reg; + const uint32_t ker_mask = RCC_OCENR_HSIKERON | + RCC_OCENR_CSIKERON | + RCC_OCENR_HSEKERON; + + /* Enable ck_xxx_ker clocks if ck_xxx was on */ + reg = mmio_read_32(rcc_base + RCC_OCENSETR) << 1U; + mmio_write_32(rcc_base + RCC_OCENSETR, reg & ker_mask); +} + +static void clear_rcc_reset_status(void) +{ + /* Clear reset status fields */ + mmio_write_32(stm32mp_rcc_base() + RCC_MP_RSTSCLRR, 0U); +} + +void save_clock_pm_context(void) +{ + size_t offset = 0U; + + stm32mp1_pm_save_clock_cfg(offset, + (uint8_t *)backup_mux0_cfg, + sizeof(backup_mux0_cfg)); + offset += sizeof(backup_mux0_cfg); + + stm32mp1_pm_save_clock_cfg(offset, + (uint8_t *)backup_mux4_cfg, + sizeof(backup_mux4_cfg)); + offset += sizeof(backup_mux4_cfg); + + stm32mp1_pm_save_clock_cfg(offset, + (uint8_t *)backup_clock_sc_cfg, + sizeof(backup_clock_sc_cfg)); + offset += sizeof(backup_clock_sc_cfg); + + stm32mp1_pm_save_clock_cfg(offset, + (uint8_t *)backup_clock_cfg, + sizeof(backup_clock_cfg)); + offset += sizeof(backup_clock_cfg); + + stm32mp1_pm_save_clock_cfg(offset, + (uint8_t *)gate_refcounts, + sizeof(gate_refcounts)); +} + +void restore_clock_pm_context(void) +{ + size_t offset = 0U; + + stm32mp1_pm_restore_clock_cfg(offset, + (uint8_t *)backup_mux0_cfg, + sizeof(backup_mux0_cfg)); + offset += sizeof(backup_mux0_cfg); + + stm32mp1_pm_restore_clock_cfg(offset, + (uint8_t *)backup_mux4_cfg, + sizeof(backup_mux4_cfg)); + offset += sizeof(backup_mux4_cfg); + + stm32mp1_pm_restore_clock_cfg(offset, + (uint8_t *)backup_clock_sc_cfg, + sizeof(backup_clock_sc_cfg)); + offset += sizeof(backup_clock_sc_cfg); + + stm32mp1_pm_restore_clock_cfg(offset, + (uint8_t *)backup_clock_cfg, + sizeof(backup_clock_cfg)); + offset += sizeof(backup_clock_cfg); + + stm32mp1_pm_restore_clock_cfg(offset, + (uint8_t *)gate_refcounts, + sizeof(gate_refcounts)); +} + +void stm32mp1_clock_suspend(void) +{ + backup_regular_cfg(); + backup_sc_cfg(); + backup_mux_cfg(); + clear_rcc_reset_status(); +} + +void stm32mp1_clock_resume(void) +{ + unsigned int idx; + + restore_mux_cfg(); + restore_sc_cfg(); + restore_regular_cfg(); + + /* Sync secure and shared clocks physical state on functional state */ + for (idx = 0U; idx < NB_GATES; idx++) { + struct stm32mp1_clk_gate const *gate = gate_ref(idx); + + if (gate_is_non_secure(gate)) { + continue; + } + + if (gate_refcounts[idx] != 0U) { + VERBOSE("Resume clock %d enable\n", gate->index); + __clk_enable(gate); + } else { + VERBOSE("Resume clock %d disable\n", gate->index); + __clk_disable(gate); + } + } + + disable_kernel_clocks(); +} + +void stm32mp1_clock_stopmode_save(void) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + /* Save registers not restored after STOP mode */ + pll3cr = mmio_read_32(rcc_base + RCC_PLL3CR); + pll4cr = mmio_read_32(rcc_base + RCC_PLL4CR); + mssckselr = mmio_read_32(rcc_base + RCC_MSSCKSELR); + mcudivr = mmio_read_32(rcc_base + RCC_MCUDIVR) & RCC_MCUDIV_MASK; +#if STM32MP_SP_MIN_IN_DDR + mpapb_iwdg2 = (mmio_read_32(rcc_base + RCC_MP_APB4ENSETR) & + RCC_MP_APB4ENSETR_IWDG2APBEN); + mpapb_iwdg1 = (mmio_read_32(rcc_base + RCC_MP_APB5ENSETR) & + RCC_MP_APB5ENSETR_IWDG1APBEN); +#endif + + enable_kernel_clocks(); +} + +static bool pll_is_running(uint32_t pll_offset) +{ + uintptr_t pll_cr = stm32mp_rcc_base() + pll_offset; + + return (mmio_read_32(pll_cr) & RCC_PLLNCR_PLLON) != 0U; +} + +static bool pll_was_running(uint32_t saved_value) +{ + return (saved_value & RCC_PLLNCR_PLLON) != 0U; +} + +int stm32mp1_clock_stopmode_resume(void) +{ + int res; + uintptr_t rcc_base = stm32mp_rcc_base(); + + if (pll_was_running(pll4cr) && !pll_is_running(RCC_PLL4CR)) { + stm32mp1_pll_start(_PLL4); + } + + if (pll_was_running(pll3cr)) { + if (!pll_is_running(RCC_PLL3CR)) { + stm32mp1_pll_start(_PLL3); + } + + res = stm32mp1_pll_output(_PLL3, + pll3cr >> RCC_PLLNCR_DIVEN_SHIFT); + if (res != 0) { + return res; + } + } + + if (pll_was_running(pll4cr)) { + res = stm32mp1_pll_output(_PLL4, + pll4cr >> RCC_PLLNCR_DIVEN_SHIFT); + if (res != 0) { + return res; + } + } + + /* Restore MCU clock src after PLL3 RDY */ + mmio_write_32(rcc_base + RCC_MSSCKSELR, mssckselr); + + /* Restore MCUDIV */ + res = stm32mp1_set_clkdiv(mcudivr, rcc_base + RCC_MCUDIVR); + if (res != 0) { + return res; + } + +#if STM32MP_SP_MIN_IN_DDR + /* Restore IWDG clock */ + mmio_clrsetbits_32(rcc_base + RCC_MP_APB5ENSETR, + RCC_MP_APB5ENSETR_IWDG1APBEN, + mpapb_iwdg1); + + mmio_clrsetbits_32(rcc_base + RCC_MP_APB4ENSETR, + RCC_MP_APB4ENSETR_IWDG2APBEN, + mpapb_iwdg2); +#endif + + disable_kernel_clocks(); + + return 0; +} + +void stm32mp1_clk_mcuss_protect(bool enable) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + if (enable) { + mmio_setbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); + } else { + mmio_clrbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); + } +} + +/* Sync secure clock refcount after all drivers probe/inits, */ +void stm32mp1_dump_clocks_state(void) +{ +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + unsigned int idx; + + /* Dump clocks state */ + for (idx = 0U; idx < NB_GATES; idx++) { + const struct stm32mp1_clk_gate *gate = gate_ref(idx); + unsigned long __unused clock_id = gate->index; + unsigned int __unused refcnt = gate_refcounts[idx]; + int __unused p = stm32mp1_clk_get_parent(clock_id); + + VERBOSE("stm32mp1 clk %lu %sabled (refcnt %d) (parent %d %s)\n", + clock_id, __clk_is_enabled(gate) ? "en" : "dis", + refcnt, p, + p < 0 ? "n.a" : stm32mp1_clk_parent_sel_name[p]); + } +#endif +} + static void sync_earlyboot_clocks_state(void) { unsigned int idx; @@ -2210,6 +3432,7 @@ static void sync_earlyboot_clocks_state(void) DDRC2, DDRC2LP, DDRCAPB, DDRPHYCAPB, DDRPHYCAPBLP, DDRPHYC, DDRPHYCLP, + RTCAPB, TZC1, TZC2, TZPC, STGEN_K, @@ -2218,17 +3441,41 @@ static void sync_earlyboot_clocks_state(void) for (idx = 0U; idx < ARRAY_SIZE(secure_enable); idx++) { stm32mp_clk_enable(secure_enable[idx]); } - - if (!stm32mp_is_single_core()) { - stm32mp1_clk_enable_secure(RTCAPB); - } } +static const clk_ops_t stm32mp_clk_ops = { + .enable = stm32mp_clk_enable, + .disable = stm32mp_clk_disable, + .is_enabled = stm32mp_clk_is_enabled, + .get_rate = stm32mp_clk_get_rate, + .get_parent = stm32mp1_clk_get_parent, +}; + int stm32mp1_clk_probe(void) { + unsigned long freq_khz; + + assert(PLLCFG_NB == PLAT_MAX_PLLCFG_NB); + +#if defined(IMAGE_BL32) + if (!fdt_get_rcc_secure_state()) { + mmio_write_32(stm32mp_rcc_base() + RCC_TZCR, 0U); + } +#endif + stm32mp1_osc_init(); sync_earlyboot_clocks_state(); + /* Save current CPU operating point value */ + freq_khz = udiv_round_nearest(stm32mp_clk_get_rate(CK_MPU), 1000UL); + if (freq_khz > (unsigned long)UINT32_MAX) { + panic(); + } + + current_opp_khz = (uint32_t)freq_khz; + + clk_register(&stm32mp_clk_ops); + return 0; } diff --git a/drivers/st/clk/stm32mp_clkfunc.c b/drivers/st/clk/stm32mp_clkfunc.c index 8333f6dfbf..a119bbf386 100644 --- a/drivers/st/clk/stm32mp_clkfunc.c +++ b/drivers/st/clk/stm32mp_clkfunc.c @@ -10,9 +10,15 @@ #include +#include #include +#include +#include #include #include +#include + +#define DT_UART_COMPAT "st,stm32h7-uart" /* * Get the frequency of an oscillator from its name in device tree. @@ -43,7 +49,8 @@ int fdt_osc_read_freq(const char *name, uint32_t *freq) return ret; } - if (strncmp(cchar, name, (size_t)ret) == 0) { + if ((strncmp(cchar, name, (size_t)ret) == 0) && + (fdt_get_status(subnode) != DT_DISABLED)) { const fdt32_t *cuint; cuint = fdt_getprop(fdt, subnode, "clock-frequency", @@ -69,7 +76,7 @@ int fdt_osc_read_freq(const char *name, uint32_t *freq) * @param prop_name: property name * @return: true/false regarding search result. */ -bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name) +bool fdt_clk_read_bool(const char *node_label, const char *prop_name) { int node, subnode; void *fdt; @@ -78,10 +85,6 @@ bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name) return false; } - if (osc_id >= NB_OSC) { - return false; - } - node = fdt_path_offset(fdt, "/clocks"); if (node < 0) { return false; @@ -96,8 +99,7 @@ bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name) return false; } - if (strncmp(cchar, stm32mp_osc_node_label[osc_id], - (size_t)ret) != 0) { + if (strncmp(cchar, node_label, (size_t)ret) != 0) { continue; } @@ -110,13 +112,13 @@ bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name) } /* - * Get the value of a oscillator property from its ID. - * @param osc_id: oscillator ID + * Get the value of a oscillator property from its name. + * @param node_label: oscillator name * @param prop_name: property name * @param dflt_value: default value * @return oscillator value on success, default value if property not found. */ -uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, +uint32_t fdt_clk_read_uint32_default(const char *node_label, const char *prop_name, uint32_t dflt_value) { int node, subnode; @@ -126,10 +128,6 @@ uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, return dflt_value; } - if (osc_id >= NB_OSC) { - return dflt_value; - } - node = fdt_path_offset(fdt, "/clocks"); if (node < 0) { return dflt_value; @@ -144,8 +142,7 @@ uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, return dflt_value; } - if (strncmp(cchar, stm32mp_osc_node_label[osc_id], - (size_t)ret) != 0) { + if (strncmp(cchar, node_label, (size_t)ret) != 0) { continue; } @@ -161,9 +158,15 @@ uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, * @param fdt: Device tree reference * @return: Node offset or a negative value on error */ -int fdt_get_rcc_node(void *fdt) +static int fdt_get_rcc_node(void *fdt) { - return fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + static int node; + + if (node <= 0) { + node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + } + + return node; } /* @@ -191,6 +194,29 @@ int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count, return fdt_read_uint32_array(fdt, node, prop_name, count, array); } +/******************************************************************************* + * This function reads a property rcc-clk section. + * It reads the values indicated inside the device tree, from property name. + * Returns dflt_value if property is not found, and a property value on + * success. + ******************************************************************************/ +uint32_t fdt_rcc_read_uint32_default(const char *prop_name, uint32_t dflt_value) +{ + int node; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return dflt_value; + } + + node = fdt_get_rcc_node(fdt); + if (node < 0) { + return dflt_value; + } + + return fdt_read_uint32_default(fdt, node, prop_name, dflt_value); +} + /* * Get the subnode offset in rcc-clk section from its name in device tree * @param name: name of the RCC property @@ -249,24 +275,38 @@ const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) } /* - * Get the secure status for rcc node in device tree. - * @return: true if rcc is available from secure world, false if not. + * Get the secure state for rcc node in device tree. + * @return: true if rcc is configured for secure world access, false if not. */ -bool fdt_get_rcc_secure_status(void) +bool fdt_get_rcc_secure_state(void) { - int node; void *fdt; if (fdt_get_address(&fdt) == 0) { return false; } - node = fdt_get_rcc_node(fdt); - if (node < 0) { + if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) { return false; } - return !!(fdt_get_status(node) & DT_SECURE); + return true; +} + +/* + * This function gets interrupt name. + * It reads the values indicated the enabling status. + * Returns 0 if success, and a negative value else. + */ +int fdt_rcc_enable_it(const char *name) +{ + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + return stm32_gic_enable_spi(fdt_get_rcc_node(fdt), name); } /* @@ -291,3 +331,131 @@ int fdt_get_clock_id(int node) 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/ERRNO error code on failure. + ******************************************************************************/ +int fdt_get_clock_id_by_name(int node, const char *name) +{ + const fdt32_t *cuint; + void *fdt; + int index, len; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + 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); +} + +/* + * Get the frequency of the specified UART instance. + * @param instance: UART interface registers base address. + * @return: clock frequency on success, 0 value on failure. + */ +unsigned long fdt_get_uart_clock_freq(uintptr_t instance) +{ + void *fdt; + int node; + int clk_id; + + if (fdt_get_address(&fdt) == 0) { + return 0UL; + } + + /* Check for UART nodes */ + node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance); + if (node < 0) { + return 0UL; + } + + clk_id = fdt_get_clock_id(node); + if (clk_id < 0) { + return 0UL; + } + + return clk_get_rate((unsigned long)clk_id); +} + +/******************************************************************************* + * This function checks if PLL1 hard-coded settings have been defined in DT. + * Returns true if PLL1 node is found and enabled, false if not. + ******************************************************************************/ +bool fdt_is_pll1_predefined(void) +{ + return fdt_check_node(fdt_rcc_subnode_offset(DT_PLL1_NODE_NAME)); +} + +/******************************************************************************* + * This function configures and restores the STGEN counter depending on the + * connected clock. + ******************************************************************************/ +void stm32mp_stgen_config(unsigned long rate) +{ + uint32_t cntfid0; + unsigned long long counter; + + cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF); + + if (cntfid0 == rate) { + return; + } + + mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); + counter = stm32mp_stgen_get_counter() * rate / cntfid0; + + mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter); + mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32)); + mmio_write_32(STGEN_BASE + CNTFID_OFF, rate); + mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); + + write_cntfrq_el0((u_register_t)rate); + + /* Need to update timer with new frequency */ + generic_delay_timer_init(); +} + +/******************************************************************************* + * This function returns the STGEN counter value. + ******************************************************************************/ +unsigned long long stm32mp_stgen_get_counter(void) +{ + return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) | + mmio_read_32(STGEN_BASE + CNTCVL_OFF)); +} + +/******************************************************************************* + * This function restores the STGEN counter value. + * It takes a first input value as a counter backup value to be restored and a + * offset in ms to be added. + ******************************************************************************/ +void stm32mp_stgen_restore_counter(unsigned long long value, + unsigned long long offset_in_ms) +{ + unsigned long long cnt; + + cnt = value + ((offset_in_ms * + mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U); + + mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); + mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)cnt); + mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(cnt >> 32)); + mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN); +} diff --git a/drivers/st/crypto/stm32_hash.c b/drivers/st/crypto/stm32_hash.c index 317fd9eb88..9ee64a57e7 100644 --- a/drivers/st/crypto/stm32_hash.c +++ b/drivers/st/crypto/stm32_hash.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -189,7 +190,7 @@ int stm32_hash_update(const uint8_t *buffer, size_t length) return 0; } - stm32mp_clk_enable(stm32_hash.clock); + clk_enable(stm32_hash.clock); if (stm32_remain.length != 0U) { uint32_t copysize; @@ -231,7 +232,7 @@ int stm32_hash_update(const uint8_t *buffer, size_t length) } exit: - stm32mp_clk_disable(stm32_hash.clock); + clk_disable(stm32_hash.clock); return ret; } @@ -240,12 +241,12 @@ int stm32_hash_final(uint8_t *digest) { int ret; - stm32mp_clk_enable(stm32_hash.clock); + clk_enable(stm32_hash.clock); if (stm32_remain.length != 0U) { ret = hash_write_data(stm32_remain.buffer); if (ret != 0) { - stm32mp_clk_disable(stm32_hash.clock); + clk_disable(stm32_hash.clock); return ret; } @@ -260,7 +261,7 @@ int stm32_hash_final(uint8_t *digest) ret = hash_get_digest(digest); - stm32mp_clk_disable(stm32_hash.clock); + clk_disable(stm32_hash.clock); return ret; } @@ -280,11 +281,11 @@ int stm32_hash_final_update(const uint8_t *buffer, uint32_t length, void stm32_hash_init(enum stm32_hash_algo_mode mode) { - stm32mp_clk_enable(stm32_hash.clock); + clk_enable(stm32_hash.clock); hash_hw_init(mode); - stm32mp_clk_disable(stm32_hash.clock); + clk_disable(stm32_hash.clock); zeromem(&stm32_remain, sizeof(stm32_remain)); } @@ -321,7 +322,7 @@ int stm32_hash_register(void) stm32_hash.base = hash_info.base; stm32_hash.clock = hash_info.clock; - stm32mp_clk_enable(stm32_hash.clock); + clk_enable(stm32_hash.clock); if (hash_info.reset >= 0) { uint32_t id = (uint32_t)hash_info.reset; @@ -335,7 +336,7 @@ int stm32_hash_register(void) } } - stm32mp_clk_disable(stm32_hash.clock); + clk_disable(stm32_hash.clock); return 0; } diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c index 7d89d027e6..7e8041743e 100644 --- a/drivers/st/ddr/stm32mp1_ddr.c +++ b/drivers/st/ddr/stm32mp1_ddr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019, STMicroelectronics - All Rights Reserved + * Copyright (C) 2018-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ struct reg_desc { #define INVALID_OFFSET 0xFFU +#define TIMESLOT_US_1US 1U #define TIMEOUT_US_1S 1000000U #define DDRCTL_REG(x, y) \ @@ -45,8 +47,34 @@ struct reg_desc { .par_offset = offsetof(struct y, x) \ } +/* + * PARAMETERS: value get from device tree : + * size / order need to be aligned with binding + * modification NOT ALLOWED !!! + */ +#define DDRCTL_REG_REG_SIZE 25 /* st,ctl-reg */ +#define DDRCTL_REG_TIMING_SIZE 12 /* st,ctl-timing */ +#define DDRCTL_REG_MAP_SIZE 9 /* st,ctl-map */ +#if STM32MP_DDR_DUAL_AXI_PORT +#define DDRCTL_REG_PERF_SIZE 17 /* st,ctl-perf */ +#else +#define DDRCTL_REG_PERF_SIZE 11 /* st,ctl-perf */ +#endif + +#if STM32MP_DDR_DUAL_AXI_PORT +#define DDRPHY_REG_REG_SIZE 11 /* st,phy-reg */ +#else +#define DDRPHY_REG_REG_SIZE 9 /* st,phy-reg */ +#endif +#define DDRPHY_REG_TIMING_SIZE 10 /* st,phy-timing */ +#if STM32MP_DDR_DUAL_AXI_PORT +#define DDRPHY_REG_CAL_SIZE 12 /* st,phy-cal */ +#else +#define DDRPHY_REG_CAL_SIZE 6 /* st,phy-cal */ +#endif + #define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg) -static const struct reg_desc ddr_reg[] = { +static const struct reg_desc ddr_reg[DDRCTL_REG_REG_SIZE] = { DDRCTL_REG_REG(mstr), DDRCTL_REG_REG(mrctrl0), DDRCTL_REG_REG(mrctrl1), @@ -75,7 +103,7 @@ static const struct reg_desc ddr_reg[] = { }; #define DDRCTL_REG_TIMING(x) DDRCTL_REG(x, stm32mp1_ddrctrl_timing) -static const struct reg_desc ddr_timing[] = { +static const struct reg_desc ddr_timing[DDRCTL_REG_TIMING_SIZE] = { DDRCTL_REG_TIMING(rfshtmg), DDRCTL_REG_TIMING(dramtmg0), DDRCTL_REG_TIMING(dramtmg1), @@ -91,7 +119,7 @@ static const struct reg_desc ddr_timing[] = { }; #define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp1_ddrctrl_map) -static const struct reg_desc ddr_map[] = { +static const struct reg_desc ddr_map[DDRCTL_REG_MAP_SIZE] = { DDRCTL_REG_MAP(addrmap1), DDRCTL_REG_MAP(addrmap2), DDRCTL_REG_MAP(addrmap3), @@ -104,7 +132,7 @@ static const struct reg_desc ddr_map[] = { }; #define DDRCTL_REG_PERF(x) DDRCTL_REG(x, stm32mp1_ddrctrl_perf) -static const struct reg_desc ddr_perf[] = { +static const struct reg_desc ddr_perf[DDRCTL_REG_PERF_SIZE] = { DDRCTL_REG_PERF(sched), DDRCTL_REG_PERF(sched1), DDRCTL_REG_PERF(perfhpr1), @@ -116,16 +144,18 @@ static const struct reg_desc ddr_perf[] = { DDRCTL_REG_PERF(pcfgqos1_0), DDRCTL_REG_PERF(pcfgwqos0_0), DDRCTL_REG_PERF(pcfgwqos1_0), +#if STM32MP_DDR_DUAL_AXI_PORT DDRCTL_REG_PERF(pcfgr_1), DDRCTL_REG_PERF(pcfgw_1), DDRCTL_REG_PERF(pcfgqos0_1), DDRCTL_REG_PERF(pcfgqos1_1), DDRCTL_REG_PERF(pcfgwqos0_1), DDRCTL_REG_PERF(pcfgwqos1_1), +#endif }; #define DDRPHY_REG_REG(x) DDRPHY_REG(x, stm32mp1_ddrphy_reg) -static const struct reg_desc ddrphy_reg[] = { +static const struct reg_desc ddrphy_reg[DDRPHY_REG_REG_SIZE] = { DDRPHY_REG_REG(pgcr), DDRPHY_REG_REG(aciocr), DDRPHY_REG_REG(dxccr), @@ -135,12 +165,14 @@ static const struct reg_desc ddrphy_reg[] = { DDRPHY_REG_REG(zq0cr1), DDRPHY_REG_REG(dx0gcr), DDRPHY_REG_REG(dx1gcr), +#if STM32MP_DDR_DUAL_AXI_PORT DDRPHY_REG_REG(dx2gcr), DDRPHY_REG_REG(dx3gcr), +#endif }; #define DDRPHY_REG_TIMING(x) DDRPHY_REG(x, stm32mp1_ddrphy_timing) -static const struct reg_desc ddrphy_timing[] = { +static const struct reg_desc ddrphy_timing[DDRPHY_REG_TIMING_SIZE] = { DDRPHY_REG_TIMING(ptr0), DDRPHY_REG_TIMING(ptr1), DDRPHY_REG_TIMING(ptr2), @@ -154,51 +186,26 @@ static const struct reg_desc ddrphy_timing[] = { }; #define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal) -static const struct reg_desc ddrphy_cal[] = { +static const struct reg_desc ddrphy_cal[DDRPHY_REG_CAL_SIZE] = { DDRPHY_REG_CAL(dx0dllcr), DDRPHY_REG_CAL(dx0dqtr), DDRPHY_REG_CAL(dx0dqstr), DDRPHY_REG_CAL(dx1dllcr), DDRPHY_REG_CAL(dx1dqtr), DDRPHY_REG_CAL(dx1dqstr), +#if STM32MP_DDR_DUAL_AXI_PORT DDRPHY_REG_CAL(dx2dllcr), DDRPHY_REG_CAL(dx2dqtr), DDRPHY_REG_CAL(dx2dqstr), DDRPHY_REG_CAL(dx3dllcr), DDRPHY_REG_CAL(dx3dqtr), DDRPHY_REG_CAL(dx3dqstr), +#endif }; -#define DDR_REG_DYN(x) \ - { \ - .name = #x, \ - .offset = offsetof(struct stm32mp1_ddrctl, x), \ - .par_offset = INVALID_OFFSET \ - } - -static const struct reg_desc ddr_dyn[] = { - DDR_REG_DYN(stat), - DDR_REG_DYN(init0), - DDR_REG_DYN(dfimisc), - DDR_REG_DYN(dfistat), - DDR_REG_DYN(swctl), - DDR_REG_DYN(swstat), - DDR_REG_DYN(pctrl_0), - DDR_REG_DYN(pctrl_1), -}; - -#define DDRPHY_REG_DYN(x) \ - { \ - .name = #x, \ - .offset = offsetof(struct stm32mp1_ddrphy, x), \ - .par_offset = INVALID_OFFSET \ - } - -static const struct reg_desc ddrphy_dyn[] = { - DDRPHY_REG_DYN(pir), - DDRPHY_REG_DYN(pgsr), -}; - +/* + * REGISTERS ARRAY: used to parse device tree and interactive mode + */ enum reg_type { REG_REG, REG_TIMING, @@ -207,12 +214,6 @@ enum reg_type { REGPHY_REG, REGPHY_TIMING, REGPHY_CAL, -/* - * Dynamic registers => managed in driver or not changed, - * can be dumped in interactive mode. - */ - REG_DYN, - REGPHY_DYN, REG_TYPE_NB }; @@ -233,55 +234,43 @@ static const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { [REG_REG] = { .name = "static", .desc = ddr_reg, - .size = ARRAY_SIZE(ddr_reg), + .size = DDRCTL_REG_REG_SIZE, .base = DDR_BASE }, [REG_TIMING] = { .name = "timing", .desc = ddr_timing, - .size = ARRAY_SIZE(ddr_timing), + .size = DDRCTL_REG_TIMING_SIZE, .base = DDR_BASE }, [REG_PERF] = { .name = "perf", .desc = ddr_perf, - .size = ARRAY_SIZE(ddr_perf), + .size = DDRCTL_REG_PERF_SIZE, .base = DDR_BASE }, [REG_MAP] = { .name = "map", .desc = ddr_map, - .size = ARRAY_SIZE(ddr_map), + .size = DDRCTL_REG_MAP_SIZE, .base = DDR_BASE }, [REGPHY_REG] = { .name = "static", .desc = ddrphy_reg, - .size = ARRAY_SIZE(ddrphy_reg), + .size = DDRPHY_REG_REG_SIZE, .base = DDRPHY_BASE }, [REGPHY_TIMING] = { .name = "timing", .desc = ddrphy_timing, - .size = ARRAY_SIZE(ddrphy_timing), + .size = DDRPHY_REG_TIMING_SIZE, .base = DDRPHY_BASE }, [REGPHY_CAL] = { .name = "cal", .desc = ddrphy_cal, - .size = ARRAY_SIZE(ddrphy_cal), - .base = DDRPHY_BASE - }, - [REG_DYN] = { - .name = "dyn", - .desc = ddr_dyn, - .size = ARRAY_SIZE(ddr_dyn), - .base = DDR_BASE - }, - [REGPHY_DYN] = { - .name = "dyn", - .desc = ddrphy_dyn, - .size = ARRAY_SIZE(ddrphy_dyn), + .size = DDRPHY_REG_CAL_SIZE, .base = DDRPHY_BASE }, }; @@ -627,7 +616,7 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) */ /* Change Bypass Mode Frequency Range */ - if (stm32mp_clk_get_rate(DDRPHYC) < 100000000U) { + if (clk_get_rate(DDRPHYC) < 100000000U) { mmio_clrbits_32((uintptr_t)&priv->phy->dllgcr, DDRPHYC_DLLGCR_BPS200); } else { @@ -641,10 +630,12 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) DDRPHYC_DXNDLLCR_DLLDIS); mmio_setbits_32((uintptr_t)&priv->phy->dx1dllcr, DDRPHYC_DXNDLLCR_DLLDIS); +#if STM32MP_DDR_DUAL_AXI_PORT mmio_setbits_32((uintptr_t)&priv->phy->dx2dllcr, DDRPHYC_DXNDLLCR_DLLDIS); mmio_setbits_32((uintptr_t)&priv->phy->dx3dllcr, DDRPHYC_DXNDLLCR_DLLDIS); +#endif /* 12. Exit the self-refresh state by setting PWRCTL.selfref_sw = 0. */ mmio_clrbits_32((uintptr_t)&priv->ctl->pwrctl, @@ -675,7 +666,8 @@ static void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) /* Quasi-dynamic register update*/ mmio_setbits_32((uintptr_t)&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); - mmio_clrbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + mmio_clrbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN | + DDRCTRL_PWRCTL_SELFREF_EN); mmio_clrbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); stm32mp1_wait_sw_done_ack(ctl); @@ -693,11 +685,92 @@ static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, mmio_setbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); } + if ((pwrctl & DDRCTRL_PWRCTL_SELFREF_EN) != 0U) { + mmio_setbits_32((uintptr_t)&ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_EN); + } mmio_setbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); stm32mp1_wait_sw_done_ack(ctl); } +static void stm32mp1_refresh_cmd(struct stm32mp1_ddrctl *ctl) +{ + uint32_t dbgstat; + + do { + dbgstat = mmio_read_32((uintptr_t)&ctl->dbgstat); + } while ((dbgstat & DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY) != 0U); + + mmio_setbits_32((uintptr_t)&ctl->dbgcmd, DDRCTRL_DBGCMD_RANK0_REFRESH); +} + +/* Refresh compensation by forcing refresh command + * Rule1: Tref should be always < tREFW ? R x tREBW/8 + * Rule2: refcomp = RU(Tref/tREFI) = RU(RxTref/tREFW) + */ +static +void stm32mp1_refresh_compensation(const struct stm32mp1_ddr_config *config, + struct stm32mp1_ddrctl *ctl, + uint64_t start) +{ + uint32_t tck_ps; + uint64_t time_us, tref, trefi, refcomp, i; + + time_us = timeout_init_us(0) - start; + tck_ps = 1000000000U / config->info.speed; + if (tck_ps == 0U) { + return; + } + /* ref = refresh time in tck */ + tref = time_us * 1000000U / tck_ps; + trefi = ((mmio_read_32((uintptr_t)&ctl->rfshtmg) & + DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK) + >> DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT) * 32U; + if (trefi == 0U) { + return; + } + + /* div round up : number of refresh to compensate */ + refcomp = (tref + trefi - 1U) / trefi; + + for (i = 0; i < refcomp; i++) { + stm32mp1_refresh_cmd(ctl); + } +} + +static void stm32mp1_self_refresh_zcal(struct ddr_info *priv, uint32_t zdata) +{ + /* sequence for PUBL I/O Data Retention during Power-Down */ + + /* 10. Override ZQ calibration with previously (pre-retention) + * calibrated values. This is done by writing 1 to ZQ0CRN.ZDEN + * and the override data to ZQ0CRN.ZDATA. + */ + mmio_setbits_32((uintptr_t)&priv->phy->zq0cr0, DDRPHYC_ZQ0CRN_ZDEN); + + mmio_clrsetbits_32((uintptr_t)&priv->phy->zq0cr0, + DDRPHYC_ZQ0CRN_ZDATA_MASK, + zdata << DDRPHYC_ZQ0CRN_ZDATA_SHIFT); + + /* 11. De-assert the PHY_top data retention enable signals + * (ret_en or ret_en_i/ret_en_n_i). + */ + mmio_setbits_32((uintptr_t)(priv->pwr) + PWR_CR3, PWR_CR3_DDRSRDIS); + mmio_clrbits_32((uintptr_t)(priv->pwr) + PWR_CR3, PWR_CR3_DDRRETEN); + + /* 12. Remove ZQ calibration override by writing 0 to ZQ0CRN.ZDEN. */ + mmio_clrbits_32((uintptr_t)&priv->phy->zq0cr0, DDRPHYC_ZQ0CRN_ZDEN); + + /* 13. Trigger ZQ calibration by writing 1 to PIR.INIT + * and '1' to PIR.ZCAL + */ + /* 14. Wait for ZQ calibration to finish by polling a 1 status + * on PGSR.IDONE. + */ + stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_ZCAL); +} + static int board_ddr_power_init(enum ddr_type ddr_type) { if (dt_pmic_status() > 0) { @@ -710,7 +783,7 @@ static int board_ddr_power_init(enum ddr_type ddr_type) void stm32mp1_ddr_init(struct ddr_info *priv, struct stm32mp1_ddr_config *config) { - uint32_t pir; + uint32_t pir, ddr_reten; int ret = -EINVAL; if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { @@ -730,6 +803,25 @@ void stm32mp1_ddr_init(struct ddr_info *priv, VERBOSE("name = %s\n", config->info.name); VERBOSE("speed = %d kHz\n", config->info.speed); VERBOSE("size = 0x%x\n", config->info.size); + if (config->self_refresh) { + VERBOSE("sel-refresh exit (zdata = 0x%x)\n", config->zdata); + } + + /* Check DDR PHY pads retention */ + ddr_reten = mmio_read_32((uint32_t)(priv->pwr) + PWR_CR3) & + PWR_CR3_DDRRETEN; + if (config->self_refresh) { + if (ddr_reten == 0U) { + VERBOSE("self-refresh aborted: no retention\n"); + config->self_refresh = false; + } + } + + if (!config->self_refresh) { + VERBOSE("disable DDR PHY retention\n"); + mmio_setbits_32((uint32_t)(priv->pwr) + PWR_CR3, PWR_CR3_DDRSRDIS); + mmio_clrbits_32((uint32_t)(priv->pwr) + PWR_CR3, PWR_CR3_DDRRETEN); + } /* DDR INIT SEQUENCE */ @@ -790,6 +882,12 @@ void stm32mp1_ddr_init(struct ddr_info *priv, set_reg(priv, REG_TIMING, &config->c_timing); set_reg(priv, REG_MAP, &config->c_map); + /* Keep the controller in self-refresh mode */ + if (config->self_refresh) { + mmio_setbits_32((uintptr_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + } + /* Skip CTRL init, SDRAM init is done by PHY PUBL */ mmio_clrsetbits_32((uintptr_t)&priv->ctl->init0, DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK, @@ -811,7 +909,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv, */ set_reg(priv, REGPHY_REG, &config->p_reg); set_reg(priv, REGPHY_TIMING, &config->p_timing); - set_reg(priv, REGPHY_CAL, &config->p_cal); + if (config->p_cal_present) { + set_reg(priv, REGPHY_CAL, &config->p_cal); + } /* DDR3 = don't set DLLOFF for init mode */ if ((config->c_reg.mstr & @@ -843,8 +943,20 @@ void stm32mp1_ddr_init(struct ddr_info *priv, pir |= DDRPHYC_PIR_DRAMRST; /* Only for DDR3 */ } + /* Treat self-refresh exit : hot boot */ + if (config->self_refresh) { + /* DDR in self refresh mode, remove zcal & reset & init */ + pir &= ~(DDRPHYC_PIR_ZCAL & DDRPHYC_PIR_DRAMRST + & DDRPHYC_PIR_DRAMINIT); + pir |= DDRPHYC_PIR_ZCALBYP; + } + stm32mp1_ddrphy_init(priv->phy, pir); + if (config->self_refresh) { + stm32mp1_self_refresh_zcal(priv, config->zdata); + } + /* * 6. SET DFIMISC.dfi_init_complete_en to 1 * Enable quasi-dynamic register programming. @@ -865,6 +977,13 @@ void stm32mp1_ddr_init(struct ddr_info *priv, */ /* Wait uMCTL2 ready */ + + /* Trigger self-refresh exit */ + if (config->self_refresh) { + mmio_clrbits_32((uintptr_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + } + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); /* Switch to DLL OFF mode */ @@ -872,37 +991,53 @@ void stm32mp1_ddr_init(struct ddr_info *priv, stm32mp1_ddr3_dll_off(priv); } - VERBOSE("DDR DQS training : "); - - /* - * 8. Disable Auto refresh and power down by setting - * - RFSHCTL3.dis_au_refresh = 1 - * - PWRCTL.powerdown_en = 0 - * - DFIMISC.dfiinit_complete_en = 0 - */ - stm32mp1_refresh_disable(priv->ctl); - - /* - * 9. Program PUBL PGCR to enable refresh during training - * and rank to train - * not done => keep the programed value in PGCR - */ - - /* - * 10. configure PUBL PIR register to specify which training step - * to run - * Warning : RVTRN is not supported by this PUBL - */ - stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_QSTRN); - - /* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training sequence */ - stm32mp1_ddrphy_idone_wait(priv->phy); + if (config->p_cal_present) { + VERBOSE("DDR DQS training skipped.\n"); + } else { + uint64_t time; + + VERBOSE("DDR DQS training.\n"); + + time = timeout_init_us(0); + + /* + * 8. Disable Auto refresh and power down by setting + * - RFSHCTL3.dis_au_refresh = 1 + * - PWRCTL.powerdown_en = 0 + * - DFIMISC.dfiinit_complete_en = 0 + */ + stm32mp1_refresh_disable(priv->ctl); + + /* + * 9. Program PUBL PGCR to enable refresh during training + * and rank to train + * not done => keep the programed value in PGCR + */ + + /* + * 10. configure PUBL PIR register to specify which training + * step to run + * Warning : RVTRN is not supported by this PUBL + */ + stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_QSTRN); + + /* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training + * sequence + */ + stm32mp1_ddrphy_idone_wait(priv->phy); + + /* Refresh compensation: forcing refresh command */ + if (config->self_refresh) { + stm32mp1_refresh_compensation(config, priv->ctl, time); + } - /* - * 12. set back registers in step 8 to the orginal values if desidered - */ - stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, - config->c_reg.pwrctl); + /* + * 12. set back registers in step 8 to the orginal values + * if desidered + */ + stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, + config->c_reg.pwrctl); + } /* Enable uMCTL2 AXI port 0 */ mmio_setbits_32((uintptr_t)&priv->ctl->pctrl_0, @@ -911,10 +1046,12 @@ void stm32mp1_ddr_init(struct ddr_info *priv, (uintptr_t)&priv->ctl->pctrl_0, mmio_read_32((uintptr_t)&priv->ctl->pctrl_0)); +#if STM32MP_DDR_DUAL_AXI_PORT /* Enable uMCTL2 AXI port 1 */ mmio_setbits_32((uintptr_t)&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); VERBOSE("[0x%lx] pctrl_1 = 0x%x\n", (uintptr_t)&priv->ctl->pctrl_1, mmio_read_32((uintptr_t)&priv->ctl->pctrl_1)); +#endif } diff --git a/drivers/st/ddr/stm32mp1_ddr_helpers.c b/drivers/st/ddr/stm32mp1_ddr_helpers.c index fcb4cfcfdf..dab63d191b 100644 --- a/drivers/st/ddr/stm32mp1_ddr_helpers.c +++ b/drivers/st/ddr/stm32mp1_ddr_helpers.c @@ -1,24 +1,615 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ #include +#include +#include +#include +#include #include +#include #include +#define TIMEOUT_500US 500U + +static enum stm32mp1_ddr_sr_mode saved_ddr_sr_mode; + void ddr_enable_clock(void) { stm32mp1_clk_rcc_regs_lock(); mmio_setbits_32(stm32mp_rcc_base() + RCC_DDRITFCR, RCC_DDRITFCR_DDRC1EN | +#if STM32MP_DDR_DUAL_AXI_PORT RCC_DDRITFCR_DDRC2EN | +#endif RCC_DDRITFCR_DDRPHYCEN | RCC_DDRITFCR_DDRPHYCAPBEN | RCC_DDRITFCR_DDRCAPBEN); stm32mp1_clk_rcc_regs_unlock(); } + +static void do_sw_handshake(void) +{ + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + mmio_clrbits_32(ddrctrl_base + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); +} + +static void do_sw_ack(void) +{ + uint64_t timeout; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + mmio_setbits_32(ddrctrl_base + DDRCTRL_SWCTL, DDRCTRL_SWCTL_SW_DONE); + + timeout = timeout_init_us(TIMEOUT_500US); + while ((mmio_read_32(ddrctrl_base + DDRCTRL_SWSTAT) & + DDRCTRL_SWSTAT_SW_DONE_ACK) == 0U) { + if (timeout_elapsed(timeout)) { + panic(); + } + } +} + +static int ddr_sw_self_refresh_in(void) +{ + uint64_t timeout; + uint32_t stat; + uint32_t operating_mode; + uint32_t selref_type; + uint8_t op_mode_changed = 0; + uintptr_t rcc_base = stm32mp_rcc_base(); + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + uintptr_t ddrphyc_base = stm32mp_ddrphyc_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_clk_rcc_regs_unlock(); + + /* Blocks AXI ports from taking anymore transactions */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); +#endif + + /* Waits unit all AXI ports are idle + * Poll PSTAT.rd_port_busy_n = 0 + * Poll PSTAT.wr_port_busy_n = 0 + */ + timeout = timeout_init_us(TIMEOUT_500US); + while (mmio_read_32(ddrctrl_base + DDRCTRL_PSTAT)) { + if (timeout_elapsed(timeout)) { + 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. + */ + timeout = timeout_init_us(TIMEOUT_500US); + while (!timeout_elapsed(timeout)) { + 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(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDR); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDD_MASK, + DDRPHYC_ACIOCR_CKPDD_0); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDR_MASK, + DDRPHYC_ACIOCR_CKPDR_0); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CSPDD_MASK, + DDRPHYC_ACIOCR_CSPDD_0); + + /* Disable command/address output driver */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_ODTPDD_MASK, + DDRPHYC_DSGCR_ODTPDD_0); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + mmio_clrsetbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_CKEPDD_MASK, + DDRPHYC_DSGCR_CKEPDD_0); + + /* Disable PZQ cell (PUBL register) */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Set latch */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); + + /* Additional delay to avoid early latch */ + udelay(10); + + /* Activate sw retention in PWRCTRL */ + stm32mp_pwr_regs_lock(); + mmio_setbits_32(pwr_base + PWR_CR3, PWR_CR3_DDRRETEN); + stm32mp_pwr_regs_unlock(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + stm32mp1_clk_rcc_regs_lock(); + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + stm32mp1_clk_rcc_regs_unlock(); + + /* Disable all DLLs: GLITCH window */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX0DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX1DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX2DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX3DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); +#endif + + stm32mp1_clk_rcc_regs_lock(); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL output clock */ + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + + /* Deactivate all DDR clocks */ + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | +#if STM32MP_DDR_DUAL_AXI_PORT + RCC_DDRITFCR_DDRC2EN | +#endif + RCC_DDRITFCR_DDRCAPBEN | + RCC_DDRITFCR_DDRPHYCAPBEN); + + stm32mp1_clk_rcc_regs_unlock(); + + 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); +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); +#endif + + return -1; +} + +int ddr_sw_self_refresh_exit(void) +{ + uint64_t timeout; + uintptr_t rcc_base = stm32mp_rcc_base(); + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + uintptr_t ddrphyc_base = stm32mp_ddrphyc_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 */ + stm32mp1_clk_rcc_regs_lock(); + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + stm32mp1_clk_rcc_regs_unlock(); + + /* Enable all DLLs: GLITCH window */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX0DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX1DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX2DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX3DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); +#endif + + /* Additional delay to avoid early DLL clock switch */ + udelay(50); + + /* Switch controller clocks (uMCTL2/PUBL) to DLL ref clock */ + stm32mp1_clk_rcc_regs_lock(); + mmio_clrbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_GSKPCTRL); + stm32mp1_clk_rcc_regs_unlock(); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLSRST); + + udelay(10); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACDLLCR, + DDRPHYC_ACDLLCR_DLLSRST); + + /* PHY partial init: (DLL lock and ITM reset) */ + mmio_write_32(ddrphyc_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); + + /* Pool end of init */ + timeout = timeout_init_us(TIMEOUT_500US); + + while ((mmio_read_32(ddrphyc_base + DDRPHYC_PGSR) & + DDRPHYC_PGSR_IDONE) == 0U) { + if (timeout_elapsed(timeout)) { + 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 */ + stm32mp_pwr_regs_lock(); + mmio_clrbits_32(pwr_base + PWR_CR3, PWR_CR3_DDRRETEN); + stm32mp_pwr_regs_unlock(); + + /* Enable PZQ cell (PUBL register) */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ZQ0CR0, DDRPHYC_ZQ0CRN_ZQPD); + + /* Enable pad drivers */ + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACPDD); + + /* Enable command/address output driver */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_ACIOCR, DDRPHYC_ACIOCR_ACOE); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CKPDD_MASK); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_ACIOCR, + DDRPHYC_ACIOCR_CSPDD_MASK); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDD); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DXCCR, DDRPHYC_DXCCR_DXPDR); + + /* Release latch */ + mmio_setbits_32(ddrphyc_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_CKOE); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_ODTPDD_MASK); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, DDRPHYC_DSGCR_NL2PD); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DSGCR, + DDRPHYC_DSGCR_CKEPDD_MASK); + + /* Remove selfrefresh */ + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PWRCTL, + DDRCTRL_PWRCTL_SELFREF_SW); + + /* Wait operating_mode == normal */ + timeout = timeout_init_us(TIMEOUT_500US); + while ((mmio_read_32(ddrctrl_base + DDRCTRL_STAT) & + DDRCTRL_STAT_OPERATING_MODE_MASK) != + DDRCTRL_STAT_OPERATING_MODE_NORMAL) { + if (timeout_elapsed(timeout)) { + return -1; + } + } + + /* AXI ports are no longer blocked from taking transactions */ + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_0, + DDRCTRL_PCTRL_N_PORT_EN); +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); +#endif + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_clk_rcc_regs_unlock(); + + return 0; +} + +uint32_t ddr_get_io_calibration_val(void) +{ + uintptr_t ddrphyc_base = stm32mp_ddrphyc_base(); + + return mmio_read_32(ddrphyc_base + DDRPHYC_ZQ0CR0) & + DDRPHYC_ZQ0CRN_ZDATA_MASK; +} + +int ddr_standby_sr_entry(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + + /* Put DDR in Self-Refresh */ + if (ddr_sw_self_refresh_in() != 0) { + return -1; + } + + /* Enable I/O retention mode in standby */ + stm32mp_pwr_regs_lock(); + mmio_setbits_32(pwr_base + PWR_CR3, PWR_CR3_DDRSREN); + stm32mp_pwr_regs_unlock(); + + return 0; +} + +static void ddr_sr_mode_ssr(void) +{ + uintptr_t rcc_ddritfcr = stm32mp_rcc_base() + RCC_DDRITFCR; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1EN); + +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2EN); +#endif + + 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); + + stm32mp1_clk_rcc_regs_unlock(); + + /* 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); +} + +static void ddr_sr_mode_asr(void) +{ + uintptr_t rcc_ddritfcr = stm32mp_rcc_base() + RCC_DDRITFCR; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN); + +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); +#endif + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN); + + mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_ASR1); + + stm32mp1_clk_rcc_regs_unlock(); + + /* 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); +} + +static void ddr_sr_mode_hsr(void) +{ + uintptr_t rcc_ddritfcr = stm32mp_rcc_base() + RCC_DDRITFCR; + uintptr_t ddrctrl_base = stm32mp_ddrctrl_base(); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1LPEN); + +#if STM32MP_DDR_DUAL_AXI_PORT + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); +#endif + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN); + + mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_HSR1); + + stm32mp1_clk_rcc_regs_unlock(); + + /* 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); +} + +enum stm32mp1_ddr_sr_mode ddr_read_sr_mode(void) +{ + uint32_t pwrctl = mmio_read_32(stm32mp_ddrctrl_base() + DDRCTRL_PWRCTL); + + switch (pwrctl & (DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | + DDRCTRL_PWRCTL_SELFREF_EN)) { + case 0U: + return DDR_SSR_MODE; + + case DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE: + return DDR_HSR_MODE; + + case DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE | DDRCTRL_PWRCTL_SELFREF_EN: + return DDR_ASR_MODE; + + default: + return DDR_SR_MODE_INVALID; + } +} + +void ddr_set_sr_mode(enum stm32mp1_ddr_sr_mode mode) +{ + switch (mode) { + case DDR_SSR_MODE: + ddr_sr_mode_ssr(); + break; + + case DDR_HSR_MODE: + ddr_sr_mode_hsr(); + break; + + case DDR_ASR_MODE: + ddr_sr_mode_asr(); + break; + + default: + ERROR("Unknown Self Refresh mode\n"); + panic(); + } +} + +void ddr_save_sr_mode(void) +{ + saved_ddr_sr_mode = ddr_read_sr_mode(); +} + +void ddr_restore_sr_mode(void) +{ + ddr_set_sr_mode(saved_ddr_sr_mode); +} + +bool ddr_is_nonsecured_area(uintptr_t address, uint32_t length) +{ + uint64_t pa; + + write_ats1cpw(address); + + isb(); + + pa = read64_par(); + + if ((((pa >> PAR_NS_SHIFT) & PAR_NS_MASK) != PAR_NS_MASK) || + (((pa >> PAR_F_SHIFT) & PAR_F_MASK) == PAR_F_MASK)) { + return false; + } + + write_ats1cpw(address + length - 1U); + + isb(); + + pa = read64_par(); + + if ((((pa >> PAR_NS_SHIFT) & PAR_NS_MASK) == PAR_NS_MASK) && + (((pa >> PAR_F_SHIFT) & PAR_F_MASK) != PAR_F_MASK)) { + return true; + } + + return false; +} diff --git a/drivers/st/ddr/stm32mp1_ram.c b/drivers/st/ddr/stm32mp1_ram.c index b21c8949fe..3918eb7be5 100644 --- a/drivers/st/ddr/stm32mp1_ram.c +++ b/drivers/st/ddr/stm32mp1_ram.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #define DDR_ANTIPATTERN 0x55555555U static struct ddr_info ddr_priv_data; +static bool ddr_self_refresh; int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed) { @@ -29,7 +31,7 @@ int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed) ddr_enable_clock(); - ddrphy_clk = stm32mp_clk_get_rate(DDRPHYC); + ddrphy_clk = clk_get_rate(DDRPHYC); VERBOSE("DDR: mem_speed (%d kHz), RCC %ld kHz\n", mem_speed, ddrphy_clk / 1000U); @@ -50,6 +52,26 @@ int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed) return 0; } +/******************************************************************************* + * This function tests a simple read/write access to the DDR. + * Note that the previous content is restored after test. + * Returns 0 if success, and address value else. + ******************************************************************************/ +static uint32_t ddr_test_rw_access(void) +{ + uint32_t saved_value = mmio_read_32(STM32MP_DDR_BASE); + + mmio_write_32(STM32MP_DDR_BASE, DDR_PATTERN); + + if (mmio_read_32(STM32MP_DDR_BASE) != DDR_PATTERN) { + return (uint32_t)STM32MP_DDR_BASE; + } + + mmio_write_32(STM32MP_DDR_BASE, saved_value); + + return 0; +} + /******************************************************************************* * This function tests the DDR data bus wiring. * This is inspired from the Data Bus Test algorithm written by Michael Barr @@ -172,20 +194,23 @@ static int stm32mp1_ddr_setup(void) uint32_t uret, idx; void *fdt; -#define PARAM(x, y) \ +#define PARAM(x, y, z) \ { \ .name = x, \ .offset = offsetof(struct stm32mp1_ddr_config, y), \ - .size = sizeof(config.y) / sizeof(uint32_t) \ + .size = sizeof(config.y) / sizeof(uint32_t), \ + .present = z \ } -#define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x) -#define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x) +#define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x, NULL) +#define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x, NULL) +#define PHY_PARAM_OPT(x) PARAM("st,phy-"#x, p_##x, &config.p_##x##_present) const struct { const char *name; /* Name in DT */ const uint32_t offset; /* Offset in config struct */ const uint32_t size; /* Size of parameters */ + bool * const present; /* presence indication for opt */ } param[] = { CTL_PARAM(reg), CTL_PARAM(timing), @@ -193,7 +218,7 @@ static int stm32mp1_ddr_setup(void) CTL_PARAM(perf), PHY_PARAM(reg), PHY_PARAM(timing), - PHY_PARAM(cal) + PHY_PARAM_OPT(cal) }; if (fdt_get_address(&fdt) == 0) { @@ -231,11 +256,27 @@ static int stm32mp1_ddr_setup(void) VERBOSE("%s: %s[0x%x] = %d\n", __func__, param[idx].name, param[idx].size, ret); - if (ret != 0) { - ERROR("%s: Cannot read %s\n", - __func__, param[idx].name); + if ((ret != 0) && + ((ret != -FDT_ERR_NOTFOUND) || + (param[idx].present == NULL))) { + ERROR("%s: Cannot read %s, error=%d\n", + __func__, param[idx].name, ret); return -EINVAL; } + if (param[idx].present != NULL) { + /* save presence of optional parameters */ + *(param[idx].present) = true; + if (ret == -FDT_ERR_NOTFOUND) { + *(param[idx].present) = false; + } + } + } + + config.self_refresh = false; + + if (stm32mp1_is_wakeup_from_standby()) { + config.self_refresh = true; + config.zdata = stm32_get_zdata_from_context(); } /* Disable axidcg clock gating during init */ @@ -255,34 +296,61 @@ static int stm32mp1_ddr_setup(void) panic(); } - uret = ddr_test_data_bus(); - if (uret != 0U) { - ERROR("DDR data bus test: can't access memory @ 0x%x\n", - uret); - panic(); - } + if (config.self_refresh) { + uret = ddr_test_rw_access(); + if (uret != 0U) { + ERROR("DDR rw test: Can't access memory @ 0x%x\n", + uret); + panic(); + } - uret = ddr_test_addr_bus(); - if (uret != 0U) { - ERROR("DDR addr bus test: can't access memory @ 0x%x\n", - uret); - panic(); - } + /* Restore area overwritten by training */ + stm32_restore_ddr_training_area(); + } else { + uret = ddr_test_data_bus(); + if (uret != 0U) { + ERROR("DDR data bus test: can't access memory @ 0x%x\n", + uret); + panic(); + } - uret = ddr_check_size(); - if (uret < config.info.size) { - ERROR("DDR size: 0x%x does not match DT config: 0x%x\n", - uret, config.info.size); - panic(); + uret = ddr_test_addr_bus(); + if (uret != 0U) { + ERROR("DDR addr bus test: can't access memory @ 0x%x\n", + uret); + panic(); + } + + uret = ddr_check_size(); + if (uret < config.info.size) { + ERROR("DDR size: 0x%x does not match DT config: 0x%x\n", + uret, config.info.size); + panic(); + } } + /* + * Initialization sequence has configured DDR registers with settings. + * The Self Refresh (SR) mode corresponding to these settings has now + * to be set. + */ + ddr_set_sr_mode(ddr_read_sr_mode()); + if (stm32mp_unmap_ddr() != 0) { panic(); } + /* Save DDR self_refresh state */ + ddr_self_refresh = config.self_refresh; + return 0; } +bool stm32mp1_ddr_is_restored(void) +{ + return ddr_self_refresh; +} + int stm32mp1_ddr_probe(void) { struct ddr_info *priv = &ddr_priv_data; diff --git a/drivers/st/etzpc/etzpc.c b/drivers/st/etzpc/etzpc.c index ff52a22d9d..eb1c79cf7b 100644 --- a/drivers/st/etzpc/etzpc.c +++ b/drivers/st/etzpc/etzpc.c @@ -66,6 +66,10 @@ struct etzpc_instance { /* Only 1 instance of the ETZPC is expected per platform */ static struct etzpc_instance etzpc_dev; +struct dt_id_attr { + fdt32_t id_attr[STM32MP1_ETZPC_MAX_ID]; +}; + /* * Implementation uses uint8_t to store each securable DECPROT configuration. * When resuming from deep suspend, the DECPROT configurations are restored. @@ -85,6 +89,50 @@ static bool valid_tzma_id(unsigned int id) } #endif +static int etzpc_dt_conf_decprot(int node) +{ + const struct dt_id_attr *conf_list; + void *fdt; + unsigned int i; + int len = 0; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + conf_list = (const struct dt_id_attr *)fdt_getprop(fdt, node, + "st,decprot", &len); + if (conf_list == NULL) { + INFO("No ETZPC configuration in DT, use default\n"); + return 0; + } + + for (i = 0U; i < (unsigned int)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); + assert(valid_decprot_id(id)); + + mode = (value >> ETZPC_MODE_SHIFT) & ETZPC_MODE_MASK; + attr = stm32mp_etzpc_binding2decprot(mode); + + stm32mp1_register_etzpc_decprot(id, attr); + + etzpc_configure_decprot(id, attr); + + if ((value & ETZPC_LOCK_MASK) != 0U) { + etzpc_lock_decprot(id); + } + } + + return 0; +} + /* * etzpc_configure_decprot : Load a DECPROT configuration * decprot_id : ID of the IP @@ -254,5 +302,5 @@ int etzpc_init(void) VERBOSE("ETZPC version 0x%x", etzpc_dev.revision); - return 0; + return etzpc_dt_conf_decprot(node); } diff --git a/drivers/st/fmc/stm32_fmc2_nand.c b/drivers/st/fmc/stm32_fmc2_nand.c index a58a243ad6..503e259876 100644 --- a/drivers/st/fmc/stm32_fmc2_nand.c +++ b/drivers/st/fmc/stm32_fmc2_nand.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -162,7 +163,7 @@ static uintptr_t fmc2_base(void) static void stm32_fmc2_nand_setup_timing(void) { struct stm32_fmc2_nand_timings tims; - unsigned long hclk = stm32mp_clk_get_rate(stm32_fmc2.clock_id); + unsigned long hclk = clk_get_rate(stm32_fmc2.clock_id); unsigned long hclkp = FMC2_PSEC_PER_MSEC / (hclk / 1000U); unsigned long timing, tar, tclr, thiz, twait; unsigned long tset_mem, tset_att, thold_mem, thold_att; @@ -918,7 +919,7 @@ int stm32_fmc2_init(void) } /* Enable Clock */ - stm32mp_clk_enable(stm32_fmc2.clock_id); + clk_enable(stm32_fmc2.clock_id); /* Reset IP */ ret = stm32mp_reset_assert(stm32_fmc2.reset_id, TIMEOUT_US_1_MS); diff --git a/drivers/st/gpio/stm32_gpio.c b/drivers/st/gpio/stm32_gpio.c index 7d63262d71..75707e63a1 100644 --- a/drivers/st/gpio/stm32_gpio.c +++ b/drivers/st/gpio/stm32_gpio.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -208,7 +209,7 @@ void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, assert(pin <= GPIO_PIN_MAX); - stm32mp_clk_enable(clock); + clk_enable(clock); mmio_clrbits_32(base + GPIO_MODE_OFFSET, ((uint32_t)GPIO_MODE_MASK << (pin << 1))); @@ -254,7 +255,7 @@ void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, VERBOSE("GPIO %u mode alternate high to 0x%x\n", bank, mmio_read_32(base + GPIO_AFRH_OFFSET)); - stm32mp_clk_disable(clock); + clk_disable(clock); if (status == DT_SECURE) { stm32mp_register_secure_gpio(bank, pin); @@ -273,7 +274,7 @@ void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure) assert(pin <= GPIO_PIN_MAX); - stm32mp_clk_enable(clock); + clk_enable(clock); if (secure) { mmio_setbits_32(base + GPIO_SECR_OFFSET, BIT(pin)); @@ -281,5 +282,5 @@ void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure) mmio_clrbits_32(base + GPIO_SECR_OFFSET, BIT(pin)); } - stm32mp_clk_disable(clock); + clk_disable(clock); } diff --git a/drivers/st/i2c/stm32_i2c.c b/drivers/st/i2c/stm32_i2c.c index ed880522b0..3e4b96a2c3 100644 --- a/drivers/st/i2c/stm32_i2c.c +++ b/drivers/st/i2c/stm32_i2c.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2020, STMicroelectronics - All Rights Reserved * - * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ #include +#include #include #include @@ -13,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -38,8 +41,87 @@ #define I2C_NSEC_PER_SEC 1000000000L -/* I2C Timing hard-coded value, for I2C clock source is HSI at 64MHz */ -#define I2C_TIMING 0x10D07DB5 +/* + * struct i2c_spec_s - Private I2C timing specifications. + * @rate: 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 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; +}; + +/* + * I2C specification values as per version 6.0, 4th of April 2014 [1], + * table 10 page 48: Characteristics of the SDA and SCL bus lines for + * Standard, Fast, and Fast-mode Plus I2C-bus devices. + * + * [1] https://www.i2c-bus.org/specification/ + */ +static const struct i2c_spec_s i2c_specs[] = { + /* Standard - 100KHz */ + { + .rate = STANDARD_RATE, + .fall_max = 300, + .rise_max = 1000, + .hddat_min = 0, + .vddat_max = 3450, + .sudat_min = 250, + .l_min = 4700, + .h_min = 4000, + }, + /* Fast - 400KHz */ + { + .rate = FAST_RATE, + .fall_max = 300, + .rise_max = 300, + .hddat_min = 0, + .vddat_max = 900, + .sudat_min = 100, + .l_min = 1300, + .h_min = 600, + }, + /* FastPlus - 1MHz */ + { + .rate = FAST_PLUS_RATE, + .fall_max = 100, + .rise_max = 120, + .hddat_min = 0, + .vddat_max = 450, + .sudat_min = 50, + .l_min = 500, + .h_min = 260, + }, +}; + static void notif_i2c_timeout(struct i2c_handle_s *hi2c) { @@ -48,6 +130,298 @@ static void notif_i2c_timeout(struct i2c_handle_s *hi2c) hi2c->i2c_state = I2C_STATE_READY; } +static const struct i2c_spec_s *get_specs(uint32_t rate) +{ + size_t i; + + for (i = 0U; i < ARRAY_SIZE(i2c_specs); i++) { + if (rate <= i2c_specs[i].rate) { + return &i2c_specs[i]; + } + } + + /* NOT REACHED */ + return NULL; +} + +#define RATE_MIN(rate) (((rate) / 100U) * 80U) +/* + * @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) +{ + const struct i2c_spec_s *specs; + 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; + int sdadel_min; + int sdadel_max; + uint32_t sdadel_min_u; + uint32_t sdadel_max_u; + uint32_t scldel_min; + int s = -1; + struct i2c_timing_s solutions[I2C_TIMINGR_PRESC_MAX]; + + specs = get_specs(init->bus_rate); + if (specs == NULL) { + ERROR("I2C speed out of bound {%d}\n", init->bus_rate); + return -EINVAL; + } + + speed_freq = specs->rate; + i2cbus = udiv_round_nearest(I2C_NSEC_PER_SEC, speed_freq); + clk_error_prev = INT_MAX; + + if ((init->rise_time > specs->rise_max) || + (init->fall_time > specs->fall_max)) { + ERROR(" I2C timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", + init->rise_time, specs->rise_max, + init->fall_time, specs->fall_max); + return -EINVAL; + } + + if (init->digital_filter_coef > STM32_I2C_DIGITAL_FILTER_MAX) { + ERROR("DNF out of bound %d/%d\n", + init->digital_filter_coef, STM32_I2C_DIGITAL_FILTER_MAX); + return -EINVAL; + } + + /* 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 = specs->hddat_min + init->fall_time - + af_delay_min - ((init->digital_filter_coef + 3) * i2cclk); + + sdadel_max = specs->vddat_max - init->rise_time - + af_delay_max - ((init->digital_filter_coef + 4) * i2cclk); + + scldel_min = init->rise_time + specs->sudat_min; + + if (sdadel_min < 0) { + sdadel_min_u = 0; + } else { + sdadel_min_u = (uint32_t)sdadel_min; + } + + if (sdadel_max < 0) { + sdadel_max_u = 0; + } else { + sdadel_max_u = (uint32_t)sdadel_max; + } + + VERBOSE("I2C SDADEL(min/max): %u/%u, SCLDEL(Min): %u\n", + sdadel_min_u, sdadel_max_u, scldel_min); + + zeromem(&solutions, 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_u) && + (sdadel <= sdadel_max_u) && + (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) { + ERROR(" I2C no Prescaler solution\n"); + return -EPERM; + } + + tsync = af_delay_min + dnf_delay + (2 * i2cclk); + clk_max = I2C_NSEC_PER_SEC / RATE_MIN(specs->rate); + clk_min = I2C_NSEC_PER_SEC / specs->rate; + + /* + * 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 < specs->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 >= specs->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) { + ERROR(" I2C no solution at all\n"); + return -EPERM; + } + + /* 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); + + VERBOSE("I2C TIMINGR (PRESC/SCLDEL/SDADEL): %i/%i/%i\n", + s, solutions[s].scldel, solutions[s].sdadel); + VERBOSE("I2C TIMINGR (SCLH/SCLL): %i/%i\n", + solutions[s].sclh, solutions[s].scll); + VERBOSE("I2C TIMINGR: 0x%x\n", *timing); + + return 0; +} + +static uint32_t get_lower_rate(uint32_t rate) +{ + int i; + + for (i = ARRAY_SIZE(i2c_specs) - 1; i >= 0; i--) { + if (rate > i2c_specs[i].rate) { + return i2c_specs[i].rate; + } + } + + return i2c_specs[0].rate; +} + +/* + * @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 = (uint32_t)clk_get_rate(hi2c->clock); + if (clock_src == 0U) { + ERROR("I2C clock rate is 0\n"); + return -EINVAL; + } + + /* + * If the timing has already been computed, and the frequency is the + * same as when it was computed, then use the saved timing. + */ + if (clock_src == hi2c->saved_frequency) { + *timing = hi2c->saved_timing; + return 0; + } + + do { + rc = i2c_compute_timing(init, clock_src, timing); + if (rc != 0) { + ERROR("Failed to compute I2C timings\n"); + if (init->bus_rate > STANDARD_RATE) { + init->bus_rate = get_lower_rate(init->bus_rate); + WARN("Downgrade I2C speed to %uHz)\n", + init->bus_rate); + } else { + break; + } + } + } while (rc != 0); + + if (rc != 0) { + ERROR("Impossible to compute I2C timings\n"); + return rc; + } + + VERBOSE("I2C Freq(%i), Clk Source(%i)\n", + init->bus_rate, clock_src); + VERBOSE("I2C Rise(%i) and Fall(%i) Time\n", + init->rise_time, init->fall_time); + VERBOSE("I2C Analog Filter(%s), DNF(%i)\n", + (init->analog_filter ? "On" : "Off"), + init->digital_filter_coef); + + hi2c->saved_timing = *timing; + hi2c->saved_frequency = clock_src; + + return 0; +} + /* * @brief Configure I2C Analog noise filter. * @param hi2c: Pointer to a struct i2c_handle_s structure that contains @@ -88,49 +462,35 @@ static int i2c_config_analog_filter(struct i2c_handle_s *hi2c, /* * @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) +int stm32_i2c_get_setup_from_fdt(int node, struct stm32_i2c_init_s *init) { - const fdt32_t *cuint; + uint32_t read_val; + void *fdt; - 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); + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; } - 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); - } + init->rise_time = fdt_read_uint32_default(fdt, node, + "i2c-scl-rising-time-ns", + STM32_I2C_RISE_TIME_DEFAULT); - 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; - } + init->fall_time = fdt_read_uint32_default(fdt, node, + "i2c-scl-falling-time-ns", + STM32_I2C_FALL_TIME_DEFAULT); + + read_val = fdt_read_uint32_default(fdt, node, "clock-frequency", + STANDARD_RATE); + if (read_val > FAST_PLUS_RATE) { + ERROR("Invalid bus speed (%i > %i)\n", read_val, + FAST_PLUS_RATE); + return -FDT_ERR_BADVALUE; } + init->bus_rate = read_val; return dt_set_pinctrl_config(node); } @@ -146,7 +506,7 @@ int stm32_i2c_init(struct i2c_handle_s *hi2c, struct stm32_i2c_init_s *init_data) { int rc = 0; - uint32_t timing = I2C_TIMING; + uint32_t timing; if (hi2c == NULL) { return -ENOENT; @@ -158,7 +518,12 @@ int stm32_i2c_init(struct i2c_handle_s *hi2c, hi2c->i2c_state = I2C_STATE_BUSY; - stm32mp_clk_enable(hi2c->clock); + rc = i2c_setup_timing(hi2c, init_data, &timing); + if (rc != 0) { + return rc; + } + + clk_enable(hi2c->clock); /* Disable the selected I2C peripheral */ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE); @@ -220,11 +585,11 @@ int stm32_i2c_init(struct i2c_handle_s *hi2c, I2C_ANALOGFILTER_DISABLE); if (rc != 0) { ERROR("Cannot initialize I2C analog filter (%d)\n", rc); - stm32mp_clk_disable(hi2c->clock); + clk_disable(hi2c->clock); return rc; } - stm32mp_clk_disable(hi2c->clock); + clk_disable(hi2c->clock); return rc; } @@ -548,7 +913,7 @@ static int i2c_write(struct i2c_handle_s *hi2c, uint16_t dev_addr, return -EINVAL; } - stm32mp_clk_enable(hi2c->clock); + clk_enable(hi2c->clock); hi2c->lock = 1; @@ -648,7 +1013,7 @@ static int i2c_write(struct i2c_handle_s *hi2c, uint16_t dev_addr, bail: hi2c->lock = 0; - stm32mp_clk_disable(hi2c->clock); + clk_disable(hi2c->clock); return rc; } @@ -729,7 +1094,7 @@ static int i2c_read(struct i2c_handle_s *hi2c, uint16_t dev_addr, return -EINVAL; } - stm32mp_clk_enable(hi2c->clock); + clk_enable(hi2c->clock); hi2c->lock = 1; @@ -817,7 +1182,7 @@ static int i2c_read(struct i2c_handle_s *hi2c, uint16_t dev_addr, bail: hi2c->lock = 0; - stm32mp_clk_disable(hi2c->clock); + clk_disable(hi2c->clock); return rc; } @@ -882,7 +1247,7 @@ bool stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c, return rc; } - stm32mp_clk_enable(hi2c->clock); + clk_enable(hi2c->clock); hi2c->lock = 1; hi2c->i2c_mode = I2C_MODE_NONE; @@ -974,8 +1339,7 @@ bool stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c, bail: hi2c->lock = 0; - stm32mp_clk_disable(hi2c->clock); + clk_disable(hi2c->clock); return rc; } - diff --git a/drivers/st/io/io_stm32image.c b/drivers/st/io/io_stm32image.c index 3e377cd483..ccd3379a39 100644 --- a/drivers/st/io/io_stm32image.c +++ b/drivers/st/io/io_stm32image.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -182,53 +182,26 @@ static int stm32image_partition_size(io_entity_t *entity, size_t *length) return result; } - /* Reset magic header value */ - header->magic = 0; - - while (header->magic == 0U) { - result = io_seek(backend_handle, IO_SEEK_SET, *stm32_img); - if (result != 0) { - ERROR("%s: io_seek (%i)\n", __func__, result); - break; - } - - result = io_read(backend_handle, (uintptr_t)header, - MAX_LBA_SIZE, (size_t *)&bytes_read); - if (result != 0) { - if (current_part->bkp_offset == 0U) { - ERROR("%s: io_read (%i)\n", __func__, result); - } - header->magic = 0; - } - - if ((header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) || - (header->binary_type != current_part->binary_type) || - (header->image_length >= stm32image_dev.device_size)) { - VERBOSE("%s: partition %s not found at %x\n", - __func__, current_part->name, *stm32_img); - - if (current_part->bkp_offset == 0U) { - result = -ENOMEM; - break; - } - - /* Header not correct, check next offset for backup */ - *stm32_img += current_part->bkp_offset; - if (*stm32_img > stm32image_dev.device_size) { - /* No backup found, end of device reached */ - WARN("%s : partition %s not found\n", - __func__, current_part->name); - result = -ENOMEM; - break; - } - header->magic = 0; - } + result = io_seek(backend_handle, IO_SEEK_SET, *stm32_img); + if (result != 0) { + ERROR("%s: io_seek (%i)\n", __func__, result); + goto out; } - io_close(backend_handle); - + result = io_read(backend_handle, (uintptr_t)header, + MAX_LBA_SIZE, &bytes_read); if (result != 0) { - return result; + ERROR("%s: io_read (%i)\n", __func__, result); + goto out; + } + + if ((header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) || + (header->binary_type != current_part->binary_type) || + (header->image_length >= stm32image_dev.device_size)) { + VERBOSE("%s: partition %s not found at %x\n", + __func__, current_part->name, *stm32_img); + result = -ENOMEM; + goto out; } if (header->image_length < stm32image_dev.lba_size) { @@ -239,17 +212,24 @@ static int stm32image_partition_size(io_entity_t *entity, size_t *length) INFO("STM32 Image size : %lu\n", (unsigned long)*length); - return 0; +out: + io_close(backend_handle); + + return result; } /* Read data from a partition */ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { - int result; + int offset; + int local_length; + uintptr_t backend_handle; + int result = -EINVAL; uint8_t *local_buffer; boot_api_image_header_t *header = (boot_api_image_header_t *)first_lba_buffer; + size_t hdr_sz = sizeof(boot_api_image_header_t); assert(entity != NULL); assert(buffer != 0U); @@ -258,99 +238,53 @@ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, local_buffer = (uint8_t *)buffer; *length_read = 0U; - while (*length_read == 0U) { - int offset; - int local_length; - uintptr_t backend_handle; - - if (header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) { - /* Check for backup as image is corrupted */ - if (current_part->bkp_offset == 0U) { - result = -ENOMEM; - break; - } - - *stm32_img += current_part->bkp_offset; - if (*stm32_img >= stm32image_dev.device_size) { - /* End of device reached */ - result = -ENOMEM; - break; - } - - local_buffer = (uint8_t *)buffer; - - result = stm32image_partition_size(entity, &length); - if (result != 0) { - break; - } - } +#if TRUSTED_BOARD_BOOT + stm32mp_save_loaded_header(header); +#endif - /* Part of image already loaded with the header */ - memcpy(local_buffer, (uint8_t *)first_lba_buffer + - sizeof(boot_api_image_header_t), - MAX_LBA_SIZE - sizeof(boot_api_image_header_t)); - local_buffer += MAX_LBA_SIZE - sizeof(boot_api_image_header_t); - offset = MAX_LBA_SIZE; - - /* New image length to be read */ - local_length = round_up(length - - ((MAX_LBA_SIZE) - - sizeof(boot_api_image_header_t)), - stm32image_dev.lba_size); - - if ((header->load_address != 0U) && - (header->load_address != buffer)) { - ERROR("Wrong load address\n"); - panic(); - } + /* Part of image already loaded with the header */ + memcpy(local_buffer, (uint8_t *)first_lba_buffer + hdr_sz, + MAX_LBA_SIZE - hdr_sz); + local_buffer += MAX_LBA_SIZE - hdr_sz; + offset = MAX_LBA_SIZE; - result = io_open(backend_dev_handle, backend_image_spec, - &backend_handle); + /* New image length to be read */ + local_length = round_up(length - ((MAX_LBA_SIZE) - hdr_sz), + stm32image_dev.lba_size); - if (result != 0) { - ERROR("%s: io_open (%i)\n", __func__, result); - break; - } - - result = io_seek(backend_handle, IO_SEEK_SET, - *stm32_img + offset); - - if (result != 0) { - ERROR("%s: io_seek (%i)\n", __func__, result); - *length_read = 0; - io_close(backend_handle); - break; - } - - result = io_read(backend_handle, (uintptr_t)local_buffer, - local_length, length_read); + if ((header->load_address != 0U) && (header->load_address != buffer)) { + ERROR("Wrong load address\n"); + panic(); + } - /* Adding part of size already read from header */ - *length_read += MAX_LBA_SIZE - sizeof(boot_api_image_header_t); + result = io_open(backend_dev_handle, backend_image_spec, + &backend_handle); + if (result != 0) { + ERROR("%s: io_open (%i)\n", __func__, result); + return result; + } - if (result != 0) { - ERROR("%s: io_read (%i)\n", __func__, result); - *length_read = 0; - header->magic = 0; - continue; - } + result = io_seek(backend_handle, IO_SEEK_SET, *stm32_img + offset); + if (result != 0) { + ERROR("%s: io_seek (%i)\n", __func__, result); + goto out; + } - result = stm32mp_check_header(header, buffer); - if (result != 0) { - ERROR("Header check failed\n"); - *length_read = 0; - header->magic = 0; - } + result = io_read(backend_handle, (uintptr_t)local_buffer, + local_length, length_read); + if (result != 0) { + ERROR("%s: io_read (%i)\n", __func__, result); + goto out; + } - result = stm32mp_auth_image(header, buffer); - if (result != 0) { - ERROR("Authentication Failed (%i)\n", result); - return result; - } + /* Adding part of size already read from header */ + *length_read += MAX_LBA_SIZE - hdr_sz; - io_close(backend_handle); - } + inv_dcache_range(round_up((uintptr_t)(local_buffer + length - hdr_sz), + CACHE_WRITEBACK_GRANULE), *length_read - length + hdr_sz); +out: + io_close(backend_handle); return result; } diff --git a/drivers/st/iwdg/stm32_iwdg.c b/drivers/st/iwdg/stm32_iwdg.c index c052b4dfbe..f68f95371f 100644 --- a/drivers/st/iwdg/stm32_iwdg.c +++ b/drivers/st/iwdg/stm32_iwdg.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,30 @@ #include #include +#define IWDG_TIMEOUT_MS U(100) + /* 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(11, 0) struct stm32_iwdg_instance { uintptr_t base; @@ -52,6 +72,122 @@ static int stm32_iwdg_get_dt_node(struct dt_node_info *info, int offset) return node; } +#if defined(IMAGE_BL32) +void __dead2 stm32_iwdg_it_handler(int id) +{ + struct stm32_iwdg_instance *iwdg; + unsigned int instance; + + for (instance = 0; instance < IWDG_MAX_INSTANCE; instance++) { + if (stm32_iwdg[instance].num_irq == id) { + break; + } + } + + if (instance == IWDG_MAX_INSTANCE) { + panic(); + } + + iwdg = &stm32_iwdg[instance]; + +#if DEBUG + INFO("CPU %x IT Watchdog %u\n", plat_my_core_pos(), instance + 1U); + + stm32mp_dump_core_registers(true); +#endif + stm32_iwdg_refresh(); + + clk_enable(iwdg->clock); + + mmio_clrsetbits_32(iwdg->base + IWDG_EWCR_OFFSET, + IWDG_EWCR_EWIE, IWDG_EWCR_EWIC); + + clk_disable(iwdg->clock); + + /* Ack interrupt as we do not return from next call */ + gicv2_end_of_interrupt(id); + +#if DEBUG + if (!stm32mp_is_single_core()) { + unsigned int sec_cpu = (plat_my_core_pos() == STM32MP_PRIMARY_CPU) ? + STM32MP_SECONDARY_CPU : STM32MP_PRIMARY_CPU; + + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_1, sec_cpu); + } +#endif + + for ( ; ; ) { + ; + } +} + +static int stm32_iwdg_get_secure_timeout(int node) +{ + void *fdt; + const fdt32_t *cuint; + + if (fdt_get_address(&fdt) == 0) { + return -1; + } + + 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(int node, struct stm32_iwdg_instance *iwdg) +{ + int id_lsi; + int dt_secure_timeout = stm32_iwdg_get_secure_timeout(node); + uint32_t reload, status; + unsigned int timeout = IWDG_TIMEOUT_MS; + unsigned long long reload_ll; + + if (dt_secure_timeout < 0) { + return 0; + } + + if (dt_secure_timeout == 0) { + return -EINVAL; + } + + id_lsi = fdt_get_clock_id_by_name(node, "lsi"); + if (id_lsi < 0) { + return -EINVAL; + } + + /* Prescaler fix to 256 */ + reload_ll = (unsigned long long)dt_secure_timeout * clk_get_rate(id_lsi); + reload = ((uint32_t)(reload_ll >> 8) - 1U) & IWDG_EWCR_EWIT_MASK; + + clk_enable(iwdg->clock); + + mmio_write_32(iwdg->base + IWDG_KR_OFFSET, IWDG_KR_START_KEY); + mmio_write_32(iwdg->base + IWDG_KR_OFFSET, IWDG_KR_ACCESS_KEY); + mmio_write_32(iwdg->base + IWDG_PR_OFFSET, IWDG_PR_DIV_256); + mmio_write_32(iwdg->base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIE | reload); + + do { + status = mmio_read_32(iwdg->base + IWDG_SR_OFFSET) & + IWDG_SR_EWU; + timeout--; + mdelay(1); + } while ((status != 0U) && (timeout != 0U)); + + iwdg->num_irq = stm32_gic_enable_spi(node, NULL); + if (iwdg->num_irq < 0) { + panic(); + } + + clk_disable(iwdg->clock); + + return (timeout == 0U) ? -ETIMEDOUT : 0; +} +#endif + void stm32_iwdg_refresh(void) { uint8_t i; @@ -61,12 +197,12 @@ void stm32_iwdg_refresh(void) /* 0x00000000 is not a valid address for IWDG peripherals */ if (iwdg->base != 0U) { - stm32mp_clk_enable(iwdg->clock); + clk_enable(iwdg->clock); mmio_write_32(iwdg->base + IWDG_KR_OFFSET, IWDG_KR_RELOAD_KEY); - stm32mp_clk_disable(iwdg->clock); + clk_disable(iwdg->clock); } } } @@ -74,6 +210,7 @@ void stm32_iwdg_refresh(void) int stm32_iwdg_init(void) { int node = -1; + int __unused res; struct dt_node_info dt_info; void *fdt; uint32_t __unused count = 0; @@ -143,6 +280,14 @@ int stm32_iwdg_init(void) stm32mp_register_secure_periph_iomem(iwdg->base); } +#if defined(IMAGE_BL32) + res = stm32_iwdg_conf_etimeout(node, iwdg); + if (res != 0) { + ERROR("IWDG%x early timeout config failed (%d)\n", + idx + 1, res); + return res; + } +#endif #if defined(IMAGE_BL2) if (stm32_iwdg_shadow_update(idx, iwdg->flags) != BSEC_OK) { return -1; diff --git a/drivers/st/mmc/stm32_sdmmc2.c b/drivers/st/mmc/stm32_sdmmc2.c index cff3a344f7..8c203a4c03 100644 --- a/drivers/st/mmc/stm32_sdmmc2.c +++ b/drivers/st/mmc/stm32_sdmmc2.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ /* SDMMC power control register */ #define SDMMC_POWER_PWRCTRL GENMASK(1, 0) +#define SDMMC_POWER_PWRCTRL_PWR_CYCLE BIT(1) #define SDMMC_POWER_DIRPOL BIT(4) /* SDMMC clock control register */ @@ -117,6 +119,13 @@ #define TIMEOUT_US_10_MS 10000U #define TIMEOUT_US_1_S 1000000U +/* Power cycle delays in ms */ +#define VCC_POWER_OFF_DELAY 2 +#define VCC_POWER_ON_DELAY 2 +#define POWER_CYCLE_DELAY 2 +#define POWER_OFF_DELAY 2 +#define POWER_ON_DELAY 1 + #define DT_SDMMC2_COMPAT "st,stm32-sdmmc2" static void stm32_sdmmc2_init(void); @@ -138,12 +147,35 @@ static const struct mmc_ops stm32_sdmmc2_ops = { static struct stm32_sdmmc2_params sdmmc2_params; +static bool next_cmd_is_acmd; + #pragma weak plat_sdmmc2_use_dma bool plat_sdmmc2_use_dma(unsigned int instance, unsigned int memory) { return false; } +static void dump_registers(void) +{ + uintptr_t base = sdmmc2_params.reg_base; + + INFO("SDMMC_POWER = 0x%x\n", mmio_read_32(base + SDMMC_POWER)); + INFO("SDMMC_CLKCR = 0x%x\n", mmio_read_32(base + SDMMC_CLKCR)); + INFO("SDMMC_ARGR = 0x%x\n", mmio_read_32(base + SDMMC_ARGR)); + INFO("SDMMC_CMDR = 0x%x\n", mmio_read_32(base + SDMMC_CMDR)); + INFO("SDMMC_RESPCMDR = 0x%x\n", mmio_read_32(base + SDMMC_RESPCMDR)); + INFO("SDMMC_RESP1R = 0x%x\n", mmio_read_32(base + SDMMC_RESP1R)); + INFO("SDMMC_RESP2R = 0x%x\n", mmio_read_32(base + SDMMC_RESP2R)); + INFO("SDMMC_RESP3R = 0x%x\n", mmio_read_32(base + SDMMC_RESP3R)); + INFO("SDMMC_RESP4R = 0x%x\n", mmio_read_32(base + SDMMC_RESP4R)); + INFO("SDMMC_DTIMER = 0x%x\n", mmio_read_32(base + SDMMC_DTIMER)); + INFO("SDMMC_DLENR = 0x%x\n", mmio_read_32(base + SDMMC_DLENR)); + INFO("SDMMC_DCTRLR = 0x%x\n", mmio_read_32(base + SDMMC_DCTRLR)); + INFO("SDMMC_DCNTR = 0x%x\n", mmio_read_32(base + SDMMC_DCNTR)); + INFO("SDMMC_MASKR = 0x%x\n", mmio_read_32(base + SDMMC_MASKR)); + INFO("SDMMC_ACKTIMER = 0x%x\n", mmio_read_32(base + SDMMC_ACKTIMER)); +} + static void stm32_sdmmc2_init(void) { uint32_t clock_div; @@ -154,6 +186,26 @@ static void stm32_sdmmc2_init(void) freq = MIN(sdmmc2_params.max_freq, freq); } + if (sdmmc2_params.vmmc_regu.id != -1) { + stm32mp_regulator_register(&sdmmc2_params.vmmc_regu); + stm32mp_regulator_disable(&sdmmc2_params.vmmc_regu); + } + + mdelay(VCC_POWER_OFF_DELAY); + + mmio_write_32(base + SDMMC_POWER, + SDMMC_POWER_PWRCTRL_PWR_CYCLE | sdmmc2_params.dirpol); + mdelay(POWER_CYCLE_DELAY); + + if (sdmmc2_params.vmmc_regu.id != -1) { + stm32mp_regulator_enable(&sdmmc2_params.vmmc_regu); + } + + mdelay(VCC_POWER_ON_DELAY); + + mmio_write_32(base + SDMMC_POWER, sdmmc2_params.dirpol); + mdelay(POWER_OFF_DELAY); + clock_div = div_round_up(sdmmc2_params.clk_rate, freq * 2U); mmio_write_32(base + SDMMC_CLKCR, SDMMC_CLKCR_HWFC_EN | clock_div | @@ -163,7 +215,7 @@ static void stm32_sdmmc2_init(void) mmio_write_32(base + SDMMC_POWER, SDMMC_POWER_PWRCTRL | sdmmc2_params.dirpol); - mdelay(1); + mdelay(POWER_ON_DELAY); } static int stm32_sdmmc2_stop_transfer(void) @@ -221,6 +273,20 @@ static int stm32_sdmmc2_send_cmd_req(struct mmc_cmd *cmd) case MMC_CMD(1): arg_reg |= OCR_POWERUP; break; + case MMC_CMD(6): + if ((sdmmc2_params.device_info->mmc_dev_type == MMC_IS_SD_HC) && + (!next_cmd_is_acmd)) { + cmd_reg |= SDMMC_CMDR_CMDTRANS; + if (sdmmc2_params.use_dma) { + flags_data |= SDMMC_STAR_DCRCFAIL | + SDMMC_STAR_DTIMEOUT | + SDMMC_STAR_DATAEND | + SDMMC_STAR_RXOVERR | + SDMMC_STAR_IDMATE | + SDMMC_STAR_DBCKEND; + } + } + break; case MMC_CMD(8): if (sdmmc2_params.device_info->mmc_dev_type == MMC_IS_EMMC) { cmd_reg |= SDMMC_CMDR_CMDTRANS; @@ -258,6 +324,8 @@ static int stm32_sdmmc2_send_cmd_req(struct mmc_cmd *cmd) break; } + next_cmd_is_acmd = (cmd->cmd_idx == MMC_CMD(55)); + mmio_write_32(base + SDMMC_ICR, SDMMC_STATIC_FLAGS); /* @@ -265,8 +333,7 @@ static int stm32_sdmmc2_send_cmd_req(struct mmc_cmd *cmd) * Skip CMD55 as the next command could be data related, and * the register could have been set in prepare function. */ - if (((cmd_reg & SDMMC_CMDR_CMDTRANS) == 0U) && - (cmd->cmd_idx != MMC_CMD(55))) { + if (((cmd_reg & SDMMC_CMDR_CMDTRANS) == 0U) && !next_cmd_is_acmd) { mmio_write_32(base + SDMMC_DCTRLR, 0U); } @@ -563,6 +630,7 @@ static int stm32_sdmmc2_read(int lba, uintptr_t buf, size_t size) if ((status & error_flags) != 0U) { ERROR("%s: Read error (status = %x)\n", __func__, status); + dump_registers(); mmio_write_32(base + SDMMC_DCTRLR, SDMMC_DCTRLR_FIFORST); @@ -580,6 +648,7 @@ static int stm32_sdmmc2_read(int lba, uintptr_t buf, size_t size) if (timeout_elapsed(timeout)) { ERROR("%s: timeout 1s (status = %x)\n", __func__, status); + dump_registers(); mmio_write_32(base + SDMMC_ICR, SDMMC_STATIC_FLAGS); @@ -628,6 +697,7 @@ static int stm32_sdmmc2_dt_get_config(void) int sdmmc_node; void *fdt = NULL; const fdt32_t *cuint; + struct dt_node_info dt_info; if (fdt_get_address(&fdt) == 0) { return -FDT_ERR_NOTFOUND; @@ -637,27 +707,14 @@ static int stm32_sdmmc2_dt_get_config(void) return -FDT_ERR_NOTFOUND; } - sdmmc_node = fdt_node_offset_by_compatible(fdt, -1, DT_SDMMC2_COMPAT); - - while (sdmmc_node != -FDT_ERR_NOTFOUND) { - cuint = fdt_getprop(fdt, sdmmc_node, "reg", NULL); - if (cuint == NULL) { - continue; - } - - if (fdt32_to_cpu(*cuint) == sdmmc2_params.reg_base) { - break; - } - - sdmmc_node = fdt_node_offset_by_compatible(fdt, sdmmc_node, - DT_SDMMC2_COMPAT); - } - + sdmmc_node = dt_match_instance_by_compatible(DT_SDMMC2_COMPAT, + sdmmc2_params.reg_base); if (sdmmc_node == -FDT_ERR_NOTFOUND) { return -FDT_ERR_NOTFOUND; } - if (fdt_get_status(sdmmc_node) == DT_DISABLED) { + dt_fill_device_info(&dt_info, sdmmc_node); + if (dt_info.status == DT_DISABLED) { return -FDT_ERR_NOTFOUND; } @@ -665,21 +722,8 @@ static int stm32_sdmmc2_dt_get_config(void) return -FDT_ERR_BADVALUE; } - cuint = fdt_getprop(fdt, sdmmc_node, "clocks", NULL); - if (cuint == NULL) { - return -FDT_ERR_NOTFOUND; - } - - cuint++; - sdmmc2_params.clock_id = fdt32_to_cpu(*cuint); - - cuint = fdt_getprop(fdt, sdmmc_node, "resets", NULL); - if (cuint == NULL) { - return -FDT_ERR_NOTFOUND; - } - - cuint++; - sdmmc2_params.reset_id = fdt32_to_cpu(*cuint); + sdmmc2_params.clock_id = dt_info.clock; + sdmmc2_params.reset_id = dt_info.reset; if ((fdt_getprop(fdt, sdmmc_node, "st,use-ckin", NULL)) != NULL) { sdmmc2_params.pin_ckin = SDMMC_CLKCR_SELCLKRX_0; @@ -714,6 +758,11 @@ static int stm32_sdmmc2_dt_get_config(void) sdmmc2_params.max_freq = fdt32_to_cpu(*cuint); } + cuint = fdt_getprop(fdt, sdmmc_node, "vmmc-supply", NULL); + if (cuint != NULL) { + sdmmc2_params.vmmc_regu.id = fdt32_to_cpu(*cuint); + } + return 0; } @@ -734,12 +783,14 @@ int stm32_sdmmc2_mmc_init(struct stm32_sdmmc2_params *params) memcpy(&sdmmc2_params, params, sizeof(struct stm32_sdmmc2_params)); + sdmmc2_params.vmmc_regu.id = -1; + if (stm32_sdmmc2_dt_get_config() != 0) { ERROR("%s: DT error\n", __func__); return -ENOMEM; } - stm32mp_clk_enable(sdmmc2_params.clock_id); + clk_enable(sdmmc2_params.clock_id); rc = stm32mp_reset_assert(sdmmc2_params.reset_id, TIMEOUT_US_1_MS); if (rc != 0) { @@ -752,7 +803,7 @@ int stm32_sdmmc2_mmc_init(struct stm32_sdmmc2_params *params) } mdelay(1); - sdmmc2_params.clk_rate = stm32mp_clk_get_rate(sdmmc2_params.clock_id); + sdmmc2_params.clk_rate = clk_get_rate(sdmmc2_params.clock_id); sdmmc2_params.device_info->ocr_voltage = OCR_3_2_3_3 | OCR_3_3_3_4; return mmc_init(&stm32_sdmmc2_ops, sdmmc2_params.clk_rate, diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index b2bb482f9d..4e505debdf 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include @@ -22,36 +23,56 @@ #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 REGULATOR_MODE_STANDBY 8U + #define STPMIC1_DEFAULT_START_UP_DELAY_MS 1 +#define CMD_GET_MIN_VOLTAGE 0U +#define CMD_CONFIG_BOOT_ON 1U +#define CMD_CONFIG_LP 2U + static struct i2c_handle_s i2c_handle; static uint32_t pmic_i2c_addr; static int dt_get_pmic_node(void *fdt) { - return fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1"); + static int node = -FDT_ERR_BADOFFSET; + + if (node == -FDT_ERR_BADOFFSET) { + node = fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1"); + } + + return node; } int dt_pmic_status(void) { + static int status = -FDT_ERR_BADVALUE; int node; void *fdt; + if (status != -FDT_ERR_BADVALUE) { + return status; + } + if (fdt_get_address(&fdt) == 0) { return -ENOENT; } node = dt_get_pmic_node(fdt); if (node <= 0) { - return -FDT_ERR_NOTFOUND; + status = -FDT_ERR_NOTFOUND; + + return status; } - return fdt_get_status(node); + status = (int)fdt_get_status(node); + + return status; } static bool dt_pmic_is_secure(void) @@ -63,53 +84,173 @@ static bool dt_pmic_is_secure(void) (i2c_handle.dt_status == DT_SECURE); } -/* - * Get PMIC and its I2C bus configuration from the device tree. - * Return 0 on success, negative on error, 1 if no PMIC node is found. - */ -static int dt_pmic_i2c_config(struct dt_node_info *i2c_info, - struct stm32_i2c_init_s *init) +static int dt_pmic_get_regulator_voltage(void *fdt, int node, + uint16_t *min_mv, uint16_t *max_mv) { - int pmic_node, i2c_node; - void *fdt; const fdt32_t *cuint; - if (fdt_get_address(&fdt) == 0) { - return -ENOENT; + cuint = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; } - pmic_node = dt_get_pmic_node(fdt); - if (pmic_node < 0) { - return 1; + if (min_mv != NULL) { + *min_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); } - cuint = fdt_getprop(fdt, pmic_node, "reg", NULL); + cuint = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); if (cuint == NULL) { return -FDT_ERR_NOTFOUND; } - pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1; - if (pmic_i2c_addr > UINT16_MAX) { - return -EINVAL; + if (max_mv != NULL) { + *max_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); } - i2c_node = fdt_parent_offset(fdt, pmic_node); - if (i2c_node < 0) { - return -FDT_ERR_NOTFOUND; + return 0; +} + +static int pmic_config_boot_on(void *fdt, int node, const char *regu_name) +{ + uint16_t voltage = 0U; + uint16_t voltage_min; + uint16_t voltage_max; + int status; + int pmic_voltage; + + if ((fdt_getprop(fdt, node, "regulator-boot-on", NULL) == NULL) && + (fdt_getprop(fdt, node, "regulator-always-on", NULL) == NULL)) { + return 0; } - dt_fill_device_info(i2c_info, i2c_node); - if (i2c_info->base == 0U) { - return -FDT_ERR_NOTFOUND; + if (fdt_getprop(fdt, node, "regulator-pull-down", NULL) != NULL) { + + status = stpmic1_regulator_pull_down_set(regu_name); + if (status < 0) { + return status; + } + } + + if (fdt_getprop(fdt, node, "st,mask-reset", NULL) != NULL) { + + status = stpmic1_regulator_mask_reset_set(regu_name); + if (status < 0) { + return status; + } + } + + if (dt_pmic_get_regulator_voltage(fdt, node, &voltage_min, + &voltage_max) < 0) { + return 0; + } + + pmic_voltage = stpmic1_regulator_voltage_get(regu_name); + if (pmic_voltage < 0) { + return pmic_voltage; + } + + if ((uint16_t)pmic_voltage < voltage_min) { + voltage = voltage_min; + } + + if ((uint16_t)pmic_voltage > voltage_max) { + voltage = voltage_max; + } + + /* Only re-program voltage if not in the range provided in DT. */ + if (voltage != 0U) { + status = stpmic1_regulator_voltage_set(regu_name, voltage); + if (status < 0) { + return status; + } + } + + if (!stpmic1_is_regulator_enabled(regu_name)) { + status = stpmic1_regulator_enable(regu_name); + if (status < 0) { + return status; + } } - return stm32_i2c_get_setup_from_fdt(fdt, i2c_node, init); + return 0; +} + +#if defined(IMAGE_BL32) +static int pmic_config_lp(void *fdt, int node, const char *node_name, + const char *regu_name) +{ + int status; + const fdt32_t *cuint; + int regulator_state_node; + + status = stpmic1_powerctrl_on(); + if (status < 0) { + return status; + }; + + /* + * First, copy active configuration (Control register) to + * PWRCTRL Control register, even if regulator_state_node + * does not exist. + */ + status = stpmic1_lp_copy_reg(regu_name); + if (status < 0) { + return status; + } + + /* Then apply configs from regulator_state_node */ + regulator_state_node = fdt_subnode_offset(fdt, node, node_name); + if (regulator_state_node <= 0) { + return 0; + } + + if (fdt_getprop(fdt, regulator_state_node, "regulator-on-in-suspend", + NULL) != NULL) { + status = stpmic1_lp_reg_on_off(regu_name, 1); + if (status < 0) { + return status; + } + } + + if (fdt_getprop(fdt, regulator_state_node, "regulator-off-in-suspend", + NULL) != NULL) { + status = stpmic1_lp_reg_on_off(regu_name, 0); + if (status < 0) { + return status; + } + } + + cuint = fdt_getprop(fdt, regulator_state_node, + "regulator-suspend-microvolt", NULL); + if (cuint != NULL) { + uint16_t voltage = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + + status = stpmic1_lp_set_voltage(regu_name, voltage); + if (status < 0) { + return status; + } + } + + cuint = fdt_getprop(fdt, regulator_state_node, "regulator-mode", NULL); + if (cuint != NULL) { + if (fdt32_to_cpu(*cuint) == REGULATOR_MODE_STANDBY) { + status = stpmic1_lp_set_mode(regu_name, 1); + if (status < 0) { + return status; + } + } + } + + return 0; } +#endif -int dt_pmic_configure_boot_on_regulators(void) +static int pmic_operate(uint8_t command, const char *node_name, + uint16_t *voltage_mv) { - int pmic_node, regulators_node, regulator_node; + int pmic_node, regulators_node, subnode; void *fdt; + int ret = -EIO; if (fdt_get_address(&fdt) == 0) { return -ENOENT; @@ -117,70 +258,166 @@ int dt_pmic_configure_boot_on_regulators(void) pmic_node = dt_get_pmic_node(fdt); if (pmic_node < 0) { - return -FDT_ERR_NOTFOUND; + return -ENOENT; } regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); + if (regulators_node < 0) { + return -ENOENT; + } - fdt_for_each_subnode(regulator_node, fdt, regulators_node) { - const fdt32_t *cuint; - const char *node_name = fdt_get_name(fdt, regulator_node, NULL); - uint16_t voltage; - int status; - -#if defined(IMAGE_BL2) - if ((fdt_getprop(fdt, regulator_node, "regulator-boot-on", - NULL) == NULL) && - (fdt_getprop(fdt, regulator_node, "regulator-always-on", - NULL) == NULL)) { -#else - if (fdt_getprop(fdt, regulator_node, "regulator-boot-on", - NULL) == NULL) { -#endif - continue; - } + fdt_for_each_subnode(subnode, fdt, regulators_node) { + const char *regu_name = fdt_get_name(fdt, subnode, NULL); - if (fdt_getprop(fdt, regulator_node, "regulator-pull-down", - NULL) != NULL) { + switch (command) { + case CMD_GET_MIN_VOLTAGE: + assert(node_name != NULL); + assert(voltage_mv != NULL); - status = stpmic1_regulator_pull_down_set(node_name); - if (status != 0) { - return status; + if (strcmp(regu_name, node_name) != 0) { + continue; } - } - if (fdt_getprop(fdt, regulator_node, "st,mask-reset", - NULL) != NULL) { + ret = dt_pmic_get_regulator_voltage(fdt, subnode, + voltage_mv, NULL); + if (ret < 0) { + return -ENXIO; + } - status = stpmic1_regulator_mask_reset_set(node_name); - if (status != 0) { - return status; + return ret; + + case CMD_CONFIG_BOOT_ON: + ret = pmic_config_boot_on(fdt, subnode, regu_name); + if (ret < 0) { + return ret; } + break; + +#if defined(IMAGE_BL32) + case CMD_CONFIG_LP: + assert(node_name != NULL); + + ret = pmic_config_lp(fdt, subnode, node_name, + regu_name); + if (ret < 0) { + return ret; + } + break; +#endif + + default: + return -EINVAL; + } + } + + return ret; +} + +/* + * 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_i2c_init_s *init) +{ + static int i2c_node = -FDT_ERR_NOTFOUND; + + if (i2c_node == -FDT_ERR_NOTFOUND) { + void *fdt; + int pmic_node; + const fdt32_t *cuint; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return 1; } - cuint = fdt_getprop(fdt, regulator_node, - "regulator-min-microvolt", NULL); + cuint = fdt_getprop(fdt, pmic_node, "reg", NULL); if (cuint == NULL) { - continue; + return -FDT_ERR_NOTFOUND; } - /* DT uses microvolts, whereas driver awaits millivolts */ - voltage = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1; + if (pmic_i2c_addr > UINT16_MAX) { + return -FDT_ERR_BADVALUE; + } - status = stpmic1_regulator_voltage_set(node_name, voltage); - if (status != 0) { - return status; + i2c_node = fdt_parent_offset(fdt, pmic_node); + if (i2c_node < 0) { + return -FDT_ERR_NOTFOUND; } + } - if (stpmic1_is_regulator_enabled(node_name) == 0U) { - status = stpmic1_regulator_enable(node_name); - if (status != 0) { - return status; - } + dt_fill_device_info(i2c_info, i2c_node); + if (i2c_info->base == 0U) { + return -FDT_ERR_NOTFOUND; + } + + return stm32_i2c_get_setup_from_fdt(i2c_node, init); +} + +int pmic_configure_boot_on_regulators(void) +{ + return pmic_operate(CMD_CONFIG_BOOT_ON, NULL, NULL); +} + +int pmic_set_lp_config(const char *node_name) +{ + return pmic_operate(CMD_CONFIG_LP, node_name, NULL); +} + +int dt_pmic_find_supply(const char **supply_name, const char *regu_name) +{ + int pmic_node, regulators_node, subnode; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); + if (regulators_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_subnode(subnode, fdt, regulators_node) { + const char *name; + + name = fdt_getprop(fdt, subnode, "regulator-name", NULL); + if ((name != NULL) && + (strcmp(name, regu_name) == 0)) { + *supply_name = fdt_get_name(fdt, subnode, NULL); + return 0; } } - return 0; + return -FDT_ERR_NOTFOUND; +} + +int pmic_set_regulator_min_voltage(const char *regu_name) +{ + int rc = -ENOENT; + const char *supply_name; + + if (dt_pmic_find_supply(&supply_name, regu_name) == 0) { + uint16_t min_mv; + + rc = pmic_operate(CMD_GET_MIN_VOLTAGE, supply_name, &min_mv); + if (rc == 0) { + rc = stpmic1_regulator_voltage_set(supply_name, min_mv); + } + } + + return rc; } bool initialize_pmic_i2c(void) @@ -204,6 +441,7 @@ bool initialize_pmic_i2c(void) i2c->i2c_base_addr = i2c_info.base; i2c->dt_status = i2c_info.status; i2c->clock = i2c_info.clock; + i2c->i2c_state = I2C_STATE_RESET; i2c_init.own_address1 = pmic_i2c_addr; i2c_init.addressing_mode = I2C_ADDRESSINGMODE_7BIT; i2c_init.dual_address_mode = I2C_DUALADDRESS_DISABLE; @@ -245,16 +483,83 @@ static void register_pmic_shared_peripherals(void) } } -void initialize_pmic(void) +static int pmic_regulator_enable(struct stm32mp_regulator *regu) { - unsigned long pmic_version; + void *fdt; + const char *node_name; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node_name = fdt_get_name(fdt, fdt_node_offset_by_phandle(fdt, regu->id), + NULL); + + return stpmic1_regulator_enable(node_name); +} + +static int pmic_regulator_disable(struct stm32mp_regulator *regu) +{ + void *fdt; + const char *node_name; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node_name = fdt_get_name(fdt, fdt_node_offset_by_phandle(fdt, regu->id), + NULL); + + return stpmic1_regulator_disable(node_name); +} + +static const struct stm32mp_regulator_ops pmic_regu_ops = { + .enable = pmic_regulator_enable, + .disable = pmic_regulator_disable, +}; + +bool is_pmic_regulator(struct stm32mp_regulator *regu) +{ + void *fdt; + int parent_node; + + if (fdt_get_address(&fdt) == 0) { + return false; + } + + parent_node = fdt_parent_offset(fdt, + fdt_node_offset_by_phandle(fdt, + regu->id)); + return (fdt_node_check_compatible(fdt, parent_node, + "st,stpmic1-regulators") == 0); +} + +void bind_pmic_regulator(struct stm32mp_regulator *regu) +{ + regu->ops = &pmic_regu_ops; +} +void initialize_pmic(void) +{ if (!initialize_pmic_i2c()) { VERBOSE("No PMIC\n"); return; } register_pmic_shared_peripherals(); +} + +void configure_pmic(void) +{ + if (pmic_configure_boot_on_regulators() < 0) { + panic(); + }; +} + +#if DEBUG +void print_pmic_info_and_debug(void) +{ + unsigned long pmic_version; if (stpmic1_get_version(&pmic_version) != 0) { ERROR("Failed to access PMIC\n"); @@ -263,19 +568,20 @@ void initialize_pmic(void) INFO("PMIC version = 0x%02lx\n", pmic_version); stpmic1_dump_regulators(); - -#if defined(IMAGE_BL2) - if (dt_pmic_configure_boot_on_regulators() != 0) { - panic(); - }; -#endif } +#endif int pmic_ddr_power_init(enum ddr_type ddr_type) { bool buck3_at_1v8 = false; uint8_t read_val; int status; + uint16_t buck2_mv; + uint16_t ldo3_mv; + + if (pmic_operate(CMD_GET_MIN_VOLTAGE, "buck2", &buck2_mv) != 0) { + return -EPERM; + } switch (ddr_type) { case STM32MP_DDR3: @@ -295,7 +601,7 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) return status; } - status = stpmic1_regulator_voltage_set("buck2", 1350); + status = stpmic1_regulator_voltage_set("buck2", buck2_mv); if (status != 0) { return status; } @@ -345,7 +651,6 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) read_val &= ~STPMIC1_LDO3_MODE; read_val &= ~STPMIC1_LDO12356_OUTPUT_MASK; - read_val |= STPMIC1_LDO3_1800000; if (buck3_at_1v8) { read_val |= STPMIC1_LDO3_MODE; } @@ -355,7 +660,16 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) return status; } - status = stpmic1_regulator_voltage_set("buck2", 1200); + if (pmic_operate(CMD_GET_MIN_VOLTAGE, "ldo3", &ldo3_mv) != 0) { + return -EPERM; + } + + status = stpmic1_regulator_voltage_set("ldo3", ldo3_mv); + if (status != 0) { + return status; + } + + status = stpmic1_regulator_voltage_set("buck2", buck2_mv); if (status != 0) { return status; } diff --git a/drivers/st/pmic/stpmic1.c b/drivers/st/pmic/stpmic1.c index 9999630545..a1db120dc1 100644 --- a/drivers/st/pmic/stpmic1.c +++ b/drivers/st/pmic/stpmic1.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include @@ -419,6 +420,10 @@ static const uint16_t vref_ddr_voltage_table[] = { 3300, }; +static const uint16_t fixed_5v_voltage_table[] = { + 5000, +}; + /* Table of Regulators in PMIC SoC */ static const struct regul_struct regulators_table[] = { { @@ -528,6 +533,27 @@ static const struct regul_struct regulators_table[] = { .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = VREF_DDR_MASK_RESET, }, + { + .dt_node_name = "boost", + .voltage_table = fixed_5v_voltage_table, + .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), + .control_reg = USB_CONTROL_REG, + .mask_reset = BOOST_ENABLED, + }, + { + .dt_node_name = "pwr_sw1", + .voltage_table = fixed_5v_voltage_table, + .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), + .control_reg = USB_CONTROL_REG, + .mask_reset = USBSW_OTG_SWITCH_ENABLED, + }, + { + .dt_node_name = "pwr_sw2", + .voltage_table = fixed_5v_voltage_table, + .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), + .control_reg = USB_CONTROL_REG, + .mask_reset = SWIN_SWOUT_ENABLED, + }, }; #define MAX_REGUL ARRAY_SIZE(regulators_table) @@ -581,17 +607,19 @@ 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)); + return stpmic1_register_update(regul->control_reg, LDO_BUCK_ENABLE_MASK, + LDO_BUCK_ENABLE_MASK); } 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)); + return stpmic1_register_update(regul->control_reg, 0, + LDO_BUCK_ENABLE_MASK); } -uint8_t stpmic1_is_regulator_enabled(const char *name) +bool stpmic1_is_regulator_enabled(const char *name) { uint8_t val; const struct regul_struct *regul = get_regulator_data(name); @@ -600,7 +628,7 @@ uint8_t stpmic1_is_regulator_enabled(const char *name) panic(); } - return (val & 0x1U); + return (val & LDO_BUCK_ENABLE_MASK) == LDO_BUCK_ENABLE_MASK; } int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts) @@ -648,11 +676,68 @@ int stpmic1_regulator_mask_reset_set(const char *name) regul->mask_reset); } +/* Low-power functions */ +int stpmic1_lp_copy_reg(const char *name) +{ + uint8_t val; + int status; + const struct regul_struct *regul = get_regulator_data(name); + + if (regul->low_power_reg == 0U) { + return 0; + } + + 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_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_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_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); +} + int stpmic1_regulator_voltage_get(const char *name) { const struct regul_struct *regul = get_regulator_data(name); uint8_t value; uint8_t mask; + int status; /* Voltage can be set for buck or ldo (except ldo4) regulators */ if (strncmp(name, "buck", 4) == 0) { @@ -664,13 +749,16 @@ int stpmic1_regulator_voltage_get(const char *name) return 0; } - if (stpmic1_register_read(regul->control_reg, &value)) - return -1; + status = stpmic1_register_read(regul->control_reg, &value); + if (status < 0) { + return status; + } value = (value & mask) >> LDO_BUCK_VOLTAGE_SHIFT; - if (value > regul->voltage_table_size) - return -1; + if (value > regul->voltage_table_size) { + return -ERANGE; + } return (int)regul->voltage_table[value]; } @@ -706,7 +794,7 @@ int stpmic1_register_write(uint8_t register_id, uint8_t value) } if (readval != value) { - return -1; + return -EIO; } } #endif @@ -751,12 +839,12 @@ void stpmic1_dump_regulators(void) int stpmic1_get_version(unsigned long *version) { - int rc; uint8_t read_val; + int status; - rc = stpmic1_register_read(VERSION_STATUS_REG, &read_val); - if (rc) { - return -1; + status = stpmic1_register_read(VERSION_STATUS_REG, &read_val); + if (status < 0) { + return status; } *version = (unsigned long)read_val; diff --git a/drivers/st/regulator/stm32mp_dummy_regulator.c b/drivers/st/regulator/stm32mp_dummy_regulator.c new file mode 100644 index 0000000000..1003aba054 --- /dev/null +++ b/drivers/st/regulator/stm32mp_dummy_regulator.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +static int dummy_regulator_enable(struct stm32mp_regulator *regu) +{ + return 0; +} + +static int dummy_regulator_disable(struct stm32mp_regulator *regu) +{ + return 0; +} + +static const struct stm32mp_regulator_ops dummy_regu_ops = { + .enable = dummy_regulator_enable, + .disable = dummy_regulator_disable, +}; + +void bind_dummy_regulator(struct stm32mp_regulator *regu) +{ + regu->ops = &dummy_regu_ops; +} diff --git a/drivers/st/regulator/stm32mp_regulator.c b/drivers/st/regulator/stm32mp_regulator.c new file mode 100644 index 0000000000..f0e4a4ae5c --- /dev/null +++ b/drivers/st/regulator/stm32mp_regulator.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#pragma weak plat_bind_regulator +int plat_bind_regulator(struct stm32mp_regulator *regu) +{ + return -1; +} + +int stm32mp_regulator_enable(struct stm32mp_regulator *regu) +{ + assert((regu->ops != NULL) && (regu->ops->enable != NULL)); + + return regu->ops->enable(regu); +} + +int stm32mp_regulator_disable(struct stm32mp_regulator *regu) +{ + assert((regu->ops != NULL) && (regu->ops->disable != NULL)); + + if (regu->always_on) { + return 0; + } + + return regu->ops->disable(regu); +} + +int stm32mp_regulator_register(struct stm32mp_regulator *regu) +{ + return plat_bind_regulator(regu); +} diff --git a/drivers/st/reset/stm32mp1_reset.c b/drivers/st/reset/stm32mp1_reset.c index 98c8dcf710..a803923fca 100644 --- a/drivers/st/reset/stm32mp1_reset.c +++ b/drivers/st/reset/stm32mp1_reset.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -67,3 +67,32 @@ int stm32mp_reset_deassert(uint32_t id, unsigned int to_us) return 0; } + +void stm32mp_reset_assert_deassert_to_mcu(bool assert_not_deassert) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + /* + * The RCC_MP_GCR is a read/write register. + * Assert the MCU HOLD_BOOT means clear the BOOT_MCU bit + * Deassert the MCU HOLD_BOOT means set the BOOT_MCU the bit + */ + if (assert_not_deassert) { + mmio_clrbits_32(rcc_base + RCC_MP_GCR, RCC_MP_GCR_BOOT_MCU); + } else { + mmio_setbits_32(rcc_base + RCC_MP_GCR, RCC_MP_GCR_BOOT_MCU); + } +} + +void __dead2 stm32mp_system_reset(void) +{ + uintptr_t rcc_base = stm32mp_rcc_base(); + + mmio_setbits_32(rcc_base + RCC_MP_GRSTCSETR, + RCC_MP_GRSTCSETR_MPSYSRST); + + /* Loop in case system reset is not immediately caught */ + for ( ; ; ) { + ; + } +} diff --git a/drivers/st/rng/stm32_rng.c b/drivers/st/rng/stm32_rng.c new file mode 100644 index 0000000000..461d50c814 --- /dev/null +++ b/drivers/st/rng/stm32_rng.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 +#define RNG_TIMEOUT_STEP_US 10 + +#define TIMEOUT_US_1MS U(1000) + +struct stm32_rng_instance { + uintptr_t base; + unsigned long clock; +}; + +static struct stm32_rng_instance stm32_rng; + +/* + * stm32_rng_read - Read a number of random bytes from RNG + * out: pointer to the output buffer + * size: number of bytes to be read + * Return 0 on success, non-0 on failure + */ +int stm32_rng_read(uint8_t *out, uint32_t size) +{ + uint8_t *buf = out; + uint32_t len = size; + int nb_tries; + uint32_t data32; + int rc = 0; + int count; + + if (stm32_rng.base == 0U) { + return -EPERM; + } + + clk_enable(stm32_rng.clock); + + if ((mmio_read_32(stm32_rng.base + RNG_CR) & RNG_CR_RNGEN) == 0U) { + mmio_write_32(stm32_rng.base + RNG_CR, + RNG_CR_RNGEN | RNG_CR_CED); + } + + while (len != 0U) { + nb_tries = RNG_TIMEOUT_US / RNG_TIMEOUT_STEP_US; + do { + uint32_t status = mmio_read_32(stm32_rng.base + RNG_SR); + + if ((status & (RNG_SR_SECS | RNG_SR_SEIS)) != 0U) { + uint8_t i; + + /* Recommended by the SoC reference manual */ + mmio_clrbits_32(stm32_rng.base + RNG_SR, + RNG_SR_SEIS); + dmb(); + for (i = 12; i != 0; i--) { + (void)mmio_read_32(stm32_rng.base + + RNG_DR); + } + dmb(); + + if ((mmio_read_32(stm32_rng.base + RNG_SR) & + RNG_SR_SEIS) != 0U) { + ERROR("RNG noise\n"); + panic(); + } + } + + udelay(RNG_TIMEOUT_STEP_US); + nb_tries--; + if (nb_tries == 0) { + rc = -ETIMEDOUT; + goto bail; + } + } while ((mmio_read_32(stm32_rng.base + RNG_SR) & + RNG_SR_DRDY) == 0U); + + count = 4; + while (len != 0U) { + data32 = mmio_read_32(stm32_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: + clk_disable(stm32_rng.clock); + + if (rc != 0) { + memset(out, 0, buf - out); + } + + return rc; +} + +/* + * stm32_rng_init: Initialize rng from DT + * return 0 on success, negative value on failure + */ +int stm32_rng_init(void) +{ + void *fdt; + struct dt_node_info dt_rng; + int node; + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + node = dt_get_node(&dt_rng, -1, DT_RNG_COMPAT); + if (node < 0) { + return 0; + } + + if ((dt_rng.status & DT_SECURE) == 0) { + return 0; + } + + assert(dt_rng.base != 0U); + + stm32_rng.base = dt_rng.base; + + if ((dt_rng.status & DT_NON_SECURE) == DT_NON_SECURE) { + stm32mp_register_non_secure_periph_iomem(stm32_rng.base); + } else { + stm32mp_register_secure_periph_iomem(stm32_rng.base); + } + + if (dt_rng.clock < 0) { + panic(); + } + stm32_rng.clock = (unsigned long)dt_rng.clock; + + if (dt_rng.reset >= 0) { + int ret; + + ret = stm32mp_reset_assert((unsigned long)dt_rng.reset, + TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + + udelay(20); + + ret = stm32mp_reset_deassert((unsigned long)dt_rng.reset, + TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + } + + VERBOSE("Init RNG done\n"); + + return 0; +} diff --git a/drivers/st/rtc/stm32_rtc.c b/drivers/st/rtc/stm32_rtc.c new file mode 100644 index 0000000000..58d599b17a --- /dev/null +++ b/drivers/st/rtc/stm32_rtc.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2018-2019, 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 GENMASK(3, 0) +#define RTC_TR_ST_MASK GENMASK(6, 4) +#define RTC_TR_ST_SHIFT 4 +#define RTC_TR_MNU_MASK GENMASK(11, 8) +#define RTC_TR_MNU_SHIFT 8 +#define RTC_TR_MNT_MASK GENMASK(14, 12) +#define RTC_TR_MNT_SHIFT 12 +#define RTC_TR_HU_MASK GENMASK(19, 16) +#define RTC_TR_HU_SHIFT 16 +#define RTC_TR_HT_MASK GENMASK(21, 20) +#define RTC_TR_HT_SHIFT 20 +#define RTC_TR_PM BIT(22) + +#define RTC_DR_DU_MASK GENMASK(3, 0) +#define RTC_DR_DT_MASK GENMASK(5, 4) +#define RTC_DR_DT_SHIFT 4 +#define RTC_DR_MU_MASK GENMASK(11, 8) +#define RTC_DR_MU_SHIFT 8 +#define RTC_DR_MT BIT(12) +#define RTC_DR_MT_SHIFT 12 +#define RTC_DR_WDU_MASK GENMASK(15, 13) +#define RTC_DR_WDU_SHIFT 13 +#define RTC_DR_YU_MASK GENMASK(19, 16) +#define RTC_DR_YU_SHIFT 16 +#define RTC_DR_YT_MASK GENMASK(23, 20) +#define RTC_DR_YT_SHIFT 20 + +#define RTC_SSR_SS_MASK GENMASK(15, 0) + +#define RTC_ICSR_ALRAWF BIT(0) +#define RTC_ICSR_RSF BIT(5) + +#define RTC_PRER_PREDIV_S_MASK GENMASK(14, 0) + +#define RTC_CR_BYPSHAD BIT(5) +#define RTC_CR_BYPSHAD_SHIFT 5 +#define RTC_CR_ALRAE BIT(8) +#define RTC_CR_ALRAIE BIT(12) +#define RTC_CR_TAMPTS BIT(25) + +#define RTC_SMCR_TS_DPROT BIT(3) + +#define RTC_TSDR_DU_MASK GENMASK(3, 0) +#define RTC_TSDR_DU_SHIFT 0 +#define RTC_TSDR_DT_MASK GENMASK(5, 4) +#define RTC_TSDR_DT_SHIFT 4 +#define RTC_TSDR_MU_MASK GENMASK(11, 8) +#define RTC_TSDR_MU_SHIFT 8 + +#define RTC_ALRMAR_DU_SHIFT 24 + +#define RTC_SR_TSF BIT(3) +#define RTC_SR_TSOVF BIT(4) + +#define RTC_SCR_CTSF BIT(3) +#define RTC_SCR_CTSOVF BIT(4) + +#define RTC_WPR_KEY1 0xCA +#define RTC_WPR_KEY2 0x53 +#define RTC_WPR_KEY_LOCK 0xFF + +static struct dt_node_info rtc_dev; + +static struct spinlock lock; + +void stm32_rtc_regs_lock(void) +{ + if (stm32mp_lock_available()) { + spin_lock(&lock); + } +} + +void stm32_rtc_regs_unlock(void) +{ + if (stm32mp_lock_available()) { + spin_unlock(&lock); + } +} + +static void stm32_rtc_write_unprotect(void) +{ + mmio_write_32(rtc_dev.base + RTC_WPR, RTC_WPR_KEY1); + mmio_write_32(rtc_dev.base + RTC_WPR, RTC_WPR_KEY2); +} + +static void stm32_rtc_write_protect(void) +{ + mmio_write_32(rtc_dev.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(rtc_dev.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) +{ + bool bypshad = stm32_rtc_get_bypshad(); + + if (!bypshad) { + mmio_clrbits_32((uint32_t)(rtc_dev.base + RTC_ICSR), + RTC_ICSR_RSF); + while ((mmio_read_32(rtc_dev.base + RTC_ICSR) & RTC_ICSR_RSF) != + RTC_ICSR_RSF) { + ; + } + } + + calendar->ssr = mmio_read_32(rtc_dev.base + RTC_SSR); + calendar->tr = mmio_read_32(rtc_dev.base + RTC_TR); + calendar->dr = mmio_read_32(rtc_dev.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) +{ + assert(cal != NULL); + assert(tm != NULL); + + 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) +{ + assert(cal != NULL); + assert(tm != NULL); + + 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) +{ + assert(time != NULL); + + struct stm32_rtc_calendar cal_tamp; + + cal_tamp.tr = mmio_read_32(rtc_dev.base + RTC_TSTR); + cal_tamp.dr = mmio_read_32(rtc_dev.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) +{ + bool read_twice = stm32mp1_rtc_get_read_twice(); + + stm32_rtc_regs_lock(); + clk_enable(rtc_dev.clock); + + stm32_rtc_read_calendar(calendar); + + if (read_twice) { + uint32_t tr_save = calendar->tr; + + stm32_rtc_read_calendar(calendar); + + if (calendar->tr != tr_save) { + stm32_rtc_read_calendar(calendar); + } + } + + clk_disable(rtc_dev.clock); + stm32_rtc_regs_unlock(); +} + +/******************************************************************************* + * 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(rtc_dev.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 signed long long stm32_rtc_diff_frac(struct stm32_rtc_calendar *cur, + struct stm32_rtc_calendar *ref) +{ + return (signed long long)stm32_rtc_get_second_fraction(cur) - + (signed long long)stm32_rtc_get_second_fraction(ref); +} + +/******************************************************************************* + * This function computes the time difference between two timestamps. + * It includes seconds, minutes and hours. + * Here again the returned value is in milliseconds. + ******************************************************************************/ +static signed long long stm32_rtc_diff_time(struct stm32_rtc_time *current, + struct stm32_rtc_time *ref) +{ + 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); + + return (curr_s - ref_s) * 1000; +} + +/******************************************************************************* + * 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 signed 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 (24 * 60 * 60 * 1000) * (signed 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) +{ + signed long long diff_in_ms = 0; + struct stm32_rtc_time curr_t; + struct stm32_rtc_time ref_t; + + clk_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); + + clk_disable(rtc_dev.clock); + + return (unsigned long long)diff_in_ms; +} + +/******************************************************************************* + * This function fill the RTC timestamp structure. + ******************************************************************************/ +void stm32_rtc_get_timestamp(struct stm32_rtc_time *tamp_ts) +{ + stm32_rtc_regs_lock(); + clk_enable(rtc_dev.clock); + + if ((mmio_read_32(rtc_dev.base + RTC_SR) & RTC_SR_TSF) != 0U) { + /* Print timestamp for tamper event */ + stm32_rtc_read_timestamp(tamp_ts); + mmio_setbits_32(rtc_dev.base + RTC_SCR, RTC_SCR_CTSF); + if ((mmio_read_32(rtc_dev.base + RTC_SR) & RTC_SR_TSOVF) != + 0U) { + /* Overflow detected */ + mmio_setbits_32(rtc_dev.base + RTC_SCR, RTC_SCR_CTSOVF); + } + } + + clk_disable(rtc_dev.clock); + stm32_rtc_regs_unlock(); +} + +/******************************************************************************* + * This function enable the timestamp bit for tamper and secure timestamp + * access. + ******************************************************************************/ +void stm32_rtc_set_tamper_timestamp(void) +{ + stm32_rtc_regs_lock(); + clk_enable(rtc_dev.clock); + + stm32_rtc_write_unprotect(); + + /* Enable tamper timestamper */ + mmio_setbits_32(rtc_dev.base + RTC_CR, RTC_CR_TAMPTS); + + /* Secure Timestamp bit */ + mmio_clrbits_32(rtc_dev.base + RTC_SMCR, RTC_SMCR_TS_DPROT); + + stm32_rtc_write_protect(); + + clk_disable(rtc_dev.clock); + stm32_rtc_regs_unlock(); +} + +/******************************************************************************* + * This function return state of tamper timestamp. + ******************************************************************************/ +bool stm32_rtc_is_timestamp_enable(void) +{ + bool ret; + + clk_enable(rtc_dev.clock); + + ret = (mmio_read_32(rtc_dev.base + RTC_CR) & RTC_CR_TAMPTS) != 0U; + + clk_disable(rtc_dev.clock); + + return ret; +} + +/******************************************************************************* + * RTC initialisation function. + ******************************************************************************/ +int stm32_rtc_init(void) +{ + int node; + + node = dt_get_node(&rtc_dev, -1, RTC_COMPAT); + if (node < 0) { + return node; + } + + if (rtc_dev.status == DT_SECURE) { + stm32mp_register_secure_periph_iomem(rtc_dev.base); + } else { + stm32mp_register_non_secure_periph_iomem(rtc_dev.base); + } + + return 0; +} diff --git a/drivers/st/spi/stm32_qspi.c b/drivers/st/spi/stm32_qspi.c index d67f8313f3..966716d68f 100644 --- a/drivers/st/spi/stm32_qspi.c +++ b/drivers/st/spi/stm32_qspi.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -363,7 +364,7 @@ static void stm32_qspi_release_bus(void) static int stm32_qspi_set_speed(unsigned int hz) { - unsigned long qspi_clk = stm32mp_clk_get_rate(stm32_qspi.clock_id); + unsigned long qspi_clk = clk_get_rate(stm32_qspi.clock_id); uint32_t prescaler = UINT8_MAX; uint32_t csht; int ret; @@ -493,7 +494,7 @@ int stm32_qspi_init(void) stm32_qspi.clock_id = (unsigned long)info.clock; stm32_qspi.reset_id = (unsigned int)info.reset; - stm32mp_clk_enable(stm32_qspi.clock_id); + clk_enable(stm32_qspi.clock_id); ret = stm32mp_reset_assert(stm32_qspi.reset_id, TIMEOUT_US_1_MS); if (ret != 0) { diff --git a/drivers/st/tamper/stm32_tamp.c b/drivers/st/tamper/stm32_tamp.c new file mode 100644 index 0000000000..c9a9e9aef8 --- /dev/null +++ b/drivers/st/tamper/stm32_tamp.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define DT_TAMP_COMPAT "st,stm32-tamp" +/* STM32 Registers */ +#define STM32_TAMP_CR1 0x00U +#define STM32_TAMP_CR2 0x04U +#define STM32_TAMP_FLTCR 0x0CU +#define STM32_TAMP_ATCR 0x10U +#define STM32_TAMP_ATSEEDR 0x14U +#define STM32_TAMP_ATOR 0x18U +#define STM32_TAMP_SMCR 0x20U +#define STM32_TAMP_IER 0x2CU +#define STM32_TAMP_SR 0x30U +#define STM32_TAMP_SCR 0x3CU +#define STM32_TAMP_COUNTR 0x40U +#define STM32_TAMP_OR 0x50U +#define STM32_TAMP_HWCFGR2 0x3ECU +#define STM32_TAMP_HWCFGR1 0x3F0U +#define STM32_TAMP_VERR 0x3F4U +#define STM32_TAMP_IPIDR 0x3F8U +#define STM32_TAMP_SIDR 0x3FCU + +/* STM32_TAMP_FLTCR bit fields */ +#define STM32_TAMP_FLTCR_TAMPFREQ GENMASK(2, 0) +#define STM32_TAMP_FLTCR_TAMPFLT GENMASK(4, 3) +#define STM32_TAMP_FLTCR_TAMPPRCH GENMASK(6, 5) +#define STM32_TAMP_FLTCR_TAMPPUDIS BIT(7) + +/* STM32_TAMP_ATCR bit fields */ +#define STM32_TAMP_ATCR_ATCKSEL GENMASK(18, 16) +#define STM32_TAMP_ATCR_ATPER GENMASK(26, 24) +#define STM32_TAMP_ATCR_ATOSHARE BIT(30) +#define STM32_TAMP_ATCR_FLTEN BIT(31) + +/* STM32_TAMP_ATOR bit fields */ +#define STM32_TAMP_PRNG GENMASK(7, 0) +#define STM32_TAMP_SEEDF BIT(14) +#define STM32_TAMP_INITS BIT(15) + +/* STM32_TAMP_IER bit fields */ +#define STM32_TAMP_IER_TAMPXIE_ALL GENMASK(7, 0) +#define STM32_TAMP_IER_ITAMPXIE_ALL GENMASK(31, 16) + +/* STM32_TAMP_SR bit fields */ +#define STM32_TAMP_SR_TAMPXF_MASK GENMASK(7, 0) +#define STM32_TAMP_SR_ITAMPXF_MASK GENMASK(31, 16) + +/* STM32_TAMP_SMCR but fields */ +#define STM32_TAMP_SMCR_DPROT BIT(31) + +/* STM32_TAMP_CFGR bit fields */ +#define STM32_TAMP_OR_OUT3RMP BIT(0) + +/* STM32_TAMP_HWCFGR2 bit fields */ +#define STM32_TAMP_HWCFGR2_TZ GENMASK(11, 8) +#define STM32_TAMP_HWCFGR2_OR GENMASK(7, 0) + +/* STM32_TAMP_HWCFGR1 bit fields */ +#define STM32_TAMP_HWCFGR1_BKPREG GENMASK(7, 0) +#define STM32_TAMP_HWCFGR1_TAMPER GENMASK(11, 8) +#define STM32_TAMP_HWCFGR1_ACTIVE GENMASK(15, 12) +#define STM32_TAMP_HWCFGR1_INTERN GENMASK(31, 16) + +/* STM32_TAMP_VERR bit fields */ +#define STM32_TAMP_VERR_MINREV GENMASK(3, 0) +#define STM32_TAMP_VERR_MAJREV GENMASK(7, 4) + +#define STM32_TAMP_MAX_INTERNAL 16U +#define STM32_TAMP_MAX_EXTERNAL 8U + +struct stm32_tamp_instance { + uintptr_t base; + uint32_t clock; + uint32_t hwconf1; + uint32_t hwconf2; + uint16_t int_nbtamp; + uint8_t ext_nbtamp; + struct stm32_tamp_int *int_tamp; + struct stm32_tamp_ext *ext_tamp; +}; + +static struct stm32_tamp_instance stm32_tamp; + +static void stm32_tamp_set_secured(unsigned long base) +{ + mmio_clrbits_32(base + STM32_TAMP_SMCR, STM32_TAMP_SMCR_DPROT); +} + +static void stm32_tamp_configure_or(unsigned long base, uint32_t out3) +{ + mmio_setbits_32(base + STM32_TAMP_OR, out3); +} + +static int stm32_tamp_seed_init(unsigned long base) +{ + /* Need RNG access */ + uint32_t timeout = 100; + uint8_t idx; + + for (idx = 0; idx < 4U; idx++) { + uint32_t rnd; + + if (stm32_rng_read((uint8_t *)&rnd, sizeof(uint32_t)) != 0) { + return -1; + } + + VERBOSE("Seed init %u\n", rnd); + mmio_write_32(base + STM32_TAMP_ATSEEDR, rnd); + } + + while (((mmio_read_32(base + STM32_TAMP_ATOR) & + STM32_TAMP_SEEDF) != 0U) && + (timeout != 0U)) { + timeout--; + } + + if (timeout == 0U) { + return -1; + } + + return 0; +} + +static void stm32_tamp_reset_register(unsigned long base) +{ + /* Disable all internal tamper */ + mmio_write_32(base + STM32_TAMP_CR1, 0U); + + /* Disable all external tamper */ + mmio_write_32(base + STM32_TAMP_CR2, 0U); + + /* Clean configuration registers */ + mmio_write_32(base + STM32_TAMP_FLTCR, 0U); + mmio_write_32(base + STM32_TAMP_ATCR, 0U); + mmio_clrbits_32(base + STM32_TAMP_SMCR, STM32_TAMP_SMCR_DPROT); + + /* Clean Tamper IT */ + mmio_write_32(base + STM32_TAMP_IER, 0U); + mmio_write_32(base + STM32_TAMP_SCR, ~0U); + + mmio_clrbits_32(base + STM32_TAMP_OR, STM32_TAMP_OR_OUT3RMP); +} + +void stm32_tamp_write_mcounter(void) +{ + mmio_write_32(stm32_tamp.base + STM32_TAMP_COUNTR, 1U); +} + +void stm32_tamp_configure_internal(struct stm32_tamp_int *tamper_list, + uint16_t nb_tamper) +{ + uint16_t i; + + assert(nb_tamper < STM32_TAMP_MAX_INTERNAL); + + for (i = 0; i < nb_tamper; i++) { + int id = tamper_list[i].id; + uint32_t u_id; + + if (id == -1) { + continue; + } + + u_id = (uint32_t)id; + + if ((stm32_tamp.hwconf1 & BIT(u_id + 16U)) != 0U) { + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_CR1, + BIT(u_id + 16U)); + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_IER, + BIT(u_id + 16U)); + } + } + + stm32_tamp.int_tamp = tamper_list; + stm32_tamp.int_nbtamp = nb_tamper; +} + +void stm32_tamp_configure_external(struct stm32_tamp_ext *ext_tamper_list, + uint8_t nb_tamper, uint32_t passive_conf, + uint32_t active_conf) +{ + /* External configuration */ + uint8_t i, active_tamp = 0; + + assert(nb_tamper < STM32_TAMP_MAX_EXTERNAL); + + /* Enable external Tamp */ + for (i = 0; i < nb_tamper; i++) { + int id = ext_tamper_list[i].id; + uint32_t reg = 0, u_id; + + if (id == -1) { + continue; + } + + u_id = (uint32_t)id; + + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_CR1, + BIT(u_id)); + + if (ext_tamper_list[i].mode == TAMP_TRIG_ON) { + reg |= BIT(u_id + 24U); + } + + if (ext_tamper_list[i].mode == TAMP_ACTIVE) { + active_tamp |= BIT(u_id); + } + + if (ext_tamper_list[i].erase != 0U) { + reg |= BIT(u_id); + } + + if (ext_tamper_list[i].evt_mask != 0U) { + reg |= BIT(u_id + 16U); + } else { + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_IER, + BIT(u_id)); + } + + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_CR2, reg); + } + + /* Filter mode register set */ + mmio_write_32(stm32_tamp.base + STM32_TAMP_FLTCR, passive_conf); + + /* Active mode configuration */ + if (active_tamp != 0U) { + mmio_write_32(stm32_tamp.base + STM32_TAMP_ATCR, + active_conf | active_tamp); + if (stm32_tamp_seed_init(stm32_tamp.base) != 0) { + ERROR("Active tamper: SEED not initialized\n"); + panic(); + } + } + + stm32_tamp.ext_tamp = ext_tamper_list; + stm32_tamp.ext_nbtamp = nb_tamper; +} + +void stm32_tamp_it_handler(void) +{ + uint32_t it = mmio_read_32(stm32_tamp.base + STM32_TAMP_SR); + uint8_t tamp = 0; + struct stm32_rtc_time tamp_ts; + struct stm32_tamp_int *int_list = stm32_tamp.int_tamp; + struct stm32_tamp_ext *ext_list = stm32_tamp.ext_tamp; + + if (stm32_rtc_is_timestamp_enable()) { + stm32_rtc_get_timestamp(&tamp_ts); + INFO("Tamper Event Occurred\n"); + INFO("Date : %u/%u\n \t Time : %u:%u:%u\n", + tamp_ts.day, tamp_ts.month, tamp_ts.hour, + tamp_ts.min, tamp_ts.sec); + } + + /* Internal tamper interrupt */ + if ((it & STM32_TAMP_IER_ITAMPXIE_ALL) == 0U) { + goto tamp_ext; + } + + while ((it != 0U) && (tamp < stm32_tamp.int_nbtamp)) { + uint32_t int_id = (uint32_t)int_list[tamp].id; + + if ((it & BIT(int_id + 16U)) != 0U) { + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_SCR, BIT(int_id + 16U)); + it &= ~BIT(int_id + 16U); + if (int_list[tamp].func != NULL) { + int_list[tamp].func(int_id); + } + } + tamp++; + } + +tamp_ext: + tamp = 0; + /* External tamper interrupt */ + if ((it == 0U) || ((it & STM32_TAMP_IER_TAMPXIE_ALL) == 0U)) { + return; + } + + while ((it != 0U) && (tamp < stm32_tamp.ext_nbtamp)) { + uint32_t ext_id = (uint32_t)ext_list[tamp].id; + + if ((it & BIT(ext_id)) != 0U) { + if (ext_list[tamp].func != NULL) { + ext_list[tamp].func(ext_id); + } + + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_SCR, + BIT(ext_id)); + it &= ~BIT(ext_id); + } + tamp++; + } +} + +int stm32_tamp_init(void) +{ + int node; + struct dt_node_info dt_tamp; + void *fdt; + uint32_t rev __unused; + + if (fdt_get_address(&fdt) == 0) { + return -EPERM; + } + + node = dt_get_node(&dt_tamp, -1, DT_TAMP_COMPAT); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + assert(dt_tamp.base != 0U); + assert(dt_tamp.clock != -1); + + stm32_tamp.base = dt_tamp.base; + stm32_tamp.clock = (uint32_t)dt_tamp.clock; + + /* Init Tamp clock */ + clk_enable(stm32_tamp.clock); + + /* Reset Tamp register without modifying backup registers conf */ + stm32_tamp_reset_register(stm32_tamp.base); + + /* Check if TAMP is enabled */ + if ((dt_tamp.status != DT_SECURE) && + (dt_tamp.status != DT_SHARED)) { + return 0; + } + + stm32_tamp.hwconf1 = mmio_read_32(stm32_tamp.base + STM32_TAMP_HWCFGR1); + stm32_tamp.hwconf2 = mmio_read_32(stm32_tamp.base + STM32_TAMP_HWCFGR2); + + rev = mmio_read_32(stm32_tamp.base + STM32_TAMP_VERR); + VERBOSE("STM32 TAMPER V%u.%u\n", (rev & STM32_TAMP_VERR_MAJREV) >> 4, + rev & STM32_TAMP_VERR_MINREV); + + if ((stm32_tamp.hwconf2 & STM32_TAMP_HWCFGR2_TZ) == 0U) { + ERROR("Tamper IP doesn't support trustzone"); + return -EPERM; + } + + stm32_tamp_set_secured(stm32_tamp.base); + + if (dt_set_pinctrl_config(node) != -FDT_ERR_NOTFOUND) { + if (fdt_getprop(fdt, node, "st,out3-pc13", NULL) != NULL) { + stm32_tamp_configure_or(stm32_tamp.base, 1); + } + } + + if (stm32_gic_enable_spi(node, NULL) < 0) { + panic(); + } + + if (fdt_getprop(fdt, node, "wakeup-source", NULL) != NULL) { + mmio_setbits_32(EXTI_BASE + EXTI_TZENR1, EXTI_TZENR1_TZEN18); + mmio_setbits_32(EXTI_BASE + EXTI_C1IMR1, EXTI_IMR1_IM18); + } + + return 1; +} diff --git a/drivers/st/timer/stm32_timer.c b/drivers/st/timer/stm32_timer.c new file mode 100644 index 0000000000..1899707f76 --- /dev/null +++ b/drivers/st/timer/stm32_timer.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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(2, 0) /* Slave mode selection */ +#define TIM_SMCR_TS GENMASK(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(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 +#define TIM_MIN_FREQ_CALIB 50000000U +#define TIM_THRESHOLD 1U + +struct stm32_timer_instance { + uintptr_t base; + unsigned long clk; + unsigned long freq; + uint8_t cal_input; +}; + +static struct stm32_timer_instance stm32_timer[TIM_MAX_INSTANCE]; + +static int stm32_timer_get_dt_node(struct dt_node_info *info, int offset) +{ + int node; + + node = dt_get_node(info, offset, TIM_COMPAT); + if (node < 0) { + if (offset == -1) { + WARN("%s: No TIMER found\n", __func__); + } + return -FDT_ERR_NOTFOUND; + } + + return node; +} + +static int stm32_timer_config(struct stm32_timer_instance *timer) +{ + clk_enable(timer->clk); + + timer->freq = clk_get_rate(timer->clk); + + if (timer->freq < TIM_MIN_FREQ_CALIB) { + WARN("Timer is not accurate enough for calibration\n"); + clk_disable(timer->clk); + return -EINVAL; + } + + if ((mmio_read_32(timer->base + TIM_TISEL) & TIM_TISEL_TI1SEL_MASK) != + timer->cal_input) { + mmio_clrsetbits_32(timer->base + TIM_CCMR1, + TIM_CCMR_CC1S_TI1 | TIM_CCMR_CC1S_TI2, + TIM_CCMR_CC1S_TI1); + + mmio_clrbits_32(timer->base + TIM_CCER, + TIM_CCER_CC1P | TIM_CCER_CC1NP); + + mmio_clrsetbits_32(timer->base + TIM_SMCR, + TIM_SMCR_TS | TIM_SMCR_SMS, + (TIM_SMCR_TS_TI1FP1 << TIM_SMCR_TS_SHIFT) | + TIM_SMCR_SMS_RESET); + + mmio_write_32(timer->base + TIM_TISEL, timer->cal_input); + mmio_setbits_32(timer->base + TIM_CR1, TIM_CR1_CEN); + mmio_setbits_32(timer->base + TIM_CCER, TIM_CCER_CC1E); + } + + clk_disable(timer->clk); + + return 0; +} + +static uint32_t stm32_timer_start_capture(struct stm32_timer_instance *timer) +{ + uint32_t timeout = TIM_TIMEOUT_US / TIM_TIMEOUT_STEP_US; + uint32_t counter = 0U; + uint32_t old_counter; + uint64_t conv_timeout; + + if (stm32_timer_config(timer) < 0) { + return 0U; + } + + clk_enable(timer->clk); + + mmio_write_32(timer->base + TIM_SR, 0U); + while (((mmio_read_32(timer->base + TIM_SR) & + TIM_SR_UIF) == 0U) && (timeout != 0U)) { + udelay(TIM_TIMEOUT_STEP_US); + timeout--; + } + + if (timeout == 0U) { + goto out; + } + + mmio_write_32(timer->base + TIM_SR, 0U); + + conv_timeout = timeout_init_us(TIM_TIMEOUT_US); + do { + if (timeout_elapsed(conv_timeout)) { + WARN("Timer counter not stable\n"); + timeout = 0U; + goto out; + } + + timeout = TIM_TIMEOUT_US / TIM_TIMEOUT_STEP_US; + while (((mmio_read_32(timer->base + TIM_SR) & + TIM_SR_CC1IF) == 0U) && (timeout != 0U)) { + udelay(TIM_TIMEOUT_STEP_US); + timeout--; + } + + if (timeout == 0U) { + goto out; + } + + old_counter = counter; + counter = mmio_read_32(timer->base + TIM_CCR1); + } while ((MAX(counter, old_counter) - MIN(counter, old_counter)) > + TIM_THRESHOLD); + +out: + clk_disable(timer->clk); + + if (timeout == 0U) { + return 0U; + } + + return counter; +} + +unsigned long stm32_timer_hsi_freq(void) +{ + struct stm32_timer_instance *timer = &stm32_timer[HSI_CAL]; + unsigned long hsi_freq; + uint32_t counter; + + if (timer->base == 0) { + return 0; + } + + counter = stm32_timer_start_capture(timer); + VERBOSE("Counter value %i\n", counter); + + if (counter == 0U) { + return 0; + } + + hsi_freq = (timer->freq / counter) << TIM_PRESCAL_HSI; + + return hsi_freq; +} + +unsigned long stm32_timer_csi_freq(void) +{ + struct stm32_timer_instance *timer = &stm32_timer[CSI_CAL]; + unsigned long csi_freq; + uint32_t counter; + + if (timer->base == 0) { + return 0; + } + + counter = stm32_timer_start_capture(timer); + VERBOSE("Counter value %i\n", counter); + + if (counter == 0U) { + return 0; + } + + csi_freq = (timer->freq / counter) << TIM_PRESCAL_CSI; + + return csi_freq; +} + +/* + * 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) +{ + switch (type) { + case HSI_CAL: + if (stm32_timer[HSI_CAL].base != 0) { + *timer_freq_cb = stm32_timer_hsi_freq; + } + break; + + case CSI_CAL: + if (stm32_timer[CSI_CAL].base != 0) { + *timer_freq_cb = stm32_timer_csi_freq; + } + break; + + default: + panic(); + } +} + +/* + * Initialize timer from DT + * return 0 if disabled, 1 if enabled, else < 0 + */ +int stm32_timer_init(void) +{ + void *fdt; + struct dt_node_info dt_timer; + int node = -1; + uint8_t nb_timer = 0; + + if (fdt_get_address(&fdt) == 0) { + return -EPERM; + } + + for (node = stm32_timer_get_dt_node(&dt_timer, node); + node != -FDT_ERR_NOTFOUND; + node = stm32_timer_get_dt_node(&dt_timer, node)) { + + if (dt_timer.status == DT_SECURE) { + struct stm32_timer_instance *timer; + const fdt32_t *cuint; + + nb_timer++; + + cuint = fdt_getprop(fdt, node, "st,hsi-cal-input", + NULL); + if (cuint != NULL) { + timer = &stm32_timer[HSI_CAL]; + timer->base = dt_timer.base; + timer->clk = dt_timer.clock; + timer->freq = clk_get_rate(timer->clk); + timer->cal_input = + (uint8_t)fdt32_to_cpu(*cuint); + if (stm32_timer_config(timer) < 0) { + timer->base = 0; + continue; + } + } + + cuint = fdt_getprop(fdt, node, "st,csi-cal-input", + NULL); + if (cuint != NULL) { + timer = &stm32_timer[CSI_CAL]; + timer->base = dt_timer.base; + timer->clk = dt_timer.clock; + timer->freq = clk_get_rate(timer->clk); + timer->cal_input = + (uint8_t)fdt32_to_cpu(*cuint); + if (stm32_timer_config(timer) < 0) { + timer->base = 0; + continue; + } + } + } + } + + VERBOSE("%u TIMER instance%s found\n", nb_timer, + nb_timer > 1 ? "s" : ""); + + if (nb_timer == 0U) { + return -FDT_ERR_NOTFOUND; + } + + return 0; +} diff --git a/drivers/st/uart/aarch32/stm32_console.S b/drivers/st/uart/aarch32/stm32_console.S index 686b18b969..2cb04a88fc 100644 --- a/drivers/st/uart/aarch32/stm32_console.S +++ b/drivers/st/uart/aarch32/stm32_console.S @@ -122,17 +122,15 @@ register_fail: pop {r4, pc} endfunc console_stm32_register - /* --------------------------------------------------------------- + /* -------------------------------------------------------- * int console_core_putc(int c, uintptr_t base_addr) - * - * Function to output a character over the console. It returns the - * character printed on success or -1 on error. - * + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. * In : r0 - character to be printed * r1 - console base address * Out : return -1 on error else return character. - * Clobber list : r2 - * --------------------------------------------------------------- + * Clobber list : r2, r3 + * -------------------------------------------------------- */ func console_stm32_core_putc /* Check the input parameter */ @@ -140,13 +138,19 @@ func console_stm32_core_putc beq putc_error /* Check Transmit Data Register Empty */ + mov r3, #USART_TIMEOUT txe_loop: + subs r3, r3, #1 + beq putc_error ldr r2, [r1, #USART_ISR] tst r2, #USART_ISR_TXE beq txe_loop str r0, [r1, #USART_TDR] /* Check transmit complete flag */ + mov r3, #USART_TIMEOUT tc_loop: + subs r3, r3, #1 + beq putc_error ldr r2, [r1, #USART_ISR] tst r2, #USART_ISR_TC beq tc_loop @@ -200,7 +204,7 @@ endfunc console_stm32_core_getc * * In : r0 - console base address * Out : void. - * Clobber list : r0, r1 + * Clobber list : r0, r1, r2 * --------------------------------------------------------------- */ func console_stm32_core_flush @@ -209,7 +213,10 @@ func console_stm32_core_flush ASM_ASSERT(ne) #endif /* ENABLE_ASSERTIONS */ /* Check Transmit Data Register Empty */ + mov r2, #USART_TIMEOUT txe_loop_3: + subs r2, r2, #1 + beq plat_panic_handler ldr r1, [r0, #USART_ISR] tst r1, #USART_ISR_TXE beq txe_loop_3 diff --git a/drivers/st/uart/stm32_uart.c b/drivers/st/uart/stm32_uart.c new file mode 100644 index 0000000000..a0d21dcf33 --- /dev/null +++ b/drivers/st/uart/stm32_uart.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* UART time-out value */ +#define STM32_UART_TIMEOUT_US 20000U + +/* Mask to clear ALL the configuration register */ + +#define STM32_UART_CR1_FIELDS \ + (USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | \ + USART_CR1_RE | USART_CR1_OVER8 | USART_CR1_FIFOEN) + +#define STM32_UART_CR2_FIELDS \ + (USART_CR2_SLVEN | USART_CR2_DIS_NSS | USART_CR2_ADDM7 | \ + USART_CR2_LBDL | USART_CR2_LBDIE | USART_CR2_LBCL | \ + USART_CR2_CPHA | USART_CR2_CPOL | USART_CR2_CLKEN | \ + USART_CR2_STOP | USART_CR2_LINEN | USART_CR2_SWAP | \ + USART_CR2_RXINV | USART_CR2_TXINV | USART_CR2_DATAINV | \ + USART_CR2_MSBFIRST | USART_CR2_ABREN | USART_CR2_ABRMODE | \ + USART_CR2_RTOEN | USART_CR2_ADD) + +#define STM32_UART_CR3_FIELDS \ + (USART_CR3_EIE | USART_CR3_IREN | USART_CR3_IRLP | \ + USART_CR3_HDSEL | USART_CR3_NACK | USART_CR3_SCEN | \ + USART_CR3_DMAR | USART_CR3_DMAT | USART_CR3_RTSE | \ + USART_CR3_CTSE | USART_CR3_CTSIE | USART_CR3_ONEBIT | \ + USART_CR3_OVRDIS | USART_CR3_DDRE | USART_CR3_DEM | \ + USART_CR3_DEP | USART_CR3_SCARCNT | USART_CR3_WUS | \ + USART_CR3_WUFIE | USART_CR3_TXFTIE | USART_CR3_TCBGTIE | \ + USART_CR3_RXFTCFG | USART_CR3_RXFTIE | USART_CR3_TXFTCFG) + +#define STM32_UART_ISR_ERRORS \ + (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE) + +static const uint16_t presc_table[STM32_UART_PRESCALER_MAX + 1] = { + 1U, 2U, 4U, 6U, 8U, 10U, 12U, 16U, 32U, 64U, 128U, 256U +}; + +/* @brief BRR division operation to set BRR register in 8-bit oversampling + * mode. + * @param clockfreq: UART clock. + * @param baud_rate: Baud rate set by the user. + * @param prescaler: UART prescaler value. + * @retval Division result. + */ +static uint32_t uart_div_sampling8(unsigned long clockfreq, + uint32_t baud_rate, + uint32_t prescaler) +{ + uint32_t scaled_freq = clockfreq / presc_table[prescaler]; + + return ((scaled_freq * 2) + (baud_rate / 2)) / baud_rate; + +} + +/* @brief BRR division operation to set BRR register in 16-bit oversampling + * mode. + * @param clockfreq: UART clock. + * @param baud_rate: Baud rate set by the user. + * @param prescaler: UART prescaler value. + * @retval Division result. + */ +static uint32_t uart_div_sampling16(unsigned long clockfreq, + uint32_t baud_rate, + uint32_t prescaler) +{ + uint32_t scaled_freq = clockfreq / presc_table[prescaler]; + + return (scaled_freq + (baud_rate / 2)) / baud_rate; + +} + +/* + * @brief Return the UART clock frequency. + * @param huart: UART handle. + * @retval Frequency value in Hz. + */ +static unsigned long uart_get_clock_freq(struct stm32_uart_handle_s *huart) +{ + return fdt_get_uart_clock_freq((uintptr_t)huart->base); +} + +/* + * @brief Configure the UART peripheral. + * @param huart: UART handle. + * @retval UART status. + */ +static int uart_set_config(struct stm32_uart_handle_s *huart, + const struct stm32_uart_init_s *init) +{ + uint32_t tmpreg; + unsigned long clockfreq; + uint32_t brrtemp; + + /* + * ---------------------- USART CR1 Configuration -------------------- + * Clear M, PCE, PS, TE, RE and OVER8 bits and configure + * the UART word length, parity, mode and oversampling: + * - set the M bits according to init->word_length value, + * - set PCE and PS bits according to init->parity value, + * - set TE and RE bits according to init->mode value, + * - set OVER8 bit according to init->over_sampling value. + */ + tmpreg = init->word_length | + init->parity | + init->mode | + init->over_sampling | + init->fifo_mode; + mmio_clrsetbits_32(huart->base + USART_CR1, STM32_UART_CR1_FIELDS, tmpreg); + + /* + * --------------------- USART CR2 Configuration --------------------- + * Configure the UART Stop Bits: Set STOP[13:12] bits according + * to init->stop_bits value. + */ + mmio_clrsetbits_32(huart->base + USART_CR2, STM32_UART_CR2_FIELDS, + init->stop_bits); + + /* + * --------------------- USART CR3 Configuration --------------------- + * Configure: + * - UART HardWare Flow Control: set CTSE and RTSE bits according + * to init->hw_flow_control value, + * - one-bit sampling method versus three samples' majority rule + * according to init->one_bit_sampling (not applicable to + * LPUART), + * - set TXFTCFG bit according to init->tx_fifo_threshold value, + * - set RXFTCFG bit according to init->rx_fifo_threshold value. + */ + tmpreg = init->hw_flow_control | init->one_bit_sampling; + + if (init->fifo_mode == USART_CR1_FIFOEN) { + tmpreg |= init->tx_fifo_threshold | + init->rx_fifo_threshold; + } + + mmio_clrsetbits_32(huart->base + USART_CR3, STM32_UART_CR3_FIELDS, tmpreg); + + /* + * --------------------- USART PRESC Configuration ------------------- + * Configure UART Clock Prescaler : set PRESCALER according to + * init->prescaler value. + */ + assert(init->prescaler <= STM32_UART_PRESCALER_MAX); + mmio_clrsetbits_32(huart->base + USART_PRESC, USART_PRESC_PRESCALER, + init->prescaler); + + /*---------------------- USART BRR configuration --------------------*/ + clockfreq = uart_get_clock_freq(huart); + if (clockfreq == 0UL) { + return -ENODEV; + } + + if (init->over_sampling == STM32_UART_OVERSAMPLING_8) { + uint32_t usartdiv = uart_div_sampling8(clockfreq, + init->baud_rate, + init->prescaler); + + brrtemp = (usartdiv & USART_BRR_DIV_MANTISSA) | + ((usartdiv & USART_BRR_DIV_FRACTION) >> 1); + } else { + brrtemp = uart_div_sampling16(clockfreq, + init->baud_rate, + init->prescaler) & + (USART_BRR_DIV_FRACTION | USART_BRR_DIV_MANTISSA); + } + mmio_write_32(huart->base + USART_BRR, brrtemp); + + return 0U; +} + +/* + * @brief Handle UART communication timeout. + * @param huart: UART handle. + * @param flag: Specifies the UART flag to check. + * @retval UART status. + */ +static int stm32_uart_wait_flag(struct stm32_uart_handle_s *huart, uint32_t flag) +{ + uint64_t timeout_ref = timeout_init_us(STM32_UART_TIMEOUT_US); + + while ((mmio_read_32(huart->base + USART_ISR) & flag) == 0U) { + if (timeout_elapsed(timeout_ref)) { + return -ETIMEDOUT; + } + } + + return 0U; +} + +/* + * @brief Check the UART idle State. + * @param huart: UART handle. + * @retval UART status. + */ +static int stm32_uart_check_idle(struct stm32_uart_handle_s *huart) +{ + int ret; + + /* Check if the transmitter is enabled */ + if ((mmio_read_32(huart->base + USART_CR1) & USART_CR1_TE) == USART_CR1_TE) { + ret = stm32_uart_wait_flag(huart, USART_ISR_TEACK); + if (ret != 0U) { + return ret; + } + } + + /* Check if the receiver is enabled */ + if ((mmio_read_32(huart->base + USART_CR1) & USART_CR1_RE) == USART_CR1_RE) { + ret = stm32_uart_wait_flag(huart, USART_ISR_REACK); + if (ret != 0U) { + return ret; + } + } + + return 0U; +} + +/* + * @brief Compute RDR register mask depending on word length. + * @param huart: UART handle. + * @retval Mask value. + */ +static unsigned int stm32_uart_rdr_mask(const struct stm32_uart_init_s *init) +{ + unsigned int mask; + + switch (init->word_length) { + case STM32_UART_WORDLENGTH_9B: + mask = GENMASK(8, 0); + break; + case STM32_UART_WORDLENGTH_8B: + mask = GENMASK(7, 0); + break; + case STM32_UART_WORDLENGTH_7B: + mask = GENMASK(6, 0); + break; + default: + return 0U; + } + + if (init->parity != STM32_UART_PARITY_NONE) { + mask >>= 1; + } + + return mask; +} + +/* + * @brief Stop the UART. + * @param base: UART base address. + */ +void stm32_uart_stop(uintptr_t base) +{ + mmio_clrbits_32(base + USART_CR1, USART_CR1_UE); +} + +/* + * @brief Initialize UART. + * @param huart: UART handle. + * @param base_addr: base address of UART. + * @param init: UART initialization parameter. + * @retval UART status. + */ + +int stm32_uart_init(struct stm32_uart_handle_s *huart, + uintptr_t base_addr, + const struct stm32_uart_init_s *init) +{ + int ret; + + if (huart == NULL || init == NULL || base_addr == 0U) { + return -EINVAL; + } + + huart->base = base_addr; + + /* Disable the peripheral */ + stm32_uart_stop(huart->base); + + /* Computation of UART mask to apply to RDR register */ + huart->rdr_mask = stm32_uart_rdr_mask(init); + + /* Init the peripheral */ + ret = uart_set_config(huart, init); + if (ret != 0U) { + return ret; + } + + /* Enable the peripheral */ + mmio_setbits_32(huart->base + USART_CR1, USART_CR1_UE); + + /* TEACK and/or REACK to check */ + return stm32_uart_check_idle(huart); +} + +/* + * @brief Check interrupt and status errors. + * @retval True if error detected, false otherwise. + */ +static bool stm32_uart_error_detected(struct stm32_uart_handle_s *huart) +{ + return (mmio_read_32(huart->base + USART_ISR) & STM32_UART_ISR_ERRORS) != 0U; +} + +/* + * @brief Clear status errors. + */ +static void stm32_uart_error_clear(struct stm32_uart_handle_s *huart) +{ + mmio_write_32(huart->base + USART_ICR, STM32_UART_ISR_ERRORS); +} + +/* + * @brief Transmit one data in no blocking mode + * @param huart: UART handle. + * @param c: data to sent. + * @retval UART status. + */ +int stm32_uart_putc(struct stm32_uart_handle_s *huart, int c) +{ + int ret; + + if (huart == NULL) { + return -EINVAL; + } + + ret = stm32_uart_wait_flag(huart, USART_ISR_TXE); + if (ret != 0) { + return ret; + + } + mmio_write_32(huart->base + USART_TDR, c); + if (stm32_uart_error_detected(huart)) { + stm32_uart_error_clear(huart); + return -EFAULT; + } + + return 0U; +} + +/* + * @brief Flush TX Transmit fifo + * @param huart: UART handle. + * @retval UART status. + */ +int stm32_uart_flush(struct stm32_uart_handle_s *huart) +{ + int ret; + + if (huart == NULL) { + return -EINVAL; + } + ret = stm32_uart_wait_flag(huart, USART_ISR_TXE); + if (ret != 0) { + return ret; + + } + ret = stm32_uart_wait_flag(huart, USART_ISR_TC); + + return ret; +} + +/* + * @brief Receive a data in no blocking mode. + * @retval value if >0 or UART status. + */ +int stm32_uart_getc(struct stm32_uart_handle_s *huart) +{ + uint32_t data; + + if (huart == NULL) { + return -EINVAL; + } + + /* check if data is available */ + if ((mmio_read_32(huart->base + USART_ISR) & USART_ISR_RXNE) == 0U) { + return -EAGAIN; + } + + data = mmio_read_32(huart->base + USART_RDR) & huart->rdr_mask; + + if (stm32_uart_error_detected(huart)) { + stm32_uart_error_clear(huart); + return -EFAULT; + } + + return data; +} diff --git a/drivers/st/usb_dwc2/usb_dwc2.c b/drivers/st/usb_dwc2/usb_dwc2.c new file mode 100644 index 0000000000..fc3f771c7c --- /dev/null +++ b/drivers/st/usb_dwc2/usb_dwc2.c @@ -0,0 +1,1094 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#define USB_OTG_MODE_DEVICE 0U +#define USB_OTG_MODE_HOST 1U +#define USB_OTG_MODE_DRD 2U + +#define EP_TYPE_CTRL 0U +#define EP_TYPE_ISOC 1U +#define EP_TYPE_BULK 2U +#define EP_TYPE_INTR 3U + +#define USBD_FIFO_FLUSH_TIMEOUT_US 1000U +#define EP0_FIFO_SIZE 64U + +/* OTG registers offsets */ +#define OTG_GOTGINT 0x004U +#define OTG_GAHBCFG 0x008U +#define OTG_GUSBCFG 0x00CU +#define OTG_GRSTCTL 0x010U +#define OTG_GINTSTS 0x014U +#define OTG_GINTMSK 0x018U +#define OTG_GRXSTSP 0x020U +#define OTG_GLPMCFG 0x054U +#define OTG_DCFG 0x800U +#define OTG_DCTL 0x804U +#define OTG_DSTS 0x808U +#define OTG_DIEPMSK 0x810U +#define OTG_DOEPMSK 0x814U +#define OTG_DAINT 0x818U +#define OTG_DAINTMSK 0x81CU +#define OTG_DIEPEMPMSK 0x834U + +/* Definitions for OTG_DIEPx registers */ +#define OTG_DIEP_BASE 0x900U +#define OTG_DIEP_SIZE 0x20U +#define OTG_DIEPCTL 0x00U +#define OTG_DIEPINT 0x08U +#define OTG_DIEPTSIZ 0x10U +#define OTG_DIEPDMA 0x14U +#define OTG_DTXFSTS 0x18U +#define OTG_DIEP_MAX_NB 9U + +/* Definitions for OTG_DOEPx registers */ +#define OTG_DOEP_BASE 0xB00U +#define OTG_DOEP_SIZE 0x20U +#define OTG_DOEPCTL 0x00U +#define OTG_DOEPINT 0x08U +#define OTG_DOEPTSIZ 0x10U +#define OTG_DOEPDMA 0x14U +#define OTG_D0EP_MAX_NB 9U + +/* Definitions for OTG_DAINT registers */ +#define OTG_DAINT_OUT_MASK GENMASK(31, 16) +#define OTG_DAINT_OUT_SHIFT 16U +#define OTG_DAINT_IN_MASK GENMASK(15, 0) +#define OTG_DAINT_IN_SHIFT 0U + +#define OTG_DAINT_EP0_IN BIT(16) +#define OTG_DAINT_EP0_OUT BIT(0) + +/* Definitions for FIFOs */ +#define OTG_FIFO_BASE 0x1000U +#define OTG_FIFO_SIZE 0x1000U + +/* Bit definitions for OTG_GOTGINT register */ +#define OTG_GOTGINT_SEDET BIT(2) + +/* Bit definitions for OTG_GAHBCFG register */ +#define OTG_GAHBCFG_GINT BIT(0) + +/* Bit definitions for OTG_GUSBCFG register */ +#define OTG_GUSBCFG_TRDT GENMASK(13, 10) +#define OTG_GUSBCFG_TRDT_SHIFT 10U + +#define USBD_HS_TRDT_VALUE 9U + +/* Bit definitions for OTG_GRSTCTL register */ +#define OTG_GRSTCTL_RXFFLSH BIT(4) +#define OTG_GRSTCTL_TXFFLSH BIT(5) +#define OTG_GRSTCTL_TXFNUM_SHIFT 6U + +/* Bit definitions for OTG_GINTSTS register */ +#define OTG_GINTSTS_CMOD BIT(0) +#define OTG_GINTSTS_MMIS BIT(1) +#define OTG_GINTSTS_OTGINT BIT(2) +#define OTG_GINTSTS_SOF BIT(3) +#define OTG_GINTSTS_RXFLVL BIT(4) +#define OTG_GINTSTS_USBSUSP BIT(11) +#define OTG_GINTSTS_USBRST BIT(12) +#define OTG_GINTSTS_ENUMDNE BIT(13) +#define OTG_GINTSTS_IEPINT BIT(18) +#define OTG_GINTSTS_OEPINT BIT(19) +#define OTG_GINTSTS_IISOIXFR BIT(20) +#define OTG_GINTSTS_IPXFR_INCOMPISOOUT BIT(21) +#define OTG_GINTSTS_LPMINT BIT(27) +#define OTG_GINTSTS_SRQINT BIT(30) +#define OTG_GINTSTS_WKUPINT BIT(31) + +/* Bit definitions for OTG_GRXSTSP register */ +#define OTG_GRXSTSP_EPNUM GENMASK(3, 0) +#define OTG_GRXSTSP_BCNT GENMASK(14, 4) +#define OTG_GRXSTSP_BCNT_SHIFT 4U +#define OTG_GRXSTSP_PKTSTS GENMASK(20, 17) +#define OTG_GRXSTSP_PKTSTS_SHIFT 17U + +#define STS_GOUT_NAK 1U +#define STS_DATA_UPDT 2U +#define STS_XFER_COMP 3U +#define STS_SETUP_COMP 4U +#define STS_SETUP_UPDT 6U + +/* Bit definitions for OTG_GLPMCFG register */ +#define OTG_GLPMCFG_BESL GENMASK(5, 2) + +/* Bit definitions for OTG_DCFG register */ +#define OTG_DCFG_DAD GENMASK(10, 4) +#define OTG_DCFG_DAD_SHIFT 4U + +/* Bit definitions for OTG_DCTL register */ +#define OTG_DCTL_RWUSIG BIT(0) +#define OTG_DCTL_SDIS BIT(1) +#define OTG_DCTL_CGINAK BIT(8) + +/* Bit definitions for OTG_DSTS register */ +#define OTG_DSTS_SUSPSTS BIT(0) +#define OTG_DSTS_ENUMSPD_MASK GENMASK(2, 1) +#define OTG_DSTS_FNSOF0 BIT(8) + +#define OTG_DSTS_ENUMSPD(val) ((val) << 1) +#define OTG_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ OTG_DSTS_ENUMSPD(0U) +#define OTG_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ OTG_DSTS_ENUMSPD(1U) +#define OTG_DSTS_ENUMSPD_LS_PHY_6MHZ OTG_DSTS_ENUMSPD(2U) +#define OTG_DSTS_ENUMSPD_FS_PHY_48MHZ OTG_DSTS_ENUMSPD(3U) + +/* Bit definitions for OTG_DIEPMSK register */ +#define OTG_DIEPMSK_XFRCM BIT(0) +#define OTG_DIEPMSK_EPDM BIT(1) +#define OTG_DIEPMSK_TOM BIT(3) + +/* Bit definitions for OTG_DOEPMSK register */ +#define OTG_DOEPMSK_XFRCM BIT(0) +#define OTG_DOEPMSK_EPDM BIT(1) +#define OTG_DOEPMSK_STUPM BIT(3) + +/* Bit definitions for OTG_DIEPCTLx registers */ +#define OTG_DIEPCTL_MPSIZ GENMASK(10, 0) +#define OTG_DIEPCTL_STALL BIT(21) +#define OTG_DIEPCTL_CNAK BIT(26) +#define OTG_DIEPCTL_SD0PID_SEVNFRM BIT(28) +#define OTG_DIEPCTL_SODDFRM BIT(29) +#define OTG_DIEPCTL_EPDIS BIT(30) +#define OTG_DIEPCTL_EPENA BIT(31) + +/* Bit definitions for OTG_DIEPINTx registers */ +#define OTG_DIEPINT_XFRC BIT(0) +#define OTG_DIEPINT_EPDISD BIT(1) +#define OTG_DIEPINT_TOC BIT(3) +#define OTG_DIEPINT_ITTXFE BIT(4) +#define OTG_DIEPINT_INEPNE BIT(6) +#define OTG_DIEPINT_TXFE BIT(7) +#define OTG_DIEPINT_TXFE_SHIFT 7U + +#define OTG_DIEPINT_MASK (BIT(13) | BIT(11) | GENMASK(9, 0)) + +/* Bit definitions for OTG_DIEPTSIZx registers */ +#define OTG_DIEPTSIZ_XFRSIZ GENMASK(18, 0) +#define OTG_DIEPTSIZ_PKTCNT GENMASK(28, 19) +#define OTG_DIEPTSIZ_PKTCNT_SHIFT 19U +#define OTG_DIEPTSIZ_MCNT_MASK GENMASK(30, 29) +#define OTG_DIEPTSIZ_MCNT_DATA0 BIT(29) + +#define OTG_DIEPTSIZ_PKTCNT_1 BIT(19) + +/* Bit definitions for OTG_DTXFSTSx registers */ +#define OTG_DTXFSTS_INEPTFSAV GENMASK(15, 0) + +/* Bit definitions for OTG_DOEPCTLx registers */ +#define OTG_DOEPCTL_STALL BIT(21) +#define OTG_DOEPCTL_CNAK BIT(26) +#define OTG_DOEPCTL_SD0PID_SEVNFRM BIT(28) /* other than endpoint 0 */ +#define OTG_DOEPCTL_SD1PID_SODDFRM BIT(29) /* other than endpoint 0 */ +#define OTG_DOEPCTL_EPDIS BIT(30) +#define OTG_DOEPCTL_EPENA BIT(31) + +/* Bit definitions for OTG_DOEPTSIZx registers */ +#define OTG_DOEPTSIZ_XFRSIZ GENMASK(18, 0) +#define OTG_DOEPTSIZ_PKTCNT GENMASK(28, 19) +#define OTG_DOEPTSIZ_RXDPID_STUPCNT GENMASK(30, 29) + +/* Bit definitions for OTG_DOEPINTx registers */ +#define OTG_DOEPINT_XFRC BIT(0) +#define OTG_DOEPINT_STUP BIT(3) +#define OTG_DOEPINT_OTEPDIS BIT(4) + +#define OTG_DOEPINT_MASK (GENMASK(15, 12) | GENMASK(9, 8) | GENMASK(6, 0)) + +#define EP_NB 15U +#define EP_ALL 0x10U + +/* + * @brief Flush TX FIFO. + * @param handle: PCD handle. + * @param num: FIFO number. + * This parameter can be a value from 1 to 15 or EP_ALL. + * EP_ALL= 0x10 means Flush all TX FIFOs + * @retval USB status. + */ +static usb_status_t usb_dwc2_flush_tx_fifo(void *handle, uint32_t num) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint64_t timeout = timeout_init_us(USBD_FIFO_FLUSH_TIMEOUT_US); + + mmio_write_32(usb_base_addr + OTG_GRSTCTL, + OTG_GRSTCTL_TXFFLSH | (uint32_t)(num << OTG_GRSTCTL_TXFNUM_SHIFT)); + + while ((mmio_read_32(usb_base_addr + OTG_GRSTCTL) & + OTG_GRSTCTL_TXFFLSH) == OTG_GRSTCTL_TXFFLSH) + if (timeout_elapsed(timeout)) { + return USBD_TIMEOUT; + } + + return USBD_OK; +} + +/* + * @brief Flush RX FIFO. + * @param handle: PCD handle. + * @retval USB status. + */ +static usb_status_t usb_dwc2_flush_rx_fifo(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint64_t timeout = timeout_init_us(USBD_FIFO_FLUSH_TIMEOUT_US); + + mmio_write_32(usb_base_addr + OTG_GRSTCTL, OTG_GRSTCTL_RXFFLSH); + + while ((mmio_read_32(usb_base_addr + OTG_GRSTCTL) & + OTG_GRSTCTL_RXFFLSH) == OTG_GRSTCTL_RXFFLSH) + if (timeout_elapsed(timeout)) { + return USBD_TIMEOUT; + } + + return USBD_OK; +} + +/* + * @brief Return the global USB interrupt status. + * @param handle: PCD handle. + * @retval Interrupt register value. + */ +static uint32_t usb_dwc2_read_int(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + return mmio_read_32(usb_base_addr + OTG_GINTSTS) & + mmio_read_32(usb_base_addr + OTG_GINTMSK); +} + +/* + * @brief Return the USB device OUT endpoints interrupt. + * @param handle: PCD handle. + * @retval Device OUT endpoint interrupts. + */ +static uint32_t usb_dwc2_all_out_ep_int(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + return ((mmio_read_32(usb_base_addr + OTG_DAINT) & + mmio_read_32(usb_base_addr + OTG_DAINTMSK)) & + OTG_DAINT_OUT_MASK) >> OTG_DAINT_OUT_SHIFT; +} + +/* + * @brief Return the USB device IN endpoints interrupt. + * @param handle: PCD handle. + * @retval Device IN endpoint interrupts. + */ +static uint32_t usb_dwc2_all_in_ep_int(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + return ((mmio_read_32(usb_base_addr + OTG_DAINT) & + mmio_read_32(usb_base_addr + OTG_DAINTMSK)) & + OTG_DAINT_IN_MASK) >> OTG_DAINT_IN_SHIFT; +} + +/* + * @brief Return Device OUT EP interrupt register. + * @param handle: PCD handle. + * @param epnum: Endpoint number. + * This parameter can be a value from 0 to 15. + * @retval Device OUT EP Interrupt register. + */ +static uint32_t usb_dwc2_out_ep_int(void *handle, uint8_t epnum) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + return mmio_read_32(usb_base_addr + OTG_DOEP_BASE + + (epnum * OTG_DOEP_SIZE) + OTG_DOEPINT) & + mmio_read_32(usb_base_addr + OTG_DOEPMSK); +} + +/* + * @brief Return Device IN EP interrupt register. + * @param handle: PCD handle. + * @param epnum: Endpoint number. + * This parameter can be a value from 0 to 15. + * @retval Device IN EP Interrupt register. + */ +static uint32_t usb_dwc2_in_ep_int(void *handle, uint8_t epnum) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t msk; + uint32_t emp; + + msk = mmio_read_32(usb_base_addr + OTG_DIEPMSK); + emp = mmio_read_32(usb_base_addr + OTG_DIEPEMPMSK); + msk |= ((emp >> epnum) << OTG_DIEPINT_TXFE_SHIFT) & OTG_DIEPINT_TXFE; + + return mmio_read_32(usb_base_addr + OTG_DIEP_BASE + + (epnum * OTG_DIEP_SIZE) + OTG_DIEPINT) & msk; +} + +/* + * @brief Return USB core mode. + * @param handle: PCD handle. + * @retval Core mode. + * This parameter can be one of the these values: + * 0 : Host. + * 1 : Device. + */ +static uint32_t usb_dwc2_get_mode(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + return mmio_read_32(usb_base_addr + OTG_GINTSTS) & OTG_GINTSTS_CMOD; +} + +/* + * @brief Activate EP0 for detup transactions. + * @param handle: PCD handle. + * @retval USB status. + */ +static usb_status_t usb_dwc2_activate_setup(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uintptr_t reg_offset = usb_base_addr + OTG_DIEP_BASE; + + /* Set the MPS of the IN EP based on the enumeration speed */ + mmio_clrbits_32(reg_offset + OTG_DIEPCTL, OTG_DIEPCTL_MPSIZ); + + if ((mmio_read_32(usb_base_addr + OTG_DSTS) & OTG_DSTS_ENUMSPD_MASK) == + OTG_DSTS_ENUMSPD_LS_PHY_6MHZ) { + mmio_setbits_32(reg_offset + OTG_DIEPCTL, 3U); + } + + mmio_setbits_32(usb_base_addr + OTG_DCTL, OTG_DCTL_CGINAK); + + return USBD_OK; +} + +/* + * @brief Prepare the EP0 to start the first control setup. + * @param handle: Selected device. + * @retval USB status. + */ +static usb_status_t usb_dwc2_ep0_out_start(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uintptr_t reg_offset = usb_base_addr + OTG_DIEP_BASE + OTG_DIEPTSIZ; + uint32_t reg_value = 0U; + + /* PKTCNT = 1 and XFRSIZ = 24 bytes for endpoint 0 */ + reg_value |= OTG_DIEPTSIZ_PKTCNT_1; + reg_value |= (EP0_FIFO_SIZE & OTG_DIEPTSIZ_XFRSIZ); + reg_value |= OTG_DOEPTSIZ_RXDPID_STUPCNT; + + mmio_write_32(reg_offset, reg_value); + + return USBD_OK; +} + +/* + * @brief Write a packet into the TX FIFO associated with the EP/channel. + * @param handle: Selected device. + * @param src: Pointer to source buffer. + * @param ch_ep_num: Endpoint or host channel number. + * @param len: Number of bytes to write. + * @retval USB status. + */ +static usb_status_t usb_dwc2_write_packet(void *handle, uint8_t *src, + uint8_t ch_ep_num, uint16_t len) +{ + uint32_t reg_offset; + uint32_t count32b = (len + 3U) / 4U; + uint32_t i; + + reg_offset = (uintptr_t)handle + OTG_FIFO_BASE + + (ch_ep_num * OTG_FIFO_SIZE); + + for (i = 0U; i < count32b; i++) { + uint32_t src_copy = 0U; + uint32_t j; + + /* Data written to FIFO need to be 4 bytes aligned */ + for (j = 0U; j < 4U; j++) { + src_copy += (*(src + j)) << (8U * j); + } + + mmio_write_32(reg_offset, src_copy); + src += 4U; + } + + return USBD_OK; +} + +/* + * @brief Read a packet from the RX FIFO associated with the EP/channel. + * @param handle: Selected device. + * @param src: Source pointer. + * @param ch_ep_num: Endpoint or host channel number. + * @param len: Number of bytes to read. + * @retval Pointer to destination buffer. + */ +static void *usb_dwc2_read_packet(void *handle, uint8_t *dest, uint16_t len) +{ + uint32_t reg_offset; + uint32_t count32b = (len + 3U) / 4U; + uint32_t i; + + VERBOSE("read packet length %i to 0x%lx\n", len, (uintptr_t)dest); + + reg_offset = (uintptr_t)handle + OTG_FIFO_BASE; + + for (i = 0U; i < count32b; i++) { + *(uint32_t *)dest = mmio_read_32(reg_offset); + dest += 4U; + dsb(); + } + + return (void *)dest; +} + +/* + * @brief Setup and start a transfer over an EP. + * @param handle: Selected device + * @param ep: Pointer to endpoint structure. + * @retval USB status. + */ +static usb_status_t usb_dwc2_ep_start_xfer(void *handle, usbd_ep_t *ep) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t reg_offset; + uint32_t reg_value; + uint32_t clear_value; + + if (ep->is_in) { + reg_offset = usb_base_addr + OTG_DIEP_BASE + (ep->num * OTG_DIEP_SIZE); + clear_value = OTG_DIEPTSIZ_PKTCNT | OTG_DIEPTSIZ_XFRSIZ; + if (ep->xfer_len == 0U) { + reg_value = OTG_DIEPTSIZ_PKTCNT_1; + } else { + /* + * Program the transfer size and packet count + * as follows: + * xfersize = N * maxpacket + short_packet + * pktcnt = N + (short_packet exist ? 1 : 0) + */ + reg_value = (OTG_DIEPTSIZ_PKTCNT & + (((ep->xfer_len + ep->maxpacket - 1U) / + ep->maxpacket) << OTG_DIEPTSIZ_PKTCNT_SHIFT)) + | ep->xfer_len; + + if (ep->type == EP_TYPE_ISOC) { + clear_value |= OTG_DIEPTSIZ_MCNT_MASK; + reg_value |= OTG_DIEPTSIZ_MCNT_DATA0; + } + } + + mmio_clrsetbits_32(reg_offset + OTG_DIEPTSIZ, clear_value, reg_value); + + if ((ep->type != EP_TYPE_ISOC) && (ep->xfer_len > 0U)) { + /* Enable the TX FIFO empty interrupt for this EP */ + mmio_setbits_32(usb_base_addr + OTG_DIEPEMPMSK, BIT(ep->num)); + } + + /* EP enable, IN data in FIFO */ + reg_value = OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA; + + if (ep->type == EP_TYPE_ISOC) { + if ((mmio_read_32(usb_base_addr + OTG_DSTS) & OTG_DSTS_FNSOF0) == 0U) { + reg_value |= OTG_DIEPCTL_SODDFRM; + } else { + reg_value |= OTG_DIEPCTL_SD0PID_SEVNFRM; + } + } + + mmio_setbits_32(reg_offset + OTG_DIEPCTL, reg_value); + + if (ep->type == EP_TYPE_ISOC) { + usb_dwc2_write_packet(handle, ep->xfer_buff, ep->num, ep->xfer_len); + } + } else { + reg_offset = usb_base_addr + OTG_DOEP_BASE + (ep->num * OTG_DOEP_SIZE); + /* + * Program the transfer size and packet count as follows: + * pktcnt = N + * xfersize = N * maxpacket + */ + if (ep->xfer_len == 0U) { + reg_value = ep->maxpacket | OTG_DIEPTSIZ_PKTCNT_1; + } else { + uint16_t pktcnt = (ep->xfer_len + ep->maxpacket - 1U) / ep->maxpacket; + + reg_value = (pktcnt << OTG_DIEPTSIZ_PKTCNT_SHIFT) | + (ep->maxpacket * pktcnt); + } + + mmio_clrsetbits_32(reg_offset + OTG_DOEPTSIZ, + OTG_DOEPTSIZ_XFRSIZ & OTG_DOEPTSIZ_PKTCNT, + reg_value); + + /* EP enable */ + reg_value = OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA; + + if (ep->type == EP_TYPE_ISOC) { + if ((mmio_read_32(usb_base_addr + OTG_DSTS) & OTG_DSTS_FNSOF0) == 0U) { + reg_value |= OTG_DOEPCTL_SD1PID_SODDFRM; + } else { + reg_value |= OTG_DOEPCTL_SD0PID_SEVNFRM; + } + } + + mmio_setbits_32(reg_offset + OTG_DOEPCTL, reg_value); + } + + return USBD_OK; +} + +/* + * @brief Setup and start a transfer over the EP0. + * @param handle: Selected device. + * @param ep: Pointer to endpoint structure. + * @retval USB status. + */ +static usb_status_t usb_dwc2_ep0_start_xfer(void *handle, usbd_ep_t *ep) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t reg_offset; + uint32_t reg_value; + + if (ep->is_in) { + reg_offset = usb_base_addr + OTG_DIEP_BASE + + (ep->num * OTG_DIEP_SIZE); + + if (ep->xfer_len == 0U) { + reg_value = OTG_DIEPTSIZ_PKTCNT_1; + } else { + /* + * Program the transfer size and packet count + * as follows: + * xfersize = N * maxpacket + short_packet + * pktcnt = N + (short_packet exist ? 1 : 0) + */ + + if (ep->xfer_len > ep->maxpacket) { + ep->xfer_len = ep->maxpacket; + } + + reg_value = OTG_DIEPTSIZ_PKTCNT_1 | ep->xfer_len; + } + + mmio_clrsetbits_32(reg_offset + OTG_DIEPTSIZ, + OTG_DIEPTSIZ_XFRSIZ | OTG_DIEPTSIZ_PKTCNT, + reg_value); + + /* Enable the TX FIFO empty interrupt for this EP */ + if (ep->xfer_len > 0U) { + mmio_setbits_32(usb_base_addr + OTG_DIEPEMPMSK, + BIT(ep->num)); + } + + /* EP enable, IN data in FIFO */ + mmio_setbits_32(reg_offset + OTG_DIEPCTL, + OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA); + } else { + reg_offset = usb_base_addr + OTG_DOEP_BASE + + (ep->num * OTG_DOEP_SIZE); + + /* + * Program the transfer size and packet count as follows: + * pktcnt = N + * xfersize = N * maxpacket + */ + if (ep->xfer_len > 0U) { + ep->xfer_len = ep->maxpacket; + } + + reg_value = OTG_DIEPTSIZ_PKTCNT_1 | ep->maxpacket; + + mmio_clrsetbits_32(reg_offset + OTG_DIEPTSIZ, + OTG_DIEPTSIZ_XFRSIZ | OTG_DIEPTSIZ_PKTCNT, + reg_value); + + /* EP enable */ + mmio_setbits_32(reg_offset + OTG_DOEPCTL, + OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA); + } + + return USBD_OK; +} + +/* + * @brief Set a stall condition over an EP. + * @param handle: Selected device. + * @param ep: Pointer to endpoint structure. + * @retval USB status. + */ +static usb_status_t usb_dwc2_ep_set_stall(void *handle, usbd_ep_t *ep) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t reg_offset; + uint32_t reg_value; + + if (ep->is_in) { + reg_offset = usb_base_addr + OTG_DIEP_BASE + + (ep->num * OTG_DIEP_SIZE); + reg_value = mmio_read_32(reg_offset + OTG_DIEPCTL); + + if ((reg_value & OTG_DIEPCTL_EPENA) == 0U) { + reg_value &= ~OTG_DIEPCTL_EPDIS; + } + + reg_value |= OTG_DIEPCTL_STALL; + + mmio_write_32(reg_offset + OTG_DIEPCTL, reg_value); + } else { + reg_offset = usb_base_addr + OTG_DOEP_BASE + + (ep->num * OTG_DOEP_SIZE); + reg_value = mmio_read_32(reg_offset + OTG_DOEPCTL); + + if ((reg_value & OTG_DOEPCTL_EPENA) == 0U) { + reg_value &= ~OTG_DOEPCTL_EPDIS; + } + + reg_value |= OTG_DOEPCTL_STALL; + + mmio_write_32(reg_offset + OTG_DOEPCTL, reg_value); + } + + return USBD_OK; +} + +/* + * @brief Stop the USB device mode. + * @param handle: Selected device. + * @retval USB status. + */ +static usb_status_t usb_dwc2_stop_device(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t i; + + /* Disable Int */ + mmio_clrbits_32(usb_base_addr + OTG_GAHBCFG, OTG_GAHBCFG_GINT); + + /* Clear pending interrupts */ + for (i = 0U; i < EP_NB; i++) { + mmio_write_32(usb_base_addr + OTG_DIEP_BASE + (i * OTG_DIEP_SIZE) + OTG_DIEPINT, + OTG_DIEPINT_MASK); + mmio_write_32(usb_base_addr + OTG_DOEP_BASE + (i * OTG_DOEP_SIZE) + OTG_DOEPINT, + OTG_DOEPINT_MASK); + } + + mmio_write_32(usb_base_addr + OTG_DAINT, OTG_DAINT_IN_MASK | OTG_DAINT_OUT_MASK); + + /* Clear interrupt masks */ + mmio_write_32(usb_base_addr + OTG_DIEPMSK, 0U); + mmio_write_32(usb_base_addr + OTG_DOEPMSK, 0U); + mmio_write_32(usb_base_addr + OTG_DAINTMSK, 0U); + + /* Flush the FIFO */ + usb_dwc2_flush_rx_fifo(handle); + usb_dwc2_flush_tx_fifo(handle, EP_ALL); + + /* Disconnect the USB device by disabling the pull-up/pull-down. */ + mmio_setbits_32((uintptr_t)handle + OTG_DCTL, OTG_DCTL_SDIS); + + return USBD_OK; +} + +/* + * @brief Stop the USB device mode. + * @param handle: Selected device. + * @param address: New device address to be assigned. + * This parameter can be a value from 0 to 255. + * @retval USB status. + */ +static usb_status_t usb_dwc2_set_address(void *handle, uint8_t address) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + mmio_clrsetbits_32(usb_base_addr + OTG_DCFG, + OTG_DCFG_DAD, + address << OTG_DCFG_DAD_SHIFT); + + return USBD_OK; +} + +/* + * @brief Check FIFO for the next packet to be loaded. + * @param handle: Selected device. + * @param epnum : Endpoint number. + * @param xfer_len: Block length. + * @param xfer_count: Number of blocks. + * @param maxpacket: Max packet length. + * @param xfer_buff: Buffer pointer. + * @retval USB status. + */ +static usb_status_t usb_dwc2_write_empty_tx_fifo(void *handle, + uint32_t epnum, + uint32_t xfer_len, + uint32_t *xfer_count, + uint32_t maxpacket, + uint8_t **xfer_buff) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t reg_offset; + int32_t len; + uint32_t len32b; + usb_status_t ret; + + len = xfer_len - *xfer_count; + + if ((len > 0) && ((uint32_t)len > maxpacket)) { + len = maxpacket; + } + + len32b = (len + 3U) / 4U; + + reg_offset = usb_base_addr + OTG_DIEP_BASE + (epnum * OTG_DIEP_SIZE); + + while (((mmio_read_32(reg_offset + OTG_DTXFSTS) & + OTG_DTXFSTS_INEPTFSAV) > len32b) && + (*xfer_count < xfer_len) && (xfer_len != 0U)) { + /* Write the FIFO */ + len = xfer_len - *xfer_count; + + if ((len > 0) && ((uint32_t)len > maxpacket)) { + len = maxpacket; + } + + len32b = (len + 3U) / 4U; + + ret = usb_dwc2_write_packet(handle, *xfer_buff, epnum, len); + if (ret != USBD_OK) { + return ret; + } + + *xfer_buff += len; + *xfer_count += len; + } + + if (len <= 0) { + mmio_clrbits_32(usb_base_addr + OTG_DIEPEMPMSK, BIT(epnum)); + } + + return USBD_OK; +} + +/* + * @brief Handle PCD interrupt request. + * @param handle: PCD handle. + * @param param: Pointer to information updated by the IT handling. + * @retval Action to do after IT handling. + */ +static usb_action_t usb_dwc2_it_handler(void *handle, uint32_t *param) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + uint32_t ep_intr; + uint32_t epint; + uint32_t epnum; + uint32_t temp; + usb_status_t ret; + + if (usb_dwc2_get_mode(handle) != USB_OTG_MODE_DEVICE) { + return USB_NOTHING; + } + + /* Avoid spurious interrupt */ + if (usb_dwc2_read_int(handle) == 0U) { + return USB_NOTHING; + } + + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_MMIS) != 0U) { + /* Incorrect mode, acknowledge the interrupt */ + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_MMIS); + } + + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_OEPINT) != 0U) { + uint32_t reg_offset; + + /* Read in the device interrupt bits */ + ep_intr = usb_dwc2_all_out_ep_int(handle); + epnum = 0U; + while ((ep_intr & BIT(0)) != BIT(0)) { + epnum++; + ep_intr >>= 1; + } + + reg_offset = usb_base_addr + OTG_DOEP_BASE + (epnum * OTG_DOEP_SIZE) + OTG_DOEPINT; + + epint = usb_dwc2_out_ep_int(handle, epnum); + + if ((epint & OTG_DOEPINT_XFRC) == OTG_DOEPINT_XFRC) { + mmio_write_32(reg_offset, OTG_DOEPINT_XFRC); + *param = epnum; + + return USB_DATA_OUT; + } + + if ((epint & OTG_DOEPINT_STUP) == OTG_DOEPINT_STUP) { + /* Inform that a setup packet is available */ + mmio_write_32(reg_offset, OTG_DOEPINT_STUP); + + return USB_SETUP; + } + + if ((epint & OTG_DOEPINT_OTEPDIS) == OTG_DOEPINT_OTEPDIS) { + mmio_write_32(reg_offset, OTG_DOEPINT_OTEPDIS); + } + } + + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_IEPINT) != 0U) { + uint32_t reg_offset; + + /* Read in the device interrupt bits */ + ep_intr = usb_dwc2_all_in_ep_int(handle); + epnum = 0U; + while ((ep_intr & BIT(0)) != BIT(0)) { + epnum++; + ep_intr >>= 1; + } + + reg_offset = usb_base_addr + OTG_DIEP_BASE + (epnum * OTG_DIEP_SIZE) + OTG_DIEPINT; + + epint = usb_dwc2_in_ep_int(handle, epnum); + + if ((epint & OTG_DIEPINT_XFRC) == OTG_DIEPINT_XFRC) { + mmio_clrbits_32(usb_base_addr + OTG_DIEPEMPMSK, BIT(epnum)); + mmio_write_32(reg_offset, OTG_DIEPINT_XFRC); + *param = epnum; + + return USB_DATA_IN; + } + + if ((epint & OTG_DIEPINT_TOC) == OTG_DIEPINT_TOC) { + mmio_write_32(reg_offset, OTG_DIEPINT_TOC); + } + + if ((epint & OTG_DIEPINT_ITTXFE) == OTG_DIEPINT_ITTXFE) { + mmio_write_32(reg_offset, OTG_DIEPINT_ITTXFE); + } + + if ((epint & OTG_DIEPINT_INEPNE) == OTG_DIEPINT_INEPNE) { + mmio_write_32(reg_offset, OTG_DIEPINT_INEPNE); + } + + if ((epint & OTG_DIEPINT_EPDISD) == OTG_DIEPINT_EPDISD) { + mmio_write_32(reg_offset, OTG_DIEPINT_EPDISD); + } + + if ((epint & OTG_DIEPINT_TXFE) == OTG_DIEPINT_TXFE) { + *param = epnum; + + return USB_WRITE_EMPTY; + } + } + + /* Handle resume interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_WKUPINT) != 0U) { + INFO("handle USB : Resume\n"); + + /* Clear the remote wake-up signaling */ + mmio_clrbits_32(usb_base_addr + OTG_DCTL, OTG_DCTL_RWUSIG); + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_WKUPINT); + + return USB_RESUME; + } + + /* Handle suspend interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_USBSUSP) != 0U) { + INFO("handle USB : Suspend int\n"); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_USBSUSP); + + if ((mmio_read_32(usb_base_addr + OTG_DSTS) & + OTG_DSTS_SUSPSTS) == OTG_DSTS_SUSPSTS) { + return USB_SUSPEND; + } + } + + /* Handle LPM interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_LPMINT) != 0U) { + INFO("handle USB : LPM int enter in suspend\n"); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_LPMINT); + *param = (mmio_read_32(usb_base_addr + OTG_GLPMCFG) & + OTG_GLPMCFG_BESL) >> 2; + + return USB_LPM; + } + + /* Handle reset interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_USBRST) != 0U) { + INFO("handle USB : Reset\n"); + + mmio_clrbits_32(usb_base_addr + OTG_DCTL, OTG_DCTL_RWUSIG); + + usb_dwc2_flush_tx_fifo(handle, 0U); + + mmio_write_32(usb_base_addr + OTG_DAINT, OTG_DAINT_IN_MASK | OTG_DAINT_OUT_MASK); + mmio_setbits_32(usb_base_addr + OTG_DAINTMSK, OTG_DAINT_EP0_IN | OTG_DAINT_EP0_OUT); + + mmio_setbits_32(usb_base_addr + OTG_DOEPMSK, OTG_DOEPMSK_STUPM | + OTG_DOEPMSK_XFRCM | + OTG_DOEPMSK_EPDM); + mmio_setbits_32(usb_base_addr + OTG_DIEPMSK, OTG_DIEPMSK_TOM | + OTG_DIEPMSK_XFRCM | + OTG_DIEPMSK_EPDM); + + /* Set default address to 0 */ + mmio_clrbits_32(usb_base_addr + OTG_DCFG, OTG_DCFG_DAD); + + /* Setup EP0 to receive SETUP packets */ + ret = usb_dwc2_ep0_out_start(handle); + if (ret != USBD_OK) { + return ret; + } + + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_USBRST); + + return USB_RESET; + } + + /* Handle enumeration done interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_ENUMDNE) != 0U) { + ret = usb_dwc2_activate_setup(handle); + if (ret != USBD_OK) { + return ret; + } + + mmio_clrbits_32(usb_base_addr + OTG_GUSBCFG, OTG_GUSBCFG_TRDT); + + mmio_setbits_32(usb_base_addr + OTG_GUSBCFG, + (USBD_HS_TRDT_VALUE << OTG_GUSBCFG_TRDT_SHIFT) & OTG_GUSBCFG_TRDT); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_ENUMDNE); + + return USB_ENUM_DONE; + } + + /* Handle RXQLevel interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_RXFLVL) != 0U) { + mmio_clrbits_32(usb_base_addr + OTG_GINTMSK, + OTG_GINTSTS_RXFLVL); + + temp = mmio_read_32(usb_base_addr + OTG_GRXSTSP); + + *param = temp & OTG_GRXSTSP_EPNUM; + *param |= (temp & OTG_GRXSTSP_BCNT) << (USBD_OUT_COUNT_SHIFT - + OTG_GRXSTSP_BCNT_SHIFT); + + if (((temp & OTG_GRXSTSP_PKTSTS) >> OTG_GRXSTSP_PKTSTS_SHIFT) == STS_DATA_UPDT) { + if ((temp & OTG_GRXSTSP_BCNT) != 0U) { + mmio_setbits_32(usb_base_addr + OTG_GINTMSK, OTG_GINTSTS_RXFLVL); + + return USB_READ_DATA_PACKET; + } + } else if (((temp & OTG_GRXSTSP_PKTSTS) >> OTG_GRXSTSP_PKTSTS_SHIFT) == + STS_SETUP_UPDT) { + mmio_setbits_32(usb_base_addr + OTG_GINTMSK, OTG_GINTSTS_RXFLVL); + + return USB_READ_SETUP_PACKET; + } + + mmio_setbits_32(usb_base_addr + OTG_GINTMSK, OTG_GINTSTS_RXFLVL); + } + + /* Handle SOF interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_SOF) != 0U) { + INFO("handle USB : SOF\n"); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_SOF); + + return USB_SOF; + } + + /* Handle incomplete ISO IN interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_IISOIXFR) != 0U) { + INFO("handle USB : ISO IN\n"); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, + OTG_GINTSTS_IISOIXFR); + } + + /* Handle incomplete ISO OUT interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_IPXFR_INCOMPISOOUT) != + 0U) { + INFO("handle USB : ISO OUT\n"); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, + OTG_GINTSTS_IPXFR_INCOMPISOOUT); + } + + /* Handle connection event interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_SRQINT) != 0U) { + INFO("handle USB : Connect\n"); + + mmio_write_32(usb_base_addr + OTG_GINTSTS, OTG_GINTSTS_SRQINT); + } + + /* Handle disconnection event interrupt */ + if ((usb_dwc2_read_int(handle) & OTG_GINTSTS_OTGINT) != 0U) { + INFO("handle USB : Disconnect\n"); + + temp = mmio_read_32(usb_base_addr + OTG_GOTGINT); + + if ((temp & OTG_GOTGINT_SEDET) == OTG_GOTGINT_SEDET) { + return USB_DISCONNECT; + } + } + + return USB_NOTHING; +} + +/* + * @brief Start the usb device mode + * @param usb_core_handle: USB core driver handle. + * @retval None. + * return USB status. + */ +static usb_status_t usb_dwc2_start_device(void *handle) +{ + uintptr_t usb_base_addr = (uintptr_t)handle; + + mmio_clrbits_32(usb_base_addr + OTG_DCTL, OTG_DCTL_SDIS); + mmio_setbits_32(usb_base_addr + OTG_GAHBCFG, OTG_GAHBCFG_GINT); + + return USBD_OK; +} + +static const usb_driver_t usb_dwc2driver = { + .ep0_out_start = usb_dwc2_ep0_out_start, + .ep_start_xfer = usb_dwc2_ep_start_xfer, + .ep0_start_xfer = usb_dwc2_ep0_start_xfer, + .write_packet = usb_dwc2_write_packet, + .read_packet = usb_dwc2_read_packet, + .ep_set_stall = usb_dwc2_ep_set_stall, + .start_device = usb_dwc2_start_device, + .stop_device = usb_dwc2_stop_device, + .set_address = usb_dwc2_set_address, + .write_empty_tx_fifo = usb_dwc2_write_empty_tx_fifo, + .it_handler = usb_dwc2_it_handler +}; + +/* + * @brief Initialize USB DWC2 driver. + * @param usb_core_handle: USB core driver handle. + * @param pcd_handle: PCD handle. + * @param base_register: USB global register base address. + * @retval None. + */ +void usb_dwc2_init_driver(usb_handle_t *usb_core_handle, + pcd_handle_t *pcd_handle, + void *base_register) +{ + register_usb_driver(usb_core_handle, pcd_handle, &usb_dwc2driver, + base_register); +} diff --git a/fdts/stm32mp15-bl2.dtsi b/fdts/stm32mp15-bl2.dtsi new file mode 100644 index 0000000000..4e3573fb12 --- /dev/null +++ b/fdts/stm32mp15-bl2.dtsi @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2020 - All Rights Reserved + */ + +/ { +#if !(STM32MP_EMMC || STM32MP_SDMMC) + aliases { + /delete-property/ mmc0; + }; +#endif + + cpus { + /delete-node/ cpu@1; + }; + + /delete-node/ psci; + + soc { + /delete-node/ timer@40006000; + /delete-node/ timer@44006000; + /delete-node/ pwr_mcu@50001014; + /delete-node/ cryp@54001000; + /delete-node/ rng@54003000; + /delete-node/ spi@5c001000; + /delete-node/ rtc@5c004000; + /delete-node/ etzpc@5c007000; + /delete-node/ stgen@5c008000; + /delete-node/ i2c@5c009000; + /delete-node/ tamp@5c00a000; +#if !(STM32MP_EMMC || STM32MP_SDMMC) + /delete-node/ sdmmc@58005000; + /delete-node/ sdmmc@58007000; +#endif +#if !STM32MP_RAW_NAND + /delete-node/ memory-controller@58002000; +#endif +#if !(STM32MP_SPI_NAND || STM32MP_SPI_NOR) + /delete-node/ spi@58003000; +#endif +#if !STM32MP_USB_PROGRAMMER + /delete-node/ usb-otg@49000000; + /delete-node/ usbphyc@5a006000; +#endif + + pin-controller@50002000 { + /delete-node/ rtc-out2-rmp-pins-0; +#if !(STM32MP_EMMC || STM32MP_SDMMC) + /delete-node/ sdmmc1-b4-0; + /delete-node/ sdmmc1-dir-0; + /delete-node/ sdmmc2-b4-0; + /delete-node/ sdmmc2-b4-1; + /delete-node/ sdmmc2-d47-0; +#endif +#if !STM32MP_RAW_NAND + /delete-node/ fmc-0; +#endif +#if !(STM32MP_SPI_NAND || STM32MP_SPI_NOR) + /delete-node/ qspi-clk-0; + /delete-node/ qspi-bk1-0; + /delete-node/ qspi-bk2-0; +#endif +#if !STM32MP_USB_PROGRAMMER + /delete-node/ usbotg_hs-0; + /delete-node/ usbotg-fs-dp-dm-0; +#endif + }; + }; + +#if !STM32MP_USE_STM32IMAGE + /* + * UUID's here are UUID RFC 4122 compliant meaning fieds are stored in + * network order (big endian) + */ + + st-io_policies { + fip-handles { + compatible = "st,io-fip-handle"; + fw_cfg_uuid = <0x5807e16a 0x845947be 0x8ed5648e 0x8dddab0e>; + bl32_uuid = <0x05d0e189 0x53dc1347 0x8d2b500a 0x4b7a3e38>; + bl32_extra1_uuid = <0x0b70c29b 0x2a5a7840 0x9f650a56 0x82738288>; + bl32_extra2_uuid = <0x8ea87bb1 0xcfa23f4d 0x85fde7bb 0xa50220d9>; + bl33_uuid = <0xd6d0eea7 0xfcead54b 0x97829934 0xf234b6e4>; + hw_cfg_uuid = <0x08b8f1d9 0xc9cf9349 0xa9626fbc 0x6b7265cc>; + tos_fw_cfg_uuid = <0x26257c1a 0xdbc67f47 0x8d96c4c4 0xb0248021>; + nt_fw_cfg_uuid = <0x28da9815 0x93e87e44 0xac661aaf 0x801550f9>; +#if TRUSTED_BOARD_BOOT + t_key_cert_uuid = <0x827ee890 0xf860e411 0xa1b4777a 0x21b4f94c>; + t_boot_fw_cert_uuid = <0xd6e269ea 0x5d63e411 0x8d8c9fba 0xbe9956a5>; + tos_fw_key_cert_uuid = <0x9477d603 0xfb60e411 0x85ddb710 0x5b8cee04>; + nt_fw_key_cert_uuid = <0x8ad5832a 0xfb60e411 0x8aafdf30 0xbbc49859>; + tos_fw_content_cert_uuid = <0xa49f4411 0x5e63e411 0x87283f05 0x722af33d>; + nt_fw_content_cert_uuid = <0x8ec4c1f3 0x5d63e411 0xa7a987ee 0x40b23fa7>; +#endif + }; + }; + +#if TRUSTED_BOARD_BOOT + tb_fw-config { + compatible = "arm,tb_fw"; + + /* Disable authentication for development */ + disable_auth = <0x0>; + + /* + * The following two entries are placeholders for Mbed TLS + * heap information. + */ + mbedtls_heap_addr = <0x0 0x0>; + mbedtls_heap_size = <0x0>; + }; + +#include "cot_descriptors.dtsi" +#endif +#endif /* !STM32MP_USE_STM32IMAGE */ +}; diff --git a/fdts/stm32mp15-bl32.dtsi b/fdts/stm32mp15-bl32.dtsi new file mode 100644 index 0000000000..d237f0d275 --- /dev/null +++ b/fdts/stm32mp15-bl32.dtsi @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2020 - All Rights Reserved + */ + +/ { + aliases { + /delete-property/ mmc0; + }; + + cpus { + /delete-node/ cpu@1; + }; + + /delete-node/ psci; + + soc { + /delete-node/ usb-otg@49000000; + /delete-node/ hash@54002000; + /delete-node/ memory-controller@58002000; + /delete-node/ spi@58003000; + /delete-node/ sdmmc@58005000; + /delete-node/ sdmmc@58007000; + /delete-node/ spi@5c001000; + /delete-node/ stgen@5c008000; + /delete-node/ i2c@5c009000; + + pin-controller@50002000 { + /delete-node/ fmc-0; + /delete-node/ qspi-clk-0; + /delete-node/ qspi-bk1-0; + /delete-node/ qspi-bk2-0; + /delete-node/ sdmmc1-b4-0; + /delete-node/ sdmmc1-dir-0; + /delete-node/ sdmmc2-b4-0; + /delete-node/ sdmmc2-b4-1; + /delete-node/ sdmmc2-d47-0; + /delete-node/ usbotg_hs-0; + /delete-node/ usbotg-fs-dp-dm-0; + }; + }; +}; diff --git a/fdts/stm32mp15-ddr-1g-fw-config.dts b/fdts/stm32mp15-ddr-1g-fw-config.dts new file mode 100644 index 0000000000..c871463062 --- /dev/null +++ b/fdts/stm32mp15-ddr-1g-fw-config.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include + +#include +#include + +/dts-v1/; + +/ { + dtb-registry { + compatible = "fconf,dyn_cfg-dtb_registry"; + + hw-config { + load-address = <0x0 STM32MP_HW_CONFIG_BASE>; + max-size = ; + id = ; + }; + + nt_fw { + load-address = <0x0 STM32MP_BL33_BASE>; + max-size = ; + id = ; + }; + +#ifdef AARCH32_SP_OPTEE + tos_fw { + load-address = <0x0 0x2FFC0000>; + max-size = <0x0001F000>; + id = ; + }; +#else + tos_fw { + load-address = <0x0 STM32MP_BL32_BASE>; + max-size = ; + id = ; + }; + + tos_fw-config { + load-address = <0x0 STM32MP_BL32_DTB_BASE>; + max-size = ; + id = ; + }; +#endif + }; + + st-mem-firewall { + compatible = "st,mem-firewall"; +#ifdef AARCH32_SP_OPTEE + memory-ranges = < + 0xc0000000 0x3e000000 TZC_REGION_S_NONE TZC_REGION_NSEC_ALL_ACCESS_RDWR + 0xfe000000 0x01e00000 TZC_REGION_S_RDWR 0 + 0xffe00000 0x00200000 TZC_REGION_S_NONE + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID)>; +#else + memory-ranges = < + 0xc0000000 0x40000000 TZC_REGION_S_NONE TZC_REGION_NSEC_ALL_ACCESS_RDWR>; +#endif + }; +}; diff --git a/fdts/stm32mp15-ddr-512m-fw-config.dts b/fdts/stm32mp15-ddr-512m-fw-config.dts new file mode 100644 index 0000000000..3d0722181a --- /dev/null +++ b/fdts/stm32mp15-ddr-512m-fw-config.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include + +#include +#include + +/dts-v1/; + +/ { + dtb-registry { + compatible = "fconf,dyn_cfg-dtb_registry"; + + hw-config { + load-address = <0x0 STM32MP_HW_CONFIG_BASE>; + max-size = ; + id = ; + }; + + nt_fw { + load-address = <0x0 STM32MP_BL33_BASE>; + max-size = ; + id = ; + }; + +#ifdef AARCH32_SP_OPTEE + tos_fw { + load-address = <0x0 0x2FFC0000>; + max-size = <0x0001F000>; + id = ; + }; +#else + tos_fw { + load-address = <0x0 STM32MP_BL32_BASE>; + max-size = ; + id = ; + }; + + tos_fw-config { + load-address = <0x0 STM32MP_BL32_DTB_BASE>; + max-size = ; + id = ; + }; +#endif + }; + + st-mem-firewall { + compatible = "st,mem-firewall"; +#ifdef AARCH32_SP_OPTEE + memory-ranges = < + 0xc0000000 0x1e000000 TZC_REGION_S_NONE TZC_REGION_NSEC_ALL_ACCESS_RDWR + 0xde000000 0x01e00000 TZC_REGION_S_RDWR 0 + 0xdfe00000 0x00200000 TZC_REGION_S_NONE + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID)>; +#else + memory-ranges = < + 0xc0000000 0x20000000 TZC_REGION_S_NONE TZC_REGION_NSEC_ALL_ACCESS_RDWR>; +#endif + }; +}; diff --git a/fdts/stm32mp15-ddr.dtsi b/fdts/stm32mp15-ddr.dtsi index 4825691f90..734943c0ea 100644 --- a/fdts/stm32mp15-ddr.dtsi +++ b/fdts/stm32mp15-ddr.dtsi @@ -14,14 +14,18 @@ clocks = <&rcc AXIDCG>, <&rcc DDRC1>, +#if STM32MP_DDR_DUAL_AXI_PORT <&rcc DDRC2>, +#endif <&rcc DDRPHYC>, <&rcc DDRCAPB>, <&rcc DDRPHYCAPB>; clock-names = "axidcg", "ddrc1", +#if STM32MP_DDR_DUAL_AXI_PORT "ddrc2", +#endif "ddrphyc", "ddrcapb", "ddrphycapb"; @@ -97,12 +101,14 @@ DDR_PCFGQOS1_0 DDR_PCFGWQOS0_0 DDR_PCFGWQOS1_0 +#if STM32MP_DDR_DUAL_AXI_PORT DDR_PCFGR_1 DDR_PCFGW_1 DDR_PCFGQOS0_1 DDR_PCFGQOS1_1 DDR_PCFGWQOS0_1 DDR_PCFGWQOS1_1 +#endif >; st,phy-reg = < @@ -115,8 +121,10 @@ DDR_ZQ0CR1 DDR_DX0GCR DDR_DX1GCR +#if STM32MP_DDR_DUAL_AXI_PORT DDR_DX2GCR DDR_DX3GCR +#endif >; st,phy-timing = < @@ -132,6 +140,7 @@ DDR_MR3 >; +#ifdef DDR_PHY_CAL_SKIP st,phy-cal = < DDR_DX0DLLCR DDR_DX0DQTR @@ -139,13 +148,16 @@ DDR_DX1DLLCR DDR_DX1DQTR DDR_DX1DQSTR +#if STM32MP_DDR_DUAL_AXI_PORT DDR_DX2DLLCR DDR_DX2DQTR DDR_DX2DQSTR DDR_DX3DLLCR DDR_DX3DQTR DDR_DX3DQSTR +#endif >; +#endif status = "okay"; }; diff --git a/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi b/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi index c0fc1f772e..127053b86a 100644 --- a/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi +++ b/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi @@ -61,13 +61,13 @@ #define DDR_DBGCMD 0x00000000 #define DDR_POISONCFG 0x00000000 #define DDR_PCCFG 0x00000010 -#define DDR_PCFGR_0 0x00010000 +#define DDR_PCFGR_0 0x00000000 #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_PCFGR_1 0x00000000 #define DDR_PCFGW_1 0x00000000 #define DDR_PCFGQOS0_1 0x02100C03 #define DDR_PCFGQOS1_1 0x00800040 diff --git a/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi b/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi index fc226d2544..5ae861fee1 100644 --- a/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi +++ b/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi @@ -61,13 +61,13 @@ #define DDR_DBGCMD 0x00000000 #define DDR_POISONCFG 0x00000000 #define DDR_PCCFG 0x00000010 -#define DDR_PCFGR_0 0x00010000 +#define DDR_PCFGR_0 0x00000000 #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_PCFGR_1 0x00000000 #define DDR_PCFGW_1 0x00000000 #define DDR_PCFGQOS0_1 0x02100C03 #define DDR_PCFGQOS1_1 0x00800040 diff --git a/fdts/stm32mp15-pinctrl.dtsi b/fdts/stm32mp15-pinctrl.dtsi index d3d1744ec4..16c40cbff9 100644 --- a/fdts/stm32mp15-pinctrl.dtsi +++ b/fdts/stm32mp15-pinctrl.dtsi @@ -194,33 +194,89 @@ uart7_pins_a: uart7-0 { pins1 { - pinmux = ; /* UART4_TX */ + pinmux = ; /* UART7_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = , /* UART4_RX */ - , /* UART4_CTS */ - ; /* UART4_RTS */ + pinmux = , /* UART7_RX */ + , /* UART7_CTS */ + ; /* UART7_RTS */ bias-disable; }; }; uart7_pins_b: uart7-1 { pins1 { - pinmux = ; /* USART7_TX */ + pinmux = ; /* UART7_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; /* USART7_RX */ + pinmux = ; /* UART7_RX */ + bias-disable; + }; + }; + + uart7_pins_c: uart7-2 { + pins1 { + pinmux = ; /* UART7_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART7_RX */ + bias-pull-up; + }; + }; + + uart8_pins_a: uart8-0 { + pins1 { + pinmux = ; /* UART8_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART8_RX */ bias-disable; }; }; usart2_pins_a: usart2-0 { + pins1 { + pinmux = , /* USART2_TX */ + ; /* USART2_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART2_RX */ + ; /* USART2_CTS_NSS */ + bias-disable; + }; + }; + + usart2_pins_b: usart2-1 { + pins1 { + pinmux = , /* USART2_TX */ + ; /* USART2_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART2_RX */ + ; /* USART2_CTS_NSS */ + bias-disable; + }; + }; + + usart2_pins_c: usart2-2 { pins1 { pinmux = , /* USART2_TX */ ; /* USART2_RTS */ @@ -236,6 +292,19 @@ }; usart3_pins_a: usart3-0 { + pins1 { + pinmux = ; /* USART3_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* USART3_RX */ + bias-disable; + }; + }; + + usart3_pins_b: usart3-1 { pins1 { pinmux = , /* USART3_TX */ ; /* USART3_RTS */ @@ -250,7 +319,7 @@ }; }; - usart3_pins_b: usart3-1 { + usart3_pins_c: usart3-2 { pins1 { pinmux = , /* USART3_TX */ ; /* USART3_RTS */ @@ -261,11 +330,11 @@ pins2 { pinmux = , /* USART3_RX */ ; /* USART3_CTS_NSS */ - bias-disable; + bias-pull-up; }; }; - usbotg_hs_pins_a: usbotg_hs-0 { + usbotg_hs_pins_a: usbotg-hs-0 { pins { pinmux = ; /* OTG_ID */ }; diff --git a/fdts/stm32mp15-ssp-bl2.dtsi b/fdts/stm32mp15-ssp-bl2.dtsi new file mode 100644 index 0000000000..107f6e2166 --- /dev/null +++ b/fdts/stm32mp15-ssp-bl2.dtsi @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2020 - All Rights Reserved + */ + +/ { + cpus { + /delete-node/ cpu@1; + }; + +#if STM32MP_USB_PROGRAMMER + aliases { + /delete-property/ serial1; + }; +#endif + + /delete-node/ cpu0_opp_table; + + nvmem_layout@0 { + /delete-property/ nvmem-cells; + /delete-property/ nvmem-cell-names; + + nvmem-cells = <&cfg0_otp>, + <&part_number_otp>, + <&monotonic_otp>, + <&nand_otp>, + <&uid_otp>, + <&package_otp>, + <&hw2_otp>, + <&pkh_otp>, + <&cfg2_otp>, + <&ssp_otp>, + <&chip_otp>, + <&rma_otp>; + + nvmem-cell-names = "cfg0_otp", + "part_number_otp", + "monotonic_otp", + "nand_otp", + "uid_otp", + "package_otp", + "hw2_otp", + "pkh_otp", + "cfg2_otp", + "ssp_otp", + "chip_otp", + "rma_otp"; + }; + + /delete-node/ psci; + + soc { + efuse@5c005000 { + cfg2_otp: cfg2_otp@8 { + reg = <0x8 0x4>; + }; + + ssp_otp: ssp_otp@20 { + reg = <0x20 0x4>; + }; + + chip_otp: chip_otp@a0 { + reg = <0xa0 0x40>; + }; + + rma_otp: rma_otp@e0 { + reg = <0xe0 0x4>; + }; + }; + + /delete-node/ timer@40006000; + /delete-node/ timer@44006000; + /delete-node/ pwr_mcu@50001014; + /delete-node/ cryp@54001000; + /delete-node/ rng@54003000; + /delete-node/ memory-controller@58002000; + /delete-node/ spi@58003000; + /delete-node/ sdmmc@58005000; + /delete-node/ sdmmc@58007000; + /delete-node/ ddr@5a003000; + /delete-node/ spi@5c001000; + /delete-node/ rtc@5c004000; + /delete-node/ etzpc@5c007000; + /delete-node/ stgen@5c008000; + /delete-node/ i2c@5c009000; + /delete-node/ tamp@5c00a000; +#if STM32MP_USB_PROGRAMMER + /delete-node/ serial@4000e000; + /delete-node/ serial@4000f000; + /delete-node/ serial@40011000; + /delete-node/ serial@40018000; + /delete-node/ serial@40019000; + /delete-node/ serial@44003000; + /delete-node/ serial@5c000000; +#endif +#if STM32MP_UART_PROGRAMMER + /delete-node/ usb-otg@49000000; + /delete-node/ usbphyc@5a006000; +#endif + + pin-controller@50002000 { + /delete-node/ fmc-0; + /delete-node/ qspi-clk-0; + /delete-node/ qspi-bk1-0; + /delete-node/ qspi-bk2-0; + /delete-node/ rtc-out2-rmp-pins-0; + /delete-node/ sdmmc1-b4-0; + /delete-node/ sdmmc1-dir-0; + /delete-node/ sdmmc2-b4-0; + /delete-node/ sdmmc2-b4-1; + /delete-node/ sdmmc2-d47-0; +#if STM32MP_USB_PROGRAMMER + /delete-node/ uart7-0; + /delete-node/ uart7-1; + /delete-node/ usart2-0; + /delete-node/ usart3-0; + /delete-node/ usart3-1; +#endif +#if STM32MP_UART_PROGRAMMER + /delete-node/ usbotg_hs-0; + /delete-node/ usbotg-fs-dp-dm-0; +#endif + }; + }; +}; diff --git a/fdts/stm32mp151.dtsi b/fdts/stm32mp151.dtsi index 8f175a6492..714d94710f 100644 --- a/fdts/stm32mp151.dtsi +++ b/fdts/stm32mp151.dtsi @@ -19,9 +19,41 @@ compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0>; + clocks = <&rcc CK_MPU>; + clock-names = "cpu"; + operating-points-v2 = <&cpu0_opp_table>; + nvmem-cells = <&part_number_otp>; + nvmem-cell-names = "part_number"; }; }; + cpu0_opp_table: cpu0-opp-table { + compatible = "operating-points-v2"; + opp-shared; + }; + + nvmem_layout: nvmem_layout@0 { + compatible = "st,stm32-nvmem-layout"; + + nvmem-cells = <&cfg0_otp>, + <&part_number_otp>, + <&monotonic_otp>, + <&nand_otp>, + <&uid_otp>, + <&package_otp>, + <&hw2_otp>, + <&pkh_otp>; + + nvmem-cell-names = "cfg0_otp", + "part_number_otp", + "monotonic_otp", + "nand_otp", + "uid_otp", + "package_otp", + "hw2_otp", + "pkh_otp"; + }; + psci { compatible = "arm,psci-1.0"; method = "smc"; @@ -82,12 +114,13 @@ clocks = <&rcc TIM12_K>; clock-names = "int"; status = "disabled"; + secure-status = "disabled"; }; usart2: serial@4000e000 { compatible = "st,stm32h7-uart"; reg = <0x4000e000 0x400>; - interrupts-extended = <&exti 27 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; clocks = <&rcc USART2_K>; resets = <&rcc USART2_R>; status = "disabled"; @@ -96,7 +129,7 @@ usart3: serial@4000f000 { compatible = "st,stm32h7-uart"; reg = <0x4000f000 0x400>; - interrupts-extended = <&exti 28 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; clocks = <&rcc USART3_K>; resets = <&rcc USART3_R>; status = "disabled"; @@ -115,7 +148,7 @@ uart5: serial@40011000 { compatible = "st,stm32h7-uart"; reg = <0x40011000 0x400>; - interrupts-extended = <&exti 31 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; clocks = <&rcc UART5_K>; resets = <&rcc UART5_R>; status = "disabled"; @@ -124,7 +157,7 @@ uart7: serial@40018000 { compatible = "st,stm32h7-uart"; reg = <0x40018000 0x400>; - interrupts-extended = <&exti 32 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; clocks = <&rcc UART7_K>; resets = <&rcc UART7_R>; status = "disabled"; @@ -133,7 +166,7 @@ uart8: serial@40019000 { compatible = "st,stm32h7-uart"; reg = <0x40019000 0x400>; - interrupts-extended = <&exti 33 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; clocks = <&rcc UART8_K>; resets = <&rcc UART8_R>; status = "disabled"; @@ -142,7 +175,7 @@ usart6: serial@44003000 { compatible = "st,stm32h7-uart"; reg = <0x44003000 0x400>; - interrupts-extended = <&exti 29 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; clocks = <&rcc USART6_K>; resets = <&rcc USART6_R>; status = "disabled"; @@ -156,16 +189,17 @@ clocks = <&rcc TIM15_K>; clock-names = "int"; status = "disabled"; + secure-status = "disabled"; }; usbotg_hs: usb-otg@49000000 { - compatible = "st,stm32mp1-hsotg", "snps,dwc2"; + compatible = "st,stm32mp15-hsotg", "snps,dwc2"; reg = <0x49000000 0x10000>; clocks = <&rcc USBO_K>; clock-names = "otg"; resets = <&rcc USBO_R>; reset-names = "dwc2"; - interrupts-extended = <&exti 44 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; g-rx-fifo-size = <512>; g-np-tx-fifo-size = <32>; g-tx-fifo-size = <256 16 16 16 16 16 16 16>; @@ -175,7 +209,7 @@ }; rcc: rcc@50000000 { - compatible = "st,stm32mp1-rcc", "syscon"; + compatible = "st,stm32mp1-rcc-secure", "st,stm32mp1-rcc", "syscon"; reg = <0x50000000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -254,6 +288,7 @@ clocks = <&rcc HASH1>; resets = <&rcc HASH1_R>; status = "disabled"; + secure-status = "disabled"; }; rng1: rng@54003000 { @@ -262,6 +297,7 @@ clocks = <&rcc RNG1_K>; resets = <&rcc RNG1_R>; status = "disabled"; + secure-status = "disabled"; }; fmc: memory-controller@58002000 { @@ -301,6 +337,8 @@ interrupts = ; clocks = <&rcc QSPI_K>; resets = <&rcc QSPI_R>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; }; @@ -341,6 +379,7 @@ clocks = <&rcc IWDG2>, <&rcc CK_LSI>; clock-names = "pclk", "lsi"; status = "disabled"; + secure-status = "disabled"; }; usbphyc: usbphyc@5a006000 { @@ -373,6 +412,7 @@ clocks = <&rcc USART1_K>; resets = <&rcc USART1_R>; status = "disabled"; + secure-status = "disabled"; }; spi6: spi@5c001000 { @@ -384,6 +424,7 @@ clocks = <&rcc SPI6_K>; resets = <&rcc SPI6_R>; status = "disabled"; + secure-status = "disabled"; }; i2c4: i2c@5c002000 { @@ -399,6 +440,7 @@ st,syscfg-fmp = <&syscfg 0x4 0x8>; wakeup-source; status = "disabled"; + secure-status = "disabled"; }; iwdg1: watchdog@5c003000 { @@ -408,6 +450,7 @@ clocks = <&rcc IWDG1>, <&rcc CK_LSI>; clock-names = "pclk", "lsi"; status = "disabled"; + secure-status = "disabled"; }; rtc: rtc@5c004000 { @@ -417,19 +460,49 @@ clock-names = "pclk", "rtc_ck"; interrupts-extended = <&exti 19 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; + secure-status = "disabled"; }; - bsec: nvmem@5c005000 { + bsec: efuse@5c005000 { compatible = "st,stm32mp15-bsec"; reg = <0x5c005000 0x400>; #address-cells = <1>; #size-cells = <1>; + + cfg0_otp: cfg0_otp@0 { + reg = <0x0 0x1>; + }; + part_number_otp: part_number_otp@4 { + reg = <0x4 0x1>; + }; + monotonic_otp: monotonic_otp@10 { + reg = <0x10 0x4>; + }; + nand_otp: nand_otp@24 { + reg = <0x24 0x4>; + }; + uid_otp: uid_otp@34 { + reg = <0x34 0xc>; + }; + package_otp: package_otp@40 { + reg = <0x40 0x4>; + }; + hw2_otp: hw2_otp@48 { + reg = <0x48 0x4>; + }; ts_cal1: calib@5c { reg = <0x5c 0x2>; }; ts_cal2: calib@5e { reg = <0x5e 0x2>; }; + pkh_otp: pkh_otp@60 { + reg = <0x60 0x20>; + }; + ethernet_mac_address: mac@e4 { + reg = <0xe4 0x8>; + st,non-secure-otp; + }; }; etzpc: etzpc@5c007000 { @@ -458,6 +531,7 @@ st,syscfg-fmp = <&syscfg 0x4 0x20>; wakeup-source; status = "disabled"; + secure-status = "disabled"; }; tamp: tamp@5c00a000 { @@ -621,6 +695,7 @@ st,bank-name = "GPIOZ"; st,bank-ioport = <11>; status = "disabled"; + secure-status = "disabled"; }; }; }; diff --git a/fdts/stm32mp153.dtsi b/fdts/stm32mp153.dtsi index 0a0bb8dc19..617380a52f 100644 --- a/fdts/stm32mp153.dtsi +++ b/fdts/stm32mp153.dtsi @@ -14,6 +14,7 @@ reg = <1>; clocks = <&rcc CK_MPU>; clock-names = "cpu"; + operating-points-v2 = <&cpu0_opp_table>; }; }; }; diff --git a/fdts/stm32mp157a-avenger96-fw-config.dts b/fdts/stm32mp157a-avenger96-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157a-avenger96-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157a-avenger96.dts b/fdts/stm32mp157a-avenger96.dts index b967736e47..57c1b02d46 100644 --- a/fdts/stm32mp157a-avenger96.dts +++ b/fdts/stm32mp157a-avenger96.dts @@ -10,9 +10,11 @@ /dts-v1/; #include "stm32mp157.dtsi" +#include "stm32mp15xa.dtsi" #include "stm32mp15-pinctrl.dtsi" #include "stm32mp15xxac-pinctrl.dtsi" #include +#include #include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" / { @@ -35,6 +37,22 @@ }; }; +&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_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; + &i2c4 { pinctrl-names = "default"; pinctrl-0 = <&i2c4_pins_a>; @@ -273,10 +291,12 @@ &rng1 { status = "okay"; + secure-status = "okay"; }; &rtc { status = "okay"; + secure-status = "okay"; }; &sdmmc1 { diff --git a/fdts/stm32mp157a-dk1-fw-config.dts b/fdts/stm32mp157a-dk1-fw-config.dts new file mode 100644 index 0000000000..256d0db935 --- /dev/null +++ b/fdts/stm32mp157a-dk1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-512m-fw-config.dts" diff --git a/fdts/stm32mp157a-dk1.dts b/fdts/stm32mp157a-dk1.dts index a73bef8ee4..5d5c0a5f74 100644 --- a/fdts/stm32mp157a-dk1.dts +++ b/fdts/stm32mp157a-dk1.dts @@ -7,9 +7,11 @@ /dts-v1/; #include "stm32mp157.dtsi" +#include "stm32mp15xa.dtsi" #include "stm32mp15-pinctrl.dtsi" #include "stm32mp15xxac-pinctrl.dtsi" #include "stm32mp15xx-dkx.dtsi" +#include / { model = "STMicroelectronics STM32MP157A-DK1 Discovery Board"; @@ -25,3 +27,19 @@ stdout-path = "serial0:115200n8"; }; }; + +&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_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157a-ed1-fw-config.dts b/fdts/stm32mp157a-ed1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157a-ed1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157a-ed1.dts b/fdts/stm32mp157a-ed1.dts new file mode 100644 index 0000000000..1527b642a4 --- /dev/null +++ b/fdts/stm32mp157a-ed1.dts @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ +/dts-v1/; + +#include "stm32mp157.dtsi" +#include "stm32mp15xa.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxaa-pinctrl.dtsi" +#include "stm32mp15xx-edx.dtsi" +#include + +/ { + model = "STMicroelectronics STM32MP157A eval daughter"; + compatible = "st,stm32mp157a-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + +&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_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157a-ev1-fw-config.dts b/fdts/stm32mp157a-ev1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157a-ev1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157a-ev1.dts b/fdts/stm32mp157a-ev1.dts new file mode 100644 index 0000000000..3cb35698ad --- /dev/null +++ b/fdts/stm32mp157a-ev1.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ +/dts-v1/; + +#include "stm32mp157a-ed1.dts" +#include "stm32mp15xx-evx.dtsi" + +/ { + model = "STMicroelectronics STM32MP157A eval daughter on eval mother"; + compatible = "st,stm32mp157a-ev1", "st,stm32mp157a-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + serial0 = &uart4; + serial1 = &usart3; + }; +}; + diff --git a/fdts/stm32mp157c-dk2-fw-config.dts b/fdts/stm32mp157c-dk2-fw-config.dts new file mode 100644 index 0000000000..256d0db935 --- /dev/null +++ b/fdts/stm32mp157c-dk2-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-512m-fw-config.dts" diff --git a/fdts/stm32mp157c-dk2.dts b/fdts/stm32mp157c-dk2.dts index be8300e9e0..ff5c4509fe 100644 --- a/fdts/stm32mp157c-dk2.dts +++ b/fdts/stm32mp157c-dk2.dts @@ -11,6 +11,7 @@ #include "stm32mp15-pinctrl.dtsi" #include "stm32mp15xxac-pinctrl.dtsi" #include "stm32mp15xx-dkx.dtsi" +#include / { model = "STMicroelectronics STM32MP157C-DK2 Discovery Board"; @@ -31,3 +32,20 @@ &cryp1 { status = "okay"; }; + +&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) + DECPROT(STM32MP1_ETZPC_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157c-ed1-fw-config.dts b/fdts/stm32mp157c-ed1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157c-ed1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157c-ed1.dts b/fdts/stm32mp157c-ed1.dts index a6b98b7d93..8d80147518 100644 --- a/fdts/stm32mp157c-ed1.dts +++ b/fdts/stm32mp157c-ed1.dts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved - * Author: Ludovic Barre for STMicroelectronics. + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. */ /dts-v1/; @@ -9,8 +9,8 @@ #include "stm32mp15xc.dtsi" #include "stm32mp15-pinctrl.dtsi" #include "stm32mp15xxaa-pinctrl.dtsi" -#include -#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" +#include "stm32mp15xx-edx.dtsi" +#include / { model = "STMicroelectronics STM32MP157C eval daughter"; @@ -19,318 +19,25 @@ chosen { stdout-path = "serial0:115200n8"; }; - - - memory@c0000000 { - device_type = "memory"; - reg = <0xC0000000 0x40000000>; - }; - - aliases { - serial0 = &uart4; - }; -}; - -&bsec { - board_id: board_id@ec { - reg = <0xec 0x4>; - status = "okay"; - secure-status = "okay"; - }; -}; - -&clk_hse { - st,digbypass; -}; - -&cpu0 { - cpu-supply = <&vddcore>; -}; - -&cpu1 { - cpu-supply = <&vddcore>; }; &cryp1 { - status="okay"; -}; - -&hash1 { - status = "okay"; -}; - -&i2c4 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c4_pins_a>; - i2c-scl-rising-time-ns = <185>; - i2c-scl-falling-time-ns = <20>; - clock-frequency = <400000>; - 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"; - - regulators { - compatible = "st,stpmic1-regulators"; - ldo1-supply = <&v3v3>; - ldo2-supply = <&v3v3>; - ldo3-supply = <&vdd_ddr>; - ldo5-supply = <&v3v3>; - ldo6-supply = <&v3v3>; - pwr_sw1-supply = <&bst_out>; - pwr_sw2-supply = <&bst_out>; - - 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"; - }; - - 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; - }; - - bst_out: boost { - regulator-name = "bst_out"; - }; - - vbus_otg: pwr_sw1 { - regulator-name = "vbus_otg"; - }; - - vbus_sw: pwr_sw2 { - regulator-name = "vbus_sw"; - regulator-active-discharge = <1>; - }; - }; - - onkey { - compatible = "st,stpmic1-onkey"; - power-off-time-sec = <10>; - status = "okay"; - }; - - watchdog { - compatible = "st,stpmic1-wdt"; - status = "disabled"; - }; - }; -}; - -&iwdg2 { - timeout-sec = <32>; status = "okay"; }; -&pwr_regulators { - vdd-supply = <&vdd>; - vdd_3v3_usbfs-supply = <&vdd_usb>; -}; - -&rcc { - secure-status = "disabled"; - st,clksrc = < - CLK_MPU_PLL1P - CLK_AXI_PLL2P - CLK_MCU_PLL3P - CLK_PLL12_HSE - CLK_PLL3_HSE - CLK_PLL4_HSE - CLK_RTC_LSE - CLK_MCO1_DISABLED - CLK_MCO2_DISABLED - >; - - st,clkdiv = < - 1 /*MPU*/ - 0 /*AXI*/ - 0 /*MCU*/ - 1 /*APB1*/ - 1 /*APB2*/ - 1 /*APB3*/ - 1 /*APB4*/ - 2 /*APB5*/ - 23 /*RTC*/ - 0 /*MCO1*/ - 0 /*MCO2*/ - >; - - st,pkcs = < - CLK_CKPER_HSE - CLK_FMC_ACLK - CLK_QSPI_ACLK - CLK_ETH_DISABLED - CLK_SDMMC12_PLL4P - CLK_DSI_DSIPLL - CLK_STGEN_HSE - CLK_USBPHY_HSE - CLK_SPI2S1_PLL3Q - CLK_SPI2S23_PLL3Q - CLK_SPI45_HSI - CLK_SPI6_HSI - CLK_I2C46_HSI - CLK_SDMMC3_PLL4P - CLK_USBO_USBPHY - CLK_ADC_CKPER - CLK_CEC_LSE - CLK_I2C12_HSI - CLK_I2C35_HSI - CLK_UART1_HSI - CLK_UART24_HSI - CLK_UART35_HSI - CLK_UART6_HSI - CLK_UART78_HSI - CLK_SPDIF_PLL4P - CLK_FDCAN_PLL4R - CLK_SAI1_PLL3Q - CLK_SAI2_PLL3Q - CLK_SAI3_PLL3Q - CLK_SAI4_PLL3Q - CLK_RNG1_LSI - CLK_RNG2_LSI - CLK_LPTIM1_PCLK1 - CLK_LPTIM23_PCLK3 - CLK_LPTIM45_LSE +&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) + DECPROT(STM32MP1_ETZPC_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) >; - - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - cfg = < 2 80 0 0 0 PQR(1,0,0) >; - frac = < 0x800 >; - }; - - /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ - pll2: st,pll@1 { - cfg = < 2 65 1 0 0 PQR(1,1,1) >; - frac = < 0x1400 >; - }; - - /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ - pll3: st,pll@2 { - cfg = < 1 33 1 16 36 PQR(1,1,1) >; - frac = < 0x1a04 >; - }; - - /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ - pll4: st,pll@3 { - cfg = < 3 98 5 7 7 PQR(1,1,1) >; - }; -}; - -&rng1 { - status = "okay"; -}; - -&rtc { - status = "okay"; -}; - -&sdmmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>; - disable-wp; - 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; - 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 = <&vdd>; - mmc-ddr-3_3v; - status = "okay"; -}; - -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&uart4_pins_a>; - status = "okay"; }; diff --git a/fdts/stm32mp157c-ev1-fw-config.dts b/fdts/stm32mp157c-ev1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157c-ev1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157c-ev1.dts b/fdts/stm32mp157c-ev1.dts index c5d12e3b23..d12d30093c 100644 --- a/fdts/stm32mp157c-ev1.dts +++ b/fdts/stm32mp157c-ev1.dts @@ -1,11 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved - * Author: Ludovic Barre for STMicroelectronics. + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. */ /dts-v1/; #include "stm32mp157c-ed1.dts" +#include "stm32mp15xx-evx.dtsi" / { model = "STMicroelectronics STM32MP157C eval daughter on eval mother"; @@ -16,47 +17,7 @@ }; aliases { + serial0 = &uart4; serial1 = &usart3; }; }; - -&fmc { - pinctrl-names = "default"; - pinctrl-0 = <&fmc_pins_a>; - status = "okay"; - - nand-controller@4,0 { - status = "okay"; - - 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>; - 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>; - }; -}; - -&usart3 { - pinctrl-names = "default"; - pinctrl-0 = <&usart3_pins_a>; - status = "disabled"; -}; diff --git a/fdts/stm32mp157d-dk1-fw-config.dts b/fdts/stm32mp157d-dk1-fw-config.dts new file mode 100644 index 0000000000..256d0db935 --- /dev/null +++ b/fdts/stm32mp157d-dk1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-512m-fw-config.dts" diff --git a/fdts/stm32mp157d-dk1.dts b/fdts/stm32mp157d-dk1.dts new file mode 100644 index 0000000000..79297b831f --- /dev/null +++ b/fdts/stm32mp157d-dk1.dts @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +/dts-v1/; + +#include "stm32mp157.dtsi" +#include "stm32mp15xd.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxac-pinctrl.dtsi" +#include "stm32mp15xx-dkx.dtsi" +#include + +/ { + model = "STMicroelectronics STM32MP157D-DK1 Discovery Board"; + compatible = "st,stm32mp157d-dk1", "st,stm32mp157"; + + aliases { + serial0 = &uart4; + serial1 = &usart3; + serial2 = &uart7; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + +&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_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157d-ed1-fw-config.dts b/fdts/stm32mp157d-ed1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157d-ed1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157d-ed1.dts b/fdts/stm32mp157d-ed1.dts new file mode 100644 index 0000000000..2c67ec0acb --- /dev/null +++ b/fdts/stm32mp157d-ed1.dts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ +/dts-v1/; + +#include "stm32mp157.dtsi" +#include "stm32mp15xd.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxaa-pinctrl.dtsi" +#include "stm32mp15xx-edx.dtsi" +#include + +/ { + model = "STMicroelectronics STM32MP157D eval daughter"; + compatible = "st,stm32mp157d-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + + +&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_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157d-ev1-fw-config.dts b/fdts/stm32mp157d-ev1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157d-ev1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157d-ev1.dts b/fdts/stm32mp157d-ev1.dts new file mode 100644 index 0000000000..4a40f5fe51 --- /dev/null +++ b/fdts/stm32mp157d-ev1.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ +/dts-v1/; + +#include "stm32mp157d-ed1.dts" +#include "stm32mp15xx-evx.dtsi" + +/ { + model = "STMicroelectronics STM32MP157D eval daughter on eval mother"; + compatible = "st,stm32mp157d-ev1", "st,stm32mp157d-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + serial0 = &uart4; + serial1 = &usart3; + }; +}; diff --git a/fdts/stm32mp157f-dk2-fw-config.dts b/fdts/stm32mp157f-dk2-fw-config.dts new file mode 100644 index 0000000000..256d0db935 --- /dev/null +++ b/fdts/stm32mp157f-dk2-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-512m-fw-config.dts" diff --git a/fdts/stm32mp157f-dk2.dts b/fdts/stm32mp157f-dk2.dts new file mode 100644 index 0000000000..680ca0f6e7 --- /dev/null +++ b/fdts/stm32mp157f-dk2.dts @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +/dts-v1/; + +#include "stm32mp157.dtsi" +#include "stm32mp15xf.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxac-pinctrl.dtsi" +#include "stm32mp15xx-dkx.dtsi" +#include + +/ { + model = "STMicroelectronics STM32MP157F-DK2 Discovery Board"; + compatible = "st,stm32mp157f-dk2", "st,stm32mp157"; + + aliases { + serial0 = &uart4; + serial1 = &usart3; + serial2 = &uart7; + serial3 = &usart2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + +&cryp1 { + status = "okay"; +}; + +&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) + DECPROT(STM32MP1_ETZPC_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157f-ed1-fw-config.dts b/fdts/stm32mp157f-ed1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157f-ed1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157f-ed1.dts b/fdts/stm32mp157f-ed1.dts new file mode 100644 index 0000000000..1aa26cdbbe --- /dev/null +++ b/fdts/stm32mp157f-ed1.dts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ +/dts-v1/; + +#include "stm32mp157.dtsi" +#include "stm32mp15xf.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxaa-pinctrl.dtsi" +#include "stm32mp15xx-edx.dtsi" +#include + +/ { + model = "STMicroelectronics STM32MP157F eval daughter"; + compatible = "st,stm32mp157f-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + +&cryp1 { + status = "okay"; +}; + +&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) + DECPROT(STM32MP1_ETZPC_DDRCTRL_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_NS_R_S_W, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_STGENC_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_BKPSRAM_ID, DECPROT_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_IWDG1_ID, DECPROT_S_RW, DECPROT_LOCK) + >; +}; diff --git a/fdts/stm32mp157f-ev1-fw-config.dts b/fdts/stm32mp157f-ev1-fw-config.dts new file mode 100644 index 0000000000..10f9402c4a --- /dev/null +++ b/fdts/stm32mp157f-ev1-fw-config.dts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + */ + +#include "stm32mp15-ddr-1g-fw-config.dts" diff --git a/fdts/stm32mp157f-ev1.dts b/fdts/stm32mp157f-ev1.dts new file mode 100644 index 0000000000..caf5dfe11a --- /dev/null +++ b/fdts/stm32mp157f-ev1.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ +/dts-v1/; + +#include "stm32mp157f-ed1.dts" +#include "stm32mp15xx-evx.dtsi" + +/ { + model = "STMicroelectronics STM32MP157F eval daughter on eval mother"; + compatible = "st,stm32mp157f-ev1", "st,stm32mp157f-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + serial0 = &uart4; + serial1 = &usart3; + }; +}; diff --git a/fdts/stm32mp15xa.dtsi b/fdts/stm32mp15xa.dtsi new file mode 100644 index 0000000000..5ed7e594f4 --- /dev/null +++ b/fdts/stm32mp15xa.dtsi @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +&cpu0_opp_table { + opp-650000000 { + opp-hz = /bits/ 64 <650000000>; + opp-microvolt = <1200000>; + opp-supported-hw = <0x1>; + }; +}; diff --git a/fdts/stm32mp15xc.dtsi b/fdts/stm32mp15xc.dtsi index b06a55a2fa..68d822d8c1 100644 --- a/fdts/stm32mp15xc.dtsi +++ b/fdts/stm32mp15xc.dtsi @@ -4,6 +4,8 @@ * Author: Alexandre Torgue for STMicroelectronics. */ +#include "stm32mp15xa.dtsi" + / { soc { cryp1: cryp@54001000 { @@ -13,6 +15,7 @@ clocks = <&rcc CRYP1>; resets = <&rcc CRYP1_R>; status = "disabled"; + secure-status = "disabled"; }; }; }; diff --git a/fdts/stm32mp15xd.dtsi b/fdts/stm32mp15xd.dtsi new file mode 100644 index 0000000000..18b05ee380 --- /dev/null +++ b/fdts/stm32mp15xd.dtsi @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +&cpu0_opp_table { + opp-800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <1350000>; + opp-supported-hw = <0x2>; + }; + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + opp-microvolt = <1200000>; + opp-supported-hw = <0x2>; + opp-suspend; + }; +}; diff --git a/fdts/stm32mp15xf.dtsi b/fdts/stm32mp15xf.dtsi new file mode 100644 index 0000000000..526a1627cf --- /dev/null +++ b/fdts/stm32mp15xf.dtsi @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include "stm32mp15xd.dtsi" + +/ { + soc { + cryp1: cryp@54001000 { + compatible = "st,stm32mp1-cryp"; + reg = <0x54001000 0x400>; + interrupts = ; + clocks = <&rcc CRYP1>; + resets = <&rcc CRYP1_R>; + status = "disabled"; + secure-status = "disabled"; + }; + }; +}; diff --git a/fdts/stm32mp15xx-dkx.dtsi b/fdts/stm32mp15xx-dkx.dtsi index 52b914b84e..e5166706ee 100644 --- a/fdts/stm32mp15xx-dkx.dtsi +++ b/fdts/stm32mp15xx-dkx.dtsi @@ -5,6 +5,7 @@ */ #include +#include #include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi" / { @@ -52,6 +53,7 @@ i2c-scl-falling-time-ns = <20>; clock-frequency = <400000>; status = "okay"; + secure-status = "okay"; pmic: stpmic@33 { compatible = "st,stpmic1"; @@ -60,6 +62,7 @@ interrupt-controller; #interrupt-cells = <2>; status = "okay"; + secure-status = "okay"; regulators { compatible = "st,stpmic1-regulators"; @@ -139,9 +142,6 @@ vdd_usb: ldo4 { regulator-name = "vdd_usb"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; }; vdda: ldo5 { @@ -182,17 +182,48 @@ &iwdg2 { timeout-sec = <32>; + secure-timeout-sec = <5>; status = "okay"; secure-status = "okay"; }; +&nvmem_layout { + nvmem-cells = <&cfg0_otp>, + <&part_number_otp>, + <&monotonic_otp>, + <&nand_otp>, + <&uid_otp>, + <&package_otp>, + <&hw2_otp>, + <&pkh_otp>, + <&board_id>; + + nvmem-cell-names = "cfg0_otp", + "part_number_otp", + "monotonic_otp", + "nand_otp", + "uid_otp", + "package_otp", + "hw2_otp", + "pkh_otp", + "board_id"; +}; + &pwr_regulators { + 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 = ; vdd-supply = <&vdd>; vdd_3v3_usbfs-supply = <&vdd_usb>; }; &rcc { - secure-status = "disabled"; + st,hsi-cal; + st,csi-cal; + st,cal-sec = <60>; st,clksrc = < CLK_MPU_PLL1P CLK_AXI_PLL2P @@ -257,14 +288,6 @@ CLK_LPTIM45_LSE >; - /* VCO = 1300.0 MHz => P = 650 (CPU) */ - pll1: st,pll@0 { - compatible = "st,stm32mp1-pll"; - reg = <0>; - cfg = < 2 80 0 0 0 PQR(1,0,0) >; - frac = < 0x800 >; - }; - /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ pll2: st,pll@1 { compatible = "st,stm32mp1-pll"; @@ -291,10 +314,12 @@ &rng1 { status = "okay"; + secure-status = "okay"; }; &rtc { status = "okay"; + secure-status = "okay"; }; &sdmmc1 { @@ -309,6 +334,8 @@ &timers15 { secure-status = "okay"; + st,hsi-cal-input = <7>; + st,csi-cal-input = <8>; }; &uart4 { @@ -319,13 +346,13 @@ &uart7 { pinctrl-names = "default"; - pinctrl-0 = <&uart7_pins_b>; + pinctrl-0 = <&uart7_pins_c>; status = "disabled"; }; &usart3 { pinctrl-names = "default"; - pinctrl-0 = <&usart3_pins_b>; + pinctrl-0 = <&usart3_pins_c>; uart-has-rtscts; status = "disabled"; }; @@ -348,3 +375,128 @@ &usbphyc_port1 { phy-supply = <&vdd_usb>; }; + +/* Low-power states of regulators */ +&v1v2_hdmi { + 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 { + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + 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; + }; +}; + +&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; + }; +}; + +&vdda { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&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_usb { + 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; + }; +}; + +&vtt_ddr { + lp-stop { + regulator-off-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; diff --git a/fdts/stm32mp15xx-edx.dtsi b/fdts/stm32mp15xx-edx.dtsi new file mode 100644 index 0000000000..540f5b9d97 --- /dev/null +++ b/fdts/stm32mp15xx-edx.dtsi @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ + +#include +#include +#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" + +/ { + memory@c0000000 { + device_type = "memory"; + reg = <0xC0000000 0x40000000>; + }; + + aliases { + serial0 = &uart4; + }; + + vin: vin { + compatible = "regulator-fixed"; + regulator-name = "vin"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; +}; + +&bsec { + board_id: board_id@ec { + reg = <0xec 0x4>; + st,non-secure-otp; + }; +}; + +&clk_hse { + st,digbypass; +}; + +&cpu0{ + cpu-supply = <&vddcore>; +}; + +&cpu1{ + cpu-supply = <&vddcore>; +}; + +&hash1 { + status = "okay"; +}; + +&i2c4 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c4_pins_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + clock-frequency = <400000>; + status = "okay"; + secure-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"; + secure-status = "okay"; + + regulators { + compatible = "st,stpmic1-regulators"; + buck1-supply = <&vin>; + buck2-supply = <&vin>; + buck3-supply = <&vin>; + buck4-supply = <&vin>; + ldo1-supply = <&v3v3>; + ldo2-supply = <&v3v3>; + ldo3-supply = <&vdd_ddr>; + ldo4-supply = <&vin>; + ldo5-supply = <&v3v3>; + ldo6-supply = <&v3v3>; + vref_ddr-supply = <&vin>; + boost-supply = <&vin>; + pwr_sw1-supply = <&bst_out>; + pwr_sw2-supply = <&bst_out>; + + 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"; + }; + + 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; + }; + + bst_out: boost { + regulator-name = "bst_out"; + }; + + vbus_otg: pwr_sw1 { + regulator-name = "vbus_otg"; + }; + + vbus_sw: pwr_sw2 { + regulator-name = "vbus_sw"; + regulator-active-discharge = <1>; + }; + }; + }; +}; + +&iwdg2 { + timeout-sec = <32>; + secure-timeout-sec = <5>; + status = "okay"; + secure-status = "okay"; +}; + +&nvmem_layout { + nvmem-cells = <&cfg0_otp>, + <&part_number_otp>, + <&monotonic_otp>, + <&nand_otp>, + <&uid_otp>, + <&package_otp>, + <&hw2_otp>, + <&pkh_otp>, + <&board_id>; + + nvmem-cell-names = "cfg0_otp", + "part_number_otp", + "monotonic_otp", + "nand_otp", + "uid_otp", + "package_otp", + "hw2_otp", + "pkh_otp", + "board_id"; +}; + +&pwr_regulators { + system_suspend_supported_soc_modes = < + STM32_PM_CSLEEP_RUN + STM32_PM_CSTOP_ALLOW_LP_STOP + STM32_PM_CSTOP_ALLOW_LPLV_STOP + STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR + >; + system_off_soc_mode = ; + vdd-supply = <&vdd>; + vdd_3v3_usbfs-supply = <&vdd_usb>; +}; + +&rcc { + st,hsi-cal; + st,csi-cal; + st,cal-sec = <60>; + st,clksrc = < + CLK_MPU_PLL1P + CLK_AXI_PLL2P + CLK_MCU_PLL3P + CLK_PLL12_HSE + CLK_PLL3_HSE + CLK_PLL4_HSE + CLK_RTC_LSE + CLK_MCO1_DISABLED + CLK_MCO2_DISABLED + >; + + st,clkdiv = < + 1 /*MPU*/ + 0 /*AXI*/ + 0 /*MCU*/ + 1 /*APB1*/ + 1 /*APB2*/ + 1 /*APB3*/ + 1 /*APB4*/ + 2 /*APB5*/ + 23 /*RTC*/ + 0 /*MCO1*/ + 0 /*MCO2*/ + >; + + st,pkcs = < + CLK_CKPER_HSE + CLK_FMC_ACLK + CLK_QSPI_ACLK + CLK_ETH_DISABLED + CLK_SDMMC12_PLL4P + CLK_DSI_DSIPLL + CLK_STGEN_HSE + CLK_USBPHY_HSE + CLK_SPI2S1_PLL3Q + CLK_SPI2S23_PLL3Q + CLK_SPI45_HSI + CLK_SPI6_HSI + CLK_I2C46_HSI + CLK_SDMMC3_PLL4P + CLK_USBO_USBPHY + CLK_ADC_CKPER + CLK_CEC_LSE + CLK_I2C12_HSI + CLK_I2C35_HSI + CLK_UART1_HSI + CLK_UART24_HSI + CLK_UART35_HSI + CLK_UART6_HSI + CLK_UART78_HSI + CLK_SPDIF_PLL4P + CLK_FDCAN_PLL4R + CLK_SAI1_PLL3Q + CLK_SAI2_PLL3Q + CLK_SAI3_PLL3Q + CLK_SAI4_PLL3Q + CLK_RNG1_LSI + CLK_RNG2_LSI + CLK_LPTIM1_PCLK1 + CLK_LPTIM23_PCLK3 + CLK_LPTIM45_LSE + >; + + /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU), R = 533 (DDR) */ + pll2: st,pll@1 { + compatible = "st,stm32mp1-pll"; + reg = <1>; + cfg = <2 65 1 0 0 PQR(1,1,1)>; + frac = <0x1400>; + }; + + /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */ + pll3: st,pll@2 { + compatible = "st,stm32mp1-pll"; + reg = <2>; + cfg = <1 33 1 16 36 PQR(1,1,1)>; + frac = <0x1a04>; + }; + + /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */ + pll4: st,pll@3 { + compatible = "st,stm32mp1-pll"; + reg = <3>; + cfg = <3 98 5 7 7 PQR(1,1,1)>; + }; +}; + +&rng1 { + status = "okay"; + secure-status = "okay"; +}; + +&rtc { + status = "okay"; + secure-status = "okay"; +}; + +&sdmmc1 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>; + disable-wp; + 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; + 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 = <&vdd>; + mmc-ddr-3_3v; + status = "okay"; +}; + +&timers15 { + secure-status = "okay"; + st,hsi-cal-input = <7>; + st,csi-cal-input = <8>; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins_a>; + status = "okay"; +}; + +&usbotg_hs { + vbus-supply = <&vbus_otg>; +}; + +&usbphyc_port0 { + phy-supply = <&vdd_usb>; +}; + +&usbphyc_port1 { + phy-supply = <&vdd_usb>; +}; + +/* Low-power states of regulators */ +&v1v8 { + 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; + }; +}; + +&v3v3 { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vdd { + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + lplv-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; + }; +}; + +&vdda { + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vddcore { + lp-stop { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1200000>; + }; + lplv-stop { + regulator-on-in-suspend; + regulator-suspend-microvolt = <900000>; + }; + 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; + }; + lplv-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_sd { + 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; + }; +}; + +&vref_ddr { + lp-stop { + regulator-on-in-suspend; + }; + lplv-stop { + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; + +&vtt_ddr { + lp-stop { + regulator-off-in-suspend; + }; + lplv-stop { + regulator-off-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; +}; diff --git a/fdts/stm32mp15xx-evx.dtsi b/fdts/stm32mp15xx-evx.dtsi new file mode 100644 index 0000000000..ce1982d45a --- /dev/null +++ b/fdts/stm32mp15xx-evx.dtsi @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ + +&fmc { + pinctrl-names = "default"; + pinctrl-0 = <&fmc_pins_a>; + status = "okay"; + + nand-controller@4,0 { + status = "okay"; + + nand@0 { + reg = <0>; + nand-on-flash-bbt; + #address-cells = <1>; + #size-cells = <1>; + }; + }; +}; + +&i2c4 { + pmic: stpmic@33 { + regulators { + v1v8: ldo6 { + regulator-enable-ramp-delay = <300000>; + }; + }; + }; +}; + +&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>; + }; +}; + +&timers12 { + status = "disabled"; +}; + +&usart3 { + pinctrl-names = "default"; + pinctrl-0 = <&usart3_pins_b>; + uart-has-rtscts; + status = "disabled"; +}; + +&usbotg_hs { + pinctrl-0 = <&usbotg_hs_pins_a>; + pinctrl-names = "default"; + phys = <&usbphyc_port1 0>; + phy-names = "usb2-phy"; + status = "okay"; +}; + +&usbphyc { + status = "okay"; +}; diff --git a/fdts/stm32mp15xxaa-pinctrl.dtsi b/fdts/stm32mp15xxaa-pinctrl.dtsi index 64e566bf82..4f4130effd 100644 --- a/fdts/stm32mp15xxaa-pinctrl.dtsi +++ b/fdts/stm32mp15xxaa-pinctrl.dtsi @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) STMicroelectronics 2019 - All Rights Reserved - * Author: Alexandre Torgue + * Author: Alexandre Torgue for STMicroelectronics. */ &pinctrl { @@ -79,6 +79,7 @@ gpioz: gpio@54004000 { status = "okay"; + secure-status = "okay"; ngpios = <8>; gpio-ranges = <&pinctrl_z 0 400 8>; }; diff --git a/fdts/stm32mp15xxab-pinctrl.dtsi b/fdts/stm32mp15xxab-pinctrl.dtsi index d29af8986f..328dad140e 100644 --- a/fdts/stm32mp15xxab-pinctrl.dtsi +++ b/fdts/stm32mp15xxab-pinctrl.dtsi @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) STMicroelectronics 2019 - All Rights Reserved - * Author: Alexandre Torgue + * Author: Alexandre Torgue for STMicroelectronics. */ &pinctrl { diff --git a/fdts/stm32mp15xxac-pinctrl.dtsi b/fdts/stm32mp15xxac-pinctrl.dtsi index 5d8199fd19..866c050daf 100644 --- a/fdts/stm32mp15xxac-pinctrl.dtsi +++ b/fdts/stm32mp15xxac-pinctrl.dtsi @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) STMicroelectronics 2019 - All Rights Reserved - * Author: Alexandre Torgue + * Author: Alexandre Torgue for STMicroelectronics. */ &pinctrl { @@ -67,6 +67,7 @@ gpioz: gpio@54004000 { status = "okay"; + secure-status = "okay"; ngpios = <8>; gpio-ranges = <&pinctrl_z 0 400 8>; }; diff --git a/fdts/stm32mp15xxad-pinctrl.dtsi b/fdts/stm32mp15xxad-pinctrl.dtsi index 023f5404c4..b63e207de2 100644 --- a/fdts/stm32mp15xxad-pinctrl.dtsi +++ b/fdts/stm32mp15xxad-pinctrl.dtsi @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) STMicroelectronics 2019 - All Rights Reserved - * Author: Alexandre Torgue + * Author: Alexandre Torgue for STMicroelectronics. */ &pinctrl { diff --git a/include/arch/aarch32/arch.h b/include/arch/aarch32/arch.h index db8938ff1f..9cf8c8ee80 100644 --- a/include/arch/aarch32/arch.h +++ b/include/arch/aarch32/arch.h @@ -461,13 +461,13 @@ * system level implementation of the Generic Timer. ******************************************************************************/ /* Physical Count register. */ -#define CNTPCT_LO U(0x0) +#define CNTBASEN_CNTPCT_LO U(0x0) /* Counter Frequency register. */ #define CNTBASEN_CNTFRQ U(0x10) /* Physical Timer CompareValue register. */ -#define CNTP_CVAL_LO U(0x20) +#define CNTBASEN_CNTP_CVAL_LO U(0x20) /* Physical Timer Control register. */ -#define CNTP_CTL U(0x2c) +#define CNTBASEN_CNTP_CTL U(0x2c) /* Physical timer control register bit fields shifts and masks */ #define CNTP_CTL_ENABLE_SHIFT 0 @@ -526,6 +526,9 @@ #define HSTR p15, 4, c1, c1, 3 #define CNTHCTL p15, 4, c14, c1, 0 #define CNTKCTL p15, 0, c14, c1, 0 +#define CNTP_TVAL p15, 0, c14, c2, 0 +#define CNTP_CTL p15, 0, c14, c2, 1 +#define CNTV_CTL p15, 0, c14, c3, 1 #define VPIDR p15, 4, c0, c0, 0 #define VMPIDR p15, 4, c0, c0, 5 #define ISR p15, 0, c12, c1, 0 @@ -535,6 +538,7 @@ #define HTCR p15, 4, c2, c0, 2 #define HMAIR0 p15, 4, c10, c2, 0 #define ATS1CPR p15, 0, c7, c8, 0 +#define ATS1CPW p15, 0, c7, c8, 1 #define ATS1HR p15, 4, c7, c8, 0 #define DBGOSDLR p14, 0, c1, c3, 4 @@ -585,6 +589,12 @@ #define ICC_ASGI1R_EL1_64 p15, 1, c12 #define ICC_SGI0R_EL1_64 p15, 2, c12 +/* Fault registers. The format is: coproc, opt1, CRn, CRm, opt2 */ +#define DFSR p15, 0, c5, c0, 0 +#define IFSR p15, 0, c5, c0, 1 +#define DFAR p15, 0, c6, c0, 0 +#define IFAR p15, 0, c6, c0, 2 + /******************************************************************************* * Definitions of MAIR encodings for device and normal memory ******************************************************************************/ @@ -638,6 +648,8 @@ /* PAR fields */ #define PAR_F_SHIFT U(0) #define PAR_F_MASK ULL(0x1) +#define PAR_NS_SHIFT U(9) +#define PAR_NS_MASK U(0x1) #define PAR_ADDR_SHIFT U(12) #define PAR_ADDR_MASK (BIT_64(40) - ULL(1)) /* 40-bits-wide page address */ diff --git a/include/arch/aarch32/arch_helpers.h b/include/arch/aarch32/arch_helpers.h index 82efb188a4..9998034396 100644 --- a/include/arch/aarch32/arch_helpers.h +++ b/include/arch/aarch32/arch_helpers.h @@ -248,6 +248,9 @@ DEFINE_COPROCR_RW_FUNCS_64(ttbr1, TTBR1_64) DEFINE_COPROCR_RW_FUNCS_64(cntvoff, CNTVOFF_64) DEFINE_COPROCR_RW_FUNCS(csselr, CSSELR) DEFINE_COPROCR_RW_FUNCS(hstr, HSTR) +DEFINE_COPROCR_RW_FUNCS(cntp_tval, CNTP_TVAL) +DEFINE_COPROCR_RW_FUNCS(cntp_ctl, CNTP_CTL) +DEFINE_COPROCR_RW_FUNCS(cntv_ctl, CNTV_CTL) DEFINE_COPROCR_RW_FUNCS(cnthp_ctl_el2, CNTHP_CTL) DEFINE_COPROCR_RW_FUNCS(cnthp_tval_el2, CNTHP_TVAL) DEFINE_COPROCR_RW_FUNCS_64(cnthp_cval_el2, CNTHP_CVAL_64) @@ -290,6 +293,7 @@ DEFINE_COPROCR_READ_FUNC(pmcr, PMCR) * Address translation */ DEFINE_COPROCR_WRITE_FUNC(ats1cpr, ATS1CPR) +DEFINE_COPROCR_WRITE_FUNC(ats1cpw, ATS1CPW) DEFINE_COPROCR_WRITE_FUNC(ats1hr, ATS1HR) DEFINE_COPROCR_RW_FUNCS_64(par, PAR_64) diff --git a/include/arch/aarch32/el3_common_macros.S b/include/arch/aarch32/el3_common_macros.S index 4fd746d5a4..6bbcde2e0a 100644 --- a/include/arch/aarch32/el3_common_macros.S +++ b/include/arch/aarch32/el3_common_macros.S @@ -10,6 +10,9 @@ #include #include #include +#include + +#define PAGE_START_MASK ~(PAGE_SIZE_MASK) /* * Helper macro to initialise EL3 registers we care about. @@ -199,11 +202,18 @@ * * _exception_vectors: * Address of the exception vectors to program in the VBAR_EL3 register. + * + * _pie_fixup_size: + * Size of memory region to fixup Global Descriptor Table (GDT). + * + * A non-zero value is expected when firmware needs GDT to be fixed-up. + * * ----------------------------------------------------------------------------- */ .macro el3_entrypoint_common \ _init_sctlr, _warm_boot_mailbox, _secondary_cold_boot, \ - _init_memory, _init_c_runtime, _exception_vectors + _init_memory, _init_c_runtime, _exception_vectors, \ + _pie_fixup_size /* Make sure we are in Secure Mode */ #if ENABLE_ASSERTIONS @@ -255,6 +265,27 @@ bxne r0 .endif /* _warm_boot_mailbox */ + .if \_pie_fixup_size +#if ENABLE_PIE + /* + * ------------------------------------------------------------ + * If PIE is enabled fixup the Global descriptor Table only + * once during primary core cold boot path. + * + * Compile time base address, required for fixup, is calculated + * using "pie_fixup" label present within first page. + * ------------------------------------------------------------ + */ + pie_fixup: + ldr r0, =pie_fixup + ldr r1, =PAGE_START_MASK + and r0, r0, r1 + mov_imm r1, \_pie_fixup_size + add r1, r1, r0 + bl fixup_gdt_reloc +#endif /* ENABLE_PIE */ + .endif /* _pie_fixup_size */ + /* --------------------------------------------------------------------- * Set the exception vectors (VBAR/MVBAR). * --------------------------------------------------------------------- @@ -335,12 +366,14 @@ */ mov r7, r12 ldr r0, =__BSS_START__ - ldr r1, =__BSS_SIZE__ + ldr r1, =__BSS_END__ + sub r1, r1, r0 bl zeromem #if USE_COHERENT_MEM ldr r0, =__COHERENT_RAM_START__ - ldr r1, =__COHERENT_RAM_UNALIGNED_SIZE__ + ldr r1, =__COHERENT_RAM_END_UNALIGNED__ + sub r1, r1, r0 bl zeromem #endif @@ -354,7 +387,8 @@ */ ldr r0, =__DATA_RAM_START__ ldr r1, =__DATA_ROM_START__ - ldr r2, =__DATA_SIZE__ + ldr r2, =__DATA_RAM_END__ + sub r2, r2, r0 bl memcpy4 #endif .endif /* _init_c_runtime */ diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h index 33e1134dd5..f50a5a57c3 100644 --- a/include/arch/aarch64/arch.h +++ b/include/arch/aarch64/arch.h @@ -735,13 +735,13 @@ * system level implementation of the Generic Timer. ******************************************************************************/ /* Physical Count register. */ -#define CNTPCT_LO U(0x0) +#define CNTBASEN_CNTPCT_LO U(0x0) /* Counter Frequency register. */ #define CNTBASEN_CNTFRQ U(0x10) /* Physical Timer CompareValue register. */ -#define CNTP_CVAL_LO U(0x20) +#define CNTBASEN_CNTP_CVAL_LO U(0x20) /* Physical Timer Control register. */ -#define CNTP_CTL U(0x2c) +#define CNTBASEN_CNTP_CTL U(0x2c) /* PMCR_EL0 definitions */ #define PMCR_EL0_RESET_VAL U(0x0) diff --git a/include/common/bl_common.ld.h b/include/common/bl_common.ld.h index ab3391aa21..cb71d9aea8 100644 --- a/include/common/bl_common.ld.h +++ b/include/common/bl_common.ld.h @@ -105,12 +105,21 @@ * .rela.dyn needs to come after .data for the read-elf utility to parse * this section correctly. */ +#if __aarch64__ #define RELA_SECTION \ .rela.dyn : ALIGN(STRUCT_ALIGN) { \ __RELA_START__ = .; \ *(.rela*) \ __RELA_END__ = .; \ } +#else +#define RELA_SECTION \ + .rel.dyn : ALIGN(STRUCT_ALIGN) { \ + __RELA_START__ = .; \ + *(.rel*) \ + __RELA_END__ = .; \ + } +#endif #if !(defined(IMAGE_BL31) && RECLAIM_INIT_CODE) #define STACK_SECTION \ diff --git a/include/common/tbbr/cot_def.h b/include/common/tbbr/cot_def.h index 6ce7f80c17..0d2d9acfe5 100644 --- a/include/common/tbbr/cot_def.h +++ b/include/common/tbbr/cot_def.h @@ -35,7 +35,7 @@ #error "Invalid value for TF_MBEDTLS_KEY_SIZE" #endif #else /* Only using ECDSA keys. */ -#define PK_DER_LEN 91 +#define PK_DER_LEN 92 #endif #if TF_MBEDTLS_HASH_ALG_ID == TF_MBEDTLS_SHA256 diff --git a/include/drivers/arm/tzc400.h b/include/drivers/arm/tzc400.h index 32aeb03502..88c9237cf1 100644 --- a/include/drivers/arm/tzc400.h +++ b/include/drivers/arm/tzc400.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -32,7 +32,6 @@ #define BUILD_CONFIG_AW_MASK U(0x3f) #define BUILD_CONFIG_NR_SHIFT 0 #define BUILD_CONFIG_NR_MASK U(0x1f) - /* * Number of gate keepers is implementation defined. But we know the max for * this device is 4. Get implementation details from BUILD_CONFIG. @@ -65,8 +64,8 @@ #define FAIL_CONTROL_NS_SECURE U(0) #define FAIL_CONTROL_NS_NONSECURE U(1) #define FAIL_CONTROL_PRIV_SHIFT 20 -#define FAIL_CONTROL_PRIV_PRIV U(0) -#define FAIL_CONTROL_PRIV_UNPRIV U(1) +#define FAIL_CONTROL_PRIV_UNPRIV U(0) +#define FAIL_CONTROL_PRIV_PRIV U(1) /* * FAIL_ID_ID_MASK depends on AID_WIDTH which is platform specific. @@ -93,6 +92,8 @@ #define TZC_400_REGION_SIZE U(0x20) #define TZC_400_ACTION_OFF U(0x4) +#define FILTER_OFFSET U(0x10) + #ifndef __ASSEMBLER__ #include @@ -103,39 +104,39 @@ ******************************************************************************/ void tzc400_init(uintptr_t base); void tzc400_configure_region0(unsigned int sec_attr, - unsigned int ns_device_access); + unsigned int ns_device_access); void tzc400_configure_region(unsigned int filters, - unsigned int region, - unsigned long long region_base, - unsigned long long region_top, - unsigned int sec_attr, - unsigned int nsaid_permissions); + unsigned int region, + unsigned long long region_base, + unsigned long long region_top, + unsigned int sec_attr, + unsigned int nsaid_permissions); +void tzc400_update_filters(unsigned int region, unsigned int filters); void tzc400_set_action(unsigned int action); void tzc400_enable_filters(void); void tzc400_disable_filters(void); +void tzc400_it_handler(void); static inline void tzc_init(uintptr_t base) { tzc400_init(base); } -static inline void tzc_configure_region0( - unsigned int sec_attr, - unsigned int ns_device_access) +static inline void tzc_configure_region0(unsigned int sec_attr, + unsigned int ns_device_access) { tzc400_configure_region0(sec_attr, ns_device_access); } -static inline void tzc_configure_region( - unsigned int filters, - unsigned int region, - unsigned long long region_base, - unsigned long long region_top, - unsigned int sec_attr, - unsigned int ns_device_access) +static inline void tzc_configure_region(unsigned int filters, + unsigned int region, + unsigned long long region_base, + unsigned long long region_top, + unsigned int sec_attr, + unsigned int ns_device_access) { tzc400_configure_region(filters, region, region_base, - region_top, sec_attr, ns_device_access); + region_top, sec_attr, ns_device_access); } static inline void tzc_set_action(unsigned int action) @@ -143,7 +144,6 @@ static inline void tzc_set_action(unsigned int action) tzc400_set_action(action); } - static inline void tzc_enable_filters(void) { tzc400_enable_filters(); diff --git a/include/drivers/auth/tbbr_cot_common.h b/include/drivers/auth/tbbr_cot_common.h index a51faee1aa..21bcd520a4 100644 --- a/include/drivers/auth/tbbr_cot_common.h +++ b/include/drivers/auth/tbbr_cot_common.h @@ -25,5 +25,6 @@ extern auth_param_type_desc_t fw_config_hash; extern const auth_img_desc_t trusted_boot_fw_cert; extern const auth_img_desc_t hw_config; +extern const auth_img_desc_t fw_config; #endif /* TBBR_COT_COMMON_H */ diff --git a/include/drivers/clk.h b/include/drivers/clk.h new file mode 100644 index 0000000000..960dce742a --- /dev/null +++ b/include/drivers/clk.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CLK_H +#define CLK_H + +#include + +typedef struct clk_ops { + int (*enable)(unsigned long id); + void (*disable)(unsigned long id); + unsigned long (*get_rate)(unsigned long id); + int (*get_parent)(unsigned long id); + bool (*is_enabled)(unsigned long id); +} clk_ops_t; + +int clk_enable(unsigned long id); +void clk_disable(unsigned long id); +unsigned long clk_get_rate(unsigned long id); +bool clk_is_enabled(unsigned long id); +int clk_get_parent(unsigned long id); + +void clk_register(const clk_ops_t *ops); + +#endif /* CLK_H */ diff --git a/include/drivers/io/io_mtd.h b/include/drivers/io/io_mtd.h index 1395ff6019..b2c90205d4 100644 --- a/include/drivers/io/io_mtd.h +++ b/include/drivers/io/io_mtd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -44,11 +44,22 @@ typedef struct io_mtd_ops { * Return 0 on success, a negative error code otherwise. */ int (*write)(unsigned int offset, uintptr_t buffer, size_t length); + + /* + * Look for an offset to be added to the given offset. + * + * @base: Base address of the area. + * @offset: Offset in bytes to start read operation. + * @extra_offset: [out] Offset to be added to the previous offset. + * Return 0 on success, a negative error code otherwise. + */ + int (*seek)(uintptr_t base, unsigned int offset, size_t *extra_offset); } io_mtd_ops_t; typedef struct io_mtd_dev_spec { unsigned long long device_size; unsigned int erase_size; + size_t offset; io_mtd_ops_t ops; } io_mtd_dev_spec_t; diff --git a/include/drivers/mmc.h b/include/drivers/mmc.h index 7611f019a1..2b4f5ab8b3 100644 --- a/include/drivers/mmc.h +++ b/include/drivers/mmc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -104,6 +104,7 @@ #define MMC_STATE_SLP 10 #define MMC_FLAG_CMD23 (U(1) << 0) +#define MMC_FLAG_SD_CMD6 (U(1) << 1) #define CMD8_CHECK_PATTERN U(0xAA) #define VHS_2_7_3_6_V BIT(8) @@ -111,6 +112,9 @@ #define SD_SCR_BUS_WIDTH_1 BIT(8) #define SD_SCR_BUS_WIDTH_4 BIT(10) +#define SD_SWITCH_FUNC_CHECK 0U +#define SD_SWITCH_FUNC_SWITCH 1U + struct mmc_cmd { unsigned int cmd_idx; unsigned int cmd_arg; @@ -210,6 +214,27 @@ struct mmc_csd_sd_v2 { unsigned int csd_structure: 2; }; +struct sd_switch_status { + unsigned short max_current; + unsigned short support_g6; + unsigned short support_g5; + unsigned short support_g4; + unsigned short support_g3; + unsigned short support_g2; + unsigned short support_g1; + unsigned char sel_g6_g5; + unsigned char sel_g4_g3; + unsigned char sel_g2_g1; + unsigned char data_struct_ver; + unsigned short busy_g6; + unsigned short busy_g5; + unsigned short busy_g4; + unsigned short busy_g3; + unsigned short busy_g2; + unsigned short busy_g1; + unsigned short reserved[17]; +}; + enum mmc_device_type { MMC_IS_EMMC, MMC_IS_SD, diff --git a/include/drivers/nand.h b/include/drivers/nand.h index 1dbb008f9c..862e1792a4 100644 --- a/include/drivers/nand.h +++ b/include/drivers/nand.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2019-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -45,6 +45,16 @@ struct nand_device { int nand_read(unsigned int offset, uintptr_t buffer, size_t length, size_t *length_read); +/* + * Look for an extra offset to be added in case of bad blocks + * + * @base: Base address of the area + * @offset: Byte offset to read from in device + * @extra_offset: [out] Extra offset to be added if bad blocks are found + * Return: 0 on success, a negative errno on failure + */ +int nand_seek_bb(uintptr_t base, unsigned int offset, size_t *extra_offset); + /* * Get NAND device instance * diff --git a/include/drivers/st/bsec.h b/include/drivers/st/bsec.h index d833e7ab27..273a4e98ac 100644 --- a/include/drivers/st/bsec.h +++ b/include/drivers/st/bsec.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2015-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,13 +19,6 @@ #define BSEC_OTP_BANK_SHIFT 5 #define BSEC_TIMEOUT_VALUE 0xFFFF -#define ADDR_LOWER_OTP_PERLOCK_SHIFT 0x03 -#define DATA_LOWER_OTP_PERLOCK_BIT 0x03U /* 2 significants bits are used */ -#define DATA_LOWER_OTP_PERLOCK_MASK GENMASK(2, 0) -#define ADDR_UPPER_OTP_PERLOCK_SHIFT 0x04 -#define DATA_UPPER_OTP_PERLOCK_BIT 0x01U /* 1 significants bits are used */ -#define DATA_UPPER_OTP_PERLOCK_MASK GENMASK(3, 0) - /* * Return status */ @@ -35,110 +28,34 @@ #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_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 /* Safmem permanent lock */ -#define BSEC_WRLOCK1_OFF 0x050U -#define BSEC_WRLOCK2_OFF 0x054U -#define BSEC_SPLOCK_OFF 0x064U /* Program safmem sticky lock */ -#define BSEC_SPLOCK1_OFF 0x068U -#define BSEC_SPLOCK2_OFF 0x06CU -#define BSEC_SWLOCK_OFF 0x07CU /* Write in OTP sticky lock */ -#define BSEC_SWLOCK1_OFF 0x080U -#define BSEC_SWLOCK2_OFF 0x084U -#define BSEC_SRLOCK_OFF 0x094U /* Shadowing sticky lock */ -#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(2, 1) -#define BSEC_CONF_FRQ_SHIFT 1 -#define BSEC_CONF_PRG_WIDTH_MASK GENMASK(6, 3) -#define BSEC_CONF_PRG_WIDTH_SHIFT 3 -#define BSEC_CONF_TREAD_MASK GENMASK(8, 7) -#define BSEC_CONF_TREAD_SHIFT 7 - -/* - * BSEC_CONTROL Register - */ -#define BSEC_READ 0x000U -#define BSEC_WRITE 0x100U -#define BSEC_LOCK 0x200U - -/* - * BSEC_OTP_LOCK register - */ -#define UPPER_OTP_LOCK_MASK BIT(0) -#define UPPER_OTP_LOCK_SHIFT 0 -#define DENREG_LOCK_MASK BIT(2) -#define DENREG_LOCK_SHIFT 2 -#define GPLOCK_LOCK_MASK BIT(4) -#define GPLOCK_LOCK_SHIFT 4 +#define BSEC_TIMEOUT 0xFFFFFFF9U +#define BSEC_RETRY 0xFFFFFFF8U +#define BSEC_NOT_SUPPORTED 0xFFFFFFF7U +#define BSEC_WRITE_LOCKED 0xFFFFFFF6U +#define BSEC_ERROR_INVALID_FVR 0xFFFFFFF5U /* - * BSEC_OTP_STATUS Register + * OTP MODE */ -#define BSEC_MODE_STATUS_MASK GENMASK(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) - -/* OTP MODE*/ #define BSEC_MODE_OPEN1 0x00 #define BSEC_MODE_SECURED 0x01 #define BSEC_MODE_OPEN2 0x02 #define BSEC_MODE_INVALID 0x04 -/* BSEC_DENABLE Register */ -#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(10, 0) - -/* BSEC_FENABLE Register */ -#define BSEC_FEN_ALL_MSK GENMASK(14, 0) - /* - * OTP Lock services definition - * Value must corresponding to the bit number in the register + * OTP Lock services definition. + * Value must corresponding to the bit number in the register. + * Special case: (bit number << 1) for BSEC3. */ #define BSEC_LOCK_UPPER_OTP 0x00 +#define BSEC_LOCK_GWLOCK 0x01 #define BSEC_LOCK_DEBUG 0x02 #define BSEC_LOCK_PROGRAM 0x03 +#define BSEC_LOCK_KVLOCK 0x04 -/* Values for struct bsec_config::freq */ +/* + * Values for struct bsec_config::freq + */ #define FREQ_10_20_MHZ 0x0 #define FREQ_20_30_MHZ 0x1 #define FREQ_30_45_MHZ 0x2 @@ -146,22 +63,28 @@ /* * Device info structure, providing device-specific functions and a means of - * adding driver-specific state + * adding driver-specific state. */ struct bsec_config { + uint8_t den_lock; /* + * Debug enable sticky lock + * 1 debug enable is locked until next reset + */ + + /* BSEC2 only */ uint8_t tread; /* SAFMEM Reading current level default 0 */ uint8_t pulse_width; /* SAFMEM Programming pulse width default 1 */ - uint8_t freq; /* SAFMEM CLOCK see freq value define + uint8_t freq; /* + * SAFMEM CLOCK see freq value define * default FREQ_45_67_MHZ */ uint8_t power; /* Power up SAFMEM. 1 power up, 0 power off */ - uint8_t prog_lock; /* Programming Sticky lock + uint8_t prog_lock; /* + * Programming Sticky lock * 1 programming is locked until next reset */ - uint8_t den_lock; /* Debug enable sticky lock - * 1 debug enable is locked until next reset - */ - uint8_t upper_otp_lock; /* Shadowing of upper OTP sticky lock + uint8_t upper_otp_lock; /* + * Shadowing of upper OTP sticky lock * 1 shadowing of upper OTP is locked * until next reset */ @@ -173,16 +96,20 @@ uint32_t bsec_get_base(void); uint32_t bsec_set_config(struct bsec_config *cfg); uint32_t bsec_get_config(struct bsec_config *cfg); +uint32_t bsec_find_otp_name_in_dt(const char *name, uint32_t *otp, + uint32_t *otp_len); + 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); +void bsec_write_debug_conf(uint32_t val); uint32_t bsec_read_debug_conf(void); -uint32_t bsec_write_feature_conf(uint32_t val); -uint32_t bsec_read_feature_conf(uint32_t *val); + +void bsec_write_scratch(uint32_t val); +uint32_t bsec_read_scratch(void); uint32_t bsec_get_status(void); uint32_t bsec_get_hw_conf(void); @@ -190,14 +117,14 @@ 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); +uint32_t bsec_set_sr_lock(uint32_t otp); +uint32_t bsec_read_sr_lock(uint32_t otp, bool *value); +uint32_t bsec_set_sw_lock(uint32_t otp); +uint32_t bsec_read_sw_lock(uint32_t otp, bool *value); +uint32_t bsec_set_sp_lock(uint32_t otp); +uint32_t bsec_read_sp_lock(uint32_t otp, bool *value); +uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value); +uint32_t bsec_otp_lock(uint32_t service); uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word); uint32_t bsec_check_nsec_access_rights(uint32_t otp); diff --git a/include/drivers/st/bsec2_reg.h b/include/drivers/st/bsec2_reg.h new file mode 100644 index 0000000000..9da9526031 --- /dev/null +++ b/include/drivers/st/bsec2_reg.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef BSEC2_REG_H +#define BSEC2_REG_H + +#include + +/* IP configuration */ +#define ADDR_LOWER_OTP_PERLOCK_SHIFT 0x03 +#define DATA_LOWER_OTP_PERLOCK_BIT 0x03U /* 2 significants bits are used */ +#define DATA_LOWER_OTP_PERLOCK_MASK GENMASK(2, 0) +#define ADDR_UPPER_OTP_PERLOCK_SHIFT 0x04 +#define DATA_UPPER_OTP_PERLOCK_BIT 0x01U /* 1 significants bits are used */ +#define DATA_UPPER_OTP_PERLOCK_MASK GENMASK(3, 0) + +/* BSEC REGISTER OFFSET (base relative) */ +#define BSEC_OTP_CONF_OFF U(0x000) +#define BSEC_OTP_CTRL_OFF U(0x004) +#define BSEC_OTP_WRDATA_OFF U(0x008) +#define BSEC_OTP_STATUS_OFF U(0x00C) +#define BSEC_OTP_LOCK_OFF U(0x010) +#define BSEC_DEN_OFF U(0x014) +#define BSEC_DISTURBED_OFF U(0x01C) +#define BSEC_DISTURBED1_OFF U(0x020) +#define BSEC_DISTURBED2_OFF U(0x024) +#define BSEC_ERROR_OFF U(0x034) +#define BSEC_ERROR1_OFF U(0x038) +#define BSEC_ERROR2_OFF U(0x03C) +#define BSEC_WRLOCK_OFF U(0x04C) /* Safmem permanent lock */ +#define BSEC_WRLOCK1_OFF U(0x050) +#define BSEC_WRLOCK2_OFF U(0x054) +#define BSEC_SPLOCK_OFF U(0x064) /* Program safmem sticky lock */ +#define BSEC_SPLOCK1_OFF U(0x068) +#define BSEC_SPLOCK2_OFF U(0x06C) +#define BSEC_SWLOCK_OFF U(0x07C) /* Write in OTP sticky lock */ +#define BSEC_SWLOCK1_OFF U(0x080) +#define BSEC_SWLOCK2_OFF U(0x084) +#define BSEC_SRLOCK_OFF U(0x094) /* Shadowing sticky lock */ +#define BSEC_SRLOCK1_OFF U(0x098) +#define BSEC_SRLOCK2_OFF U(0x09C) +#define BSEC_JTAG_IN_OFF U(0x0AC) +#define BSEC_JTAG_OUT_OFF U(0x0B0) +#define BSEC_SCRATCH_OFF U(0x0B4) +#define BSEC_OTP_DATA_OFF U(0x200) +#define BSEC_IPHW_CFG_OFF U(0xFF0) +#define BSEC_IPVR_OFF U(0xFF4) +#define BSEC_IP_ID_OFF U(0xFF8) +#define BSEC_IP_MAGIC_ID_OFF U(0xFFC) + +/* BSEC_CONFIGURATION Register */ +#define BSEC_CONF_POWER_UP_MASK BIT(0) +#define BSEC_CONF_POWER_UP_SHIFT 0 +#define BSEC_CONF_FRQ_MASK GENMASK(2, 1) +#define BSEC_CONF_FRQ_SHIFT 1 +#define BSEC_CONF_PRG_WIDTH_MASK GENMASK(6, 3) +#define BSEC_CONF_PRG_WIDTH_SHIFT 3 +#define BSEC_CONF_TREAD_MASK GENMASK(8, 7) +#define BSEC_CONF_TREAD_SHIFT 7 + +/* BSEC_CONTROL Register */ +#define BSEC_READ 0 +#define BSEC_WRITE BIT(8) +#define BSEC_LOCK BIT(9) + +/* BSEC_OTP_LOCK register */ +#define UPPER_OTP_LOCK_MASK BIT(0) +#define UPPER_OTP_LOCK_SHIFT 0 +#define DENREG_LOCK_MASK BIT(2) +#define DENREG_LOCK_SHIFT 2 +#define GPLOCK_LOCK_MASK BIT(4) +#define GPLOCK_LOCK_SHIFT 4 + +/* BSEC_OTP_STATUS Register */ +#define BSEC_MODE_STATUS_MASK GENMASK(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) + +/* BSEC_DENABLE Register */ +#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(10, 0) + +/* BSEC_FENABLE Register */ +#define BSEC_FEN_ALL_MSK GENMASK(14, 0) + +/* BSEC_IPVR Register */ +#define BSEC_IPVR_MSK GENMASK(7, 0) + +#endif /* BSEC2_REG_H */ diff --git a/include/drivers/st/stm32_i2c.h b/include/drivers/st/stm32_i2c.h index 170d4cf815..e1a3782b2e 100644 --- a/include/drivers/st/stm32_i2c.h +++ b/include/drivers/st/stm32_i2c.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved * - * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ #ifndef STM32_I2C_H @@ -73,6 +73,21 @@ #define I2C_TIMINGR_SDADEL GENMASK(19, 16) #define I2C_TIMINGR_SCLDEL GENMASK(23, 20) #define I2C_TIMINGR_PRESC GENMASK(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(11, 0) @@ -111,15 +126,9 @@ #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 +#define STANDARD_RATE 100000 +#define FAST_RATE 400000 +#define FAST_PLUS_RATE 1000000 struct stm32_i2c_init_s { uint32_t own_address1; /* @@ -181,12 +190,7 @@ struct stm32_i2c_init_s { * 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. - */ + uint32_t bus_rate; /* Specifies the I2C clock frequency */ int analog_filter; /* * Specifies if the I2C analog noise @@ -238,6 +242,8 @@ struct i2c_handle_s { enum i2c_state_e i2c_state; /* Communication state */ enum i2c_mode_e i2c_mode; /* Communication mode */ uint32_t i2c_err; /* Error code */ + uint32_t saved_timing; /* Saved timing value */ + uint32_t saved_frequency; /* Saved frequency value */ }; #define I2C_ADDRESSINGMODE_7BIT 0x00000001U @@ -299,8 +305,7 @@ struct i2c_handle_s { #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); +int stm32_i2c_get_setup_from_fdt(int node, struct stm32_i2c_init_s *init); 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, diff --git a/include/drivers/st/stm32_iwdg.h b/include/drivers/st/stm32_iwdg.h index bad25244a8..c0c009779e 100644 --- a/include/drivers/st/stm32_iwdg.h +++ b/include/drivers/st/stm32_iwdg.h @@ -15,5 +15,6 @@ int stm32_iwdg_init(void); void stm32_iwdg_refresh(void); +void __dead2 stm32_iwdg_it_handler(int id); #endif /* STM32_IWDG_H */ diff --git a/include/drivers/st/stm32_rng.h b/include/drivers/st/stm32_rng.h new file mode 100644 index 0000000000..a644118656 --- /dev/null +++ b/include/drivers/st/stm32_rng.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_RNG_H +#define STM32_RNG_H + +int stm32_rng_read(uint8_t *out, uint32_t size); +int stm32_rng_init(void); + +#endif /* STM32_RNG_H */ diff --git a/include/drivers/st/stm32_rtc.h b/include/drivers/st/stm32_rtc.h new file mode 100644 index 0000000000..128dd2d14a --- /dev/null +++ b/include/drivers/st/stm32_rtc.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_RTC_H +#define STM32_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); +int stm32_rtc_init(void); + +/* SMP protection on RTC registers access */ +void stm32_rtc_regs_lock(void); +void stm32_rtc_regs_unlock(void); + +#endif /* STM32_RTC_H */ diff --git a/include/drivers/st/stm32_sdmmc2.h b/include/drivers/st/stm32_sdmmc2.h index 4853208c2b..5b4bd0e167 100644 --- a/include/drivers/st/stm32_sdmmc2.h +++ b/include/drivers/st/stm32_sdmmc2.h @@ -10,6 +10,7 @@ #include #include +#include struct stm32_sdmmc2_params { uintptr_t reg_base; @@ -24,6 +25,7 @@ struct stm32_sdmmc2_params { unsigned int reset_id; unsigned int max_freq; bool use_dma; + struct stm32mp_regulator vmmc_regu; }; unsigned long long stm32_sdmmc2_mmc_get_device_size(void); diff --git a/include/drivers/st/stm32_tamp.h b/include/drivers/st/stm32_tamp.h new file mode 100644 index 0000000000..424cbb205a --- /dev/null +++ b/include/drivers/st/stm32_tamp.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_TAMP_H +#define STM32_TAMP_H + +/* Internal Tamper */ +enum stm32_tamp_int_id { + ITAMP1 = 0, + ITAMP2, + ITAMP3, + ITAMP4, + ITAMP5, + ITAMP6, + ITAMP7, + ITAMP8, + ITAMP9, + ITAMP10, + ITAMP11, + ITAMP12, + ITAMP13, + ITAMP14, + ITAMP15, + ITAMP16 +}; + +/* External Tamper */ +enum stm32_tamp_ext_id { + EXT_TAMP1 = 0, + EXT_TAMP2, + EXT_TAMP3, + EXT_TAMP4, + EXT_TAMP5, + EXT_TAMP6, + EXT_TAMP7, + EXT_TAMP8 +}; + +enum stm32_tamp_state { + TAMP_DISABLE = 0, + TAMP_ENABLE +}; + +#define TAMP_UNUSED {.id = -1} + +/* define TAMPER modes */ +#define TAMP_TRIG_OFF 0x0U +#define TAMP_TRIG_ON 0x1U +#define TAMP_ACTIVE 0x2U +#define TAMP_ERASE 0x0U +#define TAMP_NOERASE 0x1U +#define TAMP_NO_EVT_MASK 0x0U +#define TAMP_EVT_MASK 0x1U + +/* define Passive FILTER mode */ +#define TAMP_FILTER_PRECHARGE 0x0U +#define TAMP_FILTER_PULL_UP_DISABLE 0x1U +#define TAMP_FILTER_DURATION_1_CYCLE 0x0U +#define TAMP_FILTER_DURATION_2_CYCLES 0x1U +#define TAMP_FILTER_DURATION_3_CYCLES 0x2U +#define TAMP_FILTER_DURATION_4_CYCLES 0x3U +#define TAMP_FILTER_COUNT_1 0x0U +#define TAMP_FILTER_COUNT_2 0x1U +#define TAMP_FILTER_COUNT_4 0x2U +#define TAMP_FILTER_COUNT_8 0x3U +#define TAMP_FILTER_SAMPLING_32768 0x0U +#define TAMP_FILTER_SAMPLING_16384 0x1U +#define TAMP_FILTER_SAMPLING_8192 0x2U +#define TAMP_FILTER_SAMPLING_4096 0x3U +#define TAMP_FILTER_SAMPLING_2048 0x4U +#define TAMP_FILTER_SAMPLING_1024 0x5U +#define TAMP_FILTER_SAMPLING_512 0x6U +#define TAMP_FILTER_SAMPLING_256 0x7U + +/* define active filter */ +#define TAMP_ACTIVE_FILTER_OFF 0x0U +#define TAMP_ACTIVE_FILTER_ON 0x1U +#define TAMP_ACTIVE_ATO_DEDICATED 0x0U +#define TAMP_ACTIVE_ATO_TAMPOUTSEL 0x1U +#define TAMP_ACTIVE_APER_1_OUTPUT 0x0U +#define TAMP_ACTIVE_APER_2_OUTPUTS 0x1U +#define TAMP_ACTIVE_APER_3_4_OUTPUTS 0x2U +#define TAMP_ACTIVE_APER_5_OUTPUTS 0x3U +#define TAMP_ACTIVE_CKSEL_DIV_0 0x0U +#define TAMP_ACTIVE_CKSEL_DIV_2 0x1U +#define TAMP_ACTIVE_CKSEL_DIV_4 0x2U +#define TAMP_ACTIVE_CKSEL_DIV_8 0x3U +#define TAMP_ACTIVE_CKSEL_DIV_16 0x4U +#define TAMP_ACTIVE_CKSEL_DIV_32 0x5U +#define TAMP_ACTIVE_CKSEL_DIV_64 0x6U +#define TAMP_ACTIVE_CKSEL_DIV_128 0x7U +#define TAMP_ACTIVE_ATOSEL_OUT1_(X) (0x0U << ((X) * 2)) +#define TAMP_ACTIVE_ATOSEL_OUT2_(X) (0x1U << ((X) * 2)) +#define TAMP_ACTIVE_ATOSEL_OUT3_(X) (0x2U << ((X) * 2)) +#define TAMP_ACTIVE_ATOSEL_OUT4_(X) (0x3U << ((X) * 2)) + +#define TAMP_EXT(tamp_id, trig, erase, mask) (((tamp_id) << 3) | ((trig) << 2)\ + | ((erase) << 1) | (mask)) + +#define TAMP_FLTCR(precharge, duration, count, sample) (((precharge) << 7) |\ + ((duration) << 5) |\ + ((count) << 3) |\ + (sample)) + +#define TAMP_ACT(filter, ato, aper, atcksel, atosel) (((filter) << 31) |\ + ((ato) << 30) |\ + ((aper) << 24) |\ + ((atcksel) << 16) |\ + (atosel) << 8) + +struct stm32_tamp_int { + int id; + void (*func)(int id); +}; + +struct stm32_tamp_ext { + int id; + uint8_t mode; + uint8_t erase; + uint8_t evt_mask; + void (*func)(int id); +}; + +/* + * stm32_tamp_write_mcounter : Increase monotonic counter + */ +void stm32_tamp_write_mcounter(void); + +/* + * stm32_tamp_it_handler : Interrupt handler + */ +void stm32_tamp_it_handler(void); + +/* + * stm32_tamp_configure_internal: Configure internal tamper + * tamper_list: List of tamper to enable + * nb_tamper: Number of tamper in list + */ +void stm32_tamp_configure_internal(struct stm32_tamp_int *tamper_list, + uint16_t nb_tamper); + +/* + * stm32_tamp_configure_external: Configure external tamper and associated + * configuration for filtering + * ext_tamp_list: List of external tamper to configure + * nb_tamper: Number of tamper in list + * passive_conf: Filter configuration + * active_conf: Configuration for active tamper + */ +void stm32_tamp_configure_external(struct stm32_tamp_ext *ext_tamper_list, + uint8_t nb_tamper, uint32_t passive_conf, + uint32_t active_conf); + +/* + * stm32_tamp_init: Initialize tamper from DT + * return 0 if disabled, 1 if enabled, else < 0 + */ +int stm32_tamp_init(void); + +#endif /* STM32_TAMP_H */ diff --git a/include/drivers/st/stm32_timer.h b/include/drivers/st/stm32_timer.h new file mode 100644 index 0000000000..0e2eb91fe9 --- /dev/null +++ b/include/drivers/st/stm32_timer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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); +void stm32_timer_freq_func(unsigned long (**timer_freq_cb)(void), + enum timer_cal type); +int stm32_timer_init(void); + +#endif /* STM32_TIMER_H */ diff --git a/include/drivers/st/stm32_uart.h b/include/drivers/st/stm32_uart.h new file mode 100644 index 0000000000..f00da852fe --- /dev/null +++ b/include/drivers/st/stm32_uart.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32_UART_H +#define STM32_UART_H + +/* UART word length */ +#define STM32_UART_WORDLENGTH_7B USART_CR1_M1 +#define STM32_UART_WORDLENGTH_8B 0x00000000U +#define STM32_UART_WORDLENGTH_9B USART_CR1_M0 + +/* UART number of stop bits */ +#define STM32_UART_STOPBITS_0_5 USART_CR2_STOP_0 +#define STM32_UART_STOPBITS_1 0x00000000U +#define STM32_UART_STOPBITS_1_5 (USART_CR2_STOP_0 | USART_CR2_STOP_1) +#define STM32_UART_STOPBITS_2 USART_CR2_STOP_1 + +/* UART parity */ +#define STM32_UART_PARITY_NONE 0x00000000U +#define STM32_UART_PARITY_EVEN USART_CR1_PCE +#define STM32_UART_PARITY_ODD (USART_CR1_PCE | USART_CR1_PS) + +/* UART transfer mode */ +#define STM32_UART_MODE_RX USART_CR1_RE +#define STM32_UART_MODE_TX USART_CR1_TE +#define STM32_UART_MODE_TX_RX (USART_CR1_TE | USART_CR1_RE) + +/* UART hardware flow control */ +#define STM32_UART_HWCONTROL_NONE 0x00000000U +#define STM32_UART_HWCONTROL_RTS USART_CR3_RTSE +#define STM32_UART_HWCONTROL_CTS USART_CR3_CTSE +#define STM32_UART_HWCONTROL_RTS_CTS (USART_CR3_RTSE | USART_CR3_CTSE) + +/* UART over sampling */ +#define STM32_UART_OVERSAMPLING_16 0x00000000U +#define STM32_UART_OVERSAMPLING_8 USART_CR1_OVER8 + +/* UART prescaler */ +#define STM32_UART_PRESCALER_DIV1 0x00000000U +#define STM32_UART_PRESCALER_DIV2 0x00000001U +#define STM32_UART_PRESCALER_DIV4 0x00000002U +#define STM32_UART_PRESCALER_DIV6 0x00000003U +#define STM32_UART_PRESCALER_DIV8 0x00000004U +#define STM32_UART_PRESCALER_DIV10 0x00000005U +#define STM32_UART_PRESCALER_DIV12 0x00000006U +#define STM32_UART_PRESCALER_DIV16 0x00000007U +#define STM32_UART_PRESCALER_DIV32 0x00000008U +#define STM32_UART_PRESCALER_DIV64 0x00000009U +#define STM32_UART_PRESCALER_DIV128 0x0000000AU +#define STM32_UART_PRESCALER_DIV256 0x0000000BU +#define STM32_UART_PRESCALER_MAX STM32_UART_PRESCALER_DIV256 + +/* UART fifo mode */ +#define STM32_UART_FIFOMODE_EN USART_CR1_FIFOEN +#define STM32_UART_FIFOMODE_DIS 0x00000000U + +/* UART TXFIFO threshold level */ +#define STM32_UART_TXFIFO_THRESHOLD_1EIGHTHFULL 0x00000000U +#define STM32_UART_TXFIFO_THRESHOLD_1QUARTERFUL USART_CR3_TXFTCFG_0 +#define STM32_UART_TXFIFO_THRESHOLD_HALFFULL USART_CR3_TXFTCFG_1 +#define STM32_UART_TXFIFO_THRESHOLD_3QUARTERSFULL (USART_CR3_TXFTCFG_0 | USART_CR3_TXFTCFG_1) +#define STM32_UART_TXFIFO_THRESHOLD_7EIGHTHFULL USART_CR3_TXFTCFG_2 +#define STM32_UART_TXFIFO_THRESHOLD_EMPTY (USART_CR3_TXFTCFG_2 | USART_CR3_TXFTCFG_0) + +/* UART RXFIFO threshold level */ +#define STM32_UART_RXFIFO_THRESHOLD_1EIGHTHFULL 0x00000000U +#define STM32_UART_RXFIFO_THRESHOLD_1QUARTERFULL USART_CR3_RXFTCFG_0 +#define STM32_UART_RXFIFO_THRESHOLD_HALFFULL USART_CR3_RXFTCFG_1 +#define STM32_UART_RXFIFO_THRESHOLD_3QUARTERSFULL (USART_CR3_RXFTCFG_0 | USART_CR3_RXFTCFG_1) +#define STM32_UART_RXFIFO_THRESHOLD_7EIGHTHFULL USART_CR3_RXFTCFG_2 +#define STM32_UART_RXFIFO_THRESHOLD_FULL (USART_CR3_RXFTCFG_2 | USART_CR3_RXFTCFG_0) + +struct stm32_uart_init_s { + uint32_t baud_rate; /* + * Configures the UART communication + * baud rate. + */ + + uint32_t word_length; /* + * Specifies the number of data bits + * transmitted or received in a frame. + * This parameter can be a value of + * @ref STM32_UART_WORDLENGTH_*. + */ + + uint32_t stop_bits; /* + * Specifies the number of stop bits + * transmitted. This parameter can be + * a value of @ref STM32_UART_STOPBITS_*. + */ + + uint32_t parity; /* + * Specifies the parity mode. + * This parameter can be a value of + * @ref STM32_UART_PARITY_*. + */ + + uint32_t mode; /* + * Specifies whether the receive or + * transmit mode is enabled or + * disabled. This parameter can be a + * value of @ref @ref STM32_UART_MODE_*. + */ + + uint32_t hw_flow_control; /* + * Specifies whether the hardware flow + * control mode is enabled or + * disabled. This parameter can be a + * value of @ref STM32_UARTHWCONTROL_*. + */ + + uint32_t over_sampling; /* + * Specifies whether the over sampling + * 8 is enabled or disabled. + * This parameter can be a value of + * @ref STM32_UART_OVERSAMPLING_*. + */ + + uint32_t one_bit_sampling; /* + * Specifies whether a single sample + * or three samples' majority vote is + * selected. This parameter can be 0 + * or USART_CR3_ONEBIT. + */ + + uint32_t prescaler; /* + * Specifies the prescaler value used + * to divide the UART clock source. + * This parameter can be a value of + * @ref STM32_UART_PRESCALER_*. + */ + + uint32_t fifo_mode; /* + * Specifies if the FIFO mode will be + * used. This parameter can be a value + * of @ref STM32_UART_FIFOMODE_*. + */ + + uint32_t tx_fifo_threshold; /* + * Specifies the TXFIFO threshold + * level. This parameter can be a + * value of @ref + * STM32_UART_TXFIFO_THRESHOLD_*. + */ + + uint32_t rx_fifo_threshold; /* + * Specifies the RXFIFO threshold + * level. This parameter can be a + * value of @ref + * STM32_UART_RXFIFO_THRESHOLD_*. + */ +}; + +struct stm32_uart_handle_s { + uint32_t base; + uint32_t rdr_mask; +}; + +int stm32_uart_init(struct stm32_uart_handle_s *huart, + uintptr_t base_addr, + const struct stm32_uart_init_s *init); +void stm32_uart_stop(uintptr_t base_addr); +int stm32_uart_putc(struct stm32_uart_handle_s *huart, int c); +int stm32_uart_flush(struct stm32_uart_handle_s *huart); +int stm32_uart_getc(struct stm32_uart_handle_s *huart); + +#endif /* STM32_UART_H */ diff --git a/include/drivers/st/stm32mp1_calib.h b/include/drivers/st/stm32mp1_calib.h new file mode 100644 index 0000000000..ef69cb4563 --- /dev/null +++ b/include/drivers/st/stm32mp1_calib.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_CALIB_H +#define STM32MP1_CALIB_H + +#include +#include + +bool stm32mp1_calib_get_wakeup(void); +void stm32mp1_calib_set_wakeup(bool state); +void stm32mp1_calib_it_handler(uint32_t id); +int stm32mp1_calib_start_hsi_cal(void); +int stm32mp1_calib_start_csi_cal(void); +void stm32mp1_calib_init(void); + +#endif /* STM32MP1_CLK_H */ diff --git a/include/drivers/st/stm32mp1_clk.h b/include/drivers/st/stm32mp1_clk.h index c46892b78e..c62fb20784 100644 --- a/include/drivers/st/stm32mp1_clk.h +++ b/include/drivers/st/stm32mp1_clk.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,42 +22,44 @@ enum stm32mp_osc_id { extern const char *stm32mp_osc_node_label[NB_OSC]; +#define PLL1_SETTINGS_VALID_ID U(0x504C4C31) /* "PLL1" */ + int stm32mp1_clk_probe(void); -int stm32mp1_clk_init(void); +int stm32mp1_clk_init(uint32_t pll1_freq_mhz); + +int stm32mp1_clk_compute_all_pll1_settings(uint32_t buck1_voltage); +void stm32mp1_clk_lp_save_opp_pll1_settings(uint8_t *data, size_t size); +void stm32mp1_clk_lp_load_opp_pll1_settings(uint8_t *data, size_t size); + +int stm32mp1_clk_get_maxfreq_opp(uint32_t *freq_mhz, uint32_t *voltage_mv); bool stm32mp1_rcc_is_secure(void); bool stm32mp1_rcc_is_mckprot(void); -void __stm32mp1_clk_enable(unsigned long id, bool caller_is_secure); -void __stm32mp1_clk_disable(unsigned long id, bool caller_is_secure); +void stm32mp1_clk_force_enable(unsigned long id); +void stm32mp1_clk_force_disable(unsigned long id); -static inline void stm32mp1_clk_enable_non_secure(unsigned long id) -{ - __stm32mp1_clk_enable(id, false); -} +bool stm32mp1_rtc_get_read_twice(void); + +/* SMP protection on RCC registers access */ +void stm32mp1_clk_rcc_regs_lock(void); +void stm32mp1_clk_rcc_regs_unlock(void); -static inline void stm32mp1_clk_enable_secure(unsigned long id) -{ - __stm32mp1_clk_enable(id, true); -} +int stm32mp1_round_opp_khz(uint32_t *freq_khz); +int stm32mp1_set_opp_khz(uint32_t freq_khz); -static inline void stm32mp1_clk_disable_non_secure(unsigned long id) -{ - __stm32mp1_clk_disable(id, false); -} +void stm32mp1_clock_suspend(void); +void stm32mp1_clock_resume(void); -static inline void stm32mp1_clk_disable_secure(unsigned long id) -{ - __stm32mp1_clk_disable(id, true); -} +void stm32mp1_clock_stopmode_save(void); +int stm32mp1_clock_stopmode_resume(void); -unsigned int stm32mp1_clk_get_refcount(unsigned long id); +void restore_clock_pm_context(void); +void save_clock_pm_context(void); -/* SMP protection on RCC registers access */ -void stm32mp1_clk_rcc_regs_lock(void); -void stm32mp1_clk_rcc_regs_unlock(void); +void stm32mp1_clk_mcuss_protect(bool enable); -void stm32mp1_stgen_increment(unsigned long long offset_in_ms); +void stm32mp1_dump_clocks_state(void); #ifdef STM32MP_SHARED_RESOURCES void stm32mp1_register_clock_parents_secure(unsigned long id); diff --git a/include/drivers/st/stm32mp1_ddr.h b/include/drivers/st/stm32mp1_ddr.h index 4ab37d6b44..245ad52cc2 100644 --- a/include/drivers/st/stm32mp1_ddr.h +++ b/include/drivers/st/stm32mp1_ddr.h @@ -8,9 +8,6 @@ #define STM32MP1_DDR_H #include -#include - -#define DT_DDR_COMPAT "st,stm32mp1-ddr" struct stm32mp1_ddr_size { uint64_t base; @@ -101,12 +98,14 @@ struct stm32mp1_ddrctrl_perf { uint32_t pcfgqos1_0; uint32_t pcfgwqos0_0; uint32_t pcfgwqos1_0; +#if STM32MP_DDR_DUAL_AXI_PORT uint32_t pcfgr_1; uint32_t pcfgw_1; uint32_t pcfgqos0_1; uint32_t pcfgqos1_1; uint32_t pcfgwqos0_1; uint32_t pcfgwqos1_1; +#endif }; struct stm32mp1_ddrphy_reg { @@ -119,8 +118,10 @@ struct stm32mp1_ddrphy_reg { uint32_t zq0cr1; uint32_t dx0gcr; uint32_t dx1gcr; +#if STM32MP_DDR_DUAL_AXI_PORT uint32_t dx2gcr; uint32_t dx3gcr; +#endif }; struct stm32mp1_ddrphy_timing { @@ -143,12 +144,14 @@ struct stm32mp1_ddrphy_cal { uint32_t dx1dllcr; uint32_t dx1dqtr; uint32_t dx1dqstr; +#if STM32MP_DDR_DUAL_AXI_PORT uint32_t dx2dllcr; uint32_t dx2dqtr; uint32_t dx2dqstr; uint32_t dx3dllcr; uint32_t dx3dqtr; uint32_t dx3dqstr; +#endif }; struct stm32mp1_ddr_info { @@ -166,9 +169,13 @@ struct stm32mp1_ddr_config { struct stm32mp1_ddrphy_reg p_reg; struct stm32mp1_ddrphy_timing p_timing; struct stm32mp1_ddrphy_cal p_cal; + bool p_cal_present; + bool self_refresh; + uint32_t zdata; }; int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed); void stm32mp1_ddr_init(struct ddr_info *priv, struct stm32mp1_ddr_config *config); + #endif /* STM32MP1_DDR_H */ diff --git a/include/drivers/st/stm32mp1_ddr_helpers.h b/include/drivers/st/stm32mp1_ddr_helpers.h index 38f24152a9..f09f05d555 100644 --- a/include/drivers/st/stm32mp1_ddr_helpers.h +++ b/include/drivers/st/stm32mp1_ddr_helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,24 @@ #ifndef STM32MP1_DDR_HELPERS_H #define STM32MP1_DDR_HELPERS_H +#include +#include + +enum stm32mp1_ddr_sr_mode { + DDR_SR_MODE_INVALID = 0, + DDR_SSR_MODE, + DDR_HSR_MODE, + DDR_ASR_MODE, +}; + void ddr_enable_clock(void); +int ddr_sw_self_refresh_exit(void); +uint32_t ddr_get_io_calibration_val(void); +int ddr_standby_sr_entry(void); +enum stm32mp1_ddr_sr_mode ddr_read_sr_mode(void); +void ddr_set_sr_mode(enum stm32mp1_ddr_sr_mode mode); +void ddr_save_sr_mode(void); +void ddr_restore_sr_mode(void); +bool ddr_is_nonsecured_area(uintptr_t address, uint32_t length); #endif /* STM32MP1_DDR_HELPERS_H */ diff --git a/include/drivers/st/stm32mp1_ddr_regs.h b/include/drivers/st/stm32mp1_ddr_regs.h index 342239a52d..e298fcf77b 100644 --- a/include/drivers/st/stm32mp1_ddr_regs.h +++ b/include/drivers/st/stm32mp1_ddr_regs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -128,6 +128,7 @@ struct stm32mp1_ddrctl { uint32_t pcfgwqos1_0; /* 0x4a0 Write QoS Configuration 1 */ uint8_t reserved4a4[0x4b4 - 0x4a4]; +#if STM32MP_DDR_DUAL_AXI_PORT /* PORT 1 */ uint32_t pcfgr_1; /* 0x4b4 Configuration Read */ uint32_t pcfgw_1; /* 0x4b8 Configuration Write */ @@ -137,6 +138,7 @@ struct stm32mp1_ddrctl { uint32_t pcfgqos1_1; /* 0x548 Read QoS Configuration 1 */ uint32_t pcfgwqos0_1; /* 0x54c Write QoS Configuration 0 */ uint32_t pcfgwqos1_1; /* 0x550 Write QoS Configuration 1 */ +#endif } __packed; /* DDR Physical Interface Control (DDRPHYC) registers*/ @@ -214,6 +216,7 @@ struct stm32mp1_ddrphy { uint32_t dx1dqtr; /* 0x210 Byte lane 1 DQ Timing */ uint32_t dx1dqstr; /* 0x214 Byte lane 1 QS Timing */ uint8_t res6[0x240 - 0x218]; /* 0x218 */ +#if STM32MP_DDR_DUAL_AXI_PORT uint32_t dx2gcr; /* 0x240 Byte lane 2 General Configuration */ uint32_t dx2gsr0; /* 0x244 Byte lane 2 General Status 0 */ uint32_t dx2gsr1; /* 0x248 Byte lane 2 General Status 1 */ @@ -227,6 +230,7 @@ struct stm32mp1_ddrphy { uint32_t dx3dllcr; /* 0x28c Byte lane 3 DLL Control */ uint32_t dx3dqtr; /* 0x290 Byte lane 3 DQ Timing */ uint32_t dx3dqstr; /* 0x294 Byte lane 3 QS Timing */ +#endif } __packed; /* DDR Controller registers offsets */ @@ -249,7 +253,9 @@ struct stm32mp1_ddrphy { #define DDRCTRL_SWSTAT 0x324 #define DDRCTRL_PSTAT 0x3FC #define DDRCTRL_PCTRL_0 0x490 +#if STM32MP_DDR_DUAL_AXI_PORT #define DDRCTRL_PCTRL_1 0x540 +#endif /* DDR Controller Register fields */ #define DDRCTRL_MSTR_DDR3 BIT(0) @@ -284,7 +290,7 @@ struct stm32mp1_ddrphy { #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(19, 12) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(23, 16) #define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) #define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) @@ -339,10 +345,12 @@ struct stm32mp1_ddrphy { #define DDRPHYC_DX0DLLCR 0x1CC #define DDRPHYC_DX1GCR 0x200 #define DDRPHYC_DX1DLLCR 0x20C +#if STM32MP_DDR_DUAL_AXI_PORT #define DDRPHYC_DX2GCR 0x240 #define DDRPHYC_DX2DLLCR 0x24C #define DDRPHYC_DX3GCR 0x280 #define DDRPHYC_DX3DLLCR 0x28C +#endif /* DDR PHY Register fields */ #define DDRPHYC_PIR_INIT BIT(0) @@ -380,6 +388,7 @@ struct stm32mp1_ddrphy { #define DDRPHYC_PTR0_TITMSRST_OFFSET 18 #define DDRPHYC_PTR0_TITMSRST_MASK GENMASK(21, 18) +#define DDRPHYC_ACIOCR_ACOE BIT(1) #define DDRPHYC_ACIOCR_ACPDD BIT(3) #define DDRPHYC_ACIOCR_ACPDR BIT(4) #define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK(10, 8) @@ -399,6 +408,7 @@ struct stm32mp1_ddrphy { #define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK(23, 20) #define DDRPHYC_DSGCR_ODTPDD_0 BIT(20) #define DDRPHYC_DSGCR_NL2PD BIT(24) +#define DDRPHYC_DSGCR_CKOE BIT(28) #define DDRPHYC_ZQ0CRN_ZDATA_MASK GENMASK(27, 0) #define DDRPHYC_ZQ0CRN_ZDATA_SHIFT 0 diff --git a/include/drivers/st/stm32mp1_pwr.h b/include/drivers/st/stm32mp1_pwr.h index e17df44fb7..9b662f2d1c 100644 --- a/include/drivers/st/stm32mp1_pwr.h +++ b/include/drivers/st/stm32mp1_pwr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,20 +13,39 @@ #define PWR_CR2 U(0x08) #define PWR_CR3 U(0x0C) #define PWR_MPUCR U(0x10) +#define PWR_MCUCR U(0x14) #define PWR_WKUPCR U(0x20) #define PWR_MPUWKUPENR U(0x28) +#define PWR_OFFSET_MASK GENMASK(9, 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_CR2_BRRDY BIT(16) +#define PWR_CR2_RRRDY BIT(17) + +#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_MCUCR_PDDS BIT(0) + +#define PWR_WKUPCR_MASK GENMASK(27, 16) | GENMASK(13, 8) | GENMASK(5, 0) + +#define PWR_MPUWKUPENR_MASK GENMASK(5, 0) + #endif /* STM32MP1_PWR_H */ diff --git a/include/drivers/st/stm32mp1_ram.h b/include/drivers/st/stm32mp1_ram.h index 38360e7595..adecd409df 100644 --- a/include/drivers/st/stm32mp1_ram.h +++ b/include/drivers/st/stm32mp1_ram.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2015-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,7 @@ #ifndef STM32MP1_RAM_H #define STM32MP1_RAM_H +bool stm32mp1_ddr_is_restored(void); int stm32mp1_ddr_probe(void); #endif /* STM32MP1_RAM_H */ diff --git a/include/drivers/st/stm32mp1_rcc.h b/include/drivers/st/stm32mp1_rcc.h index 2ffc3b2bc2..feaac43a13 100644 --- a/include/drivers/st/stm32mp1_rcc.h +++ b/include/drivers/st/stm32mp1_rcc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2015-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -394,7 +394,8 @@ #define RCC_HSICFGR_HSITRIM_SHIFT 8 #define RCC_HSICFGR_HSITRIM_MASK GENMASK(14, 8) #define RCC_HSICFGR_HSICAL_SHIFT 16 -#define RCC_HSICFGR_HSICAL_MASK GENMASK(27, 16) +#define RCC_HSICFGR_HSICAL_MASK GENMASK(24, 16) +#define RCC_HSICFGR_HSICAL_TEMP_MASK GENMASK(27, 25) /* Fields of RCC_CSICFGR register */ #define RCC_CSICFGR_CSITRIM_SHIFT 8 @@ -453,12 +454,18 @@ #define RCC_MP_SREQCLRR_STPREQ_P0 BIT(0) #define RCC_MP_SREQCLRR_STPREQ_P1 BIT(1) +/* Global Control Register */ +#define RCC_MP_GCR_BOOT_MCU BIT(0) + /* Values of RCC_UART24CKSELR register */ #define RCC_UART24CKSELR_HSI 0x00000002 /* Values of RCC_MP_APB1ENSETR register */ #define RCC_MP_APB1ENSETR_UART4EN BIT(16) +/* Values of RCC_MP_APB4ENSETR register */ +#define RCC_MP_APB4ENSETR_IWDG2APBEN BIT(15) + /* Values of RCC_MP_APB5ENSETR register */ #define RCC_MP_APB5ENSETR_SPI6EN BIT(0) #define RCC_MP_APB5ENSETR_I2C4EN BIT(2) @@ -466,8 +473,15 @@ #define RCC_MP_APB5ENSETR_USART1EN BIT(4) #define RCC_MP_APB5ENSETR_RTCAPBEN BIT(8) #define RCC_MP_APB5ENSETR_IWDG1APBEN BIT(15) +#define RCC_MP_APB5ENSETR_STGENEN BIT(20) /* Values of RCC_MP_AHB4ENSETR register */ +#define RCC_MP_AHB4ENSETR_GPIOAEN BIT(0) +#define RCC_MP_AHB4ENSETR_GPIOBEN BIT(1) +#define RCC_MP_AHB4ENSETR_GPIOCEN BIT(2) +#define RCC_MP_AHB4ENSETR_GPIODEN BIT(3) +#define RCC_MP_AHB4ENSETR_GPIOEEN BIT(4) +#define RCC_MP_AHB4ENSETR_GPIOFEN BIT(5) #define RCC_MP_AHB4ENSETR_GPIOGEN BIT(6) #define RCC_MP_AHB4ENSETR_GPIOHEN BIT(7) @@ -477,6 +491,12 @@ #define RCC_MP_AHB5ENSETR_HASH1EN BIT(5) #define RCC_MP_AHB5ENSETR_RNG1EN BIT(6) +/* Values of RCC_AHB6RSTSETR register */ +#define RCC_AHB6RSTSETR_GPURST BIT(5) + +/* Values of RCC_MP_APB5LPENSETR register */ +#define RCC_MP_APB5LPENSETR_STGENSTPEN BIT(21) + /* Values of RCC_MP_IWDGFZSETR register */ #define RCC_MP_IWDGFZSETR_IWDG1 BIT(0) #define RCC_MP_IWDGFZSETR_IWDG2 BIT(1) @@ -562,4 +582,12 @@ #define RCC_USBCKSELR_USBOSRC_MASK BIT(4) #define RCC_USBCKSELR_USBOSRC_SHIFT 4 +/* RCC_MPCKSELR register fields */ +#define RCC_MPCKSELR_MPUSRC_MASK GENMASK(1, 0) +#define RCC_MPCKSELR_MPUSRC_SHIFT 0 + +/* RCC_CPERCKSELR register fields */ +#define RCC_CPERCKSELR_PERSRC_MASK GENMASK(1, 0) +#define RCC_CPERCKSELR_PERSRC_SHIFT 0 + #endif /* STM32MP1_RCC_H */ diff --git a/include/drivers/st/stm32mp_clkfunc.h b/include/drivers/st/stm32mp_clkfunc.h index c7e0b6e6fb..1386fa6e29 100644 --- a/include/drivers/st/stm32mp_clkfunc.h +++ b/include/drivers/st/stm32mp_clkfunc.h @@ -14,18 +14,29 @@ #include int fdt_osc_read_freq(const char *name, uint32_t *freq); -bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name); -uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, +bool fdt_clk_read_bool(const char *node_label, const char *prop_name); +uint32_t fdt_clk_read_uint32_default(const char *node_label, const char *prop_name, uint32_t dflt_value); -int fdt_get_rcc_node(void *fdt); int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count, uint32_t *array); +uint32_t fdt_rcc_read_uint32_default(const char *prop_name, + uint32_t dflt_value); int fdt_rcc_subnode_offset(const char *name); const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp); -bool fdt_get_rcc_secure_status(void); +bool fdt_get_rcc_secure_state(void); +int fdt_rcc_enable_it(const char *name); int fdt_get_clock_id(int node); +int fdt_get_clock_id_by_name(int node, const char *name); +unsigned long fdt_get_uart_clock_freq(uintptr_t instance); + +bool fdt_is_pll1_predefined(void); + +void stm32mp_stgen_config(unsigned long rate); +void stm32mp_stgen_restore_counter(unsigned long long value, + unsigned long long offset_in_ms); +unsigned long long stm32mp_stgen_get_counter(void); #endif /* STM32MP_CLKFUNC_H */ diff --git a/include/drivers/st/stm32mp_dummy_regulator.h b/include/drivers/st/stm32mp_dummy_regulator.h new file mode 100644 index 0000000000..6804192ba0 --- /dev/null +++ b/include/drivers/st/stm32mp_dummy_regulator.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_DUMMY_REGULATOR_H +#define STM32MP_DUMMY_REGULATOR_H + +#include + +void bind_dummy_regulator(struct stm32mp_regulator *regu); + +#endif /* STM32MP_DUMMY_REGULATOR_H */ diff --git a/include/drivers/st/stm32mp_pmic.h b/include/drivers/st/stm32mp_pmic.h index 984cd60143..898a28b44c 100644 --- a/include/drivers/st/stm32mp_pmic.h +++ b/include/drivers/st/stm32mp_pmic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,8 @@ #include +#include + /* * dt_pmic_status - Check PMIC status from device tree * @@ -25,7 +27,24 @@ int dt_pmic_status(void); * * Returns 0 on success, and negative values on errors */ -int dt_pmic_configure_boot_on_regulators(void); +int pmic_configure_boot_on_regulators(void); + +int pmic_set_lp_config(const char *node_name); + +/* + * dt_pmic_find_supply - Find the supply name related to a regulator name + * + * Returns 0 on success, and negative values on errors + */ +int dt_pmic_find_supply(const char **supply_name, const char *regu_name); + +/* + * pmic_set_regulator_min_voltage - Set target supply to its device tree + * "regulator-min-microvolt" value. + * + * Returns 0 on success, and negative values on errors + */ +int pmic_set_regulator_min_voltage(const char *regu_name); /* * initialize_pmic_i2c - Initialize I2C for the PMIC control @@ -41,6 +60,24 @@ bool initialize_pmic_i2c(void); */ void initialize_pmic(void); +/* + * configure_pmic - PMIC configuration function, called at platform init + * + * Panics on errors + */ +void configure_pmic(void); + +#if DEBUG +void print_pmic_info_and_debug(void); +#else +static inline void print_pmic_info_and_debug(void) +{ +} +#endif + +bool is_pmic_regulator(struct stm32mp_regulator *regu); +void bind_pmic_regulator(struct stm32mp_regulator *regu); + /* * pmic_ddr_power_init - Initialize regulators required for DDR * diff --git a/include/drivers/st/stm32mp_regulator.h b/include/drivers/st/stm32mp_regulator.h new file mode 100644 index 0000000000..7a66b97ba7 --- /dev/null +++ b/include/drivers/st/stm32mp_regulator.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_REGULATOR_H +#define STM32MP_REGULATOR_H + +#include + +struct stm32mp_regulator; + +struct stm32mp_regulator_ops { + int (*enable)(struct stm32mp_regulator *regu); + int (*disable)(struct stm32mp_regulator *regu); +}; + +struct stm32mp_regulator { + const struct stm32mp_regulator_ops *ops; + int id; + bool always_on; +}; + +int stm32mp_regulator_enable(struct stm32mp_regulator *regu); +int stm32mp_regulator_disable(struct stm32mp_regulator *regu); +int stm32mp_regulator_register(struct stm32mp_regulator *regu); + +int plat_bind_regulator(struct stm32mp_regulator *regu); + +#endif /* STM32MP_REGULATOR_H */ diff --git a/include/drivers/st/stm32mp_reset.h b/include/drivers/st/stm32mp_reset.h index 84448050d7..8c43c0b5b1 100644 --- a/include/drivers/st/stm32mp_reset.h +++ b/include/drivers/st/stm32mp_reset.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -47,4 +47,16 @@ static inline void stm32mp_reset_release(uint32_t reset_id) (void)stm32mp_reset_deassert(reset_id, 0U); } +/* + * Manage reset control for the MCU reset + * + * @assert_not_deassert: reset requested state + */ +void stm32mp_reset_assert_deassert_to_mcu(bool assert_not_deassert); + +/* + * Manage system reset control + */ +void __dead2 stm32mp_system_reset(void); + #endif /* STM32MP_RESET_H */ diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h index f7e293b189..5c8933d84d 100644 --- a/include/drivers/st/stpmic1.h +++ b/include/drivers/st/stpmic1.h @@ -86,15 +86,15 @@ #define ITSOURCE4_REG 0xB3U /* Registers masks */ -#define LDO_VOLTAGE_MASK 0x7CU -#define BUCK_VOLTAGE_MASK 0xFCU +#define LDO_VOLTAGE_MASK GENMASK(6, 2) +#define BUCK_VOLTAGE_MASK GENMASK(7, 2) #define LDO_BUCK_VOLTAGE_SHIFT 2 -#define LDO_BUCK_ENABLE_MASK 0x01U -#define LDO_BUCK_HPLP_ENABLE_MASK 0x02U +#define LDO_BUCK_ENABLE_MASK BIT(0) +#define LDO_BUCK_HPLP_ENABLE_MASK BIT(1) #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 +#define LDO_BUCK_RANK_MASK BIT(0) +#define LDO_BUCK_RESET_MASK BIT(0) +#define LDO_BUCK_PULL_DOWN_MASK GENMASK(1, 0) /* Pull down register */ #define BUCK1_PULL_DOWN_SHIFT 0 @@ -135,12 +135,12 @@ /* 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_MASK GENMASK(1, 0) #define VINLOW_HYST_SHIFT 4 -#define VINLOW_THRESHOLD_MASK 0x7 +#define VINLOW_THRESHOLD_MASK GENMASK(2, 0) #define VINLOW_THRESHOLD_SHIFT 1 -#define VINLOW_ENABLED 0x01 -#define VINLOW_CTRL_REG_MASK 0xFF +#define VINLOW_ENABLED BIT(0) +#define VINLOW_CTRL_REG_MASK GENMASK(7, 0) /* USB Control Register */ #define BOOST_OVP_DISABLED BIT(7) @@ -148,6 +148,7 @@ #define OCP_LIMIT_HIGH BIT(3) #define SWIN_SWOUT_ENABLED BIT(2) #define USBSW_OTG_SWITCH_ENABLED BIT(1) +#define BOOST_ENABLED BIT(0) int stpmic1_powerctrl_on(void); int stpmic1_switch_off(void); @@ -156,11 +157,15 @@ 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); +bool 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_pull_down_set(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); void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr); int stpmic1_get_version(unsigned long *version); diff --git a/include/drivers/st/usb_dwc2.h b/include/drivers/st/usb_dwc2.h new file mode 100644 index 0000000000..58a0e4b6e5 --- /dev/null +++ b/include/drivers/st/usb_dwc2.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef USB_DWC2_H +#define USB_DWC2_H + +#include + +#define USB_MAX_ENDPOINT_NB 0x10 + +void usb_dwc2_init_driver(usb_handle_t *usb_core_handle, + pcd_handle_t *pcd_handle, + void *base_register); + +#endif /* USB_DWC2_H */ + diff --git a/include/dt-bindings/clock/stm32mp1-clks.h b/include/dt-bindings/clock/stm32mp1-clks.h index 67e66b23fd..cfda43310b 100644 --- a/include/dt-bindings/clock/stm32mp1-clks.h +++ b/include/dt-bindings/clock/stm32mp1-clks.h @@ -179,6 +179,12 @@ #define DAC12_K 168 #define ETHPTP_K 169 +#define PCLK1 170 +#define PCLK2 171 +#define PCLK3 172 +#define PCLK4 173 +#define PCLK5 174 + /* PLL */ #define PLL1 176 #define PLL2 177 diff --git a/include/dt-bindings/power/stm32mp1-power.h b/include/dt-bindings/power/stm32mp1-power.h new file mode 100644 index 0000000000..d588dd71f3 --- /dev/null +++ b/include/dt-bindings/power/stm32mp1-power.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ +/* + * Copyright (C) 2018-2019, STMicroelectronics - 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/include/dt-bindings/reset/stm32mp1-resets.h b/include/dt-bindings/reset/stm32mp1-resets.h index bc71924faa..f3a0ed3178 100644 --- a/include/dt-bindings/reset/stm32mp1-resets.h +++ b/include/dt-bindings/reset/stm32mp1-resets.h @@ -7,6 +7,7 @@ #ifndef _DT_BINDINGS_STM32MP1_RESET_H_ #define _DT_BINDINGS_STM32MP1_RESET_H_ +#define MCU_HOLD_BOOT_R 2144 #define LTDC_R 3072 #define DSI_R 3076 #define DDRPERFM_R 3080 @@ -117,5 +118,6 @@ #define RST_SCMI0_RNG1 8 #define RST_SCMI0_MDMA 9 #define RST_SCMI0_MCU 10 +#define RST_SCMI0_MCU_HOLD_BOOT 11 #endif /* _DT_BINDINGS_STM32MP1_RESET_H_ */ diff --git a/include/dt-bindings/soc/st,stm32-etzpc.h b/include/dt-bindings/soc/st,stm32-etzpc.h index 3f9fb3b12f..199c83154d 100644 --- a/include/dt-bindings/soc/st,stm32-etzpc.h +++ b/include/dt-bindings/soc/st,stm32-etzpc.h @@ -17,4 +17,90 @@ #define DECPROT_UNLOCK 0x0 #define DECPROT_LOCK 0x1 +/* 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_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(id, mode, lock) (((id) << 16) | ((mode) << 8) | (lock)) + #endif /* _DT_BINDINGS_STM32_ETZPC_H */ diff --git a/include/dt-bindings/soc/stm32mp1-tzc400.h b/include/dt-bindings/soc/stm32mp1-tzc400.h new file mode 100644 index 0000000000..88fbcdd814 --- /dev/null +++ b/include/dt-bindings/soc/stm32mp1-tzc400.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + * Author(s): Lionel DEBIEVE for STMicroelectronics. + */ + +#ifndef _DT_BINDINGS_STM32MP1_TZC400_H +#define _DT_BINDINGS_STM32MP1_TZC400_H + +#include + +#define STM32MP1_TZC_A7_ID U(0) +#define STM32MP1_TZC_M4_ID U(1) +#define STM32MP1_TZC_LCD_ID U(3) +#define STM32MP1_TZC_GPU_ID U(4) +#define STM32MP1_TZC_MDMA_ID U(5) +#define STM32MP1_TZC_DMA_ID U(6) +#define STM32MP1_TZC_USB_HOST_ID U(7) +#define STM32MP1_TZC_USB_OTG_ID U(8) +#define STM32MP1_TZC_SDMMC_ID U(9) +#define STM32MP1_TZC_ETH_ID U(10) +#define STM32MP1_TZC_DAP_ID U(15) + +#define TZC_REGION_NSEC_ALL_ACCESS_RDWR \ + (TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_GPU_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_LCD_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_MDMA_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_M4_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DMA_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_HOST_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_OTG_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_ETH_ID) | \ + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DAP_ID)) + +#endif /* _DT_BINDINGS_STM32MP1_TZC400_H */ diff --git a/include/lib/optee_utils.h b/include/lib/optee_utils.h index 6067caff42..ba44f998ec 100644 --- a/include/lib/optee_utils.h +++ b/include/lib/optee_utils.h @@ -9,6 +9,7 @@ #include +int get_optee_header_ep(entry_point_info_t *header_ep, uintptr_t *pc); int parse_optee_header(entry_point_info_t *header_ep, image_info_t *pager_image_info, image_info_t *paged_image_info); diff --git a/include/lib/psci/psci.h b/include/lib/psci/psci.h index b56e98b5f3..9f2a80ebeb 100644 --- a/include/lib/psci/psci.h +++ b/include/lib/psci/psci.h @@ -349,6 +349,7 @@ int psci_node_hw_state(u_register_t target_cpu, int psci_features(unsigned int psci_fid); void __dead2 psci_power_down_wfi(void); void psci_arch_setup(void); +unsigned int psci_is_last_on_cpu(void); #endif /*__ASSEMBLER__*/ diff --git a/include/lib/usb/usb_core.h b/include/lib/usb/usb_core.h new file mode 100644 index 0000000000..dbc311d8bb --- /dev/null +++ b/include/lib/usb/usb_core.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef USB_CORE_H +#define USB_CORE_H + +#include + +#include + +#define USBD_MAX_NUM_INTERFACES 1 +#define USBD_MAX_NUM_CONFIGURATION 1 + +#define USB_LEN_DEV_QUALIFIER_DESC 0x0A +#define USB_LEN_DEV_DESC 0x12 +#define USB_LEN_CFG_DESC 0x09 +#define USB_LEN_IF_DESC 0x09 +#define USB_LEN_EP_DESC 0x07 +#define USB_LEN_OTG_DESC 0x03 +#define USB_LEN_LANGID_STR_DESC 0x04 +#define USB_LEN_OTHER_SPEED_DESC_SIZ 0x09 + +#define USBD_IDX_LANGID_STR 0x00 +#define USBD_IDX_MFC_STR 0x01 +#define USBD_IDX_PRODUCT_STR 0x02 +#define USBD_IDX_SERIAL_STR 0x03 +#define USBD_IDX_CONFIG_STR 0x04 +#define USBD_IDX_INTERFACE_STR 0x05 +#define USBD_IDX_USER0_STR 0x06 + +#define USB_REQ_TYPE_STANDARD 0x00 +#define USB_REQ_TYPE_CLASS 0x20 +#define USB_REQ_TYPE_VENDOR 0x40 +#define USB_REQ_TYPE_MASK 0x60 + +#define USB_REQ_RECIPIENT_DEVICE 0x00 +#define USB_REQ_RECIPIENT_INTERFACE 0x01 +#define USB_REQ_RECIPIENT_ENDPOINT 0x02 +#define USB_REQ_RECIPIENT_MASK 0x1F + +#define USB_REQ_DIRECTION 0x80 + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_DESC_TYPE_DEVICE 0x01 +#define USB_DESC_TYPE_CONFIGURATION 0x02 +#define USB_DESC_TYPE_STRING 0x03 +#define USB_DESC_TYPE_INTERFACE 0x04 +#define USB_DESC_TYPE_ENDPOINT 0x05 +#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06 +#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07 +#define USB_DESC_TYPE_BOS 0x0F + +#define USB_CONFIG_REMOTE_WAKEUP 2 +#define USB_CONFIG_SELF_POWERED 1 + +#define USB_FEATURE_EP_HALT 0 +#define USB_FEATURE_REMOTE_WAKEUP 1 +#define USB_FEATURE_TEST_MODE 2 + +#define USB_DEVICE_CAPABITY_TYPE 0x10 + +#define USB_MAX_EP0_SIZE 64 + +/* Device Status */ +#define USBD_STATE_DEFAULT 1 +#define USBD_STATE_ADDRESSED 2 +#define USBD_STATE_CONFIGURED 3 +#define USBD_STATE_SUSPENDED 4 + +/* EP0 State */ +#define USBD_EP0_IDLE 0 +#define USBD_EP0_SETUP 1 +#define USBD_EP0_DATA_IN 2 +#define USBD_EP0_DATA_OUT 3 +#define USBD_EP0_STATUS_IN 4 +#define USBD_EP0_STATUS_OUT 5 +#define USBD_EP0_STALL 6 + +#define USBD_EP_TYPE_CTRL 0 +#define USBD_EP_TYPE_ISOC 1 +#define USBD_EP_TYPE_BULK 2 +#define USBD_EP_TYPE_INTR 3 + +#define USB_OTG_SPEED_HIGH 0 +#define USB_OTG_SPEED_HIGH_IN_FULL 1 +#define USB_OTG_SPEED_LOW 2 +#define USB_OTG_SPEED_FULL 3 + +#define USBD_OUT_EPNUM_MASK GENMASK(15, 0) +#define USBD_OUT_COUNT_MASK GENMASK(31, 16) +#define USBD_OUT_COUNT_SHIFT 16U + +#define LOBYTE(x) ((uint8_t)((x) & 0x00FF)) +#define HIBYTE(x) ((uint8_t)(((x) & 0xFF00) >> 8)) + +typedef struct { + uint8_t bm_request; + uint8_t b_request; + uint16_t value; + uint16_t index; + uint16_t length; +} usb_setup_req_t; + +struct usb_handle; + +typedef struct { + uint8_t (*init)(struct usb_handle *pdev, uint8_t cfgidx); + uint8_t (*de_init)(struct usb_handle *pdev, uint8_t cfgidx); + /* Control Endpoints*/ + uint8_t (*setup)(struct usb_handle *pdev, usb_setup_req_t *req); + uint8_t (*ep0_tx_sent)(struct usb_handle *pdev); + uint8_t (*ep0_rx_ready)(struct usb_handle *pdev); + /* Class Specific Endpoints*/ + uint8_t (*data_in)(struct usb_handle *pdev, uint8_t epnum); + uint8_t (*data_out)(struct usb_handle *pdev, uint8_t epnum); + uint8_t (*sof)(struct usb_handle *pdev); + uint8_t (*iso_in_incomplete)(struct usb_handle *pdev, uint8_t epnum); + uint8_t (*iso_out_incomplete)(struct usb_handle *pdev, uint8_t epnum); +} usb_class_t; + +/* Following USB Device status */ +typedef enum { + USBD_OK = 0, + USBD_BUSY, + USBD_FAIL, + USBD_TIMEOUT +} usb_status_t; + +/* Action to do after IT handling */ +typedef enum { + USB_NOTHING = 0, + USB_DATA_OUT, + USB_DATA_IN, + USB_SETUP, + USB_ENUM_DONE, + USB_READ_DATA_PACKET, + USB_READ_SETUP_PACKET, + USB_RESET, + USB_RESUME, + USB_SUSPEND, + USB_LPM, + USB_SOF, + USB_DISCONNECT, + USB_WRITE_EMPTY +} usb_action_t; + +/* USB Device descriptors structure */ +typedef struct { + uint8_t *(*get_device_desc)(uint16_t *length); + uint8_t *(*get_lang_id_desc)(uint16_t *length); + uint8_t *(*get_manufacturer_desc)(uint16_t *length); + uint8_t *(*get_product_desc)(uint16_t *length); + uint8_t *(*get_serial_desc)(uint16_t *length); + uint8_t *(*get_configuration_desc)(uint16_t *length); + uint8_t *(*get_interface_desc)(uint16_t *length); + uint8_t *(*get_usr_desc)(uint8_t index, uint16_t *length); + uint8_t *(*get_config_desc)(uint16_t *length); + uint8_t *(*get_device_qualifier_desc)(uint16_t *length); +} usb_desc_t; + +/* USB Device handle structure */ +typedef struct { + uint32_t status; + uint32_t total_length; + uint32_t rem_length; + uint32_t maxpacket; +} usb_endpoint_t; + +typedef struct { + uint8_t num; /* Endpoint number + * This parameter must be a number between Min_Data = 1 + * and Max_Data = 15 + */ + bool is_in; /* Endpoint direction */ + uint8_t type; /* Endpoint type */ + uint32_t maxpacket; /* Endpoint Max packet size + * This parameter must be a number between + * Min_Data = 0 and Max_Data = 64KB + */ + uint8_t *xfer_buff; /* Pointer to transfer buffer */ + uint32_t xfer_len; /* Current transfer length */ + uint32_t xfer_count; /* Partial transfer length in case of multi + * packet transfer + */ +} usbd_ep_t; + +typedef enum { + LPM_L0 = 0x00, /* on */ + LPM_L1 = 0x01, /* LPM L1 sleep */ + LPM_L2 = 0x02, /* suspend */ + LPM_L3 = 0x03, /* off */ +} pcd_lpm_state_t; + +/* USB Device descriptors structure */ +typedef struct { + usb_status_t (*ep0_out_start)(void *handle); + usb_status_t (*ep_start_xfer)(void *handle, usbd_ep_t *ep); + usb_status_t (*ep0_start_xfer)(void *handle, usbd_ep_t *ep); + usb_status_t (*write_packet)(void *handle, uint8_t *src, + uint8_t ch_ep_num, uint16_t len); + void *(*read_packet)(void *handle, uint8_t *dest, uint16_t len); + usb_status_t (*ep_set_stall)(void *handle, usbd_ep_t *ep); + usb_status_t (*start_device)(void *handle); + usb_status_t (*stop_device)(void *handle); + usb_status_t (*set_address)(void *handle, uint8_t address); + usb_status_t (*write_empty_tx_fifo)(void *handle, + uint32_t epnum, uint32_t xfer_len, + uint32_t *xfer_count, + uint32_t maxpacket, + uint8_t **xfer_buff); + usb_action_t (*it_handler)(void *handle, uint32_t *param); +} usb_driver_t; + +/* USB Peripheral Controller Drivers */ +typedef struct { + void *instance; /* Register base address */ + usbd_ep_t in_ep[15]; /* IN endpoint parameters */ + usbd_ep_t out_ep[15]; /* OUT endpoint parameters */ + uint32_t setup[12]; /* Setup packet buffer */ + pcd_lpm_state_t lpm_state; /* LPM State */ +} pcd_handle_t; + +/* USB Device handle structure */ +typedef struct usb_handle { + uint8_t id; + uint32_t dev_config; + uint32_t dev_config_status; + usb_endpoint_t ep_in[15]; + usb_endpoint_t ep_out[15]; + uint32_t ep0_state; + uint32_t ep0_data_len; + uint8_t dev_state; + uint8_t dev_old_state; + uint8_t dev_address; + uint32_t dev_remote_wakeup; + usb_setup_req_t request; + const usb_desc_t *desc; + usb_class_t *class; + void *class_data; + void *user_data; + pcd_handle_t *data; + const usb_driver_t *driver; +} usb_handle_t; + +usb_status_t usb_core_handle_it(usb_handle_t *pdev); +usb_status_t usb_core_receive(usb_handle_t *pdev, uint8_t ep_addr, + uint8_t *p_buf, uint32_t len); +usb_status_t usb_core_transmit(usb_handle_t *pdev, uint8_t ep_addr, + uint8_t *p_buf, uint32_t len); +usb_status_t usb_core_receive_ep0(usb_handle_t *pdev, uint8_t *p_buf, + uint32_t len); +usb_status_t usb_core_transmit_ep0(usb_handle_t *pdev, uint8_t *p_buf, + uint32_t len); +void usb_core_ctl_error(usb_handle_t *pdev); +usb_status_t usb_core_start(usb_handle_t *pdev); +usb_status_t usb_core_stop(usb_handle_t *pdev); +usb_status_t register_usb_driver(usb_handle_t *pdev, pcd_handle_t *pcd_handle, + const usb_driver_t *driver, + void *driver_handle); +usb_status_t register_platform(usb_handle_t *pdev, + const usb_desc_t *plat_call_back); + +#endif /* USB_CORE_H */ diff --git a/include/lib/usb/usb_st_dfu.h b/include/lib/usb/usb_st_dfu.h new file mode 100644 index 0000000000..bc043c091f --- /dev/null +++ b/include/lib/usb/usb_st_dfu.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef USB_ST_DFU_H +#define USB_ST_DFU_H + +#include + +#include + +#define DFU_DESCRIPTOR_TYPE 0x21 + +/* Max DFU Packet Size = 1024 bytes */ +#define USBD_DFU_XFER_SIZE 1024 + +#define TRANSFER_SIZE_BYTES(size) \ + ((uint8_t)((size) & 0xFF)), /* XFERSIZEB0 */\ + ((uint8_t)((size) >> 8)) /* XFERSIZEB1 */ + +/* Descriptor of DFU interface 0 Alternate setting n */ +#define USBD_DFU_IF_DESC(n) 0x09, /* Interface Descriptor size */\ + USB_DESC_TYPE_INTERFACE, /* descriptor type */\ + 0x00, /* Number of Interface */\ + (n), /* Alternate setting */\ + 0x00, /* bNumEndpoints*/\ + 0xFE, /* Application Specific Class Code */\ + 0x01, /* Device Firmware Upgrade Code */\ + 0x02, /* DFU mode protocol */ \ + USBD_IDX_USER0_STR + (n) /* iInterface: + * Index of string + * descriptor + */ + +/* DFU1.1 Standard */ +#define USB_DFU_VERSION 0x0110 +#define USB_DFU_ITF_SIZ 9 +#define USB_DFU_DESC_SIZ(itf) (USB_DFU_ITF_SIZ * ((itf) + 2)) + +/* bmAttribute : + * bitCanDnload = 1(bit 0) + * bitCanUpload = 1(bit 1) + * bitManifestationTolerant = 1 (bit 2) + * bitWillDetach = 1(bit 3) + * Reserved (bit4-6) + * bitAcceleratedST = 0(bit 7) + */ +#define DFU_BM_ATTRIBUTE 0x0F + +#define DFU_MEDIA_STATE_READY 0x00 +#define DFU_MEDIA_STATE_WRITTEN 0x01 +#define DFU_MEDIA_STATE_ERROR 0x02 + +#define DFU_STATUS_SIZE 6U + +typedef void (*p_function)(void); + +/* Callback for media access */ +typedef struct { + int (*upload)(uint8_t alt, uintptr_t *buffer, uint32_t *len, + void *user_data); + int (*download)(uint8_t alt, uintptr_t *buffer, uint32_t *len, + void *user_data); + int (*manifestation)(uint8_t alt, void *user_data); +} usb_dfu_media_t; + +/* Internal DFU handle */ +typedef struct { + uint8_t status[DFU_STATUS_SIZE]; + uint8_t dev_state; + uint8_t dev_status; + uint32_t alt_setting; + const usb_dfu_media_t *callback; +} usb_dfu_handle_t; + +void usb_dfu_register(usb_handle_t *pdev, usb_dfu_handle_t *phandle); + +int usb_dfu_loop(usb_handle_t *pdev, const usb_dfu_media_t *pmedia); + +/* Function provided by plat */ +usb_handle_t *usb_dfu_plat_init(void); + +#endif /* USB_ST_DFU_H */ diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index 2d0e9c08eb..bf93444505 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -64,6 +64,16 @@ ((val) + _div - (__typeof__(div)) 1) / _div; \ }) +/* + * Macro for unsigned integer division with nearest rounding variant. + * Default integer division rounds down. + */ +#define udiv_round_nearest(x, y) __extension__ ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + (_x + (_y / 2)) / _y; \ +}) + #define MIN(x, y) __extension__ ({ \ __typeof__(x) _x = (x); \ __typeof__(y) _y = (y); \ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index ebcc855774..ab05be1aed 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -109,6 +109,11 @@ unsigned int plat_ic_get_interrupt_id(unsigned int raw); ******************************************************************************/ uintptr_t plat_get_my_stack(void); void plat_report_exception(unsigned int exception_type); +#if AARCH32_EXCEPTION_DEBUG +void plat_report_undef_inst(unsigned int fault_address); +void plat_report_prefetch_abort(unsigned int fault_address); +void plat_report_data_abort(unsigned int fault_address); +#endif int plat_crash_console_init(void); int plat_crash_console_putc(int c); void plat_crash_console_flush(void); @@ -116,7 +121,7 @@ void plat_error_handler(int err) __dead2; void plat_panic_handler(void) __dead2; const char *plat_log_get_prefix(unsigned int log_level); void bl2_plat_preload_setup(void); -int plat_try_next_boot_source(void); +int plat_try_next_boot_source(unsigned int image_id); /******************************************************************************* * Mandatory BL1 functions @@ -284,6 +289,8 @@ int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr); int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr); int plat_set_nv_ctr2(void *cookie, const struct auth_img_desc_s *img_desc, unsigned int nv_ctr); +int plat_get_hashed_pk(void *full_pk_ptr, unsigned int full_pk_len, + void **hashed_pk_ptr, unsigned int *hash_pk_len); int get_mbedtls_heap_helper(void **heap_addr, size_t *heap_size); int plat_get_enc_key_info(enum fw_enc_status_t fw_enc_status, uint8_t *key, size_t *key_len, unsigned int *flags, diff --git a/lib/aarch32/misc_helpers.S b/lib/aarch32/misc_helpers.S index e9734ac2c6..aea975c0ad 100644 --- a/lib/aarch32/misc_helpers.S +++ b/lib/aarch32/misc_helpers.S @@ -7,6 +7,8 @@ #include #include #include +#include +#include .globl smc .globl zeromem @@ -14,6 +16,9 @@ .globl memcpy4 .globl disable_mmu_icache_secure .globl disable_mmu_secure + .globl fixup_gdt_reloc + +#define PAGE_START_MASK ~(PAGE_SIZE_MASK) func smc /* @@ -187,3 +192,124 @@ func disable_mmu_icache_secure ldr r1, =(SCTLR_M_BIT | SCTLR_C_BIT | SCTLR_I_BIT) b do_disable_mmu endfunc disable_mmu_icache_secure + +/* --------------------------------------------------------------------------- + * Helper to fixup Global Descriptor table (GDT) and dynamic relocations + * (.rel.dyn) at runtime. + * + * This function is meant to be used when the firmware is compiled with -fpie + * and linked with -pie options. We rely on the linker script exporting + * appropriate markers for start and end of the section. For GOT, we + * expect __GOT_START__ and __GOT_END__. Similarly for .rela.dyn, we expect + * __RELA_START__ and __RELA_END__. + * + * The function takes the limits of the memory to apply fixups to as + * arguments (which is usually the limits of the relocable BL image). + * r0 - the start of the fixup region + * r1 - the limit of the fixup region + * These addresses have to be 4KB page aligned. + * --------------------------------------------------------------------------- + */ + +/* Relocation codes */ +#define R_ARM_RELATIVE 23 + +func fixup_gdt_reloc + mov r6, r0 + mov r7, r1 + +#if ENABLE_ASSERTIONS + /* Test if the limits are 4K aligned */ + orr r0, r0, r1 + mov r1, #(PAGE_SIZE_MASK) + tst r0, r1 + ASM_ASSERT(eq) +#endif + /* + * Calculate the offset based on return address in lr. + * Assume that this function is called within a page at the start of + * fixup region. + */ + ldr r1, =PAGE_START_MASK + and r2, lr, r1 + subs r0, r2, r6 /* Diff(S) = Current Address - Compiled Address */ + beq 3f /* Diff(S) = 0. No relocation needed */ + + ldr r1, =__GOT_START__ + add r1, r1, r0 + ldr r2, =__GOT_END__ + add r2, r2, r0 + + /* + * GOT is an array of 32_bit addresses which must be fixed up as + * new_addr = old_addr + Diff(S). + * The new_addr is the address currently the binary is executing from + * and old_addr is the address at compile time. + */ +1: ldr r3, [r1] + + /* Skip adding offset if address is < lower limit */ + cmp r3, r6 + blo 2f + + /* Skip adding offset if address is > upper limit */ + cmp r3, r7 + bhi 2f + add r3, r3, r0 + str r3, [r1] + +2: add r1, r1, #4 + cmp r1, r2 + blo 1b + + /* Starting dynamic relocations. Use ldr to get RELA_START and END */ +3: ldr r1, =__RELA_START__ + add r1, r1, r0 + ldr r2, =__RELA_END__ + add r2, r2, r0 + + /* + * According to ELF-32 specification, the RELA data structure is as + * follows: + * typedef struct { + * Elf32_Addr r_offset; + * Elf32_Xword r_info; + * } Elf32_Rela; + * + * r_offset is address of reference + * r_info is symbol index and type of relocation (in this case + * code 23 which corresponds to R_ARM_RELATIVE). + * + * Size of Elf32_Rela structure is 8 bytes. + */ + + /* Skip R_ARM_NONE entry with code 0 */ +1: ldr r3, [r1, #4] + ands r3, r3, #0xff + beq 2f + +#if ENABLE_ASSERTIONS + /* Assert that the relocation type is R_ARM_RELATIVE */ + cmp r3, #R_ARM_RELATIVE + ASM_ASSERT(eq) +#endif + ldr r3, [r1] /* r_offset */ + add r3, r0, r3 /* Diff(S) + r_offset */ + ldr r4, [r3] + + /* Skip adding offset if address is < lower limit */ + cmp r4, r6 + blo 2f + + /* Skip adding offset if address is >= upper limit */ + cmp r4, r7 + bhs 2f + + add r4, r0, r4 + str r4, [r3] + +2: add r1, r1, #8 + cmp r1, r2 + blo 1b + bx lr +endfunc fixup_gdt_reloc diff --git a/lib/optee/optee_utils.c b/lib/optee/optee_utils.c index 0ad108242e..18dbfe319a 100644 --- a/lib/optee/optee_utils.c +++ b/lib/optee/optee_utils.c @@ -86,7 +86,7 @@ static int parse_optee_image(image_info_t *image_info, * -1 indicates loader decided address; take our pre-mapped area * for current image since arm-tf could not allocate memory dynamically */ - if (init_load_addr == -1) + if (init_load_addr == (uintptr_t)-1) init_load_addr = image_info->image_base; /* Check that the default end address doesn't overflow */ @@ -129,6 +129,36 @@ static int parse_optee_image(image_info_t *image_info, return 0; } +/******************************************************************************* + * Parse the OPTEE header for an executable entry point address. + * Return 1 on success, 0 on failure. + ******************************************************************************/ +int get_optee_header_ep(entry_point_info_t *header_ep, uintptr_t *pc) +{ + optee_header_t *optee_header; + uint32_t num; + + assert(pc && header_ep && header_ep->pc); + optee_header = (optee_header_t *)header_ep->pc; + + if (!tee_validate_header(optee_header)) + return 0; + + for (num = 0U; num < optee_header->nb_images; num++) { + optee_image_t *optee_image = + &optee_header->optee_image_list[num]; + + if (optee_image->image_id != OPTEE_PAGER_IMAGE_ID) + continue; + + *pc = ((uint64_t)optee_image->load_addr_hi << 32) | + optee_image->load_addr_lo; + return 1; + } + + return 0; +} + /******************************************************************************* * Parse the OPTEE header * Return 0 on success or a negative error code otherwise. @@ -139,7 +169,8 @@ int parse_optee_header(entry_point_info_t *header_ep, { optee_header_t *header; - int num, ret; + uint32_t num; + int ret; assert(header_ep); header = (optee_header_t *)header_ep->pc; @@ -182,7 +213,7 @@ int parse_optee_header(entry_point_info_t *header_ep, } /* Parse OPTEE image */ - for (num = 0; num < header->nb_images; num++) { + for (num = 0U; num < header->nb_images; num++) { if (header->optee_image_list[num].image_id == OPTEE_PAGER_IMAGE_ID) { ret = parse_optee_image(pager_image_info, diff --git a/lib/psci/psci_private.h b/lib/psci/psci_private.h index e2dcfa8b1e..deb1d2d815 100644 --- a/lib/psci/psci_private.h +++ b/lib/psci/psci_private.h @@ -286,7 +286,6 @@ unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info); unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info); void psci_set_pwr_domains_to_run(unsigned int end_pwrlvl); void psci_print_power_domain_map(void); -unsigned int psci_is_last_on_cpu(void); int psci_spd_migrate_info(u_register_t *mpidr); void psci_do_pwrdown_sequence(unsigned int power_level); diff --git a/lib/usb/usb_core.c b/lib/usb/usb_core.c new file mode 100644 index 0000000000..1f33095af4 --- /dev/null +++ b/lib/usb/usb_core.c @@ -0,0 +1,838 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include + +/* define for field bEndpointAddress */ +#define EP_DIR_MASK BIT(7) +#define EP_DIR_IN BIT(7) +#define EP_NUM_MASK GENMASK(3, 0) + +#define EP0_IN (0U | EP_DIR_IN) +#define EP0_OUT 0U + +/* USB address between 1 through 127 = 0x7F mask */ +#define ADDRESS_MASK GENMASK(6, 0) + +/* + * @brief Set a STALL condition over an endpoint + * @param pdev: USB handle + * @param ep_addr: endpoint address + * @retval HAL status + */ +static usb_status_t usb_core_set_stall(usb_handle_t *pdev, uint8_t ep_addr) +{ + usbd_ep_t *ep; + pcd_handle_t *hpcd = (pcd_handle_t *)pdev->data; + uint8_t num; + + num = ep_addr & EP_NUM_MASK; + if ((EP_DIR_MASK & ep_addr) == EP_DIR_IN) { + ep = &hpcd->in_ep[num]; + ep->is_in = true; + } else { + ep = &hpcd->out_ep[num]; + ep->is_in = false; + } + ep->num = num; + + pdev->driver->ep_set_stall(hpcd->instance, ep); + if (num == 0U) { + pdev->driver->ep0_out_start(hpcd->instance); + } + + return USBD_OK; +} + +/* + * usb_core_get_desc + * Handle Get Descriptor requests + * pdev : device instance + * req : usb request + * return : status + */ +static void usb_core_get_desc(usb_handle_t *pdev, usb_setup_req_t *req) +{ + uint16_t len; + uint8_t *pbuf; + uint8_t desc_type = HIBYTE(req->value); + uint8_t desc_idx = LOBYTE(req->value); + + switch (desc_type) { + case USB_DESC_TYPE_DEVICE: + pbuf = pdev->desc->get_device_desc(&len); + break; + + case USB_DESC_TYPE_CONFIGURATION: + pbuf = (uint8_t *)pdev->desc->get_config_desc(&len); + pbuf[1] = USB_DESC_TYPE_CONFIGURATION; + break; + + case USB_DESC_TYPE_STRING: + switch (desc_idx) { + case USBD_IDX_LANGID_STR: + pbuf = pdev->desc->get_lang_id_desc(&len); + break; + + case USBD_IDX_MFC_STR: + pbuf = pdev->desc->get_manufacturer_desc(&len); + break; + + case USBD_IDX_PRODUCT_STR: + pbuf = pdev->desc->get_product_desc(&len); + break; + + case USBD_IDX_SERIAL_STR: + pbuf = pdev->desc->get_serial_desc(&len); + break; + + case USBD_IDX_CONFIG_STR: + pbuf = pdev->desc->get_configuration_desc(&len); + break; + + case USBD_IDX_INTERFACE_STR: + pbuf = pdev->desc->get_interface_desc(&len); + break; + + /* for all USER string */ + case USBD_IDX_USER0_STR: + default: + pbuf = pdev->desc->get_usr_desc(desc_idx - USBD_IDX_USER0_STR, &len); + break; + } + break; + + case USB_DESC_TYPE_DEVICE_QUALIFIER: + pbuf = (uint8_t *)pdev->desc->get_device_qualifier_desc(&len); + break; + + case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: + pbuf = (uint8_t *)pdev->desc->get_config_desc(&len); + pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; + break; + + default: + ERROR("Unknown request %i\n", desc_type); + usb_core_ctl_error(pdev); + return; + } + + if ((len != 0U) && (req->length != 0U)) { + len = MIN(len, req->length); + + /* Start the transfer */ + usb_core_transmit_ep0(pdev, pbuf, len); + } +} + +/* + * usb_core_set_config + * Handle Set device configuration request + * pdev : device instance + * req : usb request + * return : status + */ +static void usb_core_set_config(usb_handle_t *pdev, usb_setup_req_t *req) +{ + static uint8_t cfgidx; + + cfgidx = LOBYTE(req->value); + + if (cfgidx > USBD_MAX_NUM_CONFIGURATION) { + usb_core_ctl_error(pdev); + return; + } + + switch (pdev->dev_state) { + case USBD_STATE_ADDRESSED: + if (cfgidx != 0U) { + pdev->dev_config = cfgidx; + pdev->dev_state = USBD_STATE_CONFIGURED; + if (!pdev->class) { + usb_core_ctl_error(pdev); + return; + } + /* Set configuration and Start the Class */ + if (pdev->class->init(pdev, cfgidx) != 0U) { + usb_core_ctl_error(pdev); + return; + } + } + break; + + case USBD_STATE_CONFIGURED: + if (cfgidx == 0U) { + pdev->dev_state = USBD_STATE_ADDRESSED; + pdev->dev_config = cfgidx; + pdev->class->de_init(pdev, cfgidx); + } else if (cfgidx != pdev->dev_config) { + if (pdev->class != NULL) { + usb_core_ctl_error(pdev); + return; + } + /* Clear old configuration */ + pdev->class->de_init(pdev, pdev->dev_config); + /* Set new configuration */ + pdev->dev_config = cfgidx; + /* Set configuration and start the Class*/ + if (pdev->class->init(pdev, cfgidx) != 0U) { + usb_core_ctl_error(pdev); + return; + } + } + break; + + default: + usb_core_ctl_error(pdev); + return; + } + + /* Send status */ + usb_core_transmit_ep0(pdev, NULL, 0U); +} + +/* + * usb_core_get_status + * Handle Get Status request + * pdev : device instance + * req : usb request + * return : status + */ +static void usb_core_get_status(usb_handle_t *pdev, usb_setup_req_t *req) +{ + if ((pdev->dev_state != USBD_STATE_ADDRESSED) && + (pdev->dev_state != USBD_STATE_CONFIGURED)) { + usb_core_ctl_error(pdev); + return; + } + + pdev->dev_config_status = USB_CONFIG_SELF_POWERED; + + if (pdev->dev_remote_wakeup != 0U) { + pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; + } + + /* Start the transfer */ + usb_core_transmit_ep0(pdev, (uint8_t *)&pdev->dev_config_status, 2U); +} + +/* + * usb_core_set_address + * Set device address + * pdev : device instance + * req : usb request + * return : status + */ +static void usb_core_set_address(usb_handle_t *pdev, usb_setup_req_t *req) +{ + uint8_t dev_addr; + + if ((req->index != 0U) || (req->length != 0U)) { + usb_core_ctl_error(pdev); + return; + } + + dev_addr = req->value & ADDRESS_MASK; + if (pdev->dev_state != USBD_STATE_DEFAULT) { + usb_core_ctl_error(pdev); + return; + } + + pdev->dev_address = dev_addr; + pdev->driver->set_address(((pcd_handle_t *)(pdev->data))->instance, dev_addr); + + /* Send status */ + usb_core_transmit_ep0(pdev, NULL, 0U); + + if (dev_addr != 0U) { + pdev->dev_state = USBD_STATE_ADDRESSED; + } else { + pdev->dev_state = USBD_STATE_DEFAULT; + } +} + +/* + * usb_core_dev_req + * Handle standard usb device requests + * pdev : device instance + * req : usb request + * return : status + */ +static usb_status_t usb_core_dev_req(usb_handle_t *pdev, usb_setup_req_t *req) +{ + VERBOSE("receive request %i\n", req->b_request); + switch (req->b_request) { + case USB_REQ_GET_DESCRIPTOR: + usb_core_get_desc(pdev, req); + break; + + case USB_REQ_SET_CONFIGURATION: + usb_core_set_config(pdev, req); + break; + + case USB_REQ_GET_STATUS: + usb_core_get_status(pdev, req); + break; + + case USB_REQ_SET_ADDRESS: + usb_core_set_address(pdev, req); + break; + + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_SET_FEATURE: + case USB_REQ_CLEAR_FEATURE: + default: + ERROR("NOT SUPPORTED %i\n", req->b_request); + usb_core_ctl_error(pdev); + break; + } + + return USBD_OK; +} + +/* + * usb_core_itf_req + * Handle standard usb interface requests + * pdev : device instance + * req : usb request + * return : status + */ +static usb_status_t usb_core_itf_req(usb_handle_t *pdev, usb_setup_req_t *req) +{ + if (pdev->dev_state != USBD_STATE_CONFIGURED) { + usb_core_ctl_error(pdev); + return USBD_OK; + } + + if (LOBYTE(req->index) <= USBD_MAX_NUM_INTERFACES) { + pdev->class->setup(pdev, req); + + if (req->length == 0U) { + usb_core_transmit_ep0(pdev, NULL, 0U); + } + } else { + usb_core_ctl_error(pdev); + } + + return USBD_OK; +} + +/* + * @brief USBD_ParseSetupRequest + * Copy buffer into setup structure + * @param pdev: device instance + * @param req: usb request + * @retval None + */ +static void usb_core_parse_req(usb_setup_req_t *req, uint8_t *pdata) +{ + req->bm_request = pdata[0]; + req->b_request = pdata[1]; + req->value = pdata[2] + (pdata[3] << 8); + req->index = pdata[4] + (pdata[5] << 8); + req->length = pdata[6] + (pdata[7] << 8); +} + +/* + * usb_core_setup_stage + * Handle the setup stage + * pdev: device instance + * return : status + */ +static usb_status_t usb_core_setup_stage(usb_handle_t *pdev, uint8_t *psetup) +{ + usb_core_parse_req(&pdev->request, psetup); + + pdev->ep0_state = USBD_EP0_SETUP; + pdev->ep0_data_len = pdev->request.length; + + switch (pdev->request.bm_request & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_DEVICE: + usb_core_dev_req(pdev, &pdev->request); + break; + + case USB_REQ_RECIPIENT_INTERFACE: + usb_core_itf_req(pdev, &pdev->request); + break; + + case USB_REQ_RECIPIENT_ENDPOINT: + default: + ERROR("receive unsupported request %i", + pdev->request.bm_request & USB_REQ_RECIPIENT_MASK); + usb_core_set_stall(pdev, pdev->request.bm_request & USB_REQ_DIRECTION); + return USBD_FAIL; + } + + return USBD_OK; +} + +/* + * usb_core_data_out + * Handle data OUT stage + * pdev: device instance + * epnum: endpoint index + * return : status + */ +static usb_status_t usb_core_data_out(usb_handle_t *pdev, uint8_t epnum, + uint8_t *pdata) +{ + usb_endpoint_t *pep; + + if (epnum == 0U) { + pep = &pdev->ep_out[0]; + if (pdev->ep0_state == USBD_EP0_DATA_OUT) { + if (pep->rem_length > pep->maxpacket) { + pep->rem_length -= pep->maxpacket; + + usb_core_receive(pdev, 0U, pdata, + MIN(pep->rem_length, + pep->maxpacket)); + } else { + if (pdev->class->ep0_rx_ready && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + pdev->class->ep0_rx_ready(pdev); + + usb_core_transmit_ep0(pdev, NULL, 0U); + } + } + } else if (pdev->class->data_out != NULL && + (pdev->dev_state == USBD_STATE_CONFIGURED)) { + pdev->class->data_out(pdev, epnum); + } + + return USBD_OK; +} + +/* + * usb_core_data_in + * Handle data in stage + * pdev: device instance + * epnum: endpoint index + * return : status + */ +static usb_status_t usb_core_data_in(usb_handle_t *pdev, uint8_t epnum, + uint8_t *pdata) +{ + if (epnum == 0U) { + usb_endpoint_t *pep = &pdev->ep_in[0]; + + if (pdev->ep0_state == USBD_EP0_DATA_IN) { + if (pep->rem_length > pep->maxpacket) { + pep->rem_length -= pep->maxpacket; + + usb_core_transmit(pdev, 0U, pdata, + pep->rem_length); + + /* Prepare endpoint for premature + * end of transfer + */ + usb_core_receive(pdev, 0U, NULL, 0U); + } else { + /* Last packet is MPS multiple, + * so send ZLP packet + */ + if ((pep->total_length % pep->maxpacket == 0U) && + (pep->total_length >= pep->maxpacket) && + (pep->total_length < pdev->ep0_data_len)) { + usb_core_transmit(pdev, 0U, NULL, 0U); + + pdev->ep0_data_len = 0U; + + /* Prepare endpoint for premature + * end of transfer + */ + usb_core_receive(pdev, 0U, NULL, 0U); + } else { + if (pdev->class->ep0_tx_sent != NULL && + (pdev->dev_state == + USBD_STATE_CONFIGURED)) + pdev->class->ep0_tx_sent(pdev); + + /* Start the transfer */ + usb_core_receive_ep0(pdev, NULL, 0U); + } + } + } + } else if (pdev->class->data_in != NULL && + (pdev->dev_state == USBD_STATE_CONFIGURED)) { + pdev->class->data_in(pdev, epnum); + } + + return USBD_OK; +} + +/* + * usb_core_suspend + * Handle suspend event + * pdev : device instance + * return : status + */ + +static usb_status_t usb_core_suspend(usb_handle_t *pdev) +{ + INFO("USB Suspend mode\n"); + pdev->dev_old_state = pdev->dev_state; + pdev->dev_state = USBD_STATE_SUSPENDED; + + return USBD_OK; +} + +/* + * usb_core_resume + * Handle resume event + * pdev : device instance + * return : status + */ + +static usb_status_t usb_core_resume(usb_handle_t *pdev) +{ + INFO("USB Resume\n"); + pdev->dev_state = pdev->dev_old_state; + + return USBD_OK; +} + +/* + * usb_core_sof + * Handle SOF event + * pdev : device instance + * return : status + */ + +static usb_status_t usb_core_sof(usb_handle_t *pdev) +{ + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + if (pdev->class->sof != NULL) + pdev->class->sof(pdev); + } + + return USBD_OK; +} + +/* + * usb_core_disconnect + * Handle device disconnection event + * pdev : device instance + * return : status + */ +static usb_status_t usb_core_disconnect(usb_handle_t *pdev) +{ + /* Free class resources */ + pdev->dev_state = USBD_STATE_DEFAULT; + pdev->class->de_init(pdev, pdev->dev_config); + + return USBD_OK; +} + +usb_status_t usb_core_handle_it(usb_handle_t *pdev) +{ + uint32_t param = 0U; + uint32_t len = 0U; + usbd_ep_t *ep; + + switch (pdev->driver->it_handler(pdev->data->instance, ¶m)) { + case USB_DATA_OUT: + usb_core_data_out(pdev, param, + pdev->data->out_ep[param].xfer_buff); + break; + + case USB_DATA_IN: + usb_core_data_in(pdev, param, + pdev->data->in_ep[param].xfer_buff); + break; + + case USB_SETUP: + usb_core_setup_stage(pdev, (uint8_t *)pdev->data->setup); + break; + + case USB_ENUM_DONE: + break; + + case USB_READ_DATA_PACKET: + ep = &pdev->data->out_ep[param & USBD_OUT_EPNUM_MASK]; + len = (param & USBD_OUT_COUNT_MASK) >> USBD_OUT_COUNT_SHIFT; + pdev->driver->read_packet(pdev->data->instance, + ep->xfer_buff, len); + ep->xfer_buff += len; + ep->xfer_count += len; + break; + + case USB_READ_SETUP_PACKET: + ep = &pdev->data->out_ep[param & USBD_OUT_EPNUM_MASK]; + len = (param & USBD_OUT_COUNT_MASK) >> 0x10; + pdev->driver->read_packet(pdev->data->instance, + (uint8_t *)pdev->data->setup, 8); + ep->xfer_count += len; + break; + + case USB_RESET: + pdev->dev_state = USBD_STATE_DEFAULT; + break; + + case USB_RESUME: + if (pdev->data->lpm_state == LPM_L1) { + pdev->data->lpm_state = LPM_L0; + } else { + usb_core_resume(pdev); + } + break; + + case USB_SUSPEND: + usb_core_suspend(pdev); + break; + + case USB_LPM: + if (pdev->data->lpm_state == LPM_L0) { + pdev->data->lpm_state = LPM_L1; + } else { + usb_core_suspend(pdev); + } + break; + + case USB_SOF: + usb_core_sof(pdev); + break; + + case USB_DISCONNECT: + usb_core_disconnect(pdev); + break; + + case USB_WRITE_EMPTY: + pdev->driver->write_empty_tx_fifo(pdev->data->instance, param, + pdev->data->in_ep[param].xfer_len, + (uint32_t *)&pdev->data->in_ep[param].xfer_count, + pdev->data->in_ep[param].maxpacket, + &pdev->data->in_ep[param].xfer_buff); + break; + + case USB_NOTHING: + default: + break; + } + + return USBD_OK; +} + +/** + * @brief Receive an amount of data + * @param pdev: USB handle + * @param ep_addr: endpoint address + * @param pBuf: pointer to the reception buffer + * @param len: amount of data to be received + * @retval status + */ +usb_status_t usb_core_receive(usb_handle_t *pdev, uint8_t ep_addr, + uint8_t *buf, uint32_t len) +{ + usbd_ep_t *ep; + pcd_handle_t *hpcd = (pcd_handle_t *)pdev->data; + uint8_t num; + + num = ep_addr & EP_NUM_MASK; + ep = &hpcd->out_ep[num]; + + /* Setup and start the Xfer */ + ep->xfer_buff = buf; + ep->xfer_len = len; + ep->xfer_count = 0U; + ep->is_in = false; + ep->num = num; + + if (num == 0U) { + pdev->driver->ep0_start_xfer(hpcd->instance, ep); + } else { + pdev->driver->ep_start_xfer(hpcd->instance, ep); + } + + return USBD_OK; +} + +/* + * @brief Send an amount of data + * @param pdev: USB handle + * @param ep_addr: endpoint address + * @param pBuf: pointer to the transmission buffer + * @param len: amount of data to be sent + * @retval status + */ +usb_status_t usb_core_transmit(usb_handle_t *pdev, uint8_t ep_addr, + uint8_t *buf, uint32_t len) +{ + usbd_ep_t *ep; + pcd_handle_t *hpcd = (pcd_handle_t *)pdev->data; + uint8_t num; + + num = ep_addr & EP_NUM_MASK; + ep = &hpcd->in_ep[num]; + + /* Setup and start the Xfer */ + ep->xfer_buff = buf; + ep->xfer_len = len; + ep->xfer_count = 0U; + ep->is_in = true; + ep->num = num; + + if (num == 0U) { + pdev->driver->ep0_start_xfer(hpcd->instance, ep); + } else { + pdev->driver->ep_start_xfer(hpcd->instance, ep); + } + + return USBD_OK; +} + +/** + * @brief Receive an amount of data on ep0 + * @param pdev: USB handle + * @param buf: pointer to the reception buffer + * @param len: amount of data to be received + * @retval status + */ +usb_status_t usb_core_receive_ep0(usb_handle_t *pdev, uint8_t *buf, + uint32_t len) +{ + /* Prepare the reception of the buffer over EP0 */ + if (len != 0U) { + pdev->ep0_state = USBD_EP0_DATA_OUT; + } else { + pdev->ep0_state = USBD_EP0_STATUS_OUT; + } + + pdev->ep_out[0].total_length = len; + pdev->ep_out[0].rem_length = len; + + /* Start the transfer */ + return usb_core_receive(pdev, 0U, buf, len); +} + +/* + * @brief Send an amount of data on ep0 + * @param pdev: USB handle + * @param buf: pointer to the transmission buffer + * @param len: amount of data to be sent + * @retval status + */ +usb_status_t usb_core_transmit_ep0(usb_handle_t *pdev, uint8_t *buf, + uint32_t len) +{ + /* Set EP0 State */ + if (len != 0U) { + pdev->ep0_state = USBD_EP0_DATA_IN; + } else { + pdev->ep0_state = USBD_EP0_STATUS_IN; + } + + pdev->ep_in[0].total_length = len; + pdev->ep_in[0].rem_length = len; + + /* Start the transfer */ + return usb_core_transmit(pdev, 0U, buf, len); +} + +/* + * @brief usb_core_ctl_error + * Handle USB low level error + * @param pdev: device instance + * @param req: usb request + * @retval None + */ + +void usb_core_ctl_error(usb_handle_t *pdev) +{ + ERROR("%s : Send an ERROR\n", __func__); + usb_core_set_stall(pdev, EP0_IN); + usb_core_set_stall(pdev, EP0_OUT); +} + +/* + * usb_core_start + * Start the USB device core. + * pdev: Device Handle + * return : USBD Status + */ +usb_status_t usb_core_start(usb_handle_t *pdev) +{ + /* Start the low level driver */ + pdev->driver->start_device(pdev->data->instance); + + return USBD_OK; +} + +/* + * usb_core_stop + * Stop the USB device core. + * pdev: Device Handle + * return : USBD Status + */ +usb_status_t usb_core_stop(usb_handle_t *pdev) +{ + /* Free class resources */ + pdev->class->de_init(pdev, pdev->dev_config); + + /* Stop the low level driver */ + pdev->driver->stop_device(pdev->data->instance); + + return USBD_OK; +} + +/* + * register_usb_driver + * Stop the USB device core. + * pdev: Device Handle + * @param hpcd: PCD handle + * @param driver: USB driver + * @param driver_handle: USB driver handle + * return : USBD Status + */ +usb_status_t register_usb_driver(usb_handle_t *pdev, pcd_handle_t *pcd_handle, + const usb_driver_t *driver, + void *driver_handle) +{ + uint8_t i; + + assert(pdev != NULL); + assert(pcd_handle != NULL); + assert(driver != NULL); + assert(driver_handle != NULL); + + /* Free class resources */ + pdev->driver = driver; + pdev->data = pcd_handle; + pdev->data->instance = driver_handle; + pdev->dev_state = USBD_STATE_DEFAULT; + pdev->ep0_state = USBD_EP0_IDLE; + + /* Copy endpoint information */ + for (i = 0U; i < 15U; i++) { + pdev->ep_in[i].maxpacket = pdev->data->in_ep[i].maxpacket; + pdev->ep_out[i].maxpacket = pdev->data->out_ep[i].maxpacket; + } + + return USBD_OK; +} + +/* + * register_platform + * Register the USB device core. + * pdev: Device Handle + * plat_call_back: callback + * return : USBD Status + */ +usb_status_t register_platform(usb_handle_t *pdev, + const usb_desc_t *plat_call_back) +{ + assert(pdev != NULL); + assert(plat_call_back != NULL); + + /* Save platform info in class resources */ + pdev->desc = plat_call_back; + + return USBD_OK; +} diff --git a/lib/usb/usb_st_dfu.c b/lib/usb/usb_st_dfu.c new file mode 100644 index 0000000000..18f5415990 --- /dev/null +++ b/lib/usb/usb_st_dfu.c @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include + +#include +#include + +/* DFU Requests DFU states */ +#define APP_STATE_IDLE 0 +#define APP_STATE_DETACH 1 +#define DFU_STATE_IDLE 2 +#define DFU_STATE_DNLOAD_SYNC 3 +#define DFU_STATE_DNLOAD_BUSY 4 +#define DFU_STATE_DNLOAD_IDLE 5 +#define DFU_STATE_MANIFEST_SYNC 6 +#define DFU_STATE_MANIFEST 7 +#define DFU_STATE_MANIFEST_WAIT_RESET 8 +#define DFU_STATE_UPLOAD_IDLE 9 +#define DFU_STATE_ERROR 10 + +/* DFU errors */ +#define DFU_ERROR_NONE 0x00 +#define DFU_ERROR_TARGET 0x01 +#define DFU_ERROR_FILE 0x02 +#define DFU_ERROR_WRITE 0x03 +#define DFU_ERROR_ERASE 0x04 +#define DFU_ERROR_CHECK_ERASED 0x05 +#define DFU_ERROR_PROG 0x06 +#define DFU_ERROR_VERIFY 0x07 +#define DFU_ERROR_ADDRESS 0x08 +#define DFU_ERROR_NOTDONE 0x09 +#define DFU_ERROR_FIRMWARE 0x0A +#define DFU_ERROR_VENDOR 0x0B +#define DFU_ERROR_USB 0x0C +#define DFU_ERROR_POR 0x0D +#define DFU_ERROR_UNKNOWN 0x0E +#define DFU_ERROR_STALLEDPKT 0x0F + +typedef enum { + DFU_DETACH = 0, + DFU_DNLOAD, + DFU_UPLOAD, + DFU_GETSTATUS, + DFU_CLRSTATUS, + DFU_GETSTATE, + DFU_ABORT +} dfu_request_t; + +static bool usb_dfu_detach_req; + +/* + * @brief usb_dfu_init + * Initialize the DFU interface + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t usb_dfu_init(usb_handle_t *pdev, uint8_t cfgidx) +{ + /* Nothing to do in this stage */ + return USBD_OK; +} + +/** + * @brief usb_dfu_de_init + * De-Initialize the DFU layer + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t usb_dfu_de_init(usb_handle_t *pdev, uint8_t cfgidx) +{ + /* Nothing to do in this stage */ + return USBD_OK; +} + +/* + * @brief usb_dfu_data_in + * handle data IN Stage + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t usb_dfu_data_in(usb_handle_t *pdev, uint8_t epnum) +{ + (void)pdev; + (void)epnum; + + return USBD_OK; +} + +/* + * @brief usb_dfu_ep0_rx_ready + * handle EP0 Rx Ready event + * @param pdev: device instance + * @retval status + */ +static uint8_t usb_dfu_ep0_rx_ready(usb_handle_t *pdev) +{ + (void)pdev; + + return USBD_OK; +} + +/* + * @brief usb_dfu_ep0_tx_ready + * handle EP0 TRx Ready event + * @param pdev: device instance + * @retval status + */ +static uint8_t usb_dfu_ep0_tx_ready(usb_handle_t *pdev) +{ + return USBD_OK; +} + +/* + * @brief usb_dfu_sof + * handle SOF event + * @param pdev: device instance + * @retval status + */ +static uint8_t usb_dfu_sof(usb_handle_t *pdev) +{ + (void)pdev; + + return USBD_OK; +} + +/* + * @brief usb_dfu_iso_in_incomplete + * handle data ISO IN Incomplete event + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t usb_dfu_iso_in_incomplete(usb_handle_t *pdev, uint8_t epnum) +{ + (void)pdev; + (void)epnum; + + return USBD_OK; +} + +/* + * @brief usb_dfu_iso_out_incomplete + * handle data ISO OUT Incomplete event + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t usb_dfu_iso_out_incomplete(usb_handle_t *pdev, uint8_t epnum) +{ + (void)pdev; + (void)epnum; + + return USBD_OK; +} + +/* + * @brief usb_dfu_data_out + * handle data OUT Stage + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t usb_dfu_data_out(usb_handle_t *pdev, uint8_t epnum) +{ + (void)pdev; + (void)epnum; + + return USBD_OK; +} + +/* + * @brief usb_dfu_detach + * Handles the DFU DETACH request. + * @param pdev: device instance + * @param req: pointer to the request structure. + * @retval None. + */ +static void usb_dfu_detach(usb_handle_t *pdev, usb_setup_req_t *req) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + INFO("Receive DFU Detach\n"); + + if ((hdfu->dev_state == DFU_STATE_IDLE) || + (hdfu->dev_state == DFU_STATE_DNLOAD_SYNC) || + (hdfu->dev_state == DFU_STATE_DNLOAD_IDLE) || + (hdfu->dev_state == DFU_STATE_MANIFEST_SYNC) || + (hdfu->dev_state == DFU_STATE_UPLOAD_IDLE)) { + /* Update the state machine */ + hdfu->dev_state = DFU_STATE_IDLE; + hdfu->dev_status = DFU_ERROR_NONE; + } + + usb_dfu_detach_req = true; +} + +/* + * @brief usb_dfu_download + * Handles the DFU DNLOAD request. + * @param pdev: device instance + * @param req: pointer to the request structure + * @retval None + */ +static void usb_dfu_download(usb_handle_t *pdev, usb_setup_req_t *req) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + uintptr_t data_ptr; + uint32_t length; + int ret; + + /* Data setup request */ + if (req->length > 0) { + /* Unsupported state */ + if ((hdfu->dev_state != DFU_STATE_IDLE) && + (hdfu->dev_state != DFU_STATE_DNLOAD_IDLE)) { + /* Call the error management function (command will be nacked) */ + usb_core_ctl_error(pdev); + return; + } + + /* Get the data address */ + length = req->length; + ret = hdfu->callback->download(hdfu->alt_setting, &data_ptr, + &length, pdev->user_data); + if (ret == 0U) { + /* Update the state machine */ + hdfu->dev_state = DFU_STATE_DNLOAD_SYNC; + /* Start the transfer */ + usb_core_receive_ep0(pdev, (uint8_t *)data_ptr, length); + } else { + usb_core_ctl_error(pdev); + } + } else { + /* End of DNLOAD operation*/ + if (hdfu->dev_state != DFU_STATE_DNLOAD_IDLE) { + /* Call the error management function (command will be nacked) */ + usb_core_ctl_error(pdev); + return; + } + /* End of DNLOAD operation*/ + hdfu->dev_state = DFU_STATE_MANIFEST_SYNC; + ret = hdfu->callback->manifestation(hdfu->alt_setting, pdev->user_data); + if (ret == 0U) { + hdfu->dev_state = DFU_STATE_MANIFEST_SYNC; + } else { + usb_core_ctl_error(pdev); + } + } +} + +/* + * @brief usb_dfu_upload + * Handles the DFU UPLOAD request. + * @param pdev: instance + * @param req: pointer to the request structure + * @retval status + */ +static void usb_dfu_upload(usb_handle_t *pdev, usb_setup_req_t *req) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + uintptr_t data_ptr; + uint32_t length; + int ret; + + /* Data setup request */ + if (req->length == 0) { + /* No Data setup request */ + hdfu->dev_state = DFU_STATE_IDLE; + return; + } + + /* Unsupported state */ + if ((hdfu->dev_state != DFU_STATE_IDLE) && (hdfu->dev_state != DFU_STATE_UPLOAD_IDLE)) { + ERROR("UPLOAD : Unsupported State\n"); + /* Call the error management function (command will be nacked) */ + usb_core_ctl_error(pdev); + return; + } + + /* Update the data address */ + length = req->length; + ret = hdfu->callback->upload(hdfu->alt_setting, &data_ptr, &length, pdev->user_data); + if (ret == 0U) { + /* Short frame */ + hdfu->dev_state = (req->length > length) ? DFU_STATE_IDLE : DFU_STATE_UPLOAD_IDLE; + + /* Start the transfer */ + usb_core_transmit_ep0(pdev, (uint8_t *)data_ptr, length); + } else { + ERROR("UPLOAD : bad block %i on alt %i\n", req->value, req->index); + hdfu->dev_state = DFU_STATE_ERROR; + hdfu->dev_status = DFU_ERROR_STALLEDPKT; + + /* Call the error management function (command will be nacked) */ + usb_core_ctl_error(pdev); + } +} + +/* + * @brief usb_dfu_get_status + * Handles the DFU GETSTATUS request. + * @param pdev: instance + * @retval status + */ +static void usb_dfu_get_status(usb_handle_t *pdev) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + hdfu->status[0] = hdfu->dev_status; /* bStatus */ + hdfu->status[1] = 0; /* bwPollTimeout[3]; */ + hdfu->status[2] = 0; + hdfu->status[3] = 0; + hdfu->status[4] = hdfu->dev_state; /* bState */ + hdfu->status[5] = 0; /* iString */ + + /* next step */ + switch (hdfu->dev_state) { + case DFU_STATE_DNLOAD_SYNC: + hdfu->dev_state = DFU_STATE_DNLOAD_IDLE; + break; + case DFU_STATE_MANIFEST_SYNC: + /* We're MainfestationTolerant */ + hdfu->status[4] = DFU_STATE_MANIFEST; + hdfu->status[1] = 1U; /* bwPollTimeout = 1ms */ + hdfu->dev_state = DFU_STATE_IDLE; + break; + + default: + break; + } + + /* Start the transfer */ + usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->status[0], sizeof(hdfu->status)); +} + +/* + * @brief usb_dfu_clear_status + * Handles the DFU CLRSTATUS request. + * @param pdev: device instance + * @retval status + */ +static void usb_dfu_clear_status(usb_handle_t *pdev) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + if (hdfu->dev_state == DFU_STATE_ERROR) { + hdfu->dev_state = DFU_STATE_IDLE; + hdfu->dev_status = DFU_ERROR_NONE; + } else { + /* State Error */ + hdfu->dev_state = DFU_STATE_ERROR; + hdfu->dev_status = DFU_ERROR_UNKNOWN; + } +} + +/* + * @brief usb_dfu_get_state + * Handles the DFU GETSTATE request. + * @param pdev: device instance + * @retval None + */ +static void usb_dfu_get_state(usb_handle_t *pdev) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + /* Return the current state of the DFU interface */ + usb_core_transmit_ep0(pdev, &hdfu->dev_state, 1); +} + +/* + * @brief usb_dfu_abort + * Handles the DFU ABORT request. + * @param pdev: device instance + * @retval None + */ +static void usb_dfu_abort(usb_handle_t *pdev) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + if ((hdfu->dev_state == DFU_STATE_IDLE) || + (hdfu->dev_state == DFU_STATE_DNLOAD_SYNC) || + (hdfu->dev_state == DFU_STATE_DNLOAD_IDLE) || + (hdfu->dev_state == DFU_STATE_MANIFEST_SYNC) || + (hdfu->dev_state == DFU_STATE_UPLOAD_IDLE)) { + hdfu->dev_state = DFU_STATE_IDLE; + hdfu->dev_status = DFU_ERROR_NONE; + } +} + +/* + * @brief usb_dfu_setup + * Handle the DFU specific requests + * @param pdev: instance + * @param req: usb requests + * @retval status + */ +static uint8_t usb_dfu_setup(usb_handle_t *pdev, usb_setup_req_t *req) +{ + uint8_t *pbuf = NULL; + uint16_t len = 0U; + uint8_t ret = USBD_OK; + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + switch (req->bm_request & USB_REQ_TYPE_MASK) { + case USB_REQ_TYPE_CLASS: + switch (req->b_request) { + case DFU_DNLOAD: + usb_dfu_download(pdev, req); + break; + + case DFU_UPLOAD: + usb_dfu_upload(pdev, req); + break; + + case DFU_GETSTATUS: + usb_dfu_get_status(pdev); + break; + + case DFU_CLRSTATUS: + usb_dfu_clear_status(pdev); + break; + + case DFU_GETSTATE: + usb_dfu_get_state(pdev); + break; + + case DFU_ABORT: + usb_dfu_abort(pdev); + break; + + case DFU_DETACH: + usb_dfu_detach(pdev, req); + break; + + default: + ERROR("unkwon request %x on alternate %i\n", + req->b_request, hdfu->alt_setting); + usb_core_ctl_error(pdev); + ret = USBD_FAIL; + break; + } + break; + case USB_REQ_TYPE_STANDARD: + switch (req->b_request) { + case USB_REQ_GET_DESCRIPTOR: + if (HIBYTE(req->value) == DFU_DESCRIPTOR_TYPE) { + pbuf = pdev->desc->get_config_desc(&len); + /* DFU descriptor at the end of the USB */ + pbuf += len - 9U; + len = 9U; + len = MIN(len, req->length); + } + + /* Start the transfer */ + usb_core_transmit_ep0(pdev, pbuf, len); + + break; + + case USB_REQ_GET_INTERFACE: + /* Start the transfer */ + usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->alt_setting, 1U); + break; + + case USB_REQ_SET_INTERFACE: + hdfu->alt_setting = LOBYTE(req->value); + break; + + default: + usb_core_ctl_error(pdev); + ret = USBD_FAIL; + break; + } + default: + break; + } + + return ret; +} + +static const usb_class_t USBD_DFU_initvalue = { + .init = usb_dfu_init, + .de_init = usb_dfu_de_init, + .setup = usb_dfu_setup, + .ep0_tx_sent = usb_dfu_ep0_tx_ready, + .ep0_rx_ready = usb_dfu_ep0_rx_ready, + .data_in = usb_dfu_data_in, + .data_out = usb_dfu_data_out, + .sof = usb_dfu_sof, + .iso_in_incomplete = usb_dfu_iso_in_incomplete, + .iso_out_incomplete = usb_dfu_iso_out_incomplete, +}; + +void usb_dfu_register(usb_handle_t *pdev, usb_dfu_handle_t *phandle) +{ + pdev->class = (usb_class_t *)&USBD_DFU_initvalue; + pdev->class_data = phandle; + + phandle->dev_state = DFU_STATE_IDLE; + phandle->dev_status = DFU_ERROR_NONE; +} + +int usb_dfu_loop(usb_handle_t *pdev, const usb_dfu_media_t *pmedia) +{ + uint32_t it_count; + usb_status_t ret; + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + hdfu->callback = pmedia; + usb_dfu_detach_req = false; + /* Continue to handle USB core IT to assure complete data transmission */ + it_count = 100U; + + /* DFU infinite loop until DETACH_REQ */ + while (it_count != 0U) { + ret = usb_core_handle_it(pdev); + if (ret != USBD_OK) { + return -EIO; + } + + /* detach request received */ + if (usb_dfu_detach_req) { + it_count--; + } + } + + return 0; +} diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index 578bd59876..4d1f63b4b4 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2020, ARM Limited. All rights reserved. +# Copyright (c) 2016-2021, ARM Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -126,6 +126,9 @@ ENC_KEY := 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef # Default dummy nonce for firmware encryption ENC_NONCE := 1234567890abcdef12345678 +# Flag to enable exception debug for AARCH32 +AARCH32_EXCEPTION_DEBUG := 0 + # Build flag to treat usage of deprecated platform and framework APIs as error. ERROR_DEPRECATED := 0 @@ -323,8 +326,5 @@ RAS_TRAP_LOWER_EL_ERR_ACCESS := 0 # Build option to create cot descriptors using fconf COT_DESC_IN_DTB := 0 -# Build option to provide openssl directory path -OPENSSL_DIR := /usr - # Build option to use the SP804 timer instead of the generic one USE_SP804_TIMER := 0 diff --git a/plat/common/aarch32/platform_helpers.S b/plat/common/aarch32/platform_helpers.S index 5b9cb59146..d2c20b4a7d 100644 --- a/plat/common/aarch32/platform_helpers.S +++ b/plat/common/aarch32/platform_helpers.S @@ -8,6 +8,11 @@ #include .weak plat_report_exception +#if AARCH32_EXCEPTION_DEBUG + .weak plat_report_undef_inst + .weak plat_report_prefetch_abort + .weak plat_report_data_abort +#endif .weak plat_reset_handler .weak plat_disable_acp .weak bl1_plat_prepare_exit @@ -23,6 +28,35 @@ func plat_report_exception bx lr endfunc plat_report_exception +#if AARCH32_EXCEPTION_DEBUG + /* ----------------------------------------------------- + * Placeholder function which should be redefined by + * each platform. + * ----------------------------------------------------- + */ +func plat_report_undef_inst + bx lr +endfunc plat_report_undef_inst + + /* ----------------------------------------------------- + * Placeholder function which should be redefined by + * each platform. + * ----------------------------------------------------- + */ +func plat_report_prefetch_abort + bx lr +endfunc plat_report_prefetch_abort + + /* ----------------------------------------------------- + * Placeholder function which should be redefined by + * each platform. + * ----------------------------------------------------- + */ +func plat_report_data_abort + bx lr +endfunc plat_report_data_abort +#endif + /* ----------------------------------------------------- * Placeholder function which should be redefined by * each platform. diff --git a/plat/common/plat_bl_common.c b/plat/common/plat_bl_common.c index 89b77ba6ce..5bbd73bd94 100644 --- a/plat/common/plat_bl_common.c +++ b/plat/common/plat_bl_common.c @@ -69,7 +69,7 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) return 0; } -int plat_try_next_boot_source(void) +int plat_try_next_boot_source(unsigned int image_id) { return 0; } diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c index 3ec7d4048a..23794db963 100644 --- a/plat/st/common/bl2_io_storage.c +++ b/plat/st/common/bl2_io_storage.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -22,28 +23,29 @@ #include #include #include -#include #include #include #include +#include #include #include +#include +#include #include +#include + +#include + +#include /* IO devices */ -static const io_dev_connector_t *dummy_dev_con; -static uintptr_t dummy_dev_handle; -static uintptr_t dummy_dev_spec; +uintptr_t fip_dev_handle; +uintptr_t storage_dev_handle; -static uintptr_t image_dev_handle; -static uintptr_t storage_dev_handle; +static const io_dev_connector_t *fip_dev_con; +static uint32_t nand_bkp_offset; #if STM32MP_SDMMC || STM32MP_EMMC -static io_block_spec_t gpt_block_spec = { - .offset = 0, - .length = 34 * MMC_BLOCK_SIZE, /* Size of GPT table */ -}; - static uint32_t block_buffer[MMC_BLOCK_SIZE] __aligned(MMC_BLOCK_SIZE); static const io_block_dev_spec_t mmc_block_dev_spec = { @@ -76,6 +78,7 @@ static io_mtd_dev_spec_t nand_dev_spec = { .ops = { .init = nand_raw_init, .read = nand_read, + .seek = nand_seek_bb }, }; @@ -87,6 +90,7 @@ static io_mtd_dev_spec_t spi_nand_dev_spec = { .ops = { .init = spi_nand_init, .read = nand_read, + .seek = nand_seek_bb }, }; #endif @@ -95,146 +99,21 @@ static io_mtd_dev_spec_t spi_nand_dev_spec = { static const io_dev_connector_t *spi_dev_con; #endif -#ifdef AARCH32_SP_OPTEE -static const struct stm32image_part_info optee_header_partition_spec = { - .name = OPTEE_HEADER_IMAGE_NAME, - .binary_type = OPTEE_HEADER_BINARY_TYPE, -}; - -static const struct stm32image_part_info optee_pager_partition_spec = { - .name = OPTEE_PAGER_IMAGE_NAME, - .binary_type = OPTEE_PAGER_BINARY_TYPE, -}; - -static const struct stm32image_part_info optee_paged_partition_spec = { - .name = OPTEE_PAGED_IMAGE_NAME, - .binary_type = OPTEE_PAGED_BINARY_TYPE, -}; -#else -static const io_block_spec_t bl32_block_spec = { - .offset = BL32_BASE, - .length = STM32MP_BL32_SIZE -}; -#endif - -static const io_block_spec_t bl2_block_spec = { - .offset = BL2_BASE, - .length = STM32MP_BL2_SIZE, -}; - -static const struct stm32image_part_info bl33_partition_spec = { - .name = BL33_IMAGE_NAME, - .binary_type = BL33_BINARY_TYPE, -}; - -enum { - IMG_IDX_BL33, -#ifdef AARCH32_SP_OPTEE - IMG_IDX_OPTEE_HEADER, - IMG_IDX_OPTEE_PAGER, - IMG_IDX_OPTEE_PAGED, +#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER +static const io_dev_connector_t *memmap_dev_con; #endif - IMG_IDX_NUM -}; -static struct stm32image_device_info stm32image_dev_info_spec __unused = { - .lba_size = MMC_BLOCK_SIZE, - .part_info[IMG_IDX_BL33] = { - .name = BL33_IMAGE_NAME, - .binary_type = BL33_BINARY_TYPE, - }, -#ifdef AARCH32_SP_OPTEE - .part_info[IMG_IDX_OPTEE_HEADER] = { - .name = OPTEE_HEADER_IMAGE_NAME, - .binary_type = OPTEE_HEADER_BINARY_TYPE, - }, - .part_info[IMG_IDX_OPTEE_PAGER] = { - .name = OPTEE_PAGER_IMAGE_NAME, - .binary_type = OPTEE_PAGER_BINARY_TYPE, - }, - .part_info[IMG_IDX_OPTEE_PAGED] = { - .name = OPTEE_PAGED_IMAGE_NAME, - .binary_type = OPTEE_PAGED_BINARY_TYPE, - }, -#endif -}; - -static io_block_spec_t stm32image_block_spec = { +io_block_spec_t image_block_spec = { .offset = 0, .length = 0, }; -static const io_dev_connector_t *stm32image_dev_con __unused; - -static int open_dummy(const uintptr_t spec); -static int open_image(const uintptr_t spec); -static int open_storage(const uintptr_t spec); - -struct plat_io_policy { - uintptr_t *dev_handle; - uintptr_t image_spec; - int (*check)(const uintptr_t spec); -}; - -static const struct plat_io_policy policies[] = { - [BL2_IMAGE_ID] = { - .dev_handle = &dummy_dev_handle, - .image_spec = (uintptr_t)&bl2_block_spec, - .check = open_dummy - }, -#ifdef AARCH32_SP_OPTEE - [BL32_IMAGE_ID] = { - .dev_handle = &image_dev_handle, - .image_spec = (uintptr_t)&optee_header_partition_spec, - .check = open_image - }, - [BL32_EXTRA1_IMAGE_ID] = { - .dev_handle = &image_dev_handle, - .image_spec = (uintptr_t)&optee_pager_partition_spec, - .check = open_image - }, - [BL32_EXTRA2_IMAGE_ID] = { - .dev_handle = &image_dev_handle, - .image_spec = (uintptr_t)&optee_paged_partition_spec, - .check = open_image - }, -#else - [BL32_IMAGE_ID] = { - .dev_handle = &dummy_dev_handle, - .image_spec = (uintptr_t)&bl32_block_spec, - .check = open_dummy - }, -#endif - [BL33_IMAGE_ID] = { - .dev_handle = &image_dev_handle, - .image_spec = (uintptr_t)&bl33_partition_spec, - .check = open_image - }, -#if STM32MP_SDMMC || STM32MP_EMMC - [GPT_IMAGE_ID] = { - .dev_handle = &storage_dev_handle, - .image_spec = (uintptr_t)&gpt_block_spec, - .check = open_storage - }, -#endif - [STM32_IMAGE_ID] = { - .dev_handle = &storage_dev_handle, - .image_spec = (uintptr_t)&stm32image_block_spec, - .check = open_storage - } -}; - -static int open_dummy(const uintptr_t spec) -{ - return io_dev_init(dummy_dev_handle, 0); -} - -static int open_image(const uintptr_t spec) +int open_fip(const uintptr_t spec) { - return io_dev_init(image_dev_handle, 0); + return io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID); } -static int open_storage(const uintptr_t spec) +int open_storage(const uintptr_t spec) { return io_dev_init(storage_dev_handle, 0); } @@ -248,17 +127,24 @@ static void print_boot_device(boot_api_context_t *boot_context) case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: INFO("Using EMMC\n"); break; - case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: - INFO("Using QSPI NOR\n"); + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI: + INFO("Using SPI NOR\n"); break; case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: INFO("Using FMC NAND\n"); break; - case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: INFO("Using SPI NAND\n"); break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + INFO("Using UART\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + INFO("Using USB\n"); + break; default: - ERROR("Boot interface not found\n"); + ERROR("Boot interface %u not found\n", + boot_context->boot_interface_selected); panic(); break; } @@ -273,11 +159,8 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, uint16_t boot_interface_instance) { int io_result __unused; - uint8_t idx; - struct stm32image_part_info *part; struct stm32_sdmmc2_params params; struct mmc_device_info device_info; - const partition_entry_t *entry; zeromem(&device_info, sizeof(struct mmc_device_info)); zeromem(¶ms, sizeof(struct stm32_sdmmc2_params)); @@ -304,6 +187,10 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, break; } + if (mmc_dev_type == MMC_IS_SD) { + params.flags = MMC_FLAG_SD_CMD6; + } + params.device_info = &device_info; if (stm32_sdmmc2_mmc_init(¶ms) != 0) { ERROR("SDMMC%u init failed\n", boot_interface_instance); @@ -319,44 +206,6 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_spec, &storage_dev_handle); assert(io_result == 0); - - partition_init(GPT_IMAGE_ID); - - io_result = io_dev_close(storage_dev_handle); - assert(io_result == 0); - - stm32image_dev_info_spec.device_size = - stm32_sdmmc2_mmc_get_device_size(); - - for (idx = 0U; idx < IMG_IDX_NUM; idx++) { - part = &stm32image_dev_info_spec.part_info[idx]; - entry = get_partition_entry(part->name); - if (entry == NULL) { - ERROR("Partition %s not found\n", part->name); - panic(); - } - - part->part_offset = entry->start; - part->bkp_offset = 0U; - } - - /* - * Re-open MMC with io_mmc, for better perfs compared to - * io_block. - */ - io_result = register_io_dev_mmc(&mmc_dev_con); - assert(io_result == 0); - - io_result = io_dev_open(mmc_dev_con, 0, &storage_dev_handle); - assert(io_result == 0); - - io_result = register_io_dev_stm32image(&stm32image_dev_con); - assert(io_result == 0); - - io_result = io_dev_open(stm32image_dev_con, - (uintptr_t)&stm32image_dev_info_spec, - &image_dev_handle); - assert(io_result == 0); } #endif /* STM32MP_SDMMC || STM32MP_EMMC */ @@ -364,8 +213,6 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, static void boot_spi_nor(boot_api_context_t *boot_context) { int io_result __unused; - uint8_t idx; - struct stm32image_part_info *part; io_result = stm32_qspi_init(); assert(io_result == 0); @@ -378,38 +225,6 @@ static void boot_spi_nor(boot_api_context_t *boot_context) (uintptr_t)&spi_nor_dev_spec, &storage_dev_handle); assert(io_result == 0); - - stm32image_dev_info_spec.device_size = spi_nor_dev_spec.device_size; - - idx = IMG_IDX_BL33; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NOR_BL33_OFFSET; - part->bkp_offset = 0U; - -#ifdef AARCH32_SP_OPTEE - idx = IMG_IDX_OPTEE_HEADER; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NOR_TEEH_OFFSET; - part->bkp_offset = 0U; - - idx = IMG_IDX_OPTEE_PAGED; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NOR_TEED_OFFSET; - part->bkp_offset = 0U; - - idx = IMG_IDX_OPTEE_PAGER; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NOR_TEEX_OFFSET; - part->bkp_offset = 0U; -#endif - - io_result = register_io_dev_stm32image(&stm32image_dev_con); - assert(io_result == 0); - - io_result = io_dev_open(stm32image_dev_con, - (uintptr_t)&stm32image_dev_info_spec, - &image_dev_handle); - assert(io_result == 0); } #endif /* STM32MP_SPI_NOR */ @@ -417,8 +232,6 @@ static void boot_spi_nor(boot_api_context_t *boot_context) static void boot_fmc2_nand(boot_api_context_t *boot_context) { int io_result __unused; - uint8_t idx; - struct stm32image_part_info *part; io_result = stm32_fmc2_init(); assert(io_result == 0); @@ -432,37 +245,7 @@ static void boot_fmc2_nand(boot_api_context_t *boot_context) &storage_dev_handle); assert(io_result == 0); - stm32image_dev_info_spec.device_size = nand_dev_spec.device_size; - - idx = IMG_IDX_BL33; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_BL33_OFFSET; - part->bkp_offset = nand_dev_spec.erase_size; - -#ifdef AARCH32_SP_OPTEE - idx = IMG_IDX_OPTEE_HEADER; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_TEEH_OFFSET; - part->bkp_offset = nand_dev_spec.erase_size; - - idx = IMG_IDX_OPTEE_PAGED; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_TEED_OFFSET; - part->bkp_offset = nand_dev_spec.erase_size; - - idx = IMG_IDX_OPTEE_PAGER; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_TEEX_OFFSET; - part->bkp_offset = nand_dev_spec.erase_size; -#endif - - io_result = register_io_dev_stm32image(&stm32image_dev_con); - assert(io_result == 0); - - io_result = io_dev_open(stm32image_dev_con, - (uintptr_t)&stm32image_dev_info_spec, - &image_dev_handle); - assert(io_result == 0); + nand_bkp_offset = nand_dev_spec.erase_size; } #endif /* STM32MP_RAW_NAND */ @@ -470,8 +253,6 @@ static void boot_fmc2_nand(boot_api_context_t *boot_context) static void boot_spi_nand(boot_api_context_t *boot_context) { int io_result __unused; - uint8_t idx; - struct stm32image_part_info *part; io_result = stm32_qspi_init(); assert(io_result == 0); @@ -485,40 +266,57 @@ static void boot_spi_nand(boot_api_context_t *boot_context) &storage_dev_handle); assert(io_result == 0); - stm32image_dev_info_spec.device_size = - spi_nand_dev_spec.device_size; - - idx = IMG_IDX_BL33; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_BL33_OFFSET; - part->bkp_offset = spi_nand_dev_spec.erase_size; - -#ifdef AARCH32_SP_OPTEE - idx = IMG_IDX_OPTEE_HEADER; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_TEEH_OFFSET; - part->bkp_offset = spi_nand_dev_spec.erase_size; - - idx = IMG_IDX_OPTEE_PAGED; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_TEED_OFFSET; - part->bkp_offset = spi_nand_dev_spec.erase_size; - - idx = IMG_IDX_OPTEE_PAGER; - part = &stm32image_dev_info_spec.part_info[idx]; - part->part_offset = STM32MP_NAND_TEEX_OFFSET; - part->bkp_offset = spi_nand_dev_spec.erase_size; -#endif + nand_bkp_offset = spi_nand_dev_spec.erase_size; +} +#endif /* STM32MP_SPI_NAND */ + +#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER +static void mmap_io_setup(void) +{ + int io_result __unused; - io_result = register_io_dev_stm32image(&stm32image_dev_con); + io_result = register_io_dev_memmap(&memmap_dev_con); assert(io_result == 0); - io_result = io_dev_open(stm32image_dev_con, - (uintptr_t)&stm32image_dev_info_spec, - &image_dev_handle); + io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL, + &storage_dev_handle); assert(io_result == 0); } -#endif /* STM32MP_SPI_NAND */ +#endif + +#if STM32MP_UART_PROGRAMMER +static void stm32cubeprogrammer_uart(unsigned int image_id) +{ + int ret __unused; + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + uintptr_t uart_base; + + uart_base = get_uart_address(boot_context->boot_interface_instance); + ret = stm32cubeprog_uart_load(image_id, uart_base, FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE, + DWL_BUFFER_BASE, DWL_BUFFER_SIZE); + assert(ret == 0); + + flush_dcache_range(FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE); +} +#endif + +#if STM32MP_USB_PROGRAMMER +static void stm32cubeprogrammer_usb(unsigned int image_id) +{ + usb_handle_t *pdev; + int ret __unused; + + /* init USB on platform */ + pdev = usb_dfu_plat_init(); + + ret = stm32cubeprog_usb_load(image_id, pdev, FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE, + DWL_BUFFER_BASE, DWL_BUFFER_SIZE); + assert(ret == 0); + + flush_dcache_range(FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE); +} +#endif void stm32mp_io_setup(void) { @@ -534,12 +332,11 @@ void stm32mp_io_setup(void) boot_context->boot_partition_used_toboot); } - io_result = register_io_dev_dummy(&dummy_dev_con); + io_result = register_io_dev_fip(&fip_dev_con); assert(io_result == 0); - io_result = io_dev_open(dummy_dev_con, dummy_dev_spec, - &dummy_dev_handle); - assert(io_result == 0); + io_result = io_dev_open(fip_dev_con, (uintptr_t)NULL, + &fip_dev_handle); switch (boot_context->boot_interface_selected) { #if STM32MP_SDMMC @@ -555,7 +352,7 @@ void stm32mp_io_setup(void) break; #endif #if STM32MP_SPI_NOR - case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI: dmbsy(); boot_spi_nor(boot_context); break; @@ -567,19 +364,106 @@ void stm32mp_io_setup(void) break; #endif #if STM32MP_SPI_NAND - case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: dmbsy(); boot_spi_nand(boot_context); break; #endif - +#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: +#endif +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: +#endif + dmbsy(); + mmap_io_setup(); + break; +#endif default: ERROR("Boot interface %d not supported\n", boot_context->boot_interface_selected); + panic(); break; } } +int bl2_plat_handle_pre_image_load(unsigned int image_id) +{ + static bool gpt_init_done __unused; + uint16_t boot_itf = stm32mp_get_boot_itf_selected(); + + switch (boot_itf) { +#if STM32MP_SDMMC || STM32MP_EMMC + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: + if (!gpt_init_done) { + const partition_entry_t *entry; + + partition_init(GPT_IMAGE_ID); + entry = get_partition_entry(FIP_IMAGE_NAME); + if (entry == NULL) { + ERROR("Could NOT find the %s partition!\n", + FIP_IMAGE_NAME); + return -ENOENT; + } + + image_block_spec.offset = entry->start; + image_block_spec.length = entry->length; + + gpt_init_done = true; + } + + break; +#endif + +#if STM32MP_RAW_NAND || STM32MP_SPI_NAND +#if STM32MP_RAW_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: +#endif +#if STM32MP_SPI_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: +#endif + image_block_spec.offset = STM32MP_NAND_FIP_OFFSET; + break; +#endif + +#if STM32MP_SPI_NOR + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI: + image_block_spec.offset = STM32MP_NOR_FIP_OFFSET; + break; +#endif + +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + if (image_id == FW_CONFIG_ID) { + stm32cubeprogrammer_uart(FIP_IMAGE_ID); + /* BL33 at SSBL load address */ + image_block_spec.offset = DWL_BUFFER_BASE; + image_block_spec.length = DWL_BUFFER_SIZE; + } + break; +#endif + +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + if (image_id == FW_CONFIG_ID) { + stm32cubeprogrammer_usb(FIP_IMAGE_ID); + /* BL33 at SSBL load address */ + image_block_spec.offset = DWL_BUFFER_BASE; + image_block_spec.length = DWL_BUFFER_SIZE; + } + break; +#endif + + default: + ERROR("FIP Not found\n"); + panic(); + } + + return 0; +} + /* * Return an IO device handle and specification which can be used to access * an image. Use this to enforce platform load policy. @@ -590,9 +474,7 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, int rc; const struct plat_io_policy *policy; - assert(image_id < ARRAY_SIZE(policies)); - - policy = &policies[image_id]; + policy = FCONF_GET_PROPERTY(stm32mp, io_policies, image_id); rc = policy->check(policy->image_spec); if (rc == 0) { *image_spec = policy->image_spec; @@ -601,3 +483,33 @@ int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, return rc; } + +/* + * This function shall return 0 if it cannot find an alternate + * image to be loaded or it returns 1 otherwise. + */ +int plat_try_next_boot_source(unsigned int image_id) +{ + static unsigned int backup_id; + static unsigned int backup_nb; + + /* No backup available */ + if (nand_bkp_offset == 0U) { + return 0; + } + + if (backup_id != image_id) { + backup_nb = 0; + backup_id = image_id; + } + + backup_nb++; + + if (backup_nb >= PLATFORM_MTD_BACKUP_BLOCKS) { + return 0; + } + + image_block_spec.offset += nand_bkp_offset; + + return 1; +} diff --git a/plat/st/common/bl2_stm32_io_storage.c b/plat/st/common/bl2_stm32_io_storage.c new file mode 100644 index 0000000000..5e7ecfad48 --- /dev/null +++ b/plat/st/common/bl2_stm32_io_storage.c @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 + +/* IO devices */ +#ifndef AARCH32_SP_OPTEE +static const io_dev_connector_t *dummy_dev_con; +static uintptr_t dummy_dev_handle; +static uintptr_t dummy_dev_spec; +#endif + +static uintptr_t image_dev_handle; +static uintptr_t storage_dev_handle; + +#if STM32MP_SDMMC || STM32MP_EMMC +static io_block_spec_t gpt_block_spec = { + .offset = 0, + .length = 34 * MMC_BLOCK_SIZE, /* Size of GPT table */ +}; + +static uint32_t block_buffer[MMC_BLOCK_SIZE] __aligned(MMC_BLOCK_SIZE); + +static const io_block_dev_spec_t mmc_block_dev_spec = { + /* It's used as temp buffer in block driver */ + .buffer = { + .offset = (size_t)&block_buffer, + .length = MMC_BLOCK_SIZE, + }, + .ops = { + .read = mmc_read_blocks, + .write = NULL, + }, + .block_size = MMC_BLOCK_SIZE, +}; + +static const io_dev_connector_t *mmc_dev_con; +#endif /* STM32MP_SDMMC || STM32MP_EMMC */ + +#if STM32MP_SPI_NOR +static io_mtd_dev_spec_t spi_nor_dev_spec = { + .ops = { + .init = spi_nor_init, + .read = spi_nor_read, + }, +}; +#endif + +#if STM32MP_RAW_NAND +static io_mtd_dev_spec_t nand_dev_spec = { + .ops = { + .init = nand_raw_init, + .read = nand_read, + }, +}; + +static const io_dev_connector_t *nand_dev_con; +#endif + +#if STM32MP_SPI_NAND +static io_mtd_dev_spec_t spi_nand_dev_spec = { + .ops = { + .init = spi_nand_init, + .read = nand_read, + }, +}; +#endif + +#if STM32MP_SPI_NAND || STM32MP_SPI_NOR +static const io_dev_connector_t *spi_dev_con; +#endif + +#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER +static const io_dev_connector_t *memmap_dev_con; +#endif + +#ifdef AARCH32_SP_OPTEE +static const struct stm32image_part_info optee_header_partition_spec = { + .name = OPTEE_HEADER_IMAGE_NAME, + .binary_type = OPTEE_HEADER_BINARY_TYPE, +}; + +static const struct stm32image_part_info optee_core_partition_spec = { + .name = OPTEE_CORE_IMAGE_NAME, + .binary_type = OPTEE_CORE_BINARY_TYPE, +}; + +static const struct stm32image_part_info optee_paged_partition_spec = { + .name = OPTEE_PAGED_IMAGE_NAME, + .binary_type = OPTEE_PAGED_BINARY_TYPE, +}; +#else +static const io_block_spec_t bl32_block_spec = { + .offset = BL32_BASE, + .length = STM32MP_BL32_SIZE +}; +#endif + +static const struct stm32image_part_info bl33_partition_spec = { + .name = BL33_IMAGE_NAME, + .binary_type = BL33_BINARY_TYPE, +}; + +enum { + IMG_IDX_BL33, +#ifdef AARCH32_SP_OPTEE + IMG_IDX_OPTEE_HEADER, + IMG_IDX_OPTEE_CORE, + IMG_IDX_OPTEE_PAGED, +#endif + IMG_IDX_NUM +}; + +static struct stm32image_device_info stm32image_dev_info_spec __unused = { + .lba_size = MMC_BLOCK_SIZE, + .part_info[IMG_IDX_BL33] = { + .name = BL33_IMAGE_NAME, + .binary_type = BL33_BINARY_TYPE, + }, +#ifdef AARCH32_SP_OPTEE + .part_info[IMG_IDX_OPTEE_HEADER] = { + .name = OPTEE_HEADER_IMAGE_NAME, + .binary_type = OPTEE_HEADER_BINARY_TYPE, + }, + .part_info[IMG_IDX_OPTEE_CORE] = { + .name = OPTEE_CORE_IMAGE_NAME, + .binary_type = OPTEE_CORE_BINARY_TYPE, + }, + .part_info[IMG_IDX_OPTEE_PAGED] = { + .name = OPTEE_PAGED_IMAGE_NAME, + .binary_type = OPTEE_PAGED_BINARY_TYPE, + }, +#endif +}; + +static io_block_spec_t image_block_spec = { + .offset = 0, + .length = 0, +}; + +static const io_dev_connector_t *stm32image_dev_con __unused; + +#ifndef AARCH32_SP_OPTEE +static int open_dummy(const uintptr_t spec); +#endif +static int open_image(const uintptr_t spec); +static int open_storage(const uintptr_t spec); + +struct plat_io_policy { + uintptr_t *dev_handle; + uintptr_t image_spec; + int (*check)(const uintptr_t spec); +}; + +static const struct plat_io_policy policies[] = { +#ifdef AARCH32_SP_OPTEE + [BL32_IMAGE_ID] = { + .dev_handle = &image_dev_handle, + .image_spec = (uintptr_t)&optee_header_partition_spec, + .check = open_image + }, + [BL32_EXTRA1_IMAGE_ID] = { + .dev_handle = &image_dev_handle, + .image_spec = (uintptr_t)&optee_core_partition_spec, + .check = open_image + }, + [BL32_EXTRA2_IMAGE_ID] = { + .dev_handle = &image_dev_handle, + .image_spec = (uintptr_t)&optee_paged_partition_spec, + .check = open_image + }, +#else + [BL32_IMAGE_ID] = { + .dev_handle = &dummy_dev_handle, + .image_spec = (uintptr_t)&bl32_block_spec, + .check = open_dummy + }, +#endif + [BL33_IMAGE_ID] = { + .dev_handle = &image_dev_handle, + .image_spec = (uintptr_t)&bl33_partition_spec, + .check = open_image + }, +#if STM32MP_SDMMC || STM32MP_EMMC + [GPT_IMAGE_ID] = { + .dev_handle = &storage_dev_handle, + .image_spec = (uintptr_t)&gpt_block_spec, + .check = open_storage + }, +#endif + [STM32_IMAGE_ID] = { + .dev_handle = &storage_dev_handle, + .image_spec = (uintptr_t)&image_block_spec, + .check = open_storage + }, +}; + +#ifndef AARCH32_SP_OPTEE +static int open_dummy(const uintptr_t spec) +{ + return io_dev_init(dummy_dev_handle, 0); +} +#endif + +static int open_image(const uintptr_t spec) +{ + return io_dev_init(image_dev_handle, 0); +} + +static int open_storage(const uintptr_t spec) +{ + return io_dev_init(storage_dev_handle, 0); +} + +static void print_boot_device(boot_api_context_t *boot_context) +{ + switch (boot_context->boot_interface_selected) { + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: + INFO("Using SDMMC\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: + INFO("Using EMMC\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI: + INFO("Using SPI NOR\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + INFO("Using FMC NAND\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: + INFO("Using SPI NAND\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + INFO("Using UART\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + INFO("Using USB\n"); + break; + default: + ERROR("Boot interface %u not found\n", + boot_context->boot_interface_selected); + panic(); + break; + } + + if (boot_context->boot_interface_instance != 0U) { + INFO(" Instance %d\n", boot_context->boot_interface_instance); + } +} + +static void stm32image_io_setup(void) +{ + int io_result __unused; + + io_result = register_io_dev_stm32image(&stm32image_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(stm32image_dev_con, + (uintptr_t)&stm32image_dev_info_spec, + &image_dev_handle); + assert(io_result == 0); +} + +#if STM32MP_SDMMC || STM32MP_EMMC +static void boot_mmc(enum mmc_device_type mmc_dev_type, + uint16_t boot_interface_instance) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + struct stm32_sdmmc2_params params; + struct mmc_device_info device_info; + const partition_entry_t *entry; + + zeromem(&device_info, sizeof(struct mmc_device_info)); + zeromem(¶ms, sizeof(struct stm32_sdmmc2_params)); + + device_info.mmc_dev_type = mmc_dev_type; + + switch (boot_interface_instance) { + case 1: + params.reg_base = STM32MP_SDMMC1_BASE; + break; + case 2: + params.reg_base = STM32MP_SDMMC2_BASE; + break; + case 3: + params.reg_base = STM32MP_SDMMC3_BASE; + break; + default: + WARN("SDMMC instance not found, using default\n"); + if (mmc_dev_type == MMC_IS_SD) { + params.reg_base = STM32MP_SDMMC1_BASE; + } else { + params.reg_base = STM32MP_SDMMC2_BASE; + } + break; + } + + if (mmc_dev_type == MMC_IS_SD) { + params.flags = MMC_FLAG_SD_CMD6; + } + + params.device_info = &device_info; + if (stm32_sdmmc2_mmc_init(¶ms) != 0) { + ERROR("SDMMC%u init failed\n", boot_interface_instance); + panic(); + } + + /* Open MMC as a block device to read GPT table */ + io_result = register_io_dev_block(&mmc_dev_con); + if (io_result != 0) { + panic(); + } + + io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + partition_init(GPT_IMAGE_ID); + + io_result = io_dev_close(storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = + stm32_sdmmc2_mmc_get_device_size(); + + for (idx = 0U; idx < IMG_IDX_NUM; idx++) { + part = &stm32image_dev_info_spec.part_info[idx]; + entry = get_partition_entry(part->name); + if (entry == NULL) { + ERROR("Partition %s not found\n", part->name); + panic(); + } + + part->part_offset = entry->start; + part->bkp_offset = 0U; + } + + /* + * Re-open MMC with io_mmc, for better perfs compared to + * io_block. + */ + io_result = register_io_dev_mmc(&mmc_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(mmc_dev_con, 0, &storage_dev_handle); + assert(io_result == 0); +} +#endif /* STM32MP_SDMMC || STM32MP_EMMC */ + +#if STM32MP_SPI_NOR +static void boot_spi_nor(boot_api_context_t *boot_context) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + + io_result = stm32_qspi_init(); + assert(io_result == 0); + + io_result = register_io_dev_mtd(&spi_dev_con); + assert(io_result == 0); + + /* Open connections to device */ + io_result = io_dev_open(spi_dev_con, + (uintptr_t)&spi_nor_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = spi_nor_dev_spec.device_size; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_BL33_OFFSET; + part->bkp_offset = 0U; + +#ifdef AARCH32_SP_OPTEE + idx = IMG_IDX_OPTEE_HEADER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_TEEH_OFFSET; + part->bkp_offset = 0U; + + idx = IMG_IDX_OPTEE_PAGED; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_TEED_OFFSET; + part->bkp_offset = 0U; + + idx = IMG_IDX_OPTEE_CORE; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_TEEX_OFFSET; + part->bkp_offset = 0U; +#endif +} +#endif /* STM32MP_SPI_NOR */ + +#if STM32MP_RAW_NAND +static void boot_fmc2_nand(boot_api_context_t *boot_context) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + + io_result = stm32_fmc2_init(); + assert(io_result == 0); + + /* Register the IO device on this platform */ + io_result = register_io_dev_mtd(&nand_dev_con); + assert(io_result == 0); + + /* Open connections to device */ + io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = nand_dev_spec.device_size; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_BL33_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; + +#ifdef AARCH32_SP_OPTEE + idx = IMG_IDX_OPTEE_HEADER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEH_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_PAGED; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEED_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_CORE; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEX_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; +#endif +} +#endif /* STM32MP_RAW_NAND */ + +#if STM32MP_SPI_NAND +static void boot_spi_nand(boot_api_context_t *boot_context) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + + io_result = stm32_qspi_init(); + assert(io_result == 0); + + io_result = register_io_dev_mtd(&spi_dev_con); + assert(io_result == 0); + + /* Open connections to device */ + io_result = io_dev_open(spi_dev_con, + (uintptr_t)&spi_nand_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = spi_nand_dev_spec.device_size; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_BL33_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; + +#ifdef AARCH32_SP_OPTEE + idx = IMG_IDX_OPTEE_HEADER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEH_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_PAGED; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEED_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_CORE; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEX_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; +#endif +} +#endif /* STM32MP_SPI_NAND */ + +#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER +static void mmap_io_setup(void) +{ + int io_result __unused; + + io_result = register_io_dev_memmap(&memmap_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL, + &storage_dev_handle); + assert(io_result == 0); +} + +static void stm32image_mmap_setup(void) +{ + uint8_t idx; + struct stm32image_part_info *part; + + stm32image_dev_info_spec.device_size = DWL_BUFFER_SIZE; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = 0; + part->bkp_offset = 0; +} +#endif + +#if STM32MP_UART_PROGRAMMER +static void stm32cubeprogrammer_uart(unsigned int image_id) +{ + int ret __unused; + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + uintptr_t uart_base; + + uart_base = get_uart_address(boot_context->boot_interface_instance); + ret = stm32cubeprog_uart_load(image_id, uart_base, FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE, + DWL_BUFFER_BASE, DWL_BUFFER_SIZE); + assert(ret == 0); + + flush_dcache_range(FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE); +} +#endif + +#if STM32MP_USB_PROGRAMMER +static void stm32cubeprogrammer_usb(unsigned int image_id) +{ + usb_handle_t *pdev; + int ret __unused; + + /* init USB on platform */ + pdev = usb_dfu_plat_init(); + + ret = stm32cubeprog_usb_load(image_id, pdev, FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE, + DWL_BUFFER_BASE, DWL_BUFFER_SIZE); + assert(ret == 0); + + flush_dcache_range(FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE); +} +#endif + +void stm32mp_io_setup(void) +{ + int io_result __unused; + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + + print_boot_device(boot_context); + + if ((boot_context->boot_partition_used_toboot == 1U) || + (boot_context->boot_partition_used_toboot == 2U)) { + INFO("Boot used partition fsbl%d\n", + boot_context->boot_partition_used_toboot); + } + +#ifndef AARCH32_SP_OPTEE + io_result = register_io_dev_dummy(&dummy_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(dummy_dev_con, dummy_dev_spec, + &dummy_dev_handle); + assert(io_result == 0); +#endif + + switch (boot_context->boot_interface_selected) { +#if STM32MP_SDMMC + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: + dmbsy(); + boot_mmc(MMC_IS_SD, boot_context->boot_interface_instance); + stm32image_io_setup(); + break; +#endif +#if STM32MP_EMMC + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: + dmbsy(); + boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance); + stm32image_io_setup(); + break; +#endif +#if STM32MP_SPI_NOR + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI: + dmbsy(); + boot_spi_nor(boot_context); + stm32image_io_setup(); + break; +#endif +#if STM32MP_RAW_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + dmbsy(); + boot_fmc2_nand(boot_context); + stm32image_io_setup(); + break; +#endif +#if STM32MP_SPI_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI: + dmbsy(); + boot_spi_nand(boot_context); + stm32image_io_setup(); + break; +#endif +#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: +#endif +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: +#endif + dmbsy(); + mmap_io_setup(); + stm32image_mmap_setup(); + stm32image_io_setup(); + break; +#endif + default: + ERROR("Boot interface %d not supported\n", + boot_context->boot_interface_selected); + panic(); + break; + } +} + +int bl2_plat_handle_pre_image_load(unsigned int image_id) +{ + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + + switch (boot_context->boot_interface_selected) { +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + if (image_id == BL33_IMAGE_ID) { + stm32cubeprogrammer_uart(STM32_IMAGE_ID); + /* BL33 at SSBL load address */ + image_block_spec.offset = DWL_BUFFER_BASE; + image_block_spec.length = DWL_BUFFER_SIZE; + } + break; +#endif +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + if (image_id == BL33_IMAGE_ID) { + stm32cubeprogrammer_usb(STM32_IMAGE_ID); + /* BL33 at SSBL load address */ + image_block_spec.offset = DWL_BUFFER_BASE; + image_block_spec.length = DWL_BUFFER_SIZE; + } + break; +#endif + default: + break; + } + + return 0; +} + +/* + * Return an IO device handle and specification which can be used to access + * an image. Use this to enforce platform load policy. + */ +int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, + uintptr_t *image_spec) +{ + int rc; + const struct plat_io_policy *policy; + + assert(image_id < ARRAY_SIZE(policies)); + + policy = &policies[image_id]; + rc = policy->check(policy->image_spec); + if (rc == 0) { + *image_spec = policy->image_spec; + *dev_handle = *(policy->dev_handle); + } + + return rc; +} + +/* + * This function shall return 0 if it cannot find an alternate + * image to be loaded and any non-zero value otherwise. + */ +int plat_try_next_boot_source(unsigned int image_id) +{ + int io_result __unused; + const struct stm32image_part_info *partition_spec; + struct stm32image_part_info *part; + const struct plat_io_policy *policy; + uint32_t idx; + static unsigned int backup_nb; + static unsigned int backup_id = MAX_NUMBER_IDS; + + assert(image_id < ARRAY_SIZE(policies)); + + if (backup_id != image_id) { + backup_id = image_id; + backup_nb = 0; + } + + backup_nb++; + + if (backup_nb >= PLATFORM_MTD_BACKUP_BLOCKS) { + return 0; + } + + policy = &policies[image_id]; + partition_spec = (struct stm32image_part_info *)policy->image_spec; + for (idx = 0U; idx < STM32_PART_NUM; idx++) { + part = &stm32image_dev_info_spec.part_info[idx]; + if (part->binary_type == partition_spec->binary_type) { + break; + } + } + + assert(idx < STM32_PART_NUM); + + if (part->bkp_offset == 0U) { + return 0; + } + + part->part_offset += part->bkp_offset; + /* + * Reopen the io_dev as it was closed in the load_auth_image() + * sequence. + */ + io_result = io_dev_open(stm32image_dev_con, + (uintptr_t)&stm32image_dev_info_spec, + &image_dev_handle); + assert(io_result == 0); + + return 1; +} diff --git a/plat/st/common/include/stm32cubeprogrammer.h b/plat/st/common/include/stm32cubeprogrammer.h new file mode 100644 index 0000000000..947ad43aea --- /dev/null +++ b/plat/st/common/include/stm32cubeprogrammer.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32CUBEPROGRAMMER_H +#define STM32CUBEROGRAMMER_H + +/* Phase definition */ +#define PHASE_FLASHLAYOUT 0U +#define PHASE_FSBL1 1U +#define PHASE_FSBL2 2U +#define PHASE_SSBL 3U +#define PHASE_CMD 0xF1U +#define PHASE_SSP 0xF3U +#define PHASE_RESET 0xFFU + +/* Command definition */ +#define GET_CMD_COMMAND 0x00U +#define GET_VER_COMMAND 0x01U +#define GET_ID_COMMAND 0x02U +#define PHASE_COMMAND 0x03U +#define READ_PART_COMMAND 0x12U +#define START_COMMAND 0x21U +#define DOWNLOAD_COMMAND 0x31U + +/* Answer defines */ +#define INIT_BYTE 0x7FU +#define ACK_BYTE 0x79U +#define NACK_BYTE 0x1FU +#define ABORT 0x5FU + +#define DEVICE_ID_BYTE1 0x05U +#define DEVICE_ID_BYTE2 0x00U + +/* Functions provided by plat */ +uint8_t usb_dfu_get_phase(uint8_t alt); + +typedef struct usb_handle usb_handle_t; +int stm32cubeprog_usb_load(unsigned int image_id, + usb_handle_t *usb_core_handle, + uintptr_t flashlayout_base, + size_t flashlayout_len, + uintptr_t ssbl_base, + size_t ssbl_len); + +int stm32cubeprog_uart_load(unsigned int image_id, + uintptr_t instance, + uintptr_t flashlayout_base, + size_t flashlayout_len, + uintptr_t ssbl_base, + size_t ssbl_len); + +int stm32cubeprog_usb_ssp(usb_handle_t *usb_core_handle, + uintptr_t cert_base, + size_t cert_len, + uintptr_t ssp_base, + size_t ssp_len); + +int stm32cubeprog_uart_ssp(uintptr_t instance, + uintptr_t cert_base, + size_t cert_len, + uintptr_t ssp_base, + size_t ssp_len); + +#endif /* STM32CUBEROGRAMMER_H */ diff --git a/plat/st/common/include/stm32mp_auth.h b/plat/st/common/include/stm32mp_auth.h deleted file mode 100644 index 3075d18ac7..0000000000 --- a/plat/st/common/include/stm32mp_auth.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef STM32MP_AUTH_H -#define STM32MP_AUTH_H - -struct stm32mp_auth_ops { - uint32_t (*check_key)(uint8_t *pubkey_in, uint8_t *pubkey_out); - uint32_t (*verify_signature)(uint8_t *hash_in, uint8_t *pubkey_in, - uint8_t *signature, uint32_t ecc_algo); -}; - -void stm32mp_init_auth(struct stm32mp_auth_ops *init_ptr); -int stm32mp_auth_image(boot_api_image_header_t *header, uintptr_t buffer); - -#endif /* STM32MP_AUTH_H */ diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h index feeb4a790d..022e22742a 100644 --- a/plat/st/common/include/stm32mp_common.h +++ b/plat/st/common/include/stm32mp_common.h @@ -7,16 +7,26 @@ #ifndef STM32MP_COMMON_H #define STM32MP_COMMON_H +#include #include #include +void __dead2 stm32mp_plat_reset(int cpu); + /* Functions to save and get boot context address given by ROM code */ void stm32mp_save_boot_ctx_address(uintptr_t address); uintptr_t stm32mp_get_boot_ctx_address(void); +uint16_t stm32mp_get_boot_itf_selected(void); +uint32_t stm32mp_get_boot_action(void); bool stm32mp_is_single_core(void); bool stm32mp_is_closed_device(void); +bool stm32mp_is_auth_supported(void); + +const char *stm32mp_get_cpu_supply_name(void); +const char *stm32mp_get_vdd_supply_name(void); +const char *stm32mp_get_usb_phy_supply_name(void); /* Return the base address of the DDR controller */ uintptr_t stm32mp_ddrctrl_base(void); @@ -30,9 +40,22 @@ uintptr_t stm32mp_pwr_base(void); /* Return the base address of the RCC peripheral */ uintptr_t stm32mp_rcc_base(void); +void stm32_gic_pcpu_init(void); +void stm32_gic_init(void); +int stm32_gic_enable_spi(int node, const char *name); + /* Check MMU status to allow spinlock use */ bool stm32mp_lock_available(void); +/* SMP protection on PWR registers access */ +void stm32mp_pwr_regs_lock(void); +void stm32mp_pwr_regs_unlock(void); + +int stm32_get_otp_index(const char *otp_name, uint32_t *otp_idx, + uint32_t *otp_len); +int stm32_get_otp_value(const char *otp_name, uint32_t *otp_val); +int stm32_get_otp_value_from_idx(const uint32_t otp_idx, uint32_t *otp_val); + /* Get IWDG platform instance ID from peripheral IO memory base address */ uint32_t stm32_iwdg_get_instance(uintptr_t base); @@ -44,6 +67,11 @@ uint32_t stm32_iwdg_get_otp_config(uint32_t iwdg_inst); uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags); #endif +#if STM32MP_UART_PROGRAMMER || defined(IMAGE_BL32) +/* Get the UART address from its instance number */ +uintptr_t get_uart_address(uint32_t instance_nb); +#endif + /* * Platform util functions for the GPIO driver * @bank: Target GPIO bank ID as per DT bindings @@ -64,24 +92,26 @@ uint32_t stm32_get_gpio_bank_offset(unsigned int bank); /* Return node offset for target GPIO bank ID @bank or a FDT error code */ int stm32_get_gpio_bank_pinctrl_node(void *fdt, unsigned int bank); +/* Get the chip revision */ +int stm32mp_get_chip_version(uint32_t *chip_version); + +/* Get SOC name */ +#define STM32_SOC_NAME_SIZE 20 +void stm32mp_get_soc_name(char name[STM32_SOC_NAME_SIZE]); + /* Print CPU information */ void stm32mp_print_cpuinfo(void); /* Print board information */ void stm32mp_print_boardinfo(void); -/* - * Util for clock gating and to get clock rate for stm32 and platform drivers - * @id: Target clock ID, ID used in clock DT bindings - */ -bool stm32mp_clk_is_enabled(unsigned long id); -void stm32mp_clk_enable(unsigned long id); -void stm32mp_clk_disable(unsigned long id); -unsigned long stm32mp_clk_get_rate(unsigned long id); +/* Check HW CPU OPP support */ +bool stm32mp_supports_cpu_opp(uint32_t opp_id); /* Initialise the IO layer and register platform IO devices */ void stm32mp_io_setup(void); +#if STM32MP_USE_STM32IMAGE /* * Check that the STM32 header of a .stm32 binary image is valid * @param header: pointer to the stm32 image header @@ -89,6 +119,13 @@ void stm32mp_io_setup(void); * @return: 0 on success, negative value in case of error */ int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer); +#endif + +#if TRUSTED_BOARD_BOOT +void stm32mp_save_loaded_header(void *header); +void stm32mp_delete_loaded_header(void); +boot_api_image_header_t *stm32mp_get_loaded_header(void); +#endif /* Functions to map DDR in MMU with non-cacheable attribute, and unmap it */ int stm32mp_map_ddr_non_cacheable(void); diff --git a/plat/st/common/include/stm32mp_dt.h b/plat/st/common/include/stm32mp_dt.h index e3b4e597ee..0ff30a3827 100644 --- a/plat/st/common/include/stm32mp_dt.h +++ b/plat/st/common/include/stm32mp_dt.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, STMicroelectronics - All Rights Reserved - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,9 @@ #define STM32MP_DT_H #include +#include + +#include #define DT_DISABLED U(0) #define DT_NON_SECURE U(1) @@ -25,16 +28,25 @@ struct dt_node_info { /******************************************************************************* * Function and variable prototypes ******************************************************************************/ -int dt_open_and_check(void); +int dt_open_and_check(uintptr_t dt_addr); int fdt_get_address(void **fdt_addr); bool fdt_check_node(int node); uint8_t fdt_get_status(int node); +int fdt_get_interrupt(int node, const fdt32_t **array, int *len, + bool *extended); int dt_set_stdout_pinctrl(void); void dt_fill_device_info(struct dt_node_info *info, int node); int dt_get_node(struct dt_node_info *info, int offset, const char *compat); int dt_get_stdout_uart_info(struct dt_node_info *info); +int dt_match_instance_by_compatible(const char *compatible, uintptr_t address); uint32_t dt_get_ddr_size(void); +int dt_get_max_opp_freqvolt(uint32_t *freq_khz, uint32_t *voltage_mv); +int dt_get_all_opp_freqvolt(uint32_t *count, uint32_t *freq_khz_array, + uint32_t *voltage_mv_array); uint32_t dt_get_pwr_vdd_voltage(void); +const char *dt_get_vdd_regulator_name(void); +const char *dt_get_cpu_regulator_name(void); +const char *dt_get_usb_phy_regulator_name(void); const char *dt_get_board_model(void); int fdt_get_gpio_bank_pin_count(unsigned int bank); diff --git a/plat/st/common/include/stm32mp_fconf_getter.h b/plat/st/common/include/stm32mp_fconf_getter.h new file mode 100644 index 0000000000..09d853f8f3 --- /dev/null +++ b/plat/st/common/include/stm32mp_fconf_getter.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_FCONF_GETTER +#define STM32MP_FCONF_GETTER + +#include + +#include + +/* IO policies */ +#define stm32mp__io_policies_getter(id) __extension__ ({ \ + assert((id) < MAX_NUMBER_IDS); \ + &policies[id]; \ +}) + +struct plat_io_policy { + uintptr_t *dev_handle; + uintptr_t image_spec; + int (*check)(const uintptr_t spec); +}; + +extern struct plat_io_policy policies[]; +int fconf_populate_stm32mp_io_policies(uintptr_t config); + +#endif /* STM32MP_FCONF_GETTER */ diff --git a/plat/st/common/include/stm32mp_io_storage.h b/plat/st/common/include/stm32mp_io_storage.h new file mode 100644 index 0000000000..48418a5bf6 --- /dev/null +++ b/plat/st/common/include/stm32mp_io_storage.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef STM32MP_IO_STORAGE_H +#define STM32MP_IO_STORAGE_H + +#include + +#include + +/* IO devices handle */ +extern uintptr_t storage_dev_handle; +extern uintptr_t fip_dev_handle; + +extern io_block_spec_t image_block_spec; + +/* Function declarations */ +int open_fip(const uintptr_t spec); +int open_storage(const uintptr_t spec); + +#endif /* STM32MP_IO_STORAGE_H */ diff --git a/plat/st/common/include/stm32mp_shres_helpers.h b/plat/st/common/include/stm32mp_shres_helpers.h index 8b786cc040..8b048284c6 100644 --- a/plat/st/common/include/stm32mp_shres_helpers.h +++ b/plat/st/common/include/stm32mp_shres_helpers.h @@ -12,63 +12,16 @@ #include /* - * 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. The counter initializes to - * either 0, 1 or 2 upon their expect default state. - * Counters saturates once above UINT_MAX / 2. + * Lock/unlock access to shared registers + * + * @lock - NULL or pointer to spin lock */ -#define SHREFCNT_NONSECURE_FLAG 0x1UL -#define SHREFCNT_SECURE_STEP 0x2UL -#define SHREFCNT_MAX (UINT32_MAX / 2) - -/* Return 1 if refcnt increments from 0, else return 0 */ -static inline int stm32mp_incr_shrefcnt(unsigned int *refcnt, bool secure) -{ - int rc = !*refcnt; - - if (secure) { - *refcnt += SHREFCNT_SECURE_STEP; - if (*refcnt >= SHREFCNT_MAX) { - panic(); - } - } else { - *refcnt |= SHREFCNT_NONSECURE_FLAG; - } - - return rc; -} - -/* Return 1 if refcnt decrements to 0, else return 0 */ -static inline int stm32mp_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) ? 1 : 0; - *refcnt &= ~SHREFCNT_NONSECURE_FLAG; - } - - return rc; -} - -static inline int stm32mp_incr_refcnt(unsigned int *refcnt) -{ - return stm32mp_incr_shrefcnt(refcnt, true); -} -static inline int stm32mp_decr_refcnt(unsigned int *refcnt) -{ - return stm32mp_decr_shrefcnt(refcnt, true); -} +void stm32mp_lock_shregs(void); +void stm32mp_unlock_shregs(void); +void stm32mp_mmio_clrsetbits_32_shregs(uintptr_t addr, uint32_t clear, + uint32_t set); +void stm32mp_mmio_clrbits_32_shregs(uintptr_t addr, uint32_t clear); +void stm32mp_mmio_setbits_32_shregs(uintptr_t addr, uint32_t set); #endif /* STM32MP_SHRES_HELPERS_H */ diff --git a/plat/st/common/stm32_gic.c b/plat/st/common/stm32_gic.c new file mode 100644 index 0000000000..ec3e3525cf --- /dev/null +++ b/plat/st/common/stm32_gic.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +struct stm32_gic_instance { + uint32_t cells; + uint32_t phandle_node; +}; + +/****************************************************************************** + * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0 + * interrupts. + *****************************************************************************/ +static const interrupt_prop_t stm32_interrupt_props[] = { + PLATFORM_G1S_PROPS(GICV2_INTR_GROUP0), + PLATFORM_G0_PROPS(GICV2_INTR_GROUP0) +}; + +/* Fix target_mask_array as secondary core is not able to initialize it */ +static unsigned int target_mask_array[PLATFORM_CORE_COUNT] = {1, 2}; + +static gicv2_driver_data_t platform_gic_data = { + .interrupt_props = stm32_interrupt_props, + .interrupt_props_num = ARRAY_SIZE(stm32_interrupt_props), + .target_masks = target_mask_array, + .target_masks_num = ARRAY_SIZE(target_mask_array), +}; + +static struct stm32_gic_instance stm32_gic; + +static uint32_t enable_gic_interrupt(const fdt32_t *array) +{ + unsigned int id, cfg; + + switch (fdt32_to_cpu(*array)) { + case GIC_SPI: + id = MIN_SPI_ID; + break; + + case GIC_PPI: + id = MIN_PPI_ID; + break; + + default: + id = MIN_SGI_ID; + break; + } + + id += fdt32_to_cpu(*(array + 1)); + cfg = (fdt32_to_cpu(*(array + 2)) < IRQ_TYPE_LEVEL_HIGH) ? + GIC_INTR_CFG_EDGE : GIC_INTR_CFG_LEVEL; + + if ((id >= MIN_SPI_ID) && (id <= MAX_SPI_ID)) { + VERBOSE("Enable IT %i\n", id); + gicv2_set_interrupt_type(id, GICV2_INTR_GROUP0); + gicv2_set_interrupt_priority(id, STM32MP_IRQ_SEC_SPI_PRIO); + gicv2_set_spi_routing(id, STM32MP_PRIMARY_CPU); + gicv2_interrupt_set_cfg(id, cfg); + gicv2_enable_interrupt(id); + } + + return id; +} + +static void find_next_interrupt(const fdt32_t **array) +{ + int node; + const fdt32_t *cuint; + void *fdt; + + assert(fdt32_to_cpu(**array) != stm32_gic.phandle_node); + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(**array)); + if (node < 0) { + panic(); + } + + cuint = fdt_getprop(fdt, node, "#interrupt-cells", NULL); + if (cuint == NULL) { + panic(); + } + + *array += fdt32_to_cpu(*cuint) + 1; +} + +void stm32_gic_init(void) +{ + int node; + void *fdt; + const fdt32_t *cuint; + struct dt_node_info dt_gic; + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + node = dt_get_node(&dt_gic, -1, "arm,cortex-a7-gic"); + if (node < 0) { + panic(); + } + + platform_gic_data.gicd_base = dt_gic.base; + + cuint = fdt_getprop(fdt, node, "reg", NULL); + if (cuint == NULL) { + panic(); + } + + platform_gic_data.gicc_base = fdt32_to_cpu(*(cuint + 2)); + + cuint = fdt_getprop(fdt, node, "#interrupt-cells", NULL); + if (cuint == NULL) { + panic(); + } + + stm32_gic.cells = fdt32_to_cpu(*cuint); + + stm32_gic.phandle_node = fdt_get_phandle(fdt, node); + if (stm32_gic.phandle_node == 0U) { + panic(); + } + + gicv2_driver_init(&platform_gic_data); + gicv2_distif_init(); + + stm32_gic_pcpu_init(); +} + +void stm32_gic_pcpu_init(void) +{ + gicv2_pcpu_distif_init(); + gicv2_set_pe_target_mask(plat_my_core_pos()); + gicv2_cpuif_enable(); +} + +int stm32_gic_enable_spi(int node, const char *name) +{ + const fdt32_t *cuint; + void *fdt; + int res, len; + int index = -1; + int i = 0; + int id = -1; + bool extended; + const fdt32_t *t_array, *max; + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + cuint = fdt_getprop(fdt, node, "interrupt-parent", NULL); + if (cuint != NULL) { + if (stm32_gic.phandle_node != fdt32_to_cpu(*cuint)) { + return -FDT_ERR_NOTFOUND; + } + } + + if (name != NULL) { + switch (fdt_get_status(node)) { + case DT_SECURE: + index = fdt_stringlist_search(fdt, node, + "interrupt-names", name); + break; + default: + index = fdt_stringlist_search(fdt, node, + "secure-interrupt-names", + name); + break; + } + + if (index < 0) { + return index; + } + } + + res = fdt_get_interrupt(node, &t_array, &len, &extended); + if (res < 0) { + return res; + } + + max = t_array + (len / sizeof(uint32_t)); + + while ((t_array < max) && ((i <= index) || (index == -1))) { + if (!extended) { + if ((index == -1) || (i == index)) { + id = enable_gic_interrupt(t_array); + } + t_array += stm32_gic.cells; + } else { + if (fdt32_to_cpu(*t_array) == stm32_gic.phandle_node) { + t_array++; + if ((index == -1) || (i == index)) { + id = enable_gic_interrupt(t_array); + } + t_array += stm32_gic.cells; + } else { + find_next_interrupt(&t_array); + } + } + i++; + } + + return id; +} diff --git a/plat/st/common/stm32cubeprogrammer_uart.c b/plat/st/common/stm32cubeprogrammer_uart.c new file mode 100644 index 0000000000..3fd16aa39c --- /dev/null +++ b/plat/st/common/stm32cubeprogrammer_uart.c @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PROGRAMMER_TIMEOUT_US 20000U + +/* USART bootloader protocol version V4.0 */ +#define USART_BL_VERSION 0x40 +#define UNDEFINED_DOWN_ADDR 0xFFFFFFFF + +static const uint8_t command_tab[] = { + GET_CMD_COMMAND, + GET_VER_COMMAND, + GET_ID_COMMAND, + PHASE_COMMAND, +#if STM32MP_SSP + READ_PART_COMMAND, +#endif + START_COMMAND, + DOWNLOAD_COMMAND +}; + +/* STM32CubeProgrammer over UART handle */ +struct stm32prog_uart_handle_s { + struct stm32_uart_handle_s uart; + uint32_t packet; + uint8_t *addr; + uint32_t len; + uint8_t phase; +#if STM32MP_SSP + uintptr_t cert_base; + size_t cert_len; +#endif + /* error msg buffer: max 255 in UART protocol, reduced in TF-A */ + uint8_t error[64]; +} handle; + +/* Trace and handle unrecoverable UART protocol error */ +#define STM32PROG_ERROR(...) \ + { \ + ERROR(__VA_ARGS__); \ + if (handle.phase != PHASE_RESET) { \ + snprintf((char *)&handle.error, sizeof(handle.error), __VA_ARGS__); \ + handle.phase = PHASE_RESET; \ + handle.addr = (uint8_t *)UNDEFINED_DOWN_ADDR; \ + handle.len = 0U; \ + handle.packet = 0U; \ + } \ + } + +static int uart_write(const uint8_t *addr, uint16_t size) +{ + while (size) { + if (stm32_uart_putc(&handle.uart, *addr) != 0) { + return -EIO; + } + + size--; + addr++; + } + + return 0; +} + +static int uart_write_8(uint8_t byte) +{ + return stm32_uart_putc(&handle.uart, byte); +} + +static int uart_write_32(uint32_t value) +{ + return uart_write((uint8_t *)&value, 4U); +} + +static int uart_read_8(uint8_t *byte) +{ + int ret; + uint64_t timeout_ref = timeout_init_us(PROGRAMMER_TIMEOUT_US); + + do { + ret = stm32_uart_getc(&handle.uart); + if (ret == -EAGAIN) { + if (timeout_elapsed(timeout_ref)) { + return -ETIMEDOUT; + } + } else if (ret < 0) { + return ret; + } + } while (ret == -EAGAIN); + + *byte = (uint8_t)ret; + + return 0; +} + +static int uart_flush_and_nack(void) +{ + int ret; + + /* read all pending data */ + do { + ret = stm32_uart_getc(&handle.uart); + } while (ret >= 0); + + return uart_write_8(NACK_BYTE); +} + +static inline int is_valid_header(fip_toc_header_t *header) +{ + if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0)) { + return 1; + } else { + return 0; + } +} + +static int uart_receive_command(uint8_t *command) +{ + uint8_t byte = 0U; + uint8_t xor = 0U; + unsigned int count; + bool found = false; + int ret; + + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + /* handle reconnection request */ + if (byte == INIT_BYTE) { + *command = byte; + return 0; + } + + for (count = 0U; count < ARRAY_SIZE(command_tab); count++) { + if (command_tab[count] == byte) { + found = true; + break; + } + } + + if (!found) { + VERBOSE("UART: Command unknown (byte=0x%x)\n", byte); + return -EPROTO; + } + + ret = uart_read_8(&xor); + if (ret != 0) { + return ret; + } + + if ((byte ^ xor) != 0xFF) { + VERBOSE("UART: Command XOR check fail (byte=0x%x, xor=0x%x)\n", + byte, xor); + return -EPROTO; + } + + *command = byte; + + return 0; +} + +static int get_cmd_command(void) +{ + int ret; + const uint8_t msg[2] = { + sizeof(command_tab), /* Length of data - 1 */ + USART_BL_VERSION + }; + + ret = uart_write(msg, sizeof(msg)); + if (ret != 0) { + return ret; + } + + return uart_write(command_tab, sizeof(command_tab)); +} + +static int get_version_command(void) +{ + return uart_write_8(STM32_TF_VERSION); +} + +static int get_id_command(void) +{ + const uint8_t msg[3] = { + sizeof(msg) - 1, + DEVICE_ID_BYTE1, + DEVICE_ID_BYTE2 + }; + + return uart_write(msg, sizeof(msg)); +} + +static int uart_send_phase(uint32_t address) +{ + int ret; + uint8_t msg_size = 5U; /* Length of data - 1 */ + uint8_t error_size = 0U; + + /* additionnal information only for RESET phase */ + if (handle.phase == PHASE_RESET) { + error_size = strnlen((char *)&handle.error, sizeof(handle.error)); + } + + ret = uart_write_8(msg_size + error_size); + if (ret != 0) { + return ret; + } + + /* Send the ID of next partition */ + ret = uart_write_8(handle.phase); + if (ret != 0) { + return ret; + } + + /* Destination address */ + ret = uart_write_32(address); + if (ret != 0) { + return ret; + } + + ret = uart_write_8(error_size); + if (ret != 0) { + return ret; + } + + /* Additional information: message error */ + if (error_size > 0U) { + ret = uart_write(handle.error, error_size); + } + + return ret; +} + +static int uart_download_part(void) +{ + uint8_t operation = 0U; + uint8_t xor; + uint8_t byte = 0U; + uint32_t packet_number = 0U; + uint32_t packet_size = 0U; + uint32_t i = 0; + int ret; + + /* Get operation number */ + ret = uart_read_8(&operation); + if (ret != 0) { + return ret; + } + + xor = operation; + + /* Get packet Number */ + for (i = 3U; i > 0U; i--) { + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + xor ^= byte; + packet_number = (packet_number << 8) | byte; + } + + if (packet_number != handle.packet) { + WARN("UART: Bad packet number receive: %i, expected %i\n", + packet_number, handle.packet); + return -EPROTO; + } + + /* Checksum */ + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + if (xor != byte) { + VERBOSE("UART: Download Command checksum xor: %x, received %x\n", + xor, byte); + return -EPROTO; + } + + ret = uart_write_8(ACK_BYTE); + if (ret != 0) { + return ret; + } + + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + xor = byte; + packet_size = byte + 1U; + if (handle.len < packet_size) { + STM32PROG_ERROR("Download overflow at %p\n", handle.addr + packet_size); + return 0; + } + + for (i = 0U; i < packet_size; i++) { + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + *(handle.addr + i) = byte; + xor ^= byte; + } + + /* Checksum */ + ret = uart_read_8(&byte) != 0; + if (ret != 0) { + return ret; + } + + if (xor != byte) { + VERBOSE("UART: Download Data checksum xor: %x, received %x\n", + xor, byte); + return -EPROTO; + } + + /* packet treated */ + handle.packet++; + handle.addr += packet_size; + handle.len -= packet_size; + + return 0; +} + +static int uart_start_cmd(unsigned int image_id, uintptr_t buffer) +{ + uint8_t byte = 0U; + uint8_t xor = 0U; + int8_t i; + uint32_t start_address = 0U; + int ret; + + /* Get address */ + for (i = 4; i > 0; i--) { + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + xor ^= byte; + start_address = (start_address << 8) | byte; + } + + /* Checksum */ + ret = uart_read_8(&byte); + if (ret != 0) { + return ret; + } + + if (xor != byte) { + VERBOSE("UART: Start Command checksum xor: %x, received %x\n", + xor, byte); + return -EPROTO; + } + + if (start_address != UNDEFINED_DOWN_ADDR) { + STM32PROG_ERROR("Invalid start at %x, for phase %d\n", + start_address, handle.phase); + return 0; + } + +#if !STM32MP_USE_STM32IMAGE + if (image_id == FIP_IMAGE_ID) { + if (!is_valid_header((fip_toc_header_t *)buffer)) { + STM32PROG_ERROR("FIP Header check failed at phase %d\n", + (uint32_t)buffer); + return -EIO; + } + + VERBOSE("FIP header looks OK.\n"); + } +#else + if (image_id == STM32_IMAGE_ID) { + /* Verify header and checksum payload */ + ret = stm32mp_check_header((boot_api_image_header_t *)buffer, + buffer + sizeof(boot_api_image_header_t)); + if (ret != 0U) { + STM32PROG_ERROR("STM32IMAGE check error at %x\n", + (uint32_t)buffer); + return -EIO; + } + + VERBOSE("STM32 header looks OK.\n"); + } +#endif + + return 0; +} + +#if STM32MP_SSP +static int uart_read_part(void) +{ + uint8_t byte = 0U; + uint8_t xor = 0U; + uint8_t partid = 0U; + uint16_t size = 0U; + uint32_t start_address = 0U; + uint32_t i; + size_t length; + uint8_t *buffer; + + /* Get partition id */ + if (uart_read_8(&partid) != 0) { + return -EIO; + } + + if ((partid != PHASE_FLASHLAYOUT) && (partid != PHASE_SSP)) { + return -EPERM; + } + + xor = partid; + + /* Get address */ + for (i = 4U; i > 0U; i--) { + if (uart_read_8(&byte) != 0) { + return -EIO; + } + + xor ^= byte; + start_address = (start_address << 8) | byte; + } + + /* Checksum */ + if (uart_read_8(&byte) != 0) { + return -EIO; + } + + if (xor != byte) { + WARN("UART: Start cmd: address checksum: %x != %x\n", + xor, byte); + return -EPROTO; + } + /* OFFSET != 0 not supported */ + if (start_address != 0U) { + return -EIO; + } + + uart_write_8(ACK_BYTE); + + /* Get number of bytes to send */ + if (uart_read_8(&byte) != 0) { + return -EIO; + } + + xor = byte; + + /* Send Size + 1 */ + size = byte++; + + /* Checksum */ + if (uart_read_8(&byte) != 0) { + return -EIO; + } + + if ((xor ^ byte) != 0xFF) { + WARN("UART: Start cmd: length checksum: %x != %x\n", xor, byte); + return -EPROTO; + } + + uart_write_8(ACK_BYTE); + + if (partid != PHASE_SSP) { + WARN("Not supported\n"); + return -EPROTO; + } + + length = handle.cert_len; + buffer = (uint8_t *)handle.cert_base; + + for (i = 0U; i < length; i++, buffer++) { + uart_write_8(*buffer); + } + for (; i < size; i++) { + uart_write_8(0x0); + } + + return 0; +} +#endif /* STM32MP_SSP */ + +static int uart_read(unsigned int image_id, uint8_t id, uintptr_t buffer, size_t length) +{ + bool start_done = false; + int ret; + uint8_t command = 0U; + + handle.phase = id; + handle.packet = 0U; + handle.addr = (uint8_t *)buffer; + handle.len = length; + + INFO("UART: read phase %i at 0x%lx size 0x%x\n", + id, buffer, length); + while (!start_done) { + + stm32_iwdg_refresh(); + + ret = uart_receive_command(&command); + if (ret != 0U) { + /* delay to wait STM32CubeProgrammer end of transmission */ + mdelay(2); + + ret = uart_flush_and_nack(); + if (ret != 0U) { + return ret; + } + + continue; + } + + uart_write_8(ACK_BYTE); + + switch (command) { + case INIT_BYTE: + INFO("UART: Connected\n"); + /* Nothing to do */ + continue; + + case GET_CMD_COMMAND: + ret = get_cmd_command(); + break; + + case GET_VER_COMMAND: + ret = get_version_command(); + break; + + case GET_ID_COMMAND: + ret = get_id_command(); + break; + + case PHASE_COMMAND: + ret = uart_send_phase((uint32_t)buffer); + if ((ret == 0U) && (handle.phase == PHASE_RESET)) { + start_done = true; + INFO("UART: Reset\n"); + } + break; + + case DOWNLOAD_COMMAND: + ret = uart_download_part(); + break; +#if STM32MP_SSP + case READ_PART_COMMAND: + ret = uart_read_part(); + break; +#endif + case START_COMMAND: + ret = uart_start_cmd(image_id, buffer); + if ((ret == 0U) && (handle.phase == id)) { + INFO("UART: Start phase %d\n", handle.phase); +#if STM32MP_SSP + if (handle.phase == PHASE_SSP) { + handle.phase = PHASE_RESET; + break; + } +#endif + start_done = true; + } + break; + + default: + /* Not supported command */ + WARN("UART: Unknown command\n"); + ret = -EINVAL; + break; + } + + if (ret == 0U) { + ret = uart_write_8(ACK_BYTE); + } else { + ret = uart_flush_and_nack(); + } + + if (ret != 0U) { + return ret; + } + } + + return 0; +} + +/* Init UART: 115200, 8bit 1stop parity even and enable FIFO mode */ +const struct stm32_uart_init_s init = { + .baud_rate = U(115200), + .word_length = STM32_UART_WORDLENGTH_9B, + .stop_bits = STM32_UART_STOPBITS_1, + .parity = STM32_UART_PARITY_EVEN, + .hw_flow_control = STM32_UART_HWCONTROL_NONE, + .mode = STM32_UART_MODE_TX_RX, + .over_sampling = STM32_UART_OVERSAMPLING_16, + .fifo_mode = STM32_UART_FIFOMODE_EN, +}; + +#if STM32MP_SSP +int stm32cubeprog_uart_ssp(uintptr_t instance, + uintptr_t cert_base, + size_t cert_len, + uintptr_t ssp_base, + size_t ssp_len) +{ + int ret; + + if (stm32_uart_init(&handle.uart, instance, &init) != 0U) { + return -EIO; + } + + /* NACK to synchronize STM32CubeProgrammer */ + ret = uart_flush_and_nack(); + if (ret != 0) { + return ret; + } + + if (cert_base == UNDEFINED_DOWN_ADDR) { + /* Send Provisioning message to programmer for reboot */ + STM32PROG_ERROR("Provisioning\n"); + } else { + handle.cert_base = cert_base; + handle.cert_len = cert_len; + handle.phase = PHASE_SSP; + } + + return uart_read(MAX_IMAGE_IDS, handle.phase, ssp_base, ssp_len); + +} +#endif + +int stm32cubeprog_uart_load(unsigned int image_id, + uintptr_t instance, + uintptr_t flashlayout_base, + size_t flashlayout_len, + uintptr_t ssbl_base, + size_t ssbl_len) +{ + int ret; + + if (stm32_uart_init(&handle.uart, instance, &init) != 0U) { + return -EIO; + } + + /* + * The following NACK_BYTE is written because STM32CubeProgrammer has + * already sent its command before TF-A has reached this point, and + * because FIFO was not configured by BootROM. + * The byte in the UART_RX register is then the checksum and not the + * command. NACK_BYTE has to be written, so that the programmer will + * re-send the good command. + */ + ret = uart_flush_and_nack(); + if (ret != 0) { + return ret; + } + + /* Get FlashLayout with PhaseId=0 */ + if (flashlayout_len > 0U) { + ret = uart_read(STM32_IMAGE_ID, PHASE_FLASHLAYOUT, + flashlayout_base, flashlayout_len); + if (ret != 0U) { + return ret; + } + } + + ret = uart_read(image_id, PHASE_SSBL, ssbl_base, ssbl_len); + + return ret; +} diff --git a/plat/st/common/stm32cubeprogrammer_usb.c b/plat/st/common/stm32cubeprogrammer_usb.c new file mode 100644 index 0000000000..eefe6413b5 --- /dev/null +++ b/plat/st/common/stm32cubeprogrammer_usb.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include + +#include + +/* Undefined download address */ +#define UNDEFINED_DOWN_ADDR 0xFFFFFFFF + +#define USB_STATE_READY 0 +#define USB_STATE_WRITTEN 1 + +#define USB_DFU_MAX_XFER_SIZE USBD_DFU_XFER_SIZE + +typedef struct { + unsigned int image_id; + uint8_t phase; + uintptr_t base; + size_t len; + uintptr_t address; + /* parameter */ + uintptr_t ssbl_base; + size_t ssbl_len; +#if STM32MP_SSP + uintptr_t cert_base; + size_t cert_len; +#endif + /* working buffer */ + uint8_t buffer[255]; +} dfu_state_t; + +static dfu_state_t dfu_state; + +#define DFU_ERROR(...) \ + { \ + ERROR(__VA_ARGS__); \ + if (dfu->phase != PHASE_RESET) { \ + snprintf((char *)&dfu->buffer[9], \ + sizeof(dfu->buffer) - 9, __VA_ARGS__); \ + dfu->phase = PHASE_RESET; \ + dfu->address = UNDEFINED_DOWN_ADDR; \ + dfu->len = 0; \ + } \ + } + +static inline bool is_valid_header(fip_toc_header_t *header) +{ + if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0U)) { + return true; + } + + return false; +} + +static int dfu_callback_upload(uint8_t alt, uintptr_t *buffer, uint32_t *len, + void *user_data) +{ + int result = 0; + uint32_t length = 0; + dfu_state_t *dfu = (dfu_state_t *)user_data; + + switch (usb_dfu_get_phase(alt)) { + case PHASE_CMD: + /* Get Pḧase */ +#if STM32MP_SSP + if (dfu->phase == PHASE_SSP) { + dfu->buffer[0] = PHASE_FLASHLAYOUT; + } else { + dfu->buffer[0] = dfu->phase; + } +#else + dfu->buffer[0] = dfu->phase; +#endif + dfu->buffer[1] = (uint8_t)(dfu->address); + dfu->buffer[2] = (uint8_t)(dfu->address >> 8); + dfu->buffer[3] = (uint8_t)(dfu->address >> 16); + dfu->buffer[4] = (uint8_t)(dfu->address >> 24); + dfu->buffer[5] = 0x00; + dfu->buffer[6] = 0x00; + dfu->buffer[7] = 0x00; + dfu->buffer[8] = 0x00; + length = 9; + if (dfu->phase == PHASE_FLASHLAYOUT && + dfu->address == UNDEFINED_DOWN_ADDR) { + INFO("Send detach request\n"); + dfu->buffer[9] = 0x01; + length = 10; + } + if (dfu->phase == PHASE_RESET) { + length = 8 + strnlen((char *)&dfu->buffer[9], + sizeof(dfu->buffer) - 9); + } + break; + +#if STM32MP_SSP + case PHASE_SSP: + /* Fix phase to flashlayout phase */ + dfu->buffer[0] = PHASE_FLASHLAYOUT; + dfu->buffer[1] = (uint8_t)(dfu_state.cert_base); + dfu->buffer[2] = (uint8_t)(dfu_state.cert_base >> 8); + dfu->buffer[3] = (uint8_t)(dfu_state.cert_base >> 16); + dfu->buffer[4] = (uint8_t)(dfu_state.cert_base >> 24); + dfu->buffer[5] = 0x00; + dfu->buffer[6] = 0x00; + dfu->buffer[7] = 0x00; + dfu->buffer[8] = 0x00; + length = 9U; + + if ((length + dfu_state.cert_len) <= sizeof(dfu->buffer)) { + memcpy(&dfu->buffer[9], (uint8_t *)dfu_state.cert_base, + dfu_state.cert_len); + length += dfu_state.cert_len; + } + + break; +#endif + default: + DFU_ERROR("phase ID :%i, alternate %i for phase %i\n", + dfu->phase, alt, usb_dfu_get_phase(alt)); + result = -EIO; + break; + } + + if (result == 0) { + *len = length; + *buffer = (uintptr_t)dfu->buffer; + } + + return result; +} + +static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len, + void *user_data) +{ + dfu_state_t *dfu = (dfu_state_t *)user_data; + + if ((dfu->phase != usb_dfu_get_phase(alt)) || + (dfu->address == UNDEFINED_DOWN_ADDR)) { + DFU_ERROR("phase ID :%i, alternate %i, address %x\n", + dfu->phase, alt, (uint32_t)dfu->address); + return -EIO; + } + + VERBOSE("Download %d %lx %x\n", alt, dfu->address, *len); + *buffer = dfu->address; + dfu->address += *len; + + if (dfu->address - dfu->base > dfu->len) { + return -EIO; + } + + return 0; +} + +static int dfu_callback_manifestation(uint8_t alt, void *user_data) +{ +#if STM32MP_USE_STM32IMAGE + int result; +#endif + boot_api_image_header_t *header __unused; + dfu_state_t *dfu = (dfu_state_t *)user_data; + + if (dfu->phase != usb_dfu_get_phase(alt)) { + ERROR("Manifestation phase ID :%i, alternate %i, address %lx\n", + dfu->phase, alt, dfu->address); + return -EIO; + } + + INFO("phase ID :%i, Manifestation %d at %lx\n", + dfu->phase, alt, dfu->address); + switch (dfu->phase) { +#if STM32MP_SSP + case PHASE_SSP: + /* Configure End with request detach */ + dfu->phase = PHASE_FLASHLAYOUT; + dfu->address = UNDEFINED_DOWN_ADDR; + dfu->len = 0; + break; +#else + case PHASE_FLASHLAYOUT: + header = (boot_api_image_header_t *)(dfu->base); + + /* TODO check data flush */ + flush_dcache_range((unsigned long)header, + header->image_length + + sizeof(boot_api_image_header_t)); + +#if STM32MP_USE_STM32IMAGE + /* Verify header and checksum payload */ + INFO("Flashlayout Header check at %lx\n", + (uintptr_t)header); + result = stm32mp_check_header(header, + (unsigned long)header + + sizeof(boot_api_image_header_t)); + if (result != 0) { + DFU_ERROR("Header check failed for phase %d\n", alt); + return -EIO; + } +#endif + /* Configure U-Boot loading */ + dfu->phase = PHASE_SSBL; + dfu->address = dfu->ssbl_base; + dfu->base = dfu->ssbl_base; + dfu->len = dfu->ssbl_len; + break; + + case PHASE_SSBL: +#if !STM32MP_USE_STM32IMAGE + if (dfu->image_id == FIP_IMAGE_ID) { + if (!is_valid_header((fip_toc_header_t *)dfu->base)) { + DFU_ERROR("FIP Header check failed for phase %d\n", alt); + return -EIO; + } + + VERBOSE("FIP header looks OK.\n"); + } +#else + if (dfu->image_id == STM32_IMAGE_ID) { + header = (boot_api_image_header_t *)dfu->base; + /* Verify header and checksum payload */ + result = stm32mp_check_header(header, + dfu->base + + sizeof(boot_api_image_header_t)); + if (result != 0) { + DFU_ERROR("STM32 Header check failed for phase %d\n", alt); + return -EIO; + } + + VERBOSE("STM32 header looks OK.\n"); + } +#endif + /* Configure End with request detach */ + dfu->phase = PHASE_FLASHLAYOUT; + dfu->address = UNDEFINED_DOWN_ADDR; + dfu->len = 0; + break; +#endif /* STM32MP_SSP */ + default: + DFU_ERROR("Unknown phase\n"); + } + + return 0; +} + +/* Open a connection to the USB device */ +static const usb_dfu_media_t usb_dfu_fops = { + .upload = dfu_callback_upload, + .download = dfu_callback_download, + .manifestation = dfu_callback_manifestation, +}; + +#if STM32MP_SSP +int stm32cubeprog_usb_ssp(usb_handle_t *usb_core_handle, + uintptr_t cert_base, + size_t cert_len, + uintptr_t ssp_base, + size_t ssp_len) +{ + int ret; + + usb_core_handle->user_data = (void *)&dfu_state; + + INFO("DFU USB START...\n"); + ret = usb_core_start(usb_core_handle); + if (ret != USBD_OK) { + return -EIO; + } + + if (cert_base == UNDEFINED_DOWN_ADDR) { + dfu_state_t *dfu = (dfu_state_t *)usb_core_handle->user_data; + + /* Send Provisioning message to programmer for reboot */ + DFU_ERROR("Provisioning\n"); + } else { + dfu_state.phase = PHASE_SSP; + dfu_state.image_id = MAX_IMAGE_IDS; + dfu_state.address = ssp_base; + dfu_state.base = ssp_base; + dfu_state.len = ssp_len; + dfu_state.cert_base = cert_base; + dfu_state.cert_len = cert_len; + } + + ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops); + if (ret != USBD_OK) { + return -EIO; + } + + INFO("DFU USB STOP...\n"); + ret = usb_core_stop(usb_core_handle); + if (ret != USBD_OK) { + return -EIO; + } + + return 0; +} +#endif + +int stm32cubeprog_usb_load(unsigned int image_id, + usb_handle_t *usb_core_handle, + uintptr_t flashlayout_base, + size_t flashlayout_len, + uintptr_t ssbl_base, + size_t ssbl_len) +{ + int ret; + + usb_core_handle->user_data = (void *)&dfu_state; + + INFO("DFU USB START...\n"); + ret = usb_core_start(usb_core_handle); + if (ret != USBD_OK) { + return -EIO; + } + + dfu_state.image_id = image_id; + dfu_state.ssbl_base = ssbl_base; + dfu_state.ssbl_len = ssbl_len; + + if (flashlayout_len) { + dfu_state.phase = PHASE_FLASHLAYOUT; + dfu_state.address = flashlayout_base; + dfu_state.base = flashlayout_base; + dfu_state.len = flashlayout_len; + } else { + dfu_state.phase = PHASE_SSBL; + dfu_state.address = ssbl_base; + dfu_state.base = ssbl_base; + dfu_state.len = ssbl_len; + } + + ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops); + if (ret != USBD_OK) { + return -EIO; + } + + INFO("DFU USB STOP...\n"); + ret = usb_core_stop(usb_core_handle); + if (ret != USBD_OK) { + return -EIO; + } + + return 0; +} diff --git a/plat/st/common/stm32mp_auth.c b/plat/st/common/stm32mp_auth.c deleted file mode 100644 index 0ef6d54548..0000000000 --- a/plat/st/common/stm32mp_auth.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2019, STMicroelectronics - All Rights Reserved - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include - -static const struct stm32mp_auth_ops *auth_ops; - -void stm32mp_init_auth(struct stm32mp_auth_ops *init_ptr) -{ - if ((init_ptr == NULL) || - (init_ptr->check_key == NULL) || - (init_ptr->verify_signature == NULL) || - (stm32_hash_register() != 0)) { - panic(); - } - - auth_ops = init_ptr; -} - -int stm32mp_auth_image(boot_api_image_header_t *header, uintptr_t buffer) -{ - int ret; - uint8_t image_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES]; - uint32_t header_skip_cksum = sizeof(header->magic) + - sizeof(header->image_signature) + - sizeof(header->payload_checksum); - - /* Check Security Status */ - if (!stm32mp_is_closed_device()) { - if (header->option_flags != 0U) { - WARN("Skip signature check (header option)\n"); - return 0; - } - INFO("Check signature on Open device\n"); - } - - ret = mmap_add_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_BASE, - STM32MP_ROM_SIZE, MT_CODE | MT_SECURE); - if (ret != 0) { - return ret; - } - - /* Check Public Key */ - if (auth_ops->check_key(header->ecc_pubk, NULL) != BOOT_API_RETURN_OK) { - ret = -EINVAL; - goto err; - } - - /* Compute end of header hash and payload hash */ - stm32_hash_init(HASH_SHA256); - - ret = stm32_hash_update((uint8_t *)&header->header_version, - sizeof(boot_api_image_header_t) - - header_skip_cksum); - if (ret != 0) { - ERROR("Hash of header failed, %i\n", ret); - goto err; - } - - ret = stm32_hash_final_update((uint8_t *)buffer, - header->image_length, image_hash); - if (ret != 0) { - ERROR("Hash of payload failed\n"); - goto err; - } - - /* Verify signature */ - if (auth_ops->verify_signature(image_hash, header->ecc_pubk, - header->image_signature, - header->ecc_algo_type) != - BOOT_API_RETURN_OK) { - ret = -EINVAL; - } - -err: - mmap_remove_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_SIZE); - return ret; -} diff --git a/plat/st/common/stm32mp_common.c b/plat/st/common/stm32mp_common.c index 89d8078386..3f385ca317 100644 --- a/plat/st/common/stm32mp_common.c +++ b/plat/st/common/stm32mp_common.c @@ -12,9 +12,16 @@ #include #include #include +#include +#include +#include #include #include +#define HEADER_VERSION_MAJOR_MASK GENMASK(23, 16) + +static struct spinlock lock; + uintptr_t plat_get_ns_image_entrypoint(void) { return BL33_BASE; @@ -25,11 +32,23 @@ unsigned int plat_get_syscnt_freq2(void) return read_cntfrq_el0(); } +#pragma weak stm32mp_plat_reset +void __dead2 stm32mp_plat_reset(int cpu) +{ + panic(); +} + static uintptr_t boot_ctx_address; +static uint16_t boot_itf_selected; +static uint32_t boot_action_saved; void stm32mp_save_boot_ctx_address(uintptr_t address) { + boot_api_context_t *boot_context = (boot_api_context_t *)address; + boot_ctx_address = address; + boot_itf_selected = boot_context->boot_interface_selected; + boot_action_saved = boot_context->boot_action; } uintptr_t stm32mp_get_boot_ctx_address(void) @@ -37,6 +56,16 @@ uintptr_t stm32mp_get_boot_ctx_address(void) return boot_ctx_address; } +uint16_t stm32mp_get_boot_itf_selected(void) +{ + return boot_itf_selected; +} + +uint32_t stm32mp_get_boot_action(void) +{ + return boot_action_saved; +} + uintptr_t stm32mp_ddrctrl_base(void) { return DDRCTRL_BASE; @@ -65,45 +94,148 @@ bool stm32mp_lock_available(void) return (read_sctlr() & c_m_bits) == c_m_bits; } -int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer) +void stm32mp_pwr_regs_lock(void) +{ + if (stm32mp_lock_available()) { + spin_lock(&lock); + } +} + +void stm32mp_pwr_regs_unlock(void) { - uint32_t i; - uint32_t img_checksum = 0U; + if (stm32mp_lock_available()) { + spin_unlock(&lock); + } +} +#if STM32MP_USE_STM32IMAGE +int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer) +{ /* * Check header/payload validity: * - Header magic * - Header version - * - Payload checksum + * - Payload checksum if no signature verification */ if (header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) { ERROR("Header magic\n"); return -EINVAL; } - if (header->header_version != BOOT_API_HEADER_VERSION) { + if ((header->header_version & HEADER_VERSION_MAJOR_MASK) != + (BOOT_API_HEADER_VERSION & HEADER_VERSION_MAJOR_MASK)) { ERROR("Header version\n"); return -EINVAL; } - for (i = 0U; i < header->image_length; i++) { - img_checksum += *(uint8_t *)(buffer + i); - } + if (header->option_flags == 1U) { + uint32_t i; + uint32_t img_checksum = 0U; - if (header->payload_checksum != img_checksum) { - ERROR("Checksum: 0x%x (awaited: 0x%x)\n", img_checksum, - header->payload_checksum); - return -EINVAL; + for (i = 0U; i < header->image_length; i++) { + img_checksum += *(uint8_t *)(buffer + i); + } + + if (header->payload_checksum != img_checksum) { + ERROR("Checksum: 0x%x (awaited: 0x%x)\n", img_checksum, + header->payload_checksum); + return -EINVAL; + } } return 0; } +#endif + +/* Return CPU supply name */ +const char *stm32mp_get_cpu_supply_name(void) +{ + const char *regulator; + const char *supply = NULL; + + regulator = dt_get_cpu_regulator_name(); + if (regulator == NULL) { + return NULL; + } + + if (dt_pmic_status() > 0) { + if (dt_pmic_find_supply(&supply, regulator) != 0) { + return NULL; + } + } + + return supply; +} + +/* Return VDD supply name */ +const char *stm32mp_get_vdd_supply_name(void) +{ + const char *supply = NULL; + + if (dt_pmic_status() > 0) { + const char *regulator = dt_get_vdd_regulator_name(); + + if (regulator != NULL) { + dt_pmic_find_supply(&supply, regulator); + } + } + + return supply; +} + +/* Return USB phy supply name */ +const char *stm32mp_get_usb_phy_supply_name(void) +{ + const char *supply = NULL; + + if (dt_pmic_status() > 0) { + const char *regulator = dt_get_usb_phy_regulator_name(); + + if (regulator != NULL) { + dt_pmic_find_supply(&supply, regulator); + } + } + + return supply; +} + +#if TRUSTED_BOARD_BOOT && STM32MP_USE_STM32IMAGE +/* Save pointer to last loaded header */ +static boot_api_image_header_t *latest_stm32_header; + +/* Save last loaded header */ +void stm32mp_save_loaded_header(void *header) +{ + assert(latest_stm32_header == NULL); + + latest_stm32_header = header; +} + +/* Discard last loaded header */ +void stm32mp_delete_loaded_header(void) +{ + if (latest_stm32_header == NULL) { + return; + } + + zeromem(latest_stm32_header, sizeof(boot_api_image_header_t)); + latest_stm32_header = NULL; +} + +/* Get last loaded header */ +boot_api_image_header_t *stm32mp_get_loaded_header(void) +{ + assert(latest_stm32_header != NULL); + + return latest_stm32_header; +} +#endif /* TRUSTED_BOARD_BOOT */ int stm32mp_map_ddr_non_cacheable(void) { return mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, STM32MP_DDR_MAX_SIZE, - MT_NON_CACHEABLE | MT_RW | MT_NS); + MT_NON_CACHEABLE | MT_RW | MT_SECURE); } int stm32mp_unmap_ddr(void) diff --git a/plat/st/common/stm32mp_cot.c b/plat/st/common/stm32mp_cot.c new file mode 100644 index 0000000000..5f673fde78 --- /dev/null +++ b/plat/st/common/stm32mp_cot.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include + +static auth_param_type_desc_t stm32_header_pk = + AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, 0); +static auth_param_type_desc_t stm32_header_sig = + AUTH_PARAM_TYPE_DESC(AUTH_PARAM_SIG, 0); +static auth_param_type_desc_t stm32_header_sig_alg = + AUTH_PARAM_TYPE_DESC(AUTH_PARAM_SIG_ALG, 0); +static auth_param_type_desc_t stm32_load = + AUTH_PARAM_TYPE_DESC(AUTH_PARAM_RAW_DATA, 0); + +#if defined(AARCH32_SP_OPTEE) +static const auth_img_desc_t bl32_image = { + .img_id = BL32_IMAGE_ID, + .img_type = IMG_PLAT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &stm32_header_pk, + .sig = &stm32_header_sig, + .alg = &stm32_header_sig_alg, + .data = &stm32_load + } + }, + }, +}; + +static const auth_img_desc_t bl32_extra1_image = { + .img_id = BL32_EXTRA1_IMAGE_ID, + .img_type = IMG_PLAT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &stm32_header_pk, + .sig = &stm32_header_sig, + .alg = &stm32_header_sig_alg, + .data = &stm32_load + } + }, + }, +}; + +static const auth_img_desc_t bl32_extra2_image = { + .img_id = BL32_EXTRA2_IMAGE_ID, + .img_type = IMG_PLAT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &stm32_header_pk, + .sig = &stm32_header_sig, + .alg = &stm32_header_sig_alg, + .data = &stm32_load + } + }, + }, +}; +#else +static const auth_img_desc_t bl32_image = { + .img_id = BL32_IMAGE_ID, + .img_type = IMG_RAW, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_NONE, /* Already verified by BL1 + * as loaded in the same time + * as BL2 + */ + } + }, +}; +#endif + +static const auth_img_desc_t bl33_image = { + .img_id = BL33_IMAGE_ID, + .img_type = IMG_PLAT, + .parent = NULL, + .img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) { + [0] = { + .type = AUTH_METHOD_SIG, + .param.sig = { + .pk = &stm32_header_pk, + .sig = &stm32_header_sig, + .alg = &stm32_header_sig_alg, + .data = &stm32_load + } + }, + }, +}; + +static const auth_img_desc_t * const cot_desc[] = { + [BL32_IMAGE_ID] = &bl32_image, +#if defined(AARCH32_SP_OPTEE) + [BL32_EXTRA1_IMAGE_ID] = &bl32_extra1_image, + [BL32_EXTRA2_IMAGE_ID] = &bl32_extra2_image, +#endif + [BL33_IMAGE_ID] = &bl33_image, +}; + +REGISTER_COT(cot_desc); diff --git a/plat/st/common/stm32mp_crypto_lib.c b/plat/st/common/stm32mp_crypto_lib.c new file mode 100644 index 0000000000..de9601d2b8 --- /dev/null +++ b/plat/st/common/stm32mp_crypto_lib.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2020-2021, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#if !STM32MP_USE_STM32IMAGE +#include +#include +#include +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CRYPTO_HASH_MAX_SIZE 32U +#define CRYPTO_SIGN_MAX_SIZE 64U +#define CRYPTO_PUBKEY_MAX_SIZE 64U + +struct stm32mp_auth_ops { + uint32_t (*verify_signature)(uint8_t *hash_in, uint8_t *pubkey_in, + uint8_t *signature, uint32_t ecc_algo); +}; + +static struct stm32mp_auth_ops auth_ops; + +static void crypto_lib_init(void) +{ + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + + if (!stm32mp_is_auth_supported()) { + return; + } + + auth_ops.verify_signature = + boot_context->bootrom_ecdsa_verify_signature; + + if (stm32_hash_register() != 0) { + panic(); + } +} + +#if STM32MP_USE_STM32IMAGE +static int crypto_verify_signature(void *data_ptr, unsigned int data_len, + void *sig_ptr, unsigned int sig_len, + void *sig_alg, unsigned int sig_alg_len, + void *pk_ptr, unsigned int pk_len) +{ + uint8_t image_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES]; + uint32_t option_flags; + uint32_t ecc_algo_type; + uint32_t header_len; + int result; + boot_api_image_header_t *header = stm32mp_get_loaded_header(); + + header_len = sizeof(boot_api_image_header_t) - sizeof(header->magic) - + sizeof(header->image_signature) - + sizeof(header->payload_checksum); + + if ((((size_t)sig_alg % __alignof__(uint32_t)) != 0) || + (sig_alg_len != sizeof(option_flags) + sizeof(ecc_algo_type))) { + return -EINVAL; + } + + option_flags = ((uint32_t *)sig_alg)[0]; + ecc_algo_type = ((uint32_t *)sig_alg)[1]; + + /* Check security status */ + if (!stm32mp_is_closed_device()) { + if (option_flags != 0U) { + WARN("Skip signature check (header option)\n"); + stm32mp_delete_loaded_header(); + return 0; + } + INFO("Check signature on Open device\n"); + } + + /* Check key/sign size */ + if ((pk_len != BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES) || + (sig_len != BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES)) { + return -EINVAL; + } + + result = mmap_add_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_BASE, + STM32MP_ROM_SIZE_2MB_ALIGNED, MT_CODE | MT_SECURE); + if (result != 0) { + return result; + } + + /* Compute hash for the data covered by the signature */ + stm32_hash_init(HASH_SHA256); + + result = stm32_hash_update((void *)&header->header_version, header_len); + if (result != 0) { + VERBOSE("Hash of header failed, %i\n", result); + goto out; + } + + result = stm32_hash_final_update((uint8_t *)data_ptr, + data_len, image_hash); + if (result != 0) { + VERBOSE("Hash of payload failed, %i\n", result); + goto out; + } + + /* Verify signature */ + if (auth_ops.verify_signature(image_hash, pk_ptr, sig_ptr, + ecc_algo_type) != BOOT_API_RETURN_OK) { + result = -EAUTH; + } + +out: + mmap_remove_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_SIZE_2MB_ALIGNED); + if (result != 0) { + stm32mp_delete_loaded_header(); + } + + return result; +} + +static int crypto_verify_hash(void *data_ptr, unsigned int data_len, + void *digest_info_ptr, + unsigned int digest_info_len) +{ + int ret; + uint8_t calc_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES]; + + if (digest_info_len != BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES) { + VERBOSE("%s: unexpected digest_len\n", __func__); + ret = -EINVAL; + goto out; + } + + stm32_hash_init(HASH_SHA256); + ret = stm32_hash_final_update(data_ptr, data_len, calc_hash); + if (ret != 0) { + VERBOSE("%s: hash failed\n", __func__); + goto out; + } + + ret = memcmp(calc_hash, digest_info_ptr, digest_info_len); + if (ret != 0) { + VERBOSE("%s: not expected digest\n", __func__); + ret = -EAUTH; + } + +out: + /* Clean header as no more used */ + stm32mp_delete_loaded_header(); + + return ret; +} +#else /* STM32MP_USE_STM32IMAGE */ +int get_plain_pk_from_asn1(void *pk_ptr, unsigned int pk_len, void **plain_pk, + unsigned int *len, int *pk_alg) +{ + int ret; + mbedtls_pk_context mbedtls_pk = {0}; + unsigned char *p, *end; + mbedtls_asn1_buf alg_params = {0}; + mbedtls_asn1_buf alg_oid = {0}; + + *plain_pk = NULL; + *len = 0U; + + /* Parse the public key */ + mbedtls_pk_init(&mbedtls_pk); + p = (unsigned char *)pk_ptr; + end = (unsigned char *)(p + pk_len); + + ret = mbedtls_asn1_get_tag(&p, end, len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + return -EINVAL; + } + + end = p + *len; + ret = mbedtls_asn1_get_alg(&p, end, &alg_oid, &alg_params); + if (ret != 0) { + VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret); + return -EINVAL; + } + + if (pk_alg != NULL) { + if ((strlen(MBEDTLS_OID_EC_GRP_SECP256R1) == alg_params.len) && + (memcmp(MBEDTLS_OID_EC_GRP_SECP256R1, alg_params.p, alg_params.len) == 0)) { + *pk_alg = BOOT_API_ECDSA_ALGO_TYPE_P256NIST; + } else { + *pk_alg = BOOT_API_ECDSA_ALGO_TYPE_BRAINPOOL256; + } + } + + ret = mbedtls_asn1_get_bitstring_null(&p, end, len); + if (ret != 0) { + VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret); + return -EINVAL; + } + + /* we remove the ident (0x04) first byte. */ + if ((*len < 1U) || (p[0] != MBEDTLS_ASN1_OCTET_STRING)) { + VERBOSE("%s: not expected len or tag\n", __func__); + return -EINVAL; + } + + *len = *len - 1U; + *plain_pk = p + 1U; + + return 0; +} + +int plat_get_hashed_pk(void *full_pk_ptr, unsigned int full_pk_len, + void **hashed_pk_ptr, unsigned int *hashed_pk_len) +{ + return get_plain_pk_from_asn1(full_pk_ptr, full_pk_len, hashed_pk_ptr, hashed_pk_len, NULL); +} + +static int get_plain_digest_from_asn1(void *digest_ptr, unsigned int digest_len, + uint8_t **out, size_t *out_len, mbedtls_md_type_t *md_alg) +{ + int ret; + mbedtls_asn1_buf hash_oid, params; + size_t len; + unsigned char *p, *end; + + *out = NULL; + *out_len = 0U; + + /* Digest info should be an MBEDTLS_ASN1_SEQUENCE */ + p = (unsigned char *)digest_ptr; + end = p + digest_len; + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + return ret; + } + + /* Get the hash algorithm */ + ret = mbedtls_asn1_get_alg(&p, end, &hash_oid, ¶ms); + if (ret != 0) { + return ret; + } + + ret = mbedtls_oid_get_md_alg(&hash_oid, md_alg); + if (ret != 0) { + return ret; + } + + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + if (ret != 0) { + return ret; + } + + /* Length of hash must match the algorithm's size */ + if (len != BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES) { + return -1; + } + + *out = p; + *out_len = len; + + return 0; +} + +static int crypto_verify_signature(void *data_ptr, unsigned int data_len, + void *sig_ptr, unsigned int sig_len, + void *sig_alg, unsigned int sig_alg_len, + void *pk_ptr, unsigned int pk_len) +{ + uint8_t image_hash[CRYPTO_HASH_MAX_SIZE]; + uint8_t sig[CRYPTO_SIGN_MAX_SIZE]; + uint8_t my_pk[CRYPTO_PUBKEY_MAX_SIZE]; + int ret; + size_t len; + mbedtls_asn1_sequence seq; + unsigned char *p, *end; + int curve_id; + mbedtls_asn1_buf sig_oid, sig_params; + mbedtls_md_type_t md_alg; + mbedtls_pk_type_t pk_alg; + + /* Get pointers to signature OID and parameters */ + p = (unsigned char *)sig_alg; + end = (unsigned char *)(p + sig_alg_len); + ret = mbedtls_asn1_get_alg(&p, end, &sig_oid, &sig_params); + if (ret != 0) { + VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + /* Get the actual signature algorithm (MD + PK) */ + ret = mbedtls_oid_get_sig_alg(&sig_oid, &md_alg, &pk_alg); + if (ret != 0) { + VERBOSE("%s: mbedtls_oid_get_sig_alg (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + if ((md_alg != MBEDTLS_MD_SHA256) || (pk_alg != MBEDTLS_PK_ECDSA)) { + VERBOSE("%s: md_alg=%d pk_alg=%d\n", __func__, md_alg, pk_alg); + return CRYPTO_ERR_SIGNATURE; + } + + ret = get_plain_pk_from_asn1(pk_ptr, pk_len, &pk_ptr, &pk_len, &curve_id); + if (ret != 0) { + VERBOSE("%s: get_plain_pk_from_asn1 (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + /* We expect a known pk_len */ + if (pk_len != sizeof(my_pk)) { + VERBOSE("%s: pk_len=%d sizeof(my_pk)=%d)\n", __func__, pk_len, sizeof(my_pk)); + return CRYPTO_ERR_SIGNATURE; + } + + /* Need to copy as auth_ops.verify_signature + * expects aligned public key. + */ + memcpy(my_pk, pk_ptr, sizeof(my_pk)); + + /* Get the signature (bitstring) */ + p = (unsigned char *)sig_ptr; + end = (unsigned char *)(p + sig_len); + ret = mbedtls_asn1_get_bitstring_null(&p, end, &len); + if (ret != 0) { + VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + /* Get r and s from sequence */ + ret = mbedtls_asn1_get_sequence_of(&p, end, &seq, MBEDTLS_ASN1_INTEGER); + if (ret != 0) { + VERBOSE("%s: mbedtls_asn1_get_sequence_of (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + /* We expect only 2 integers (r and s) from the sequence */ + if (seq.next->next != NULL) { + mbedtls_asn1_sequence *cur = seq.next; + mbedtls_asn1_sequence *next; + + VERBOSE("%s: nb seq != 2\n", __func__); + /* Free all the sequences */ + while (cur != NULL) { + next = cur->next; + mbedtls_free(cur); + cur = next; + } + + return CRYPTO_ERR_SIGNATURE; + } + + /* Integer sequence may (sometime) start with 0x00 as MSB, but we can only + * manage exactly 2*32 bytes, we remove this higher byte + * if there are not 00, we will fail either. + */ + memcpy(sig, seq.buf.p + seq.buf.len - sizeof(sig) / 2U, sizeof(sig) / 2U); + memcpy(sig + sizeof(sig) / 2U, + seq.next->buf.p + seq.next->buf.len - sizeof(sig) / 2U, + sizeof(sig) / 2U); + /* Need to free allocated 'next' in mbedtls_asn1_get_sequence_of */ + mbedtls_free(seq.next); + + /* Compute hash for the data covered by the signature */ + stm32_hash_init(HASH_SHA256); + + ret = stm32_hash_final_update((uint8_t *)data_ptr, data_len, image_hash); + if (ret != 0) { + VERBOSE("%s: stm32_hash_final_update (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + ret = mmap_add_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_BASE, + STM32MP_ROM_SIZE_2MB_ALIGNED, MT_CODE | MT_SECURE); + if (ret != 0) { + VERBOSE("%s: mmap_add_dynamic_region (%d)\n", __func__, ret); + return CRYPTO_ERR_SIGNATURE; + } + + ret = auth_ops.verify_signature(image_hash, my_pk, sig, curve_id); + if (ret != BOOT_API_RETURN_OK) { + VERBOSE("%s: auth_ops.verify_signature (%d)\n", __func__, ret); + ret = CRYPTO_ERR_SIGNATURE; + } else { + ret = 0; + } + + mmap_remove_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_SIZE_2MB_ALIGNED); + + return ret; +} + +static int crypto_verify_hash(void *data_ptr, unsigned int data_len, + void *digest_info_ptr, + unsigned int digest_info_len) +{ + int ret; + uint8_t calc_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES]; + unsigned char *p; + mbedtls_md_type_t md_alg; + size_t len; + + /* we receive an asn1 encapsulated digest, we flatten it */ + ret = get_plain_digest_from_asn1(digest_info_ptr, + digest_info_len, &p, &len, + &md_alg); + if ((ret != 0) || (md_alg != MBEDTLS_MD_SHA256)) { + return CRYPTO_ERR_HASH; + } + + digest_info_ptr = p; + digest_info_len = len; + + stm32_hash_init(HASH_SHA256); + + ret = stm32_hash_final_update(data_ptr, data_len, calc_hash); + if (ret != 0) { + VERBOSE("%s: hash failed\n", __func__); + return CRYPTO_ERR_HASH; + } + + ret = memcmp(calc_hash, digest_info_ptr, digest_info_len); + if (ret != 0) { + VERBOSE("%s: not expected digest\n", __func__); + ret = CRYPTO_ERR_HASH; + } + + return ret; +} +#endif + +REGISTER_CRYPTO_LIB("stm32_crypto_lib", + crypto_lib_init, + crypto_verify_signature, + crypto_verify_hash, + NULL); diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c index 391e5f0547..a3584cbc12 100644 --- a/plat/st/common/stm32mp_dt.c +++ b/plat/st/common/stm32mp_dt.c @@ -14,25 +14,22 @@ #include #include #include -#include -#include #include -static int fdt_checked; - -static void *fdt = (void *)(uintptr_t)STM32MP_DTB_BASE; +static void *fdt; /******************************************************************************* * This function checks device tree file with its header. * Returns 0 on success and a negative FDT error code on failure. ******************************************************************************/ -int dt_open_and_check(void) +int dt_open_and_check(uintptr_t dt_addr) { - int ret = fdt_check_header(fdt); + int ret; + ret = fdt_check_header((void *)dt_addr); if (ret == 0) { - fdt_checked = 1; + fdt = (void *)dt_addr; } return ret; @@ -45,11 +42,13 @@ int dt_open_and_check(void) ******************************************************************************/ int fdt_get_address(void **fdt_addr) { - if (fdt_checked == 1) { - *fdt_addr = fdt; + if (fdt == NULL) { + return 0; } - return fdt_checked; + *fdt_addr = fdt; + + return 1; } /******************************************************************************* @@ -115,6 +114,37 @@ static int fdt_get_node_parent_address_cells(int node) } #endif +/******************************************************************************* + * This function return interrupts from node. + ******************************************************************************/ +int fdt_get_interrupt(int node, const fdt32_t **array, int *len, bool *extended) +{ + uint8_t status = fdt_get_status(node); + + *extended = false; + + switch (status) { + case DT_SECURE: + *array = fdt_getprop(fdt, node, "interrupts-extended", len); + if (*array == NULL) { + *array = fdt_getprop(fdt, node, "interrupts", len); + } else { + *extended = true; + } + break; + + default: + *array = fdt_getprop(fdt, node, "secure-interrupts", len); + break; + } + + if (*array == NULL) { + return -FDT_ERR_NOTFOUND; + } + + return 0; +} + /******************************************************************************* * This function gets the stdout pin configuration information from the DT. * And then calls the sub-function to treat it and set GPIO registers. @@ -203,21 +233,223 @@ int dt_get_stdout_uart_info(struct dt_node_info *info) return node; } +/******************************************************************************* + * This function returns the node offset matching compatible string in the DT, + * and also matching the reg property with the given address. + * Returns value on success, and error value on failure. + ******************************************************************************/ +int dt_match_instance_by_compatible(const char *compatible, uintptr_t address) +{ + int node; + + for (node = fdt_node_offset_by_compatible(fdt, -1, compatible); + node != -FDT_ERR_NOTFOUND; + node = fdt_node_offset_by_compatible(fdt, node, compatible)) { + const fdt32_t *cuint; + + assert(fdt_get_node_parent_address_cells(node) == 1); + + cuint = fdt_getprop(fdt, node, "reg", NULL); + if (cuint == NULL) { + continue; + } + + if ((uintptr_t)fdt32_to_cpu(*cuint) == address) { + return node; + } + } + + return -FDT_ERR_NOTFOUND; +} + /******************************************************************************* * This function gets DDR size information from the DT. * Returns value in bytes on success, and 0 on failure. ******************************************************************************/ uint32_t dt_get_ddr_size(void) { + static uint32_t size; int node; + if (size != 0U) { + return size; + } + node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); if (node < 0) { - INFO("%s: Cannot read DDR node in DT\n", __func__); return 0; } - return fdt_read_uint32_default(fdt, node, "st,mem-size", 0); + size = fdt_read_uint32_default(fdt, node, "st,mem-size", 0U); + + flush_dcache_range((uintptr_t)&size, sizeof(uint32_t)); + + return size; +} + +/******************************************************************************* + * This function gets OPP table node from the DT. + * Returns node offset on success and a negative FDT error code on failure. + ******************************************************************************/ +static int dt_get_opp_table_node(void) +{ + return fdt_node_offset_by_compatible(fdt, -1, DT_OPP_COMPAT); +} + +/******************************************************************************* + * This function gets OPP parameters (frequency in KHz and voltage in mV) from + * an OPP table subnode. Platform HW support capabilities are also checked. + * Returns 0 on success and a negative FDT error code on failure. + ******************************************************************************/ +static int dt_get_opp_freqvolt_from_subnode(int subnode, uint32_t *freq_khz, + uint32_t *voltage_mv) +{ + const fdt64_t *cuint64; + const fdt32_t *cuint32; + uint64_t read_freq_64; + uint32_t read_voltage_32; + + assert(freq_khz != NULL); + assert(voltage_mv != NULL); + + cuint32 = fdt_getprop(fdt, subnode, "opp-supported-hw", NULL); + if (cuint32 != NULL) { + if (!stm32mp_supports_cpu_opp(fdt32_to_cpu(*cuint32))) { + VERBOSE("Invalid opp-supported-hw 0x%x\n", + fdt32_to_cpu(*cuint32)); + return -FDT_ERR_BADVALUE; + } + } + + cuint64 = fdt_getprop(fdt, subnode, "opp-hz", NULL); + if (cuint64 == NULL) { + VERBOSE("Missing opp-hz\n"); + return -FDT_ERR_NOTFOUND; + } + + /* Frequency value expressed in KHz must fit on 32 bits */ + read_freq_64 = fdt64_to_cpu(*cuint64) / 1000ULL; + if (read_freq_64 > (uint64_t)UINT32_MAX) { + VERBOSE("Invalid opp-hz %llu\n", read_freq_64); + return -FDT_ERR_BADVALUE; + } + + cuint32 = fdt_getprop(fdt, subnode, "opp-microvolt", NULL); + if (cuint32 == NULL) { + VERBOSE("Missing opp-microvolt\n"); + return -FDT_ERR_NOTFOUND; + } + + /* Millivolt value must fit on 16 bits */ + read_voltage_32 = fdt32_to_cpu(*cuint32) / 1000U; + if (read_voltage_32 > (uint32_t)UINT16_MAX) { + VERBOSE("Invalid opp-microvolt %u\n", read_voltage_32); + return -FDT_ERR_BADVALUE; + } + + *freq_khz = (uint32_t)read_freq_64; + + *voltage_mv = read_voltage_32; + + return 0; +} + +/******************************************************************************* + * This function parses OPP table in DT and finds the parameters for the + * highest frequency supported by the HW platform. + * If found, the new frequency and voltage values override the original ones. + * Returns 0 on success and a negative FDT error code on failure. + ******************************************************************************/ +int dt_get_max_opp_freqvolt(uint32_t *freq_khz, uint32_t *voltage_mv) +{ + int node; + int subnode; + uint32_t freq = 0U; + uint32_t voltage = 0U; + + assert(freq_khz != NULL); + assert(voltage_mv != NULL); + + node = dt_get_opp_table_node(); + if (node < 0) { + return node; + } + + fdt_for_each_subnode(subnode, fdt, node) { + uint32_t read_freq; + uint32_t read_voltage; + + if (dt_get_opp_freqvolt_from_subnode(subnode, &read_freq, + &read_voltage) != 0) { + continue; + } + + if (read_freq > freq) { + freq = read_freq; + voltage = read_voltage; + } + } + + if ((freq == 0U) || (voltage == 0U)) { + return -FDT_ERR_NOTFOUND; + } + + *freq_khz = freq; + *voltage_mv = voltage; + + return 0; +} + +/******************************************************************************* + * This function parses OPP table in DT and finds all parameters supported by + * the HW platform. + * If found, the corresponding frequency and voltage values are respectively + * stored in @*freq_khz_array and @*voltage_mv_array. + * Note that @*count has to be set by caller to the effective size allocated + * for both tables. Its value is then replaced by the number of filled elements. + * Returns 0 on success and a negative FDT error code on failure. + ******************************************************************************/ +int dt_get_all_opp_freqvolt(uint32_t *count, uint32_t *freq_khz_array, + uint32_t *voltage_mv_array) +{ + int node; + int subnode; + uint32_t idx = 0U; + + assert(count != NULL); + assert(freq_khz_array != NULL); + assert(voltage_mv_array != NULL); + + node = dt_get_opp_table_node(); + if (node < 0) { + return node; + } + + fdt_for_each_subnode(subnode, fdt, node) { + uint32_t read_freq; + uint32_t read_voltage; + + if (dt_get_opp_freqvolt_from_subnode(subnode, &read_freq, + &read_voltage) != 0) { + continue; + } + + if (idx >= *count) { + return -FDT_ERR_NOSPACE; + } + + freq_khz_array[idx] = read_freq; + voltage_mv_array[idx] = read_voltage; + idx++; + } + + if (idx == 0U) { + return -FDT_ERR_NOTFOUND; + } + + *count = idx; + + return 0; } /******************************************************************************* @@ -226,22 +458,15 @@ uint32_t dt_get_ddr_size(void) ******************************************************************************/ uint32_t dt_get_pwr_vdd_voltage(void) { - int node, pwr_regulators_node; + int node; const fdt32_t *cuint; node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); if (node < 0) { - INFO("%s: Cannot read PWR node in DT\n", __func__); return 0; } - pwr_regulators_node = fdt_subnode_offset(fdt, node, "pwr-regulators"); - if (pwr_regulators_node < 0) { - INFO("%s: Cannot read pwr-regulators node in DT\n", __func__); - return 0; - } - - cuint = fdt_getprop(fdt, pwr_regulators_node, "vdd-supply", NULL); + cuint = fdt_getprop(fdt, node, "vdd-supply", NULL); if (cuint == NULL) { return 0; } @@ -259,6 +484,84 @@ uint32_t dt_get_pwr_vdd_voltage(void) return fdt32_to_cpu(*cuint); } +/******************************************************************************* + * This function return the real regulator name from DT. + ******************************************************************************/ +static const char *dt_get_regulator_name(int node, const char *regu_name) +{ + const fdt32_t *cuint; + + if ((node < 0) || (regu_name == NULL)) { + return NULL; + } + + cuint = fdt_getprop(fdt, node, regu_name, NULL); + if (cuint == NULL) { + return NULL; + } + + node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (node < 0) { + return NULL; + } + + return (const char *)fdt_getprop(fdt, node, "regulator-name", NULL); +} + +/******************************************************************************* + * This function retrieves VDD regulator name from DT. + * Returns string taken from supply node, NULL otherwise. + ******************************************************************************/ +const char *dt_get_vdd_regulator_name(void) +{ + int node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); + + if (node < 0) { + return NULL; + } + + return dt_get_regulator_name(node, "vdd-supply"); +} + +/******************************************************************************* + * This function retrieves CPU regulator name from DT. + * Returns string taken from supply node, NULL otherwise. + ******************************************************************************/ +const char *dt_get_cpu_regulator_name(void) +{ + int node = fdt_path_offset(fdt, "/cpus/cpu@0"); + + if (node < 0) { + return NULL; + } + + return dt_get_regulator_name(node, "cpu-supply"); +} + +/******************************************************************************* + * This function retrieves USB phy regulator name from DT. + * Returns string taken from supply node, NULL otherwise. + ******************************************************************************/ +const char *dt_get_usb_phy_regulator_name(void) +{ + int node = fdt_node_offset_by_compatible(fdt, -1, DT_USBPHYC_COMPAT); + int subnode; + const char *reg_name = NULL; + + if (node < 0) { + return NULL; + } + + fdt_for_each_subnode(subnode, fdt, node) { + reg_name = dt_get_regulator_name(subnode, "phy-supply"); + if (reg_name != NULL) { + return reg_name; + } + } + + return NULL; +} + /******************************************************************************* * This function retrieves board model from DT * Returns string taken from model node, NULL otherwise diff --git a/plat/st/common/stm32mp_fconf_io.c b/plat/st/common/stm32mp_fconf_io.c new file mode 100644 index 0000000000..d07067531f --- /dev/null +++ b/plat/st/common/stm32mp_fconf_io.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if STM32MP_SDMMC || STM32MP_EMMC +static io_block_spec_t gpt_block_spec = { + .offset = 0, + .length = 34 * MMC_BLOCK_SIZE, /* Size of GPT table */ +}; +#endif + +/* By default, STM32 platforms load images from the FIP */ +struct plat_io_policy policies[MAX_NUMBER_IDS] = { + [FIP_IMAGE_ID] = { + &storage_dev_handle, + (uintptr_t)&image_block_spec, + open_storage + }, +#if STM32MP_SDMMC || STM32MP_EMMC + [GPT_IMAGE_ID] = { + &storage_dev_handle, + (uintptr_t)&gpt_block_spec, + open_storage + }, +#endif +}; + +#if TRUSTED_BOARD_BOOT +#define FCONF_ST_IO_UUID_NUMBER U(14) +#else +#define FCONF_ST_IO_UUID_NUMBER U(8) +#endif + +static io_uuid_spec_t fconf_stm32mp_uuids[FCONF_ST_IO_UUID_NUMBER]; +static OBJECT_POOL_ARRAY(fconf_stm32mp_uuids_pool, fconf_stm32mp_uuids); + +struct policies_load_info { + unsigned int image_id; + const char *name; +}; + +/* image id to property name table */ +static const struct policies_load_info load_info[FCONF_ST_IO_UUID_NUMBER] = { + {FW_CONFIG_ID, "fw_cfg_uuid"}, + {BL32_IMAGE_ID, "bl32_uuid"}, + {BL32_EXTRA1_IMAGE_ID, "bl32_extra1_uuid"}, + {BL32_EXTRA2_IMAGE_ID, "bl32_extra2_uuid"}, + {BL33_IMAGE_ID, "bl33_uuid"}, + {HW_CONFIG_ID, "hw_cfg_uuid"}, + {TOS_FW_CONFIG_ID, "tos_fw_cfg_uuid"}, + {NT_FW_CONFIG_ID, "nt_fw_cfg_uuid"}, +#if TRUSTED_BOARD_BOOT + {TRUSTED_BOOT_FW_CERT_ID, "t_boot_fw_cert_uuid"}, + {TRUSTED_KEY_CERT_ID, "t_key_cert_uuid"}, + {TRUSTED_OS_FW_KEY_CERT_ID, "tos_fw_key_cert_uuid"}, + {NON_TRUSTED_FW_KEY_CERT_ID, "nt_fw_key_cert_uuid"}, + {TRUSTED_OS_FW_CONTENT_CERT_ID, "tos_fw_content_cert_uuid"}, + {NON_TRUSTED_FW_CONTENT_CERT_ID, "nt_fw_content_cert_uuid"}, +#endif /* TRUSTED_BOARD_BOOT */ +}; + +int fconf_populate_stm32mp_io_policies(uintptr_t config) +{ + int node; + unsigned int i; + + /* As libfdt uses void *, we can't avoid this cast */ + const void *dtb = (void *)config; + + /* Assert the node offset point to "st,io-fip-handle" compatible property */ + const char *compatible_str = "st,io-fip-handle"; + + node = fdt_node_offset_by_compatible(dtb, -1, compatible_str); + if (node < 0) { + ERROR("FCONF: Can't find %s compatible in dtb\n", compatible_str); + return node; + } + + /* Locate the uuid cells and read the value for all the load info uuid */ + for (i = 0U; i < FCONF_ST_IO_UUID_NUMBER; i++) { + union uuid_helper_t uuid_helper; + io_uuid_spec_t *uuid_ptr; + int err; + unsigned int j; + + uuid_ptr = pool_alloc(&fconf_stm32mp_uuids_pool); + err = fdt_read_uint32_array(dtb, node, load_info[i].name, + 4, uuid_helper.word); + if (err < 0) { + WARN("FCONF: Read cell failed for %s\n", load_info[i].name); + return err; + } + + /* Convert uuid from big endian to little endian */ + for (j = 0U; j < 4U; j++) { + uuid_helper.word[j] = + ((uuid_helper.word[j] >> 24U) & 0xff) | + ((uuid_helper.word[j] << 8U) & 0xff0000) | + ((uuid_helper.word[j] >> 8U) & 0xff00) | + ((uuid_helper.word[j] << 24U) & 0xff000000); + } + + VERBOSE("FCONF: stm32mp-io_policies.%s found with value = 0x%x 0x%x 0x%x 0x%x\n", + load_info[i].name, + uuid_helper.word[0], uuid_helper.word[1], + uuid_helper.word[2], uuid_helper.word[3]); + + uuid_ptr->uuid = uuid_helper.uuid_struct; + policies[load_info[i].image_id].image_spec = (uintptr_t)uuid_ptr; + policies[load_info[i].image_id].dev_handle = &fip_dev_handle; + policies[load_info[i].image_id].check = open_fip; + } + + return 0; +} + +FCONF_REGISTER_POPULATOR(TB_FW, stm32mp_io, fconf_populate_stm32mp_io_policies); diff --git a/plat/st/common/stm32mp_img_parser_lib.c b/plat/st/common/stm32mp_img_parser_lib.c new file mode 100644 index 0000000000..f4c8bd642f --- /dev/null +++ b/plat/st/common/stm32mp_img_parser_lib.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include + +static void img_lib_init(void) +{ +} + +static int img_check_integrity(void *img, unsigned int img_len) +{ + return stm32mp_check_header(stm32mp_get_loaded_header(), + (uintptr_t)img); +} + +static int img_get_auth_param(const auth_param_type_desc_t *type_desc, + void *img, unsigned int img_len, void **param, + unsigned int *param_len) +{ + boot_api_image_header_t *image_header = stm32mp_get_loaded_header(); + + switch (type_desc->type) { + case AUTH_PARAM_SIG: + *param_len = sizeof(image_header->image_signature); + *param = &image_header->image_signature; + break; + case AUTH_PARAM_SIG_ALG: + *param_len = sizeof(image_header->option_flags) + + sizeof(image_header->ecc_algo_type); + *param = &image_header->option_flags; + /* + * Store option_flags and ecc_alog_type in same param + * structure because they both have the same header fields. + */ + break; + case AUTH_PARAM_PUB_KEY: + *param_len = sizeof(image_header->ecc_pubk); + *param = &image_header->ecc_pubk; + break; + case AUTH_PARAM_RAW_DATA: + if (type_desc->cookie == NULL) { + *param_len = image_header->image_length; + *param = img; + } else { + return -EINVAL; + } + break; + case AUTH_PARAM_NV_CTR: + if (type_desc->cookie == NULL) { + *param_len = sizeof(image_header->image_version); + *param = &image_header->image_version; + } else { + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +REGISTER_IMG_PARSER_LIB(IMG_PLAT, "stm32_img_parser_lib", + img_lib_init, + img_check_integrity, + img_get_auth_param); diff --git a/plat/st/common/stm32mp_shres_helpers.c b/plat/st/common/stm32mp_shres_helpers.c new file mode 100644 index 0000000000..12633e47d2 --- /dev/null +++ b/plat/st/common/stm32mp_shres_helpers.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include + +#include + +static struct spinlock shregs_lock; + +void stm32mp_lock_shregs(void) +{ + if (stm32mp_lock_available() == 0U) { + return; + } + + /* Assume interrupts are masked */ + spin_lock(&shregs_lock); +} + +void stm32mp_unlock_shregs(void) +{ + if (stm32mp_lock_available() == 0U) { + return; + } + + spin_unlock(&shregs_lock); +} + +/* Shared register access: upon shared resource lock */ +void stm32mp_mmio_clrsetbits_32_shregs(uintptr_t addr, uint32_t clear, + uint32_t set) +{ + stm32mp_lock_shregs(); + + mmio_clrsetbits_32(addr, clear, set); + + stm32mp_unlock_shregs(); +} + +void stm32mp_mmio_clrbits_32_shregs(uintptr_t addr, uint32_t clear) +{ + stm32mp_lock_shregs(); + + mmio_clrbits_32(addr, clear); + + stm32mp_unlock_shregs(); +} + +void stm32mp_mmio_setbits_32_shregs(uintptr_t addr, uint32_t set) +{ + stm32mp_lock_shregs(); + + mmio_setbits_32(addr, set); + + stm32mp_unlock_shregs(); +} diff --git a/plat/st/common/stm32mp_trusted_boot.c b/plat/st/common/stm32mp_trusted_boot.c new file mode 100644 index 0000000000..1ea30abf9b --- /dev/null +++ b/plat/st/common/stm32mp_trusted_boot.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2020-2021, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#if STM32MP_USE_STM32IMAGE +static uint8_t root_pk_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES]; +#else +static uint8_t der_sha256_header[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; +static uint8_t root_pk_hash[HASH_DER_LEN]; +#endif + +int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len, + unsigned int *flags) +{ + uint32_t otp_idx; + uint32_t otp_val; + uint32_t len; + size_t i; + size_t start_copy_idx = 0U; + + if (cookie != NULL) { + return -EINVAL; + } + + if (stm32_get_otp_index(PKH_OTP, &otp_idx, &len) != 0) { + VERBOSE("get_rot_pk_hash: get index error\n"); + return -EINVAL; + } + if (len != (CHAR_BIT * BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES)) { + VERBOSE("get_rot_pk_hash: length Error\n"); + return -EINVAL; + } + +#if !STM32MP_USE_STM32IMAGE + memcpy(root_pk_hash, der_sha256_header, sizeof(der_sha256_header)); + start_copy_idx = sizeof(der_sha256_header); +#endif + + for (i = 0U; i < BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES / sizeof(uint32_t); i++) { + uint32_t temp; + + if (stm32_get_otp_value_from_idx(otp_idx + i, &otp_val) != 0) { + return -EINVAL; + } + + temp = bswap32(otp_val); + memcpy(root_pk_hash + i * sizeof(uint32_t) + start_copy_idx, &temp, sizeof(temp)); + } + +#if STM32MP_USE_STM32IMAGE + *key_len = BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES; +#else + *key_len = HASH_DER_LEN; +#endif + *key_ptr = &root_pk_hash; + *flags = ROTPK_IS_HASH; + + if (!stm32mp_is_closed_device()) { + /* Check if key hash values in OTP are 0 or 0xFFFFFFFFF programmed : Invalid Key */ + uint32_t res; + uint32_t rootpk; + uint8_t *proot_pk = root_pk_hash; + uint8_t idx = sizeof(uint32_t); + +#if !STM32MP_USE_STM32IMAGE + idx += sizeof(der_sha256_header); + proot_pk = root_pk_hash + sizeof(der_sha256_header); +#endif + memcpy(&res, proot_pk, sizeof(uint32_t)); + if ((res == 0U) || (res == 0xFFFFFFFFU)) { + while (idx < ARRAY_SIZE(root_pk_hash)) { + memcpy(&rootpk, (proot_pk + idx), sizeof(uint32_t)); + if (res != rootpk) { + return 0; + } + + idx += sizeof(uint32_t); + } + + *flags |= ROTPK_NOT_DEPLOYED; + } + } + + return 0; +} + +int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr) +{ +#if STM32MP_USE_STM32IMAGE + if (cookie != NULL) { + return -EINVAL; + } +#endif + + /* + * This monotonic counter is the counter used by ROM code + * to identify BL2. + */ + if (stm32_get_otp_value(MONOTONIC_OTP, nv_ctr) == 0) { + return 0; + } + + return -EINVAL; +} + +int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr) +{ + return -EINVAL; +} + +#if !STM32MP_USE_STM32IMAGE +int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size) +{ + assert(heap_addr != NULL); + assert(heap_size != NULL); + +#if STM32MP_USE_EXTERNAL_HEAP + /* Retrieve the already allocated heap's info from DTB */ + *heap_addr = FCONF_GET_PROPERTY(tbbr, dyn_config, mbedtls_heap_addr); + *heap_size = FCONF_GET_PROPERTY(tbbr, dyn_config, mbedtls_heap_size); + + return 0; +#else + return get_mbedtls_heap_helper(heap_addr, heap_size); +#endif +} +#endif diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index e09ce63c21..13ce3042a5 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -1,10 +1,11 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include +#include #include #include @@ -13,28 +14,51 @@ #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 #define RESET_TIMEOUT_US_1MS 1000U +static const char debug_msg[626] = { + "***************************************************\n" + "** NOTICE NOTICE NOTICE NOTICE NOTICE **\n" + "** **\n" + "** DEBUG ACCESS PORT IS OPEN! **\n" + "** This boot image is only for debugging purpose **\n" + "** and is unsafe for production use. **\n" + "** **\n" + "** If you see this message and you are not **\n" + "** debugging report this immediately to your **\n" + "** vendor! **\n" + "** **\n" + "***************************************************\n" +}; + static console_t console; -static struct stm32mp_auth_ops stm32mp1_auth_ops; +static enum boot_device_e boot_device = BOOT_DEVICE_BOARD; static void print_reset_reason(void) { @@ -121,6 +145,11 @@ static void print_reset_reason(void) ERROR(" Unidentified reset reason\n"); } +enum boot_device_e get_boot_device(void) +{ + return boot_device; +} + void bl2_el3_early_platform_setup(u_register_t arg0, u_register_t arg1 __unused, u_register_t arg2 __unused, @@ -132,11 +161,6 @@ void bl2_el3_early_platform_setup(u_register_t arg0, void bl2_platform_setup(void) { int ret; - uint32_t ddr_ns_size; - - if (dt_pmic_status() > 0) { - initialize_pmic(); - } ret = stm32mp1_ddr_probe(); if (ret < 0) { @@ -144,29 +168,150 @@ void bl2_platform_setup(void) panic(); } - ddr_ns_size = stm32mp_get_ddr_ns_size(); - assert(ddr_ns_size > 0U); - - /* Map non secure DDR for BL33 load, now with cacheable attribute */ - ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, - ddr_ns_size, MT_MEMORY | MT_RW | MT_NS); - assert(ret == 0); - +#if STM32MP_USE_STM32IMAGE #ifdef AARCH32_SP_OPTEE INFO("BL2 runs OP-TEE setup\n"); - - /* Map secure DDR for OP-TEE paged area */ - ret = mmap_add_dynamic_region(STM32MP_DDR_BASE + ddr_ns_size, - STM32MP_DDR_BASE + ddr_ns_size, - STM32MP_DDR_S_SIZE, - MT_MEMORY | MT_RW | MT_SECURE); - assert(ret == 0); - - /* Initialize tzc400 after DDR initialization */ - stm32mp1_security_setup(); #else INFO("BL2 runs SP_MIN setup\n"); #endif +#endif + + if (!stm32mp1_ddr_is_restored()) { + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + + /* Clear backup register */ + mmio_write_32(bkpr_core1_addr, 0); + /* Clear backup register magic */ + mmio_write_32(bkpr_core1_magic, 0); + + /* Clear the context in BKPSRAM */ + stm32_clean_context(); + + if (dt_pmic_status() > 0) { + configure_pmic(); + } + } + + /* Map DDR for binary load, now with cacheable attribute */ + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + STM32MP_DDR_MAX_SIZE, MT_MEMORY | MT_RW | MT_SECURE); + if (ret < 0) { + ERROR("DDR mapping: error %d\n", ret); + panic(); + } +} + +static void update_monotonic_counter(void) +{ + uint32_t version; + uint32_t otp; + + CASSERT(STM32_TF_VERSION <= MAX_MONOTONIC_VALUE, + assert_stm32mp1_monotonic_counter_reach_max); + + /* Check if monotonic counter needs to be incremented */ + if (stm32_get_otp_index(MONOTONIC_OTP, &otp, NULL) != 0) { + panic(); + } + + if (stm32_get_otp_value(MONOTONIC_OTP, &version) != 0) { + panic(); + } + + if ((version + 1U) < BIT(STM32_TF_VERSION)) { + uint32_t result; + + /* Need to increment the monotonic counter. */ + version = BIT(STM32_TF_VERSION) - 1U; + + result = bsec_program_otp(version, otp); + if (result != BSEC_OK) { + ERROR("BSEC: MONOTONIC_OTP program Error %i\n", + result); + panic(); + } + INFO("Monotonic counter has been incremented (value 0x%x)\n", + version); + } +} + +static void initialize_clock(void) +{ + uint32_t voltage_mv = 0U; + uint32_t freq_khz = 0U; + int ret = 0; + + if (stm32mp1_is_wakeup_from_standby()) { + ret = stm32_get_pll1_settings_from_context(); + } + + /* + * If no pre-defined PLL1 settings in DT, find the highest frequency + * in the OPP table (in DT, compatible with plaform capabilities, or + * in structure restored in RAM), and set related CPU supply voltage. + * If PLL1 settings found in DT, we consider CPU supply voltage in DT + * is consistent with it. + */ + if ((ret == 0) && !fdt_is_pll1_predefined()) { + if (stm32mp1_is_wakeup_from_standby()) { + ret = stm32mp1_clk_get_maxfreq_opp(&freq_khz, + &voltage_mv); + } else { + ret = dt_get_max_opp_freqvolt(&freq_khz, &voltage_mv); + } + + if (ret != 0) { + panic(); + } + + if (dt_pmic_status() > 0) { + int read_voltage; + const char *name; + + name = stm32mp_get_cpu_supply_name(); + if (name == NULL) { + panic(); + } + + read_voltage = stpmic1_regulator_voltage_get(name); + if (read_voltage < 0) { + panic(); + } + + if (voltage_mv != (uint32_t)read_voltage) { + if (stpmic1_regulator_voltage_set(name, + (uint16_t)voltage_mv) != 0) { + panic(); + } + } + } + } + + if (stm32mp1_clk_init(freq_khz) < 0) { + panic(); + } +} + +static void reset_uart(uint32_t reset) +{ + int ret; + + ret = stm32mp_reset_assert(reset, RESET_TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + + udelay(2); + + ret = stm32mp_reset_deassert(reset, RESET_TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + + mdelay(1); } void bl2_el3_plat_arch_setup(void) @@ -179,16 +324,20 @@ void bl2_el3_plat_arch_setup(void) uint32_t clk_rate; uintptr_t pwr_base; uintptr_t rcc_base; + bool serial_uart_interface __unused = + (boot_context->boot_interface_selected == + BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART); + uintptr_t uart_prog_addr __unused; + + if (bsec_probe() != 0) { + panic(); + } mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, MT_CODE | MT_SECURE); -#ifdef AARCH32_SP_OPTEE - mmap_add_region(STM32MP_OPTEE_BASE, STM32MP_OPTEE_BASE, - STM32MP_OPTEE_SIZE, - MT_MEMORY | MT_RW | MT_SECURE); -#else +#if STM32MP_USE_STM32IMAGE && !defined(AARCH32_SP_OPTEE) /* Prevent corruption of preloaded BL32 */ mmap_add_region(BL32_BASE, BL32_BASE, BL32_LIMIT - BL32_BASE, @@ -201,7 +350,7 @@ void bl2_el3_plat_arch_setup(void) configure_mmu(); - if (dt_open_and_check() < 0) { + if (dt_open_and_check(STM32MP_DTB_BASE) < 0) { panic(); } @@ -219,10 +368,6 @@ void bl2_el3_plat_arch_setup(void) ; } - if (bsec_probe() != 0) { - panic(); - } - /* Reset backup domain on cold boot cases */ if ((mmio_read_32(rcc_base + RCC_BDCR) & RCC_BDCR_RTCSRC_MASK) == 0U) { mmio_setbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); @@ -235,25 +380,45 @@ void bl2_el3_plat_arch_setup(void) mmio_clrbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); } - /* Disable MCKPROT */ - mmio_clrbits_32(rcc_base + RCC_TZCR, RCC_TZCR_MCKPROT); + /* Enable BKP Register protection */ + mmio_write_32(TAMP_SMCR, + TAMP_BKP_SEC_NUMBER << TAMP_BKP_SEC_WDPROT_SHIFT | + TAMP_BKP_SEC_NUMBER << TAMP_BKP_SEC_RWDPROT_SHIFT); generic_delay_timer_init(); +#if STM32MP_UART_PROGRAMMER + uart_prog_addr = get_uart_address(boot_context->boot_interface_instance); + + /* Disable programmer UART before changing clock tree */ + if (serial_uart_interface) { + stm32_uart_stop(uart_prog_addr); + } +#endif +#if STM32MP_USB_PROGRAMMER + if (boot_context->boot_interface_selected == + BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB) { + boot_device = BOOT_DEVICE_USB; + } +#endif if (stm32mp1_clk_probe() < 0) { panic(); } - if (stm32mp1_clk_init() < 0) { - panic(); + if (dt_pmic_status() > 0) { + initialize_pmic(); } - stm32mp1_syscfg_init(); + initialize_clock(); result = dt_get_stdout_uart_info(&dt_uart_info); if ((result <= 0) || - (dt_uart_info.status == 0U) || + (dt_uart_info.status == DT_DISABLED) || +#if STM32MP_UART_PROGRAMMER + (serial_uart_interface && + (uart_prog_addr == dt_uart_info.base)) || +#endif (dt_uart_info.clock < 0) || (dt_uart_info.reset < 0)) { goto skip_console_init; @@ -263,23 +428,15 @@ void bl2_el3_plat_arch_setup(void) goto skip_console_init; } - stm32mp_clk_enable((unsigned long)dt_uart_info.clock); - - if (stm32mp_reset_assert((uint32_t)dt_uart_info.reset, - RESET_TIMEOUT_US_1MS) != 0) { + if (dt_uart_info.status == DT_DISABLED) { panic(); } - udelay(2); - - if (stm32mp_reset_deassert((uint32_t)dt_uart_info.reset, - RESET_TIMEOUT_US_1MS) != 0) { - panic(); - } + clk_enable((unsigned long)dt_uart_info.clock); - mdelay(1); + reset_uart((uint32_t)dt_uart_info.reset); - clk_rate = stm32mp_clk_get_rate((unsigned long)dt_uart_info.clock); + clk_rate = clk_get_rate((unsigned long)dt_uart_info.clock); if (console_stm32_register(dt_uart_info.base, clk_rate, STM32MP_UART_BAUDRATE, &console) == 0) { @@ -298,22 +455,40 @@ void bl2_el3_plat_arch_setup(void) stm32mp_print_boardinfo(); +#if TRUSTED_BOARD_BOOT if (boot_context->auth_status != BOOT_API_CTX_AUTH_NO) { NOTICE("Bootrom authentication %s\n", (boot_context->auth_status == BOOT_API_CTX_AUTH_FAILED) ? "failed" : "succeeded"); } +#endif skip_console_init: +#if !TRUSTED_BOARD_BOOT + if (stm32mp_is_closed_device()) { + /* Closed chip required authentication */ + ERROR("Secured chip must enabled TRUSTED_BOARD_BOOT\n"); + panic(); + } +#endif + + stm32mp1_syscfg_init(); + if (stm32_iwdg_init() < 0) { panic(); } stm32_iwdg_refresh(); - result = stm32mp1_dbgmcu_freeze_iwdg2(); - if (result != 0) { - INFO("IWDG2 freeze error : %i\n", result); + if (bsec_read_debug_conf() != 0U) { + result = stm32mp1_dbgmcu_freeze_iwdg2(); + if (result != 0) { + INFO("IWDG2 freeze error : %i\n", result); + } + + if (stm32mp_is_closed_device()) { + NOTICE("\n%s", debug_msg); + } } if (stm32_save_boot_interface(boot_context->boot_interface_selected, @@ -322,20 +497,57 @@ skip_console_init: ERROR("Cannot save boot interface\n"); } - stm32mp1_auth_ops.check_key = boot_context->bootrom_ecdsa_check_key; - stm32mp1_auth_ops.verify_signature = - boot_context->bootrom_ecdsa_verify_signature; - - stm32mp_init_auth(&stm32mp1_auth_ops); - stm32mp1_arch_security_setup(); print_reset_reason(); + update_monotonic_counter(); + + stm32mp1_syscfg_enable_io_compensation_finish(); + + if (dt_pmic_status() > 0) { + initialize_pmic(); + print_pmic_info_and_debug(); + } + +#if STM32MP_USE_STM32IMAGE + if (!stm32mp1_ddr_is_restored()) { + stm32mp_io_setup(); + } +#else + fconf_populate("TB_FW", STM32MP_DTB_BASE); + stm32mp_io_setup(); +#endif } -#if defined(AARCH32_SP_OPTEE) +#if defined(AARCH32_SP_OPTEE) && STM32MP_USE_STM32IMAGE +static void set_mem_params_info(entry_point_info_t *ep_info, + image_info_t *unpaged, image_info_t *paged) +{ + uintptr_t bl32_ep = 0; + + /* Use the default dram setup if no valid ep found */ + if (get_optee_header_ep(ep_info, &bl32_ep) && + (bl32_ep >= STM32MP_OPTEE_BASE) && + (bl32_ep < (STM32MP_OPTEE_BASE + STM32MP_OPTEE_SIZE))) { + assert((STM32MP_OPTEE_BASE >= BL2_LIMIT) || + ((STM32MP_OPTEE_BASE + STM32MP_OPTEE_SIZE) <= BL2_BASE)); + + unpaged->image_base = STM32MP_OPTEE_BASE; + unpaged->image_max_size = STM32MP_OPTEE_SIZE; + } else { + unpaged->image_base = STM32MP_DDR_BASE + dt_get_ddr_size() - + STM32MP_DDR_S_SIZE - + STM32MP_DDR_SHMEM_SIZE; + unpaged->image_max_size = STM32MP_DDR_S_SIZE; + } + paged->image_base = STM32MP_DDR_BASE + dt_get_ddr_size() - + STM32MP_DDR_S_SIZE - STM32MP_DDR_SHMEM_SIZE; + paged->image_max_size = STM32MP_DDR_S_SIZE; +} +#endif + /******************************************************************************* * This function can be used by the platforms to update/use image * information for given `image_id`. @@ -345,50 +557,160 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) int err = 0; bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id); bl_mem_params_node_t *bl32_mem_params; - bl_mem_params_node_t *pager_mem_params; - bl_mem_params_node_t *paged_mem_params; + bl_mem_params_node_t *pager_mem_params __unused; + bl_mem_params_node_t *paged_mem_params __unused; +#if !STM32MP_USE_STM32IMAGE + const struct dyn_cfg_dtb_info_t *config_info; + bl_mem_params_node_t *tos_fw_mem_params; + unsigned int i; + unsigned long long ddr_top __unused; + bool wakeup_ddr_sr = stm32mp1_ddr_is_restored(); + const unsigned int image_ids[] = { + BL32_IMAGE_ID, + BL33_IMAGE_ID, + HW_CONFIG_ID, + TOS_FW_CONFIG_ID, + }; +#endif assert(bl_mem_params != NULL); +#if TRUSTED_BOARD_BOOT && STM32MP_USE_STM32IMAGE + /* Clean header to avoid loaded header reused */ + stm32mp_delete_loaded_header(); +#endif + switch (image_id) { +#if !STM32MP_USE_STM32IMAGE + case FW_CONFIG_ID: + /* Set global DTB info for fixed fw_config information */ + set_config_info(STM32MP_FW_CONFIG_BASE, STM32MP_FW_CONFIG_MAX_SIZE, FW_CONFIG_ID); + fconf_populate("FW_CONFIG", STM32MP_FW_CONFIG_BASE); + + /* Iterate through all the fw config IDs */ + for (i = 0; i < ARRAY_SIZE(image_ids); i++) { + bl_mem_params = get_bl_mem_params_node(image_ids[i]); + assert(bl_mem_params != NULL); + + config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, image_ids[i]); + if (config_info == NULL) { + continue; + } + + bl_mem_params->image_info.image_base = config_info->config_addr; + bl_mem_params->image_info.image_max_size = config_info->config_max_size; + + /* + * If going back from CSTANDBY / STANDBY and DDR was in Self-Refresh, + * DDR partitions must not be reloaded. + */ + if (!(wakeup_ddr_sr && (config_info->config_addr >= STM32MP_DDR_BASE))) { + bl_mem_params->image_info.h.attr &= ~IMAGE_ATTRIB_SKIP_LOADING; + } + + switch (image_ids[i]) { + case BL32_IMAGE_ID: + bl_mem_params->ep_info.pc = config_info->config_addr; + + /* In case of OPTEE, initialize address space with tos_fw addr */ + pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID); + pager_mem_params->image_info.image_base = config_info->config_addr; + pager_mem_params->image_info.image_max_size = + config_info->config_max_size; + + /* Init base and size for pager if exist */ + paged_mem_params = get_bl_mem_params_node(BL32_EXTRA2_IMAGE_ID); + paged_mem_params->image_info.image_base = STM32MP_DDR_BASE + + (dt_get_ddr_size() - STM32MP_DDR_S_SIZE); + paged_mem_params->image_info.image_max_size = STM32MP_DDR_S_SIZE; + break; + + case BL33_IMAGE_ID: + if (wakeup_ddr_sr) { + /* + * Set ep_info PC to 0, to inform BL32 it is a reset + * after STANDBY + */ + bl_mem_params->ep_info.pc = 0U; + } else { + bl_mem_params->ep_info.pc = config_info->config_addr; + } + break; + + case HW_CONFIG_ID: + case TOS_FW_CONFIG_ID: + break; + + default: + return -EINVAL; + } + } + + break; +#endif /* !STM32MP_USE_STM32IMAGE */ + case BL32_IMAGE_ID: - bl_mem_params->ep_info.pc = - bl_mem_params->image_info.image_base; - - pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID); - assert(pager_mem_params != NULL); - pager_mem_params->image_info.image_base = STM32MP_OPTEE_BASE; - pager_mem_params->image_info.image_max_size = - STM32MP_OPTEE_SIZE; - - paged_mem_params = get_bl_mem_params_node(BL32_EXTRA2_IMAGE_ID); - assert(paged_mem_params != NULL); - paged_mem_params->image_info.image_base = STM32MP_DDR_BASE + - stm32mp_get_ddr_ns_size(); - paged_mem_params->image_info.image_max_size = - STM32MP_DDR_S_SIZE; - - err = parse_optee_header(&bl_mem_params->ep_info, - &pager_mem_params->image_info, - &paged_mem_params->image_info); - if (err) { - ERROR("OPTEE header parse error.\n"); - panic(); +#if defined(AARCH32_SP_OPTEE) || !STM32MP_USE_STM32IMAGE + bl_mem_params->ep_info.pc = bl_mem_params->image_info.image_base; + if (get_optee_header_ep(&bl_mem_params->ep_info, &bl_mem_params->ep_info.pc) == 1) { + /* BL32 is OP-TEE header */ +#if !STM32MP_USE_STM32IMAGE + if (wakeup_ddr_sr) { + bl_mem_params->ep_info.pc = stm32_pm_get_optee_ep(); + if (stm32mp1_addr_inside_backupsram(bl_mem_params->ep_info.pc)) { + clk_enable(BKPSRAM); + } + + break; + } +#endif + pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID); + paged_mem_params = get_bl_mem_params_node(BL32_EXTRA2_IMAGE_ID); + assert((pager_mem_params != NULL) && (paged_mem_params != NULL)); + +#if STM32MP_USE_STM32IMAGE + set_mem_params_info(&bl_mem_params->ep_info, &pager_mem_params->image_info, + &paged_mem_params->image_info); +#endif + + err = parse_optee_header(&bl_mem_params->ep_info, + &pager_mem_params->image_info, + &paged_mem_params->image_info); + if (err) { + ERROR("OPTEE header parse error.\n"); + panic(); + } + + /* Set optee boot info from parsed header data */ + bl_mem_params->ep_info.args.arg0 = paged_mem_params->image_info.image_base; + bl_mem_params->ep_info.args.arg1 = 0; /* Unused */ + bl_mem_params->ep_info.args.arg2 = 0; /* No DT supported */ + } else { +#if STM32MP_USE_STM32IMAGE + bl_mem_params->ep_info.pc = STM32MP_BL32_BASE; +#else + tos_fw_mem_params = get_bl_mem_params_node(TOS_FW_CONFIG_ID); + bl_mem_params->image_info.image_max_size += + tos_fw_mem_params->image_info.image_max_size; +#endif + bl_mem_params->ep_info.args.arg0 = 0; } - /* Set optee boot info from parsed header data */ - bl_mem_params->ep_info.pc = - pager_mem_params->image_info.image_base; - bl_mem_params->ep_info.args.arg0 = - paged_mem_params->image_info.image_base; - bl_mem_params->ep_info.args.arg1 = 0; /* Unused */ - bl_mem_params->ep_info.args.arg2 = 0; /* No DT supported */ + if (bl_mem_params->ep_info.pc >= STM32MP_DDR_BASE) { + stm32_context_save_bl2_param(); + } +#endif break; case BL33_IMAGE_ID: bl32_mem_params = get_bl_mem_params_node(BL32_IMAGE_ID); assert(bl32_mem_params != NULL); bl32_mem_params->ep_info.lr_svc = bl_mem_params->ep_info.pc; + +#if STM32MP_USB_PROGRAMMER || STM32MP_UART_PROGRAMMER + /* Invalidate downloaded package from cache */ + inv_dcache_range(DWL_BUFFER_BASE, DWL_BUFFER_SIZE); +#endif break; default: @@ -398,4 +720,8 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) return err; } -#endif + +void bl2_el3_plat_prepare_exit(void) +{ + stm32mp1_security_setup(); +} diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h index c16639ac40..8c9d3afb49 100644 --- a/plat/st/stm32mp1/include/boot_api.h +++ b/plat/st/stm32mp1/include/boot_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,90 @@ #include #include +/* + * Exported constants + */ + +/* + * Boot Context related definitions + */ + +/* + * Possible value of boot context field 'boot_action' + */ +/* Boot action is Process Cold Boot */ +#define BOOT_API_CTX_BOOT_ACTION_COLD_BOOT_PROCESS 0x09U +/* Boot action is Process Wakeup from CSTANDBY */ +#define BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY 0x0AU +/* Boot action is Process Wakeup from STANDBY */ +#define BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY 0x0BU +/* Boot action is Process Engineering Boot */ +#define BOOT_API_CTX_BOOT_ACTION_ENGI_BOOT 0x0CU + +#define BOOT_API_CTX_BOOT_ACTION_MPU_CORE0_RESET_PROCESS 0x0F + +/* + * Possible value of boot context field 'stby_exit_status' + */ + +/* The boot reason is not a STANDBY Exit reason */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_NO_STANDBY 0x00 + +/* STANDBY Exit with MPU_BEN=1, MCU_BEN=0 */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MPU_ONLY 0x01 + +/* + * STANDBY Exit with MPU_BEN=1, MCU_BEN=1, MPU will go for cold boot + * MCU restarted by bootROM + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_ALL_CORES 0x02 + +/* + * STANDBY Exit with MPU_BEN=1, MCU_BEN=1, MPU will go for cold boot + * but MCU restart aborted (code integrity check) : have not been restarted + * by bootROM + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_ALL_CORES_MCU_ABT 0x03 + +/* + * STANDBY Exit with MPU_BEN=0, MCU_BEN=1, MPU gone to CSTANDBY, + * MCU restarted correctly by bootROM + * This value should never be read by FSBL, because not executed in that case + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MCU_ONLY 0x04 + +/* + * STANDBY Exit with MPU_BEN=0, MCU_BEN=1, MCU restart aborted + * due code integrity check, then MPU will go for cold boot despite + * was not planned initially + */ +#define BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MCU_ONLY_MCU_ABT 0x05 + +/* + * STANDBY Exit with MPU_BEN=1, MCU_BEN=1, MCU restart aborted + * due to MCU security perimeter issue + */ +#define \ +BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_ALL_CORES_MCU_ABT_SEC_PERIMETER_ISSUE 0x06 + +/* + * STANDBY Exit with MPU_BEN=0, MCU_BEN=1, MCU restart aborted + * due to MCU security perimeter issue, then MPU will go for cold boot + * despite was not planned initially + */ +#define \ +BOOT_API_CTX_STBY_EXIT_STATUS_WKUP_MCU_ONLY_MCU_ABT_SEC_PERIMETER_ISSUE 0x07 + +/* + * Possible value of boot context field 'cstby_exit_status' + */ +/* The boot reason is not a CSTANDBY Exit reason */ +#define BOOT_API_CTX_CSTBY_EXIT_STATUS_NO_CSTBY 0x00 +/* CSTANDBY Exit with MCU detected as Not running */ +#define BOOT_API_CTX_CSTBY_EXIT_STATUS_MCU_NOT_RUNNING 0x01 +/* CSTANDBY Exit with MCU detected as Running */ +#define BOOT_API_CTX_CSTBY_EXIT_STATUS_MCU_RUNNING 0x02 + /* * Possible value of boot context field 'auth_status' */ @@ -37,10 +121,16 @@ #define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC 0x3U /* Boot occurred on QSPI NOR */ -#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI 0x4U +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI 0x4U + +/* Boot occurred on UART */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART 0x5U + +/* Boot occurred on USB */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB 0x6U /* Boot occurred on QSPI NAND */ -#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI 0x7U +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI 0x7U /** * @brief Possible value of boot context field 'EmmcXferStatus' @@ -65,6 +155,10 @@ #define BOOT_API_CTX_EMMC_ERROR_STATUS_HEADER_SIZE_ZERO 0x6U #define BOOT_API_CTX_EMMC_ERROR_STATUS_IMAGE_NOT_COMPLETE 0x7U +/* Definitions relative to 'p_rom_version_info->platform_type_ver' field */ +#define BOOT_API_CTX_ROM_VERSION_PLAT_VER_IC_EMU_FPGA 0xAA +#define BOOT_API_CTX_ROM_VERSION_PLAT_VER_FPGA_ONLY 0xBB + /* Image Header related definitions */ /* Definition of header version */ @@ -95,6 +189,64 @@ #define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xCA7FACE0U #define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xCA7FACE1U +/* + * MCU Code Integrity Check related definitions + */ + +/* + * Defines to identify RTC backup registers to be used for MCU code integrity + * check + */ + +/* + * TAMP_BCK0R contains two bits + * bit 0 : wanted value of 'RCC_TZCR.TZEN' + * bit 1 : wanted value of 'RCC_TZCR.MCKPROT' + */ + +/* + * TAMP_BCK0R bit position coding wanted value of 'RCC_TZCR.TZEN' + * trustZone aware domain enabling/disabling + */ +#define BOOT_API_MCIC_MCU_SECURITY_PERIMETER_TZEN_BIT 0 + +/* + * TAMP_BCK0R bit position coding wanted value of 'RCC_TZCR.MCKPROT' + * ability of MCU to modify some clock settings in RCC + */ +#define BOOT_API_MCIC_MCU_SECURITY_PERIMETER_MCKPROT_BIT 1 + +/* TAMP_BCK0R register index */ +#define \ +BOOT_API_MCIC_MCU_SECURITY_PERIMETER_TZEN_MCKPROT_TAMP_BCK_REG_IDX 0 + +/* + * TAMP_BCK1R register index + * This register is coding the wanted value of register 'EXTI_TZENR1' + * to be programmed by bootROM on wakeup from STANDBY when MCUBEN=1 + * that is MCU quick restart requested + */ +#define \ +BOOT_API_MCIC_MCU_SECURITY_PERIMETER_EXTI_TZENR1_TAMP_BCK_REG_IDX 1 + +/* + * TAMP_BCK2R register index + * This register is coding the wanted value of register 'EXTI_TZENR2' + * to be programmed by bootROM on wakeup from STANDBY when MCUBEN=1 + * that is MCU quick restart requested + */ +#define \ +BOOT_API_MCIC_MCU_SECURITY_PERIMETER_EXTI_TZENR2_TAMP_BCK_REG_IDX 2 + +/* + * TAMP_BCK3R register index + * This register is coding the wanted value of register 'EXTI_TZENR3' + * to be programmed by bootROM on wakeup from STANDBY when MCUBEN=1 + * that is MCU quick restart requested + */ +#define \ +BOOT_API_MCIC_MCU_SECURITY_PERIMETER_EXTI_TZENR3_TAMP_BCK_REG_IDX 3 + /* * TAMP_BCK4R register index * This register is used to write a Magic Number in order to restart @@ -109,6 +261,39 @@ */ #define BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX 5U +/* + * TAMP_BCK22R register index + * This register contains offset in bytes of code to Hash in RETRAM region + * Note : offset is intended as relative value from start of RETRAM + */ +#define \ +BOOT_API_MCIC_OFFSET_IN_BYTES_CODE_TO_HASH_RETRAM_TAMP_BCK_REG_IDX 22 + +/* + * TAMP_BCK23R register index + * This register contains the size in bytes of the single consecutive region + * of MCU Firmware in RETRAM (Retention RAM) to hash (by SHA-256) + * Note : This is required as a MCU firmware Code Integrity Check (aka : MCIC) + * to avoid bootROM restarting MCU on a corrupted firmware + */ +#define \ +BOOT_API_MCIC_RETRAM_REGION_TO_HASH_IN_BYTES_TAMP_BCK_REG_IDX 23 + +/* + * TAMP_BCK24R to TAMP_BCK31R register indexes + * Those registers contains SHA-256 digest of RETRAM MCU Firmware code between + * [(RETRAM_start + offset) -- (RETRAM_start + offset + size_to_hash)] + * in this order + * This is the MCU Code Integrity Check MCU Firmware signature + * value on 256 bits + */ + +/* First TAMP_BKP index of MCU Firmware signature : ie TAMP_BCK24R */ +#define BOOT_API_MCIC_SHA_DIGEST_FIRST_TAMP_BCK_REG_IDX 24 + +/* Last TAMP_BKP index of MCU Firmware signature : ie TAMP_BCK31R */ +#define BOOT_API_MCIC_SHA_DIGEST_LAST_TAMP_BCK_REG_IDX 31 + /* * Possible value of boot context field 'hse_clock_value_in_hz' */ @@ -135,6 +320,320 @@ #define BOOT_API_RETURN_OK 0x77U +/* Mapping of OTP Word and OTP bits managing SSP and useful to FSBL-SSP */ +/* OTP_CFG8 */ +#define BOOT_API_OTP_SSP_WORD_NB 8U +/* SSP_REQ = OTP_CFG8[8] */ +#define BOOT_API_OTP_SSP_REQ_BIT_POS 8 +/* SSP_SUCCESS = OTP_CFG8[9] */ +#define BOOT_API_OTP_SSP_SUCCESS_BIT_POS 9 + +/* + * Possible values of boot context field + * 'ssp_config_ptr_in->ssp_cmd' + */ +/* 'K' 'B' 'U' 'P' -.> 'PUBK' */ +#define BOOT_API_CTX_SSP_CMD_CALC_CHIP_PUBK 0x4B425550 + +#if STM32MP_SSP +/* 'V' 'O' 'R' 'P' -.> 'PROV' */ +#define BOOT_API_CTX_SSP_CMD_PROV_SECRET 0x564F5250 +/* + * Possible values of boot context field + * 'ssp_config_ptr_in->ssp_cmd' written by bootROM as Acknowledge + * of a request of SSP by FSBL. + */ + +/* Written by bootROM on SSP error */ +#define BOOT_API_CTX_SSP_CMD_INVALID 0x00000000 +/* + * 'A' 'B' 'U' 'P' -.> 'PUBA' : ACK of ECIES_CHIP_PUBK calculation + * request by bootROM. + */ +#define BOOT_API_CTX_SSP_CMD_CALC_CHIP_PUBK_ACK 0x41425550 +/* + * 'A' 'O' 'R' 'P' -.> 'PROA' : ACK of OEM Secret Provisioning request + * by bootROM. + */ +#define BOOT_API_CTX_SSP_CMD_PROV_SECRET_ACK 0x414F5250 + +/* + * Constants required for SSP + */ +/* '.' 'P' 'S' 'S' -.> 'SSP.' */ +#define BOOT_API_SSP_BLOB_LICENSE_TYPE_SSP_NORMAL 0x2E505353 +/* 'L' 'P' 'S' 'S' -.> 'SSPL' */ +#define BOOT_API_SSP_BLOB_LICENSE_TYPE_SSP_LIVE 0x4C505353 +/* version 1 */ +#define BOOT_API_SSP_LICENSE_LAYOUT_VERSION_TO_MATCH 0x00000001 +/* 'P' 'P' 'S' 'S' -.> 'SSPP' */ +#define BOOT_API_SSP_BLOB_PAYLOAD_MAGIC_SSP 0x50505353 +/* IV AES on 128 bits = 16 bytes and KEY AES on 128 bits = 16 bytes */ +#define BOOT_API_SSP_ENCRYPTED_IV_AND_KEY_SIZE_BYTES 32 +/* version 1 */ +#define BOOT_API_SSP_PAYLOAD_PROTOCOL_VERSION_TO_MATCH 0x00000001 +/* + * Scalar in Elliptic curve cryptography is an integer (often a Prime) + * the number of bytes of this scalar is defined below. + */ +#define BOOT_API_SSP_SCALAR_SIZE_BYTES 32 + +/* + * In Elliptic curve cryptography coordinates of points are 2D P + * (Px, Py) as concatenation of two scalars. + */ +#define BOOT_API_SSP_EC_COORDINATE_SIZE_BYTES \ + (2 * BOOT_API_SSP_SCALAR_SIZE_BYTES) + +/* In Elliptic curve cryptography Private Keys are scalars */ +#define BOOT_API_SSP_PRIVK_KEY_SIZE_BYTES \ + BOOT_API_SSP_SCALAR_SIZE_BYTES + +/* + * In ECIES algorithm the Shared Secret (SS) is + * the x coordinate (Px) of a point P(Px,Py) obtained on reference + * chosen NIST-P256 Elliptic curve. + */ +#define BOOT_API_SSP_ECDH_SHARED_SECRET_SIZE_BYTES \ + BOOT_API_SSP_SCALAR_SIZE_BYTES + +/* + * In Elliptic curve cryptography Public Keys are Points on chosen + * Elliptic curve P(x,y). + * Public Key is the x, y coordinates concatenated + * Ecies_eph_pubk and OEM_ECDSA_PUBK are each 64 bytes = 512 bits key + * sizes. + */ +#define BOOT_API_SSP_PUBK_KEY_SIZE_BYTES \ + BOOT_API_SSP_EC_COORDINATE_SIZE_BYTES + +/* + * Size in bytes of ECIES_Chip_pubk obtained from bootROM at end of SSP + * phase 1 : Chip public key calculation. + */ +#define BOOT_API_SSP_ECIES_CHIP_PUBK_SIZE_BYTES \ + BOOT_API_SSP_PUBK_KEY_SIZE_BYTES + +/* AES-GCM authentication tag size is 16 bytes = 128 bits */ +#define BOOT_API_SSP_AES_GCM_AUTH_TAG_SIZE_BYTES 16 + +/* AES-GCM Symmetric Key size is 16 bytes = 128 bits */ +#define BOOT_API_SSP_AES_GCM_KEY_SIZE_BYTES 16 + +/* AES-GCM Initialization Vector (IV) size is of 16 bytes = 128 bits */ +#define BOOT_API_SSP_AES_GCM_IV_SIZE_BYTES 16 + +/* + * 88 bytes (license_type, live_session_id, license_version, + * fsbl_min_version, rfu[8], eph_ecies_pubk[]) + */ +#define BOOT_API_SSP_AES_GCM_LICENSE_AAD_NB_BYTES_FROM_LICENSE 88 + +/* + * 32 bytes AAD License Secret from 2nd round KDF-SHA-256 + * from ECDH Shared Secret hence KDF[32..63] aka "Authorization Token" + */ +#define BOOT_API_SSP_AES_GCM_LICENSE_AAD_NB_BYTES_FROM_KDF 32 + +/* + * Total License AAD size = 88 + 32 = 120 bytes + */ +#define BOOT_API_SSP_AES_GCM_LICENSE_AAD_SIZE_BYTES \ + (BOOT_API_SSP_AES_GCM_LICENSE_AAD_NB_BYTES_FROM_LICENSE + \ + BOOT_API_SSP_AES_GCM_LICENSE_AAD_NB_BYTES_FROM_KDF) + +/* + * AAD for Payload size : composed of : + * payload_magic, payload_protocol_version, oem_ecdsa_pubk[], oem_secret_size + * = 4 + 4 + 64 + 4 = 76 bytes AAD for Payload + */ +#define BOOT_API_SSP_AES_GCM_PAYLOAD_AAD_SIZE_BYTES 76 + +/* + * OEM Secrets max size in bytes : + * [OTP[95:59] + OTP_CFG56 (RMA Unlock and Relock passwords)] x 4 bytes + * by OTP word = 152 bytes + */ +#define BOOT_API_SSP_OEM_SECRETS_MAX_SIZE_BYTES 152 + +/* + * Possible values of boot context field 'ssp_status' + * as can be read by FSBL-SSP + */ +#define BOOT_API_CTX_SSP_STATUS_NO_SSP 0 +#define BOOT_API_CTX_SSP_STATUS_CHIP_PUBK_CALC_FINISHED 1 +#define BOOT_API_CTX_SSP_STATUS_OEM_SEC_PROV_FINISHED 2 +#define BOOT_API_CTX_SSP_STATUS_OEM_SEC_PROV_FORBIDDEN 3 + +/* + * Reserved size for future use + */ +#define BOOT_API_SSP_HSM_OEM_RFU_SIZE 8 + +/* + * Exported types + */ + +/* + * SSP related definitions + */ +/* + * SSP BLOB License structure : Binary Large OBject License structure + * Should be written by FSBL-SSP to provide bootROM with SSP OEM Secret + * provisioning. + * License information data, the structure is read by bootROM. + */ +typedef struct { + /* + * License Type provided by HSM-OEM tool + * should match Normal SSP License of Live SSP License. + */ + uint32_t license_type; + + /* Live Session ID provided by HSM-OEM tool */ + uint32_t live_session_id; + + /* + * Version of the License Protocol (Structure) + * should be incremented each time a new. + */ + uint32_t license_version; + + /* + * Minimum FSBL version to be compared + * with FSBL Header field 'imageVersion'. + */ + uint32_t fsbl_min_version; + + /* RFU provided by HSM-OEM tool */ + uint8_t rfu[BOOT_API_SSP_HSM_OEM_RFU_SIZE]; + + /* + * Ephemeral ECIES Public Key from HSM-OEM + * 64 bytes = 512 bits. + */ + uint8_t eph_ecies_pubk[BOOT_API_SSP_PUBK_KEY_SIZE_BYTES]; + + /* + * Encrypted (IV,Key) : with Shared Secret based on + * 'Ephemeral ECIES Key pair' and 'ECIES Chip Key pair'. + */ + uint8_t encrypted_iv_and_key + [BOOT_API_SSP_ENCRYPTED_IV_AND_KEY_SIZE_BYTES]; + + /* + * AUTH_TAG AES-GCM from encryption of (IV, Key) + * in HSM-OEM with License AAD scheme + * License Tag is 16 bytes = 128 bits. + */ + uint8_t license_tag[BOOT_API_SSP_AES_GCM_AUTH_TAG_SIZE_BYTES]; + +} boot_api_ssp_blob_license_t; + +/* + * SSP BLOB Payload structure : Binary Large OBject Payload Structure + * Should be written by FSBL-SSP to provide bootROM with SSP OEM Secret + * provisioning input data, the structure is read by bootROM + * The BLOB Payload size is fixed to a max size of 244 bytes based + * on a max number of bytes of OEM secret derived from OTP upper free + * area in STM32MP15xx cut 2.0.In this table oem_encrypted_secrets[] + * of max size only the first 'p_blob_payload->oem_secret_size_bytes' + * bytes will be considered and used by bootROM. + */ +typedef struct { + /* + * BLOB Payload Magic : for memory validity check of BLOB Payload + * to match against BOOT_API_SSP_BLOB_PAYLOAD_MAGIC_SSP by bootROM. + */ + uint32_t payload_magic; + + /* + * SSP Payload protocol version : on 32 bits + * to be checked by bootROM for equality with + * BOOT_API_SSP_PAYLOAD_PROTOCOL_VERSION_TO_MATCH + * ie : 0x00000001 : version 1 of SSP Payload + */ + uint32_t payload_protocol_version; + + /* + * OEM_ECDSA_PUBK Public Key defined by OEM + * 64 bytes = 512 bits + */ + uint8_t oem_ecdsa_pubk[BOOT_API_SSP_PUBK_KEY_SIZE_BYTES]; + + /* + * Size of Table of OEM Secrets encrypted with AES-GCM (Key,IV) from + * License field 'encrypted_iv_and_key[]' + * should be <= BOOT_API_SSP_OEM_SECRETS_MAX_SIZE_BYTES: + * is verified by bootROM. + */ + uint32_t oem_secret_size_bytes; + + /* + * AUTH_TAG AES-GCM computed by HSM-OEM when encrypting OEM Secrets with + * (Key,IV) using special AAD scheme for Payload. + * 16 bytes = 128 bits + */ + uint8_t payload_tag[BOOT_API_SSP_AES_GCM_AUTH_TAG_SIZE_BYTES]; + + /* + * OEM Secrets encrypted with AES-GCM (IV, Key) from + * License field 'encrypted_iv_and_key[]'. + * The payload size is 'oem_secret_size_bytes' + * should be <= BOOT_API_SSP_OEM_SECRETS_MAX_SIZE_BYTES = + * 152 bytes : OEM Secrets max size in bytes : + * [OTP_CFG56, OTP_CFG59, OTP_CFG60..95] x 4 bytes by OTP word. + */ + uint8_t oem_encrypted_secrets[BOOT_API_SSP_OEM_SECRETS_MAX_SIZE_BYTES]; + +} boot_api_ssp_blob_payload_t; +#endif + +/* SSP Configuration structure */ +typedef struct { + /* SSP Command*/ + uint32_t ssp_cmd; +#if STM32MP_SSP + /* ECIES chip public key */ + uint8_t *p_chip_pubk; + /* Blob License Address */ + boot_api_ssp_blob_license_t *p_blob_license; + /* Blob Payload Address */ + boot_api_ssp_blob_payload_t *p_blob_payload; + /* Secrets Decrypted Address */ + uint8_t *p_ssp_oem_secrets_decrypted; + /* Reserved for Future Use (RFU) */ + uint32_t padding_rfu; +#else + uint8_t reserved[20]; +#endif +} boot_api_ssp_config_t; + +/* + * bootROM version information structure definition + * Total size = 24 bytes = 6 uint32_t + */ +typedef struct { + /* Chip Version */ + uint32_t chip_ver; + + /* Cut version within a fixed chip version */ + uint32_t cut_ver; + + /* Version of ROM Mask within a fixed cut version */ + uint32_t rom_mask_ver; + + /* Internal Version of bootROM code */ + uint32_t bootrom_ver; + + /* Version of bootROM adapted */ + uint32_t for_chip_design_rtl_ver; + + /* Restriction on compiled platform when it applies */ + uint32_t platform_type_ver; + +} boot_api_rom_version_info_t; + /* * Boot Context related definitions */ @@ -153,26 +652,38 @@ typedef struct { uint16_t boot_interface_instance; uint32_t reserved1[13]; uint32_t otp_afmux_values[3]; - uint32_t reserved[5]; + uint32_t reserved[2]; + /* + * Log to boot context, what was the kind of boot action + * takes values from defines BOOT_API_BOOT_ACTION_XXX above + */ + uint32_t boot_action; + /* + * STANDBY Exit status to be checked by FSBL in case + * field 'boot_action' == BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY + * take values from defines above 'BOOT_API_CTX_STBY_EXIT_STATUS_XXX' + * depending on encountered situation + */ + uint32_t stby_exit_status; + /* + * CSTANDBY Exit status to be checked by FSBL in case + * boot_action == BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY + * take values from defines above 'BOOT_API_CTX_CSTBY_EXIT_STATUS_XXX' + * depending on encountered situation + */ + uint32_t cstby_exit_status; uint32_t auth_status; /* * Pointers to bootROM External Secure Services - * - ECDSA check key * - ECDSA verify signature - * - ECDSA verify signature and go */ - uint32_t (*bootrom_ecdsa_check_key)(uint8_t *pubkey_in, - uint8_t *pubkey_out); + uint32_t reserved3; uint32_t (*bootrom_ecdsa_verify_signature)(uint8_t *hash_in, uint8_t *pubkey_in, uint8_t *signature, uint32_t ecc_algo); - uint32_t (*bootrom_ecdsa_verify_and_go)(uint8_t *hash_in, - uint8_t *pub_key_in, - uint8_t *signature, - uint32_t ecc_algo, - uint32_t *entry_in); + uint32_t reserved4; /* * Information specific to an SD boot @@ -203,6 +714,21 @@ typedef struct { * ie FSBL partition on which the boot was successful */ uint32_t boot_partition_used_toboot; + /* + * Address of SSP configuration structure : + * given and defined by bootROM + * and used by FSBL. The structure is of type + * 'boot_api_ssp_config_t' + */ + boot_api_ssp_config_t *p_ssp_config; + /* + * boot context field containing bootROM updated SSP Status + * Values can be of type BOOT_API_CTX_SSP_STATUS_XXX + */ + uint32_t ssp_status; + + /* Pointer on ROM constant containing ROM information */ + const boot_api_rom_version_info_t *p_rom_version_info; } __packed boot_api_context_t; diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h index 7076a7105f..4434afa172 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,18 +25,22 @@ #define PLATFORM_STACK_SIZE 0xC00 #endif +#if STM32MP_USE_STM32IMAGE #ifdef AARCH32_SP_OPTEE #define OPTEE_HEADER_IMAGE_NAME "teeh" +#define OPTEE_CORE_IMAGE_NAME "teex" #define OPTEE_PAGED_IMAGE_NAME "teed" -#define OPTEE_PAGER_IMAGE_NAME "teex" #define OPTEE_HEADER_BINARY_TYPE U(0x20) -#define OPTEE_PAGER_BINARY_TYPE U(0x21) +#define OPTEE_CORE_BINARY_TYPE U(0x21) #define OPTEE_PAGED_BINARY_TYPE U(0x22) #endif /* SSBL = second stage boot loader */ #define BL33_IMAGE_NAME "ssbl" #define BL33_BINARY_TYPE U(0x0) +#else /* STM32MP_USE_STM32IMAGE */ +#define FIP_IMAGE_NAME "fip" +#endif /* STM32MP_USE_STM32IMAGE */ #define STM32MP_PRIMARY_CPU U(0x0) #define STM32MP_SECONDARY_CPU U(0x1) @@ -64,14 +68,26 @@ #define BL2_LIMIT (STM32MP_BL2_BASE + \ STM32MP_BL2_SIZE) +#define BL2_RO_BASE STM32MP_BL2_RO_BASE +#define BL2_RO_LIMIT (STM32MP_BL2_RO_BASE + \ + STM32MP_BL2_RO_SIZE) + +#define BL2_RW_BASE STM32MP_BL2_RW_BASE +#define BL2_RW_LIMIT (STM32MP_BL2_RW_BASE + \ + STM32MP_BL2_RW_SIZE) /******************************************************************************* * BL32 specific defines. ******************************************************************************/ -#ifndef AARCH32_SP_OPTEE +#if STM32MP_USE_STM32IMAGE || defined(IMAGE_BL32) +#if ENABLE_PIE +#define BL32_BASE 0 +#define BL32_LIMIT STM32MP_BL32_SIZE +#else #define BL32_BASE STM32MP_BL32_BASE #define BL32_LIMIT (STM32MP_BL32_BASE + \ STM32MP_BL32_SIZE) #endif +#endif /******************************************************************************* * BL33 specific defines. @@ -83,6 +99,12 @@ */ #define PLAT_STM32MP_NS_IMAGE_OFFSET BL33_BASE +/* Needed by STM32CubeProgrammer support */ +#define FLASHLAYOUT_BASE STM32MP_DDR_BASE +#define FLASHLAYOUT_SIZE 0x00100000 +#define DWL_BUFFER_BASE (STM32MP_DDR_BASE + 0x08000000) +#define DWL_BUFFER_SIZE 0x08000000 + /******************************************************************************* * DTB specific defines. ******************************************************************************/ @@ -113,6 +135,8 @@ */ #define ARM_IRQ_SEC_PHY_TIMER U(29) +#define ARM_IRQ_NON_SEC_SGI_0 U(0) + #define ARM_IRQ_SEC_SGI_0 U(8) #define ARM_IRQ_SEC_SGI_1 U(9) #define ARM_IRQ_SEC_SGI_2 U(10) @@ -122,7 +146,15 @@ #define ARM_IRQ_SEC_SGI_6 U(14) #define ARM_IRQ_SEC_SGI_7 U(15) +/* Platform IRQ Priority */ +#define STM32MP1_IRQ_RCC_SEC_PRIO U(0x6) +#define STM32MP_IRQ_SEC_SPI_PRIO U(0x10) + #define STM32MP1_IRQ_TZC400 U(36) +#define STM32MP1_IRQ_MCU_SEV U(176) +#define STM32MP1_IRQ_RCC_WAKEUP U(177) +#define STM32MP1_IRQ_IWDG1 U(182) +#define STM32MP1_IRQ_IWDG2 U(183) #define STM32MP1_IRQ_TAMPSERRS U(229) #define STM32MP1_IRQ_AXIERRIRQ U(244) diff --git a/plat/st/stm32mp1/include/stm32mp15_mbedtls_config.h b/plat/st/stm32mp1/include/stm32mp15_mbedtls_config.h new file mode 100644 index 0000000000..0023fecd94 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp15_mbedtls_config.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015-2020, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef MBEDTLS_CONFIG_H +#define MBEDTLS_CONFIG_H + +/* + * Key algorithms currently supported on mbed TLS libraries + */ +#define TF_MBEDTLS_USE_RSA 0 +#define TF_MBEDTLS_USE_ECDSA 1 + +/* + * Hash algorithms currently supported on mbed TLS libraries + */ +#define TF_MBEDTLS_SHA256 1 +#define TF_MBEDTLS_SHA384 2 +#define TF_MBEDTLS_SHA512 3 + +/* + * Configuration file to build mbed TLS with the required features for + * Trusted Boot + */ + +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS +/* Prevent mbed TLS from using snprintf so that it can use tf_snprintf. */ +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#define MBEDTLS_PKCS1_V21 + +#define MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION +#define MBEDTLS_X509_CHECK_KEY_USAGE +#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE + +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C + +#define MBEDTLS_BASE64_C +#define MBEDTLS_BIGNUM_C + +#define MBEDTLS_ERROR_C +#define MBEDTLS_MD_C + +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_OID_C + +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PK_WRITE_C + +#define MBEDTLS_PLATFORM_C + +#if TF_MBEDTLS_USE_ECDSA +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_NO_INTERNAL_RNG +#endif +#if TF_MBEDTLS_USE_RSA +#define MBEDTLS_RSA_C +#define MBEDTLS_X509_RSASSA_PSS_SUPPORT +#endif + +#define MBEDTLS_SHA256_C +#if (TF_MBEDTLS_HASH_ALG_ID != TF_MBEDTLS_SHA256) +#define MBEDTLS_SHA512_C +#endif + +#define MBEDTLS_VERSION_C + +#define MBEDTLS_X509_USE_C +#define MBEDTLS_X509_CRT_PARSE_C + +#if TF_MBEDTLS_USE_AES_GCM +#define MBEDTLS_AES_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_GCM_C +#endif + +/* MPI / BIGNUM options */ +#define MBEDTLS_MPI_WINDOW_SIZE 2 + +#if TF_MBEDTLS_USE_RSA +#if TF_MBEDTLS_KEY_SIZE <= 2048 +#define MBEDTLS_MPI_MAX_SIZE 256 +#else +#define MBEDTLS_MPI_MAX_SIZE 512 +#endif +#else +#define MBEDTLS_MPI_MAX_SIZE 256 +#endif + +/* Memory buffer allocator options */ +#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 8 + +/* + * Prevent the use of 128-bit division which + * creates dependency on external libraries. + */ +#define MBEDTLS_NO_UDBL_DIVISION + +#ifndef __ASSEMBLER__ +/* System headers required to build mbed TLS with the current configuration */ +#include +#include +#endif + +/* + * Mbed TLS heap size is smal as we only use the asn1 + * parsing functions + * digest, signature and crypto algorithm are done by + * other library. + */ + +#define TF_MBEDTLS_HEAP_SIZE U(5120) +#endif /* MBEDTLS_CONFIG_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_context.h b/plat/st/stm32mp1/include/stm32mp1_context.h index 698415af2c..2c035cdca4 100644 --- a/plat/st/stm32mp1/include/stm32mp1_context.h +++ b/plat/st/stm32mp1/include/stm32mp1_context.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,8 +7,33 @@ #ifndef STM32MP1_CONTEXT_H #define STM32MP1_CONTEXT_H +#include #include -int stm32_save_boot_interface(uint32_t interface, uint32_t instance); +#include + +#define DDR_CRC_GRANULE 32 + +void stm32_clean_context(void); +int stm32_save_context(uint32_t zq0cr0_zdata, + struct stm32_rtc_calendar *rtc_time, + unsigned long long stgen_cnt); +int stm32_restore_context(void); +unsigned long long stm32_get_stgen_from_context(void); +int stm32_restore_backup_reg(void); +void stm32_context_get_bl2_low_power_params(uintptr_t *bl2_code_base, + uintptr_t *bl2_code_end, + uintptr_t *bl2_end); +void stm32_context_save_bl2_param(void); +uint32_t stm32_get_zdata_from_context(void); +int stm32_get_pll1_settings_from_context(void); +bool stm32_are_pll1_settings_valid_in_context(void); +bool stm32_pm_context_is_valid(void); +void stm32_save_ddr_training_area(void); +void stm32_restore_ddr_training_area(void); +uint32_t stm32_pm_get_optee_ep(void); + +void stm32mp1_pm_save_clock_cfg(size_t offset, uint8_t *data, size_t size); +void stm32mp1_pm_restore_clock_cfg(size_t offset, uint8_t *data, size_t size); #endif /* STM32MP1_CONTEXT_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_critic_power.h b/plat/st/stm32mp1/include/stm32mp1_critic_power.h new file mode 100644 index 0000000000..cd7099c444 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_critic_power.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_CRITIC_POWER_H +#define STM32MP1_CRITIC_POWER_H + + /* Only BL32 compilation unit need stm32_pwr_down_wfi + * function/variable symbol + */ +#if defined(IMAGE_BL32) + #if STM32MP_SP_MIN_IN_DDR +extern void (*stm32_pwr_down_wfi)(bool is_cstop); + #else +extern void stm32_pwr_down_wfi(bool is_cstop); + #endif +#endif +extern void stm32_pwr_down_wfi_wrapper(bool is_cstop); + +#endif /* STM32MP1_CRITIC_POWER_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_low_power.h b/plat/st/stm32mp1/include/stm32mp1_low_power.h new file mode 100644 index 0000000000..79ae3bfc92 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_low_power.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_LOW_POWER_H +#define STM32MP1_LOW_POWER_H + +#include +#include + +#include + +void stm32_rcc_wakeup_update(bool state); +void stm32_apply_pmic_suspend_config(uint32_t mode); +bool stm32_is_cstop_done(void); +void stm32_exit_cstop(void); +void stm32_enter_low_power(uint32_t mode, uint32_t nsec_addr); +void stm32_auto_stop(void); +void stm32_init_low_power(void); + +#endif /* STM32MP1_LOW_POWER_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_power_config.h b/plat/st/stm32mp1/include/stm32mp1_power_config.h new file mode 100644 index 0000000000..37312c8de6 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_power_config.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_POWER_CONFIG_H +#define STM32MP1_POWER_CONFIG_H + +#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 +}; + +void stm32mp1_init_lp_states(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); +bool stm32mp1_get_retram_enabled(void); + +#endif /* STM32MP1_POWER_CONFIG_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h index b6cb91efa0..ebf9e52bc9 100644 --- a/plat/st/stm32mp1/include/stm32mp1_private.h +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -9,19 +9,46 @@ #include +#include + +enum boot_device_e { + BOOT_DEVICE_USB, + BOOT_DEVICE_BOARD +}; + void configure_mmu(void); +void stm32mp_mask_timer(void); +void __dead2 stm32mp_wait_cpu_reset(void); + void stm32mp1_arch_security_setup(void); void stm32mp1_security_setup(void); -void stm32mp1_gic_pcpu_init(void); -void stm32mp1_gic_init(void); +enum boot_device_e get_boot_device(void); + +bool stm32mp1_addr_inside_backupsram(uintptr_t addr); +bool stm32mp1_is_wakeup_from_standby(void); + +int stm32_save_boot_interface(uint32_t interface, uint32_t instance); +int stm32_get_boot_interface(uint32_t *interface, uint32_t *instance); +bool stm32_boot_is_serial(void); + +enum etzpc_decprot_attributes stm32mp_etzpc_binding2decprot(uint32_t mode); void stm32mp1_syscfg_init(void); -void stm32mp1_syscfg_enable_io_compensation(void); +void stm32mp1_syscfg_enable_io_compensation_start(void); +void stm32mp1_syscfg_enable_io_compensation_finish(void); void stm32mp1_syscfg_disable_io_compensation(void); +#if STM32MP_USE_STM32IMAGE uint32_t stm32mp_get_ddr_ns_size(void); +#endif void stm32mp1_init_scmi_server(void); +void stm32mp1_pm_save_scmi_state(uint8_t *state, size_t size); +void stm32mp1_pm_restore_scmi_state(uint8_t *state, size_t size); + +#if defined(IMAGE_BL32) && DEBUG +void stm32mp_dump_core_registers(bool fcore); +#endif #endif /* STM32MP1_PRIVATE_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_shared_resources.h b/plat/st/stm32mp1/include/stm32mp1_shared_resources.h index 3f6367ebef..519a67bad8 100644 --- a/plat/st/stm32mp1/include/stm32mp1_shared_resources.h +++ b/plat/st/stm32mp1/include/stm32mp1_shared_resources.h @@ -7,6 +7,8 @@ #ifndef STM32MP1_SHARED_RESOURCES_H #define STM32MP1_SHARED_RESOURCES_H +#include + #include #define STM32MP1_SHRES_GPIOZ(i) (STM32MP1_SHRES_GPIOZ_0 + (i)) @@ -35,4 +37,19 @@ enum stm32mp_shres { STM32MP1_SHRES_COUNT }; + +#ifdef STM32MP_SHARED_RESOURCES +/* + * Register a (non-)secure peripheral based on the ETZPC DECPROT configuration + */ +void stm32mp1_register_etzpc_decprot(unsigned int id, + enum etzpc_decprot_attributes attr); +#else +static inline +void stm32mp1_register_etzpc_decprot(unsigned int id, + enum etzpc_decprot_attributes attr) +{ +} +#endif + #endif /* STM32MP1_SHARED_RESOURCES_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_smc.h b/plat/st/stm32mp1/include/stm32mp1_smc.h index 57240bcaf3..62210a4dd9 100644 --- a/plat/st/stm32mp1/include/stm32mp1_smc.h +++ b/plat/st/stm32mp1/include/stm32mp1_smc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +17,39 @@ /* Secure Service access from Non-secure */ +/* + * SMC function STM32_SMC_RCC. + * + * Argument a0: (input) SMCC ID. + * (output) Status return code. + * Argument a1: (input) Service ID (STM32_SMC_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_SMC_RCC 0x82001000 + +/* + * SMC function STM32_SMC_PWR. + * + * Argument a0: (input) SMCC ID. + * (output) Status return code. + * Argument a1: (input) Service ID (STM32_SMC_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_SMC_PWR 0x82001001 + +/* + * SMC functions STM32_SMC_RCC_CAL. + * + * Argument a0: (input) SMCC ID. + * (output) Status return code. + * Argument a1: (input) Clock ID (from DT clock bindings). + */ +#define STM32_SMC_RCC_CAL 0x82001002 + /* * STM32_SMC_BSEC call API * @@ -29,6 +62,37 @@ */ #define STM32_SMC_BSEC 0x82001003 +/* Low Power services */ + +/* + * SIP function STM32_SMC_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 enable target domain. + */ +#define STM32_SMC_PD_DOMAIN 0x82001008 + +/* + * SIP function STM32_SMC_RCC_OPP. + * + * Argument a0: (input) SMCC ID. + * (output) Status return code. + * Argument a1: (input) Service ID (STM32_SMC_RCC_OPP_xxx). + * (output) Rounded frequency, if applicable. + * Argument a2: (input) Requested frequency. + */ +#define STM32_SMC_RCC_OPP 0x82001009 + +/* + * SIP function STM32_SMC_AUTO_STOP - CPU auto stop for OS driver suspend + * + * Argument a0: (input) This SMCC ID: STM32_SMC_AUTO_STOP + * (output) Status return code. + */ +#define STM32_SMC_AUTO_STOP 0x8200100a + /* * STM32_SIP_SMC_SCMI_AGENT0 * STM32_SIP_SMC_SCMI_AGENT1 @@ -50,12 +114,31 @@ #define STM32_SIP_SVC_VERSION_MINOR 0x1 /* Number of STM32 SiP Calls implemented */ -#define STM32_COMMON_SIP_NUM_CALLS 3 +#define STM32_COMMON_SIP_NUM_CALLS 9 + +/* Service ID for STM32_SMC_RCC/_PWR */ +#define STM32_SMC_REG_READ 0x0 +#define STM32_SMC_REG_WRITE 0x1 +#define STM32_SMC_REG_SET 0x2 +#define STM32_SMC_REG_CLEAR 0x3 /* Service for BSEC */ #define STM32_SMC_READ_SHADOW 0x01 #define STM32_SMC_PROG_OTP 0x02 #define STM32_SMC_WRITE_SHADOW 0x03 #define STM32_SMC_READ_OTP 0x04 +#define STM32_SMC_READ_ALL 0x05 +#define STM32_SMC_WRITE_ALL 0x06 +#define STM32_SMC_WRLOCK_OTP 0x07 + +/* SMC error codes */ +#define STM32_SMC_OK 0x00000000U +#define STM32_SMC_NOT_SUPPORTED 0xFFFFFFFFU +#define STM32_SMC_FAILED 0xFFFFFFFEU +#define STM32_SMC_INVALID_PARAMS 0xFFFFFFFDU + +/* Service ID for STM32_SMC_RCC_OPP */ +#define STM32_SMC_RCC_OPP_SET 0x0 +#define STM32_SMC_RCC_OPP_ROUND 0x1 #endif /* STM32MP1_SMC_H */ diff --git a/plat/st/stm32mp1/plat_bl2_mem_params_desc.c b/plat/st/stm32mp1/plat_bl2_mem_params_desc.c index 1d407bb72b..d322da0090 100644 --- a/plat/st/stm32mp1/plat_bl2_mem_params_desc.c +++ b/plat/st/stm32mp1/plat_bl2_mem_params_desc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,6 +19,22 @@ * the next executable image id. ******************************************************************************/ static bl_mem_params_node_t bl2_mem_params_descs[] = { + /* Fill FW_CONFIG related information if it exists */ + { + .image_id = FW_CONFIG_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + IMAGE_ATTRIB_PLAT_SETUP), + + .image_info.image_base = STM32MP_FW_CONFIG_BASE, + .image_info.image_max_size = STM32MP_FW_CONFIG_MAX_SIZE, + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + /* Fill BL32 related information */ { .image_id = BL32_IMAGE_ID, @@ -27,28 +43,17 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { VERSION_2, entry_point_info_t, SECURE | EXECUTABLE | EP_FIRST_EXE), -#if !defined(AARCH32_SP_OPTEE) - .ep_info.pc = BL32_BASE, -#endif .ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, SPSR_E_LITTLE, DISABLE_ALL_EXCEPTIONS), SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t, - IMAGE_ATTRIB_PLAT_SETUP), -#if defined(AARCH32_SP_OPTEE) - /* optee header is loaded in SYSRAM above BL2 */ - .image_info.image_base = STM32MP_OPTEE_BASE, - .image_info.image_max_size = STM32MP_OPTEE_SIZE, -#else - .image_info.image_base = BL32_BASE, - .image_info.image_max_size = BL32_LIMIT - BL32_BASE, -#endif + IMAGE_ATTRIB_SKIP_LOADING), + .next_handoff_image_id = BL33_IMAGE_ID, }, -#if defined(AARCH32_SP_OPTEE) /* Fill BL32 external 1 image related information */ { .image_id = BL32_EXTRA1_IMAGE_ID, @@ -77,7 +82,31 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { .next_handoff_image_id = INVALID_IMAGE_ID, }, -#endif /* AARCH32_SP_OPTEE */ + + /* Fill HW_CONFIG related information if it exists */ + { + .image_id = HW_CONFIG_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + NON_SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + { + .image_id = TOS_FW_CONFIG_ID, + SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, /* Fill BL33 related information */ { @@ -87,17 +116,17 @@ static bl_mem_params_node_t bl2_mem_params_descs[] = { VERSION_2, entry_point_info_t, NON_SECURE | EXECUTABLE), - .ep_info.pc = PLAT_STM32MP_NS_IMAGE_OFFSET, +#if BL33_HYP + .ep_info.spsr = SPSR_MODE32(MODE32_hyp, SPSR_T_ARM, +#else .ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, +#endif SPSR_E_LITTLE, DISABLE_ALL_EXCEPTIONS), SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, - VERSION_2, image_info_t, 0), - - .image_info.image_base = PLAT_STM32MP_NS_IMAGE_OFFSET, - .image_info.image_max_size = STM32MP_DDR_MAX_SIZE - - (PLAT_STM32MP_NS_IMAGE_OFFSET - STM32MP_DDR_BASE), + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), .next_handoff_image_id = INVALID_IMAGE_ID, } diff --git a/plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c b/plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c new file mode 100644 index 0000000000..2d1332ee89 --- /dev/null +++ b/plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include + +/******************************************************************************* + * Following descriptor provides BL image/ep information that gets used + * by BL2 to load the images and also subset of this information is + * passed to next BL image. The image loading sequence is managed by + * populating the images in required loading order. The image execution + * sequence is managed by populating the `next_handoff_image_id` with + * the next executable image id. + ******************************************************************************/ +static bl_mem_params_node_t bl2_mem_params_descs[] = { + /* Fill BL32 related information */ + { + .image_id = BL32_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | EXECUTABLE | EP_FIRST_EXE), + +#if !defined(AARCH32_SP_OPTEE) + .ep_info.pc = STM32MP_BL32_BASE, +#endif + .ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, + SPSR_E_LITTLE, + DISABLE_ALL_EXCEPTIONS), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_PLAT_SETUP), +#if defined(AARCH32_SP_OPTEE) + /* optee header is loaded in SYSRAM above BL2 */ + .image_info.image_base = STM32MP_OPTEE_BASE, + .image_info.image_max_size = STM32MP_OPTEE_SIZE, +#else + .image_info.image_base = STM32MP_BL32_BASE, + .image_info.image_max_size = STM32MP_BL32_BIN_SIZE, +#endif + .next_handoff_image_id = BL33_IMAGE_ID, + }, + + /* Fill BL32 external 1 image related information */ + { + .image_id = BL32_EXTRA1_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + /* Fill BL32 external 2 image related information */ + { + .image_id = BL32_EXTRA2_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | NON_EXECUTABLE), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_SKIP_LOADING), + + .next_handoff_image_id = INVALID_IMAGE_ID, + }, + + /* Fill BL33 related information */ + { + .image_id = BL33_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + NON_SECURE | EXECUTABLE), + + .ep_info.pc = PLAT_STM32MP_NS_IMAGE_OFFSET, + +#if BL33_HYP + .ep_info.spsr = SPSR_MODE32(MODE32_hyp, SPSR_T_ARM, +#else + .ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, +#endif + SPSR_E_LITTLE, + DISABLE_ALL_EXCEPTIONS), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + 0), + + .image_info.image_base = PLAT_STM32MP_NS_IMAGE_OFFSET, + .image_info.image_max_size = STM32MP_DDR_MAX_SIZE - + (PLAT_STM32MP_NS_IMAGE_OFFSET - STM32MP_DDR_BASE), + + .next_handoff_image_id = INVALID_IMAGE_ID, + } +}; + +REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) diff --git a/plat/st/stm32mp1/plat_image_load.c b/plat/st/stm32mp1/plat_image_load.c index 6d7af741a0..c6a30de5bb 100644 --- a/plat/st/stm32mp1/plat_image_load.c +++ b/plat/st/stm32mp1/plat_image_load.c @@ -6,7 +6,10 @@ #include +#include #include +#include +#include #include /******************************************************************************* @@ -23,12 +26,38 @@ void plat_flush_next_bl_params(void) ******************************************************************************/ bl_load_info_t *plat_get_bl_image_load_info(void) { +#if STM32MP_USE_STM32IMAGE bl_mem_params_node_t *bl33 = get_bl_mem_params_node(BL33_IMAGE_ID); uint32_t ddr_ns_size = stm32mp_get_ddr_ns_size(); + /* + * If going back from CSTANDBY / STANDBY and DDR was in Self-Refresh, + * BL33 must not be loaded as it would overwrite the code already + * in DDR. For this, the BL33 part of the bl_mem_params_desc_ptr + * struct should be modified to skip its loading + */ + if (stm32mp1_is_wakeup_from_standby()) { + bl_mem_params_node_t *bl32; + + bl33->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING; + bl32 = get_bl_mem_params_node(BL32_IMAGE_ID); + bl32->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING; +#if defined(AARCH32_SP_OPTEE) + bl32->ep_info.pc = stm32_pm_get_optee_ep(); + + if (stm32mp1_addr_inside_backupsram(bl32->ep_info.pc)) { + clk_enable(BKPSRAM); + } +#else + /* Set ep_info PC to 0, to inform BL32 it is a reset after STANDBY */ + bl33->ep_info.pc = 0; +#endif + } + /* Max size is non-secure DDR end address minus image_base */ bl33->image_info.image_max_size = STM32MP_DDR_BASE + ddr_ns_size - bl33->image_info.image_base; +#endif /* STM32MP_USE_STM32IMAGE */ return get_bl_load_info_from_mem_params_desc(); } @@ -38,5 +67,9 @@ bl_load_info_t *plat_get_bl_image_load_info(void) ******************************************************************************/ bl_params_t *plat_get_next_bl_params(void) { - return get_next_bl_params_from_mem_params_desc(); + bl_params_t *bl_params = get_next_bl_params_from_mem_params_desc(); + + populate_next_bl_params_config(bl_params); + + return bl_params; } diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 359581925a..067e1fff9f 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -9,26 +9,68 @@ ARM_WITH_NEON := yes BL2_AT_EL3 := 1 USE_COHERENT_MEM := 0 +# Allow TF-A to concatenate BL2 & BL32 binaries in a single file, +# share DTB file between BL2 and BL32 +# If it is set to 0, then FIP and FCONF are used +STM32MP_USE_STM32IMAGE ?= 0 + +# Add specific ST version +ST_VERSION := r1.0 +ifeq ($(STM32MP_USE_STM32IMAGE),1) +ST_VERSION := ${ST_VERSION}-nofip +endif +VERSION_STRING := v${VERSION_MAJOR}.${VERSION_MINOR}-${ST_VERSION}(${BUILD_TYPE}):${BUILD_STRING} + +ifneq ($(STM32MP_USE_STM32IMAGE),1) +ENABLE_PIE := 1 +endif +TRUSTED_BOARD_BOOT ?= 0 +STM32MP_USE_EXTERNAL_HEAP ?= 0 + +# Please don't increment this value without good understanding of +# the monotonic counter STM32_TF_VERSION ?= 0 # Enable dynamic memory mapping PLAT_XLAT_TABLES_DYNAMIC := 1 +ifeq ($(STM32MP_USE_STM32IMAGE),1) +BL2_IN_XIP_MEM := 0 +else +BL2_IN_XIP_MEM := 1 +endif + +# DDR controller with dual AXI port +STM32MP_DDR_DUAL_AXI_PORT:= 1 + +# STM32 image header version v1.0 +STM32_HEADER_VERSION_MAJOR:= 1 +STM32_HEADER_VERSION_MINOR:= 0 + +# STM32 Secure Secret Provisioning mode (SSP) +STM32MP_SSP ?= 0 + ifeq ($(AARCH32_SP),sp_min) # Disable Neon support: sp_min runtime may conflict with non-secure world TF_CFLAGS += -mfloat-abi=soft endif +TF_CFLAGS += -Wsign-compare + # Not needed for Cortex-A7 WORKAROUND_CVE_2017_5715:= 0 +AARCH32_EXCEPTION_DEBUG := 1 + # Number of TF-A copies in the device STM32_TF_A_COPIES := 2 STM32_BL33_PARTS_NUM := 1 ifeq ($(AARCH32_SP),optee) STM32_RUNTIME_PARTS_NUM := 3 -else +else ifeq ($(STM32MP_USE_STM32IMAGE),1) STM32_RUNTIME_PARTS_NUM := 0 +else +STM32_RUNTIME_PARTS_NUM := 1 endif PLAT_PARTITION_MAX_ENTRIES := $(shell echo $$(($(STM32_TF_A_COPIES) + \ $(STM32_BL33_PARTS_NUM) + \ @@ -41,14 +83,31 @@ STM32MP_RAW_NAND ?= 0 STM32MP_SPI_NAND ?= 0 STM32MP_SPI_NOR ?= 0 -ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND} \ - ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) -$(error "No boot device driver is enabled") -endif +# Serial boot devices +STM32MP_USB_PROGRAMMER ?= 0 +STM32MP_UART_PROGRAMMER ?= 0 + +# Hypervisor mode +BL33_HYP ?= 0 # Device tree DTB_FILE_NAME ?= stm32mp157c-ev1.dtb +ifeq ($(STM32MP_USE_STM32IMAGE),1) +ifeq ($(AARCH32_SP),optee) +BL2_DTSI := stm32mp15-bl2.dtsi +FDT_SOURCES := $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl2.dts,$(DTB_FILE_NAME))) +else FDT_SOURCES := $(addprefix fdts/, $(patsubst %.dtb,%.dts,$(DTB_FILE_NAME))) +endif +else +BL2_DTSI := stm32mp15-bl2.dtsi +FDT_SOURCES := $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl2.dts,$(DTB_FILE_NAME))) +ifeq ($(AARCH32_SP),sp_min) +BL32_DTSI := stm32mp15-bl32.dtsi +FDT_SOURCES += $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl32.dts,$(DTB_FILE_NAME))) +endif +endif +DTC_CPPFLAGS += ${INCLUDES} DTC_FLAGS += -Wno-unit_address_vs_reg # Macros and rules to build TF binary @@ -66,6 +125,39 @@ endif # Variables for use with stm32image STM32IMAGEPATH ?= tools/stm32image STM32IMAGE ?= ${STM32IMAGEPATH}/stm32image${BIN_EXT} +STM32IMAGE_SRC := ${STM32IMAGEPATH}/stm32image.c + +ifneq (${STM32MP_USE_STM32IMAGE},1) +FIP_DEPS += dtbs +STM32MP_NT_FW_CONFIG := ${BL33_CFG} +STM32MP_FW_CONFIG_NAME := $(patsubst %.dtb,%-fw-config.dtb,$(DTB_FILE_NAME)) +STM32MP_FW_CONFIG := ${BUILD_PLAT}/fdts/$(STM32MP_FW_CONFIG_NAME) +ifneq (${AARCH32_SP},none) +FDT_SOURCES += $(addprefix fdts/, $(patsubst %.dtb,%.dts,$(STM32MP_FW_CONFIG_NAME))) +endif +# Add the FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${STM32MP_FW_CONFIG},--fw-config)) +# Add the NT_FW_CONFIG to FIP and specify the same to certtool +$(eval $(call TOOL_ADD_PAYLOAD,${STM32MP_NT_FW_CONFIG},--hw-config)) +ifeq (${GENERATE_COT},1) +$(eval $(call TOOL_ADD_PAYLOAD,${BUILD_PLAT}/tb_fw.crt,--tb-fw-cert)) +endif +$(eval $(call CERT_ADD_CMD_OPT,${BUILD_PLAT}/bl2.bin,--tb-fw)) +CRT_DEPS+=${BUILD_PLAT}/bl2.bin +ifeq ($(AARCH32_SP),sp_min) +STM32MP_TOS_FW_CONFIG := $(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl32.dtb,$(DTB_FILE_NAME))) +$(eval $(call TOOL_ADD_PAYLOAD,${STM32MP_TOS_FW_CONFIG},--tos-fw-config)) +else +# Add the build options to pack Trusted OS Extra1 and Trusted OS Extra2 images +# in the FIP if the platform requires. +ifneq ($(BL32_EXTRA1),) +$(eval $(call TOOL_ADD_IMG,BL32_EXTRA1,--tos-fw-extra1)) +endif +ifneq ($(BL32_EXTRA2),) +$(eval $(call TOOL_ADD_IMG,BL32_EXTRA2,--tos-fw-extra2)) +endif +endif +endif # Enable flags for C files $(eval $(call assert_booleans,\ @@ -76,12 +168,19 @@ $(eval $(call assert_booleans,\ STM32MP_SPI_NAND \ STM32MP_SPI_NOR \ PLAT_XLAT_TABLES_DYNAMIC \ + STM32MP_UART_PROGRAMMER \ + STM32MP_USB_PROGRAMMER \ + STM32MP_USE_STM32IMAGE \ + STM32MP_DDR_DUAL_AXI_PORT \ + STM32MP_SSP \ + BL33_HYP \ ))) $(eval $(call assert_numerics,\ $(sort \ STM32_TF_A_COPIES \ PLAT_PARTITION_MAX_ENTRIES \ + STM32_TF_VERSION \ ))) $(eval $(call add_defines,\ @@ -94,6 +193,13 @@ $(eval $(call add_defines,\ PLAT_XLAT_TABLES_DYNAMIC \ STM32_TF_A_COPIES \ PLAT_PARTITION_MAX_ENTRIES \ + STM32MP_UART_PROGRAMMER \ + STM32MP_USB_PROGRAMMER \ + STM32_TF_VERSION \ + STM32MP_USE_STM32IMAGE \ + STM32MP_DDR_DUAL_AXI_PORT \ + STM32MP_SSP \ + BL33_HYP \ ))) # Include paths and source files @@ -118,9 +224,10 @@ PLAT_BL_COMMON_SOURCES += ${XLAT_TABLES_LIB_SRCS} PLAT_BL_COMMON_SOURCES += lib/cpus/aarch32/cortex_a7.S PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ + drivers/clk/clk.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ - drivers/st/bsec/bsec.c \ + drivers/st/bsec/bsec2.c \ drivers/st/clk/stm32mp_clkfunc.c \ drivers/st/clk/stm32mp1_clk.c \ drivers/st/ddr/stm32mp1_ddr_helpers.c \ @@ -129,24 +236,73 @@ PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ drivers/st/iwdg/stm32_iwdg.c \ drivers/st/pmic/stm32mp_pmic.c \ drivers/st/pmic/stpmic1.c \ + drivers/st/regulator/stm32mp_dummy_regulator.c \ + drivers/st/regulator/stm32mp_regulator.c \ drivers/st/reset/stm32mp1_reset.c \ plat/st/common/stm32mp_dt.c \ + plat/st/common/stm32mp_shres_helpers.c \ plat/st/stm32mp1/stm32mp1_context.c \ plat/st/stm32mp1/stm32mp1_dbgmcu.c \ plat/st/stm32mp1/stm32mp1_helper.S \ - plat/st/stm32mp1/stm32mp1_security.c \ plat/st/stm32mp1/stm32mp1_syscfg.c +ifneq (${STM32MP_USE_STM32IMAGE},1) +BL2_SOURCES += drivers/io/io_fip.c \ + plat/st/common/bl2_io_storage.c \ + plat/st/stm32mp1/plat_bl2_mem_params_desc.c + +BL2_SOURCES += lib/fconf/fconf.c \ + lib/fconf/fconf_dyn_cfg_getter.c \ + plat/st/common/stm32mp_fconf_io.c \ + plat/st/stm32mp1/stm32mp1_fconf_firewall.c +else +BL2_SOURCES += drivers/io/io_dummy.c \ + drivers/st/io/io_stm32image.c \ + plat/st/common/bl2_stm32_io_storage.c \ + plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c \ + plat/st/stm32mp1/stm32mp1_security.c +endif + BL2_SOURCES += drivers/io/io_block.c \ - drivers/io/io_dummy.c \ drivers/io/io_mtd.c \ drivers/io/io_storage.c \ drivers/st/crypto/stm32_hash.c \ - drivers/st/io/io_stm32image.c \ - plat/st/common/stm32mp_auth.c \ - plat/st/common/bl2_io_storage.c \ plat/st/stm32mp1/bl2_plat_setup.c +ifeq (${TRUSTED_BOARD_BOOT},1) +AUTH_SOURCES := drivers/auth/auth_mod.c \ + drivers/auth/crypto_mod.c \ + drivers/auth/img_parser_mod.c + +ifneq (${STM32MP_USE_STM32IMAGE},1) +ifeq (${GENERATE_COT},1) +TFW_NVCTR_VAL := 0 +NTFW_NVCTR_VAL := 0 +KEY_SIZE := +KEY_ALG := ecdsa +HASH_ALG := sha256 +endif +TF_MBEDTLS_KEY_ALG := ecdsa +MBEDTLS_CONFIG_FILE ?= "" + +include drivers/auth/mbedtls/mbedtls_x509.mk + + +AUTH_SOURCES += drivers/auth/tbbr/tbbr_cot_common.c \ + lib/fconf/fconf_tbbr_getter.c \ + plat/st/common/stm32mp_crypto_lib.c + +BL2_SOURCES += drivers/auth/tbbr/tbbr_cot_bl2.c +else +AUTH_SOURCES += plat/st/common/stm32mp_cot.c \ + plat/st/common/stm32mp_crypto_lib.c \ + plat/st/common/stm32mp_img_parser_lib.c +endif + +BL2_SOURCES += $(AUTH_SOURCES) \ + plat/st/common/stm32mp_trusted_boot.c +endif + ifneq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC}),) BL2_SOURCES += drivers/mmc/mmc.c \ drivers/partition/gpt.c \ @@ -166,6 +322,10 @@ BL2_SOURCES += drivers/mtd/nand/spi_nand.c endif ifeq (${STM32MP_SPI_NOR},1) +ifneq (${STM32MP_FORCE_MTD_START_OFFSET},) +$(eval $(call add_define_val,STM32MP_NOR_FIP_OFFSET,${STM32MP_FORCE_MTD_START_OFFSET})) +$(eval $(call add_define_val,STM32MP_NOR_BASE_OFFSET,${STM32MP_FORCE_MTD_START_OFFSET})) +endif BL2_SOURCES += drivers/mtd/nor/spi_nor.c endif @@ -175,6 +335,10 @@ BL2_SOURCES += drivers/mtd/spi-mem/spi_mem.c \ endif ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND}),) +ifneq (${STM32MP_FORCE_MTD_START_OFFSET},) +$(eval $(call add_define_val,STM32MP_NAND_FIP_OFFSET,${STM32MP_FORCE_MTD_START_OFFSET})) +$(eval $(call add_define_val,STM32MP_NAND_BASE_OFFSET,${STM32MP_FORCE_MTD_START_OFFSET})) +endif BL2_SOURCES += drivers/mtd/nand/core.c endif @@ -182,25 +346,60 @@ ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) BL2_SOURCES += plat/st/stm32mp1/stm32mp1_boot_device.c endif +ifneq ($(filter 1,${STM32MP_UART_PROGRAMMER} ${STM32MP_USB_PROGRAMMER}),) +BL2_SOURCES += drivers/io/io_memmap.c +endif + +ifeq (${STM32MP_UART_PROGRAMMER},1) +BL2_SOURCES += drivers/st/uart/stm32_uart.c \ + plat/st/common/stm32cubeprogrammer_uart.c +endif + +ifeq (${STM32MP_USB_PROGRAMMER},1) +BL2_SOURCES += drivers/st/usb_dwc2/usb_dwc2.c \ + lib/usb/usb_core.c \ + lib/usb/usb_st_dfu.c \ + plat/st/common/stm32cubeprogrammer_usb.c \ + plat/st/stm32mp1/stm32mp1_usb.c +endif + BL2_SOURCES += drivers/st/ddr/stm32mp1_ddr.c \ drivers/st/ddr/stm32mp1_ram.c BL2_SOURCES += common/desc_image_load.c \ - plat/st/stm32mp1/plat_bl2_mem_params_desc.c \ plat/st/stm32mp1/plat_image_load.c -ifeq ($(AARCH32_SP),optee) BL2_SOURCES += lib/optee/optee_utils.c + +BL2_SOURCES += plat/st/stm32mp1/stm32mp1_critic_power.c +BL2_SOURCES += plat/st/stm32mp1/stm32mp1_critic_power_wrapper.S + +ifeq ($(STM32MP_SSP),1) +include plat/st/stm32mp1/stm32mp1_ssp.mk endif # Compilation rules -.PHONY: check_dtc_version stm32image clean_stm32image +.PHONY: check_dtc_version stm32image clean_stm32image check_boot_device .SUFFIXES: all: check_dtc_version stm32image ${STM32_TF_STM32} distclean realclean clean: clean_stm32image +bl2: check_boot_device + +check_boot_device: + @if [ ${STM32MP_EMMC} != 1 ] && \ + [ ${STM32MP_SDMMC} != 1 ] && \ + [ ${STM32MP_RAW_NAND} != 1 ] && \ + [ ${STM32MP_SPI_NAND} != 1 ] && \ + [ ${STM32MP_SPI_NOR} != 1 ] && \ + [ ${STM32MP_UART_PROGRAMMER} != 1 ] && \ + [ ${STM32MP_USB_PROGRAMMER} != 1 ]; then \ + echo "No boot device driver is enabled"; \ + false; \ + fi + stm32image: ${STM32IMAGE} ${STM32IMAGE}: ${STM32IMAGE_SRC} @@ -217,12 +416,35 @@ check_dtc_version: false; \ fi - +ifeq ($(STM32MP_USE_STM32IMAGE)$(AARCH32_SP),1sp_min) ${BUILD_PLAT}/stm32mp1-%.o: ${BUILD_PLAT}/fdts/%.dtb plat/st/stm32mp1/stm32mp1.S bl2 ${BL32_DEP} + @echo " AS stm32mp1.S" + ${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \ + -DDTB_BIN_PATH=\"$<\" \ + -c $(word 2,$^) -o $@ +else +# Create DTB file for BL2 +${BUILD_PLAT}/fdts/%-bl2.dts: fdts/%.dts fdts/${BL2_DTSI} | ${BUILD_PLAT} fdt_dirs + @echo '#include "$(patsubst fdts/%,%,$<)"' > $@ + @echo '#include "${BL2_DTSI}"' >> $@ + +${BUILD_PLAT}/fdts/%-bl2.dtb: ${BUILD_PLAT}/fdts/%-bl2.dts + +ifeq ($(AARCH32_SP),sp_min) +# Create DTB file for BL32 +${BUILD_PLAT}/fdts/%-bl32.dts: fdts/%.dts fdts/${BL32_DTSI} | ${BUILD_PLAT} fdt_dirs + @echo '#include "$(patsubst fdts/%,%,$<)"' > $@ + @echo '#include "${BL32_DTSI}"' >> $@ + +${BUILD_PLAT}/fdts/%-bl32.dtb: ${BUILD_PLAT}/fdts/%-bl32.dts +endif + +${BUILD_PLAT}/stm32mp1-%.o: ${BUILD_PLAT}/fdts/%-bl2.dtb plat/st/stm32mp1/stm32mp1.S bl2 @echo " AS stm32mp1.S" ${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \ -DDTB_BIN_PATH=\"$<\" \ -c plat/st/stm32mp1/stm32mp1.S -o $@ +endif $(eval $(call MAKE_LD,${STM32_TF_LINKERFILE},plat/st/stm32mp1/stm32mp1.ld.S,2)) @@ -243,5 +465,7 @@ tf-a-%.stm32: ${STM32IMAGE} tf-a-%.bin $(eval ENTRY = $(shell cat $(@:.stm32=.map) | grep "__BL2_IMAGE_START" | awk '{print $$1}')) ${Q}${STM32IMAGE} -s $(word 2,$^) -d $@ \ -l $(LOADADDR) -e ${ENTRY} \ - -v ${STM32_TF_VERSION} + -v ${STM32_TF_VERSION} \ + -m ${STM32_HEADER_VERSION_MAJOR} \ + -n ${STM32_HEADER_VERSION_MINOR} @echo diff --git a/plat/st/stm32mp1/services/bsec_svc.c b/plat/st/stm32mp1/services/bsec_svc.c index 2a60e43393..e3d845d2ec 100644 --- a/plat/st/stm32mp1/services/bsec_svc.c +++ b/plat/st/stm32mp1/services/bsec_svc.c @@ -1,23 +1,444 @@ /* - * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include + #include +#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "bsec_svc.h" +enum bsec_ssp_status { + BSEC_NO_SSP = 0, + BSEC_SSP_SET, + BSEC_SSP_ERROR +}; + +struct otp_exchange { + uint32_t version; + uint32_t configuration; + uint32_t reserved; + uint32_t status; + uint32_t general_lock; + uint32_t debug_conf; + uint32_t reserved1[2]; + uint32_t otp_disturb[3]; + uint32_t reserved2[3]; + uint32_t error_status[3]; + uint32_t reserved3[3]; + uint32_t permanent_lock[3]; + uint32_t reserved4[3]; + uint32_t programming_lock[3]; + uint32_t reserved5[3]; + uint32_t shadow_write_lock[3]; + uint32_t reserved6[3]; + uint32_t shadow_read_lock[3]; + uint32_t reserved7[3]; + uint32_t otp_value[STM32MP1_OTP_MAX_ID + 1]; + uint32_t reserved8[112]; + uint32_t bsec_hw_conf; + uint32_t ip_version; + uint32_t ip_id; + uint32_t ip_magic_id; +}; + +static enum bsec_ssp_status bsec_check_ssp(uint32_t otp, uint32_t update) +{ + boot_api_context_t *boot_context = + (boot_api_context_t *)BOOT_PARAM_ADDR; + + /* No SSP update or SSP already done*/ + if ((((otp & SSP_OTP_MASK) == 0U) && ((update & SSP_OTP_MASK) == 0U)) || + (((otp & SSP_OTP_MASK) == SSP_OTP_MASK) && + ((update & SSP_OTP_MASK) == SSP_OTP_MASK))) { + return BSEC_NO_SSP; + } + + /* SSP update */ + if ((update & SSP_OTP_MASK) != 0U) { + if ((update & SSP_OTP_SUCCESS) != 0U) { + return BSEC_SSP_ERROR; + } + + /* SSP boot process */ + boot_context->p_ssp_config->ssp_cmd = + BOOT_API_CTX_SSP_CMD_CALC_CHIP_PUBK; +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)boot_context->p_ssp_config, + sizeof(boot_api_ssp_config_t)); +#endif + if (dt_pmic_status() > 0) { + const char *name; + + initialize_pmic(); + + name = stm32mp_get_cpu_supply_name(); + if (name == NULL) { + return BSEC_SSP_ERROR; + } + + stpmic1_regulator_mask_reset_set(name); + } + + return BSEC_SSP_SET; + } + return BSEC_NO_SSP; +} + +static uint32_t bsec_read_all_bsec(struct otp_exchange *exchange) +{ + uint32_t i; + uint32_t result; + + if (exchange == NULL) { + return BSEC_ERROR; + } + + exchange->version = BSEC_SERVICE_VERSION; + + for (i = 0U; i <= STM32MP1_OTP_MAX_ID; i++) { + if (bsec_check_nsec_access_rights(i) == BSEC_OK) { + result = bsec_shadow_register(i); + if (result != BSEC_OK) { + return result; + } + + result = bsec_read_otp(&exchange->otp_value[i], i); + if (result != BSEC_OK) { + return result; + } + } + } + + exchange->configuration = mmio_read_32(bsec_get_base() + + BSEC_OTP_CONF_OFF); + + exchange->status = mmio_read_32(bsec_get_base() + BSEC_OTP_STATUS_OFF); + + exchange->general_lock = mmio_read_32(bsec_get_base() + + BSEC_OTP_LOCK_OFF); + + exchange->debug_conf = mmio_read_32(bsec_get_base() + BSEC_DEN_OFF); + + exchange->otp_disturb[0] = mmio_read_32(bsec_get_base() + + BSEC_DISTURBED_OFF); + + exchange->otp_disturb[1] = mmio_read_32(bsec_get_base() + + BSEC_DISTURBED1_OFF); + + exchange->otp_disturb[2] = mmio_read_32(bsec_get_base() + + BSEC_DISTURBED2_OFF); + + exchange->error_status[0] = mmio_read_32(bsec_get_base() + + BSEC_ERROR_OFF); + + exchange->error_status[1] = mmio_read_32(bsec_get_base() + + BSEC_ERROR1_OFF); + + exchange->error_status[2] = mmio_read_32(bsec_get_base() + + BSEC_ERROR2_OFF); + + exchange->permanent_lock[0] = mmio_read_32(bsec_get_base() + + BSEC_WRLOCK_OFF); + + exchange->permanent_lock[1] = mmio_read_32(bsec_get_base() + + BSEC_WRLOCK1_OFF); + + exchange->permanent_lock[2] = mmio_read_32(bsec_get_base() + + BSEC_WRLOCK2_OFF); + + exchange->programming_lock[0] = mmio_read_32(bsec_get_base() + + BSEC_SPLOCK_OFF); + + exchange->programming_lock[1] = mmio_read_32(bsec_get_base() + + BSEC_SPLOCK1_OFF); + + exchange->programming_lock[2] = mmio_read_32(bsec_get_base() + + BSEC_SPLOCK2_OFF); + + exchange->shadow_write_lock[0] = mmio_read_32(bsec_get_base() + + BSEC_SWLOCK_OFF); + + exchange->shadow_write_lock[1] = mmio_read_32(bsec_get_base() + + BSEC_SWLOCK1_OFF); + + exchange->shadow_write_lock[2] = mmio_read_32(bsec_get_base() + + BSEC_SWLOCK2_OFF); + + exchange->shadow_read_lock[0] = mmio_read_32(bsec_get_base() + + BSEC_SRLOCK_OFF); + + exchange->shadow_read_lock[1] = mmio_read_32(bsec_get_base() + + BSEC_SRLOCK1_OFF); + + exchange->shadow_read_lock[2] = mmio_read_32(bsec_get_base() + + BSEC_SRLOCK2_OFF); + + exchange->bsec_hw_conf = mmio_read_32(bsec_get_base() + + BSEC_IPHW_CFG_OFF); + + exchange->ip_version = mmio_read_32(bsec_get_base() + BSEC_IPVR_OFF); + + exchange->ip_id = mmio_read_32(bsec_get_base() + BSEC_IP_ID_OFF); + + exchange->ip_magic_id = mmio_read_32(bsec_get_base() + + BSEC_IP_MAGIC_ID_OFF); + + return BSEC_OK; +} + +static uint32_t bsec_write_all_bsec(struct otp_exchange *exchange, + uint32_t *ret_otp_value) +{ + uint32_t i; + uint32_t j; + uint32_t start_otp = 0U; + uint32_t value = 0U; + uint32_t ret; + struct bsec_config config_param; + + *ret_otp_value = 0U; + + if (exchange == NULL) { + return BSEC_ERROR; + } + + if (exchange->version != BSEC_SERVICE_VERSION) { + return BSEC_ERROR; + } + + for (i = start_otp; i <= STM32MP1_OTP_MAX_ID; i++) { + if (bsec_check_nsec_access_rights(i) != BSEC_OK) { + continue; + } + + ret = bsec_shadow_register(i); + if (ret != BSEC_OK) { + return ret; + } + + ret = bsec_read_otp(&value, i); + if (ret != BSEC_OK) { + return ret; + } + + if ((value == exchange->otp_value[i]) && + (i != BOOT_API_OTP_SSP_WORD_NB)) { + continue; + } + + if (i == BOOT_API_OTP_SSP_WORD_NB) { + *ret_otp_value = (uint32_t)bsec_check_ssp(value, + exchange->otp_value[i]); + VERBOSE("Result OTP SSP %d\n", *ret_otp_value); + if (*ret_otp_value == (uint32_t)BSEC_SSP_ERROR) { + continue; + } + } + + ret = bsec_program_otp(exchange->otp_value[i], i); + if (ret != BSEC_OK) { + return ret; + } + + ret = bsec_write_otp(exchange->otp_value[i], i); + if (ret != BSEC_OK) { + return ret; + } + } + + bsec_write_debug_conf(exchange->debug_conf); + + for (j = 0U; j < 3U; j++) { + if (exchange->permanent_lock[j] == 0U) { + continue; + } + + for (i = 0U; i < 32U; i++) { + if (bsec_check_nsec_access_rights((32U * j) + i) != + BSEC_OK) { + continue; + } + + value = (exchange->permanent_lock[j] >> i) & 1U; + if (value != 0U) { + ret = bsec_permanent_lock_otp((32U * j) + i); + if (ret != BSEC_OK) { + return ret; + } + } + } + } + + for (j = 0U; j < 3U; j++) { + if (exchange->programming_lock[j] == 0U) { + continue; + } + + for (i = 0U; i < 32U; i++) { + if (bsec_check_nsec_access_rights((32U * j) + i) != + BSEC_OK) { + continue; + } + + value = (exchange->programming_lock[j] >> i) & 1U; + if (value != 0U) { + if (bsec_set_sp_lock((32U * j) + i) != + BSEC_OK) { + return BSEC_ERROR; + } + } + } + } + + for (j = 0U; j < 3U; j++) { + if (exchange->shadow_write_lock[j] == 0U) { + continue; + } + + for (i = 0U; i < 32U; i++) { + if (bsec_check_nsec_access_rights((32U * j) + i) != + BSEC_OK) { + continue; + } + + value = (exchange->shadow_write_lock[j] >> i) & 1U; + if (value != 0U) { + if (bsec_set_sw_lock((32U * j) + i) != + BSEC_OK) { + return BSEC_ERROR; + } + } + } + } + + for (j = 0U; j < 3U; j++) { + if (exchange->shadow_read_lock[j] == 0U) { + continue; + } + + for (i = 0U; i < 32U; i++) { + if (bsec_check_nsec_access_rights((32U * j) + i) != + BSEC_OK) { + continue; + } + + value = (exchange->shadow_read_lock[j] >> i) & 1U; + if (value != 0U) { + if (bsec_set_sr_lock((32U * j) + i) != + BSEC_OK) { + return BSEC_ERROR; + } + } + } + } + + ret = bsec_get_config(&config_param); + if (ret != BSEC_OK) { + return ret; + } + + config_param.power = + (uint8_t)(exchange->configuration & BSEC_CONF_POWER_UP_MASK) >> + BSEC_CONF_POWER_UP_SHIFT; + config_param.freq = + (uint8_t)(exchange->configuration & BSEC_CONF_FRQ_MASK) >> + BSEC_CONF_FRQ_SHIFT; + config_param.pulse_width = + (uint8_t)(exchange->configuration & BSEC_CONF_PRG_WIDTH_MASK) >> + BSEC_CONF_PRG_WIDTH_SHIFT; + config_param.tread = + (uint8_t)((exchange->configuration & BSEC_CONF_TREAD_MASK) >> + BSEC_CONF_TREAD_SHIFT); + config_param.den_lock = + (uint8_t)(exchange->general_lock & DENREG_LOCK_MASK) >> + DENREG_LOCK_SHIFT; + config_param.prog_lock = + (uint8_t)(exchange->general_lock & GPLOCK_LOCK_MASK) >> + GPLOCK_LOCK_SHIFT; + + config_param.upper_otp_lock = + (uint8_t)(exchange->general_lock & UPPER_OTP_LOCK_MASK) >> + UPPER_OTP_LOCK_SHIFT; + + ret = bsec_set_config(&config_param); + if (ret != BSEC_OK) { + return ret; + } + + INFO("write all otp succeed\n"); + + return BSEC_OK; +} + uint32_t bsec_main(uint32_t x1, uint32_t x2, uint32_t x3, uint32_t *ret_otp_value) { uint32_t result; uint32_t tmp_data = 0U; + struct otp_exchange *otp_exch __unused; + uintptr_t map_begin __unused; + size_t map_size __unused = PAGE_SIZE; + int ret __unused; + + if ((x1 != STM32_SMC_READ_ALL) && (x1 != STM32_SMC_WRITE_ALL) && + (bsec_check_nsec_access_rights(x2) != BSEC_OK)) { + return STM32_SMC_INVALID_PARAMS; + } + + otp_exch = NULL; + map_begin = 0U; + + if ((x1 == STM32_SMC_READ_ALL) || (x1 == STM32_SMC_WRITE_ALL)) { + if (!stm32_boot_is_serial()) { + return STM32_SMC_FAILED; + } + + map_begin = round_down(x2, PAGE_SIZE); + + if (round_down(x2 + sizeof(struct otp_exchange), PAGE_SIZE) != + map_begin) { + /* + * Buffer end is in the next page, 2 pages need to be + * mapped. + */ + map_size += PAGE_SIZE; + } + + ret = mmap_add_dynamic_region(map_begin, + map_begin, + map_size, + MT_MEMORY | MT_RW | MT_NS); + assert(ret == 0); + + if (!ddr_is_nonsecured_area(map_begin, map_size)) { + ret = mmap_remove_dynamic_region(map_begin, map_size); + assert(ret == 0); + + return STM32_SMC_INVALID_PARAMS; + } + + otp_exch = (struct otp_exchange *)(uintptr_t)x2; + } switch (x1) { case STM32_SMC_READ_SHADOW: @@ -25,6 +446,18 @@ uint32_t bsec_main(uint32_t x1, uint32_t x2, uint32_t x3, break; case STM32_SMC_PROG_OTP: *ret_otp_value = 0U; + if (x2 == BOOT_API_OTP_SSP_WORD_NB) { + result = bsec_read_otp(&tmp_data, x2); + if (result != BSEC_OK) { + break; + } + + *ret_otp_value = (uint32_t)bsec_check_ssp(tmp_data, x3); + if (*ret_otp_value == (uint32_t)BSEC_SSP_ERROR) { + result = BSEC_OK; + break; + } + } result = bsec_program_otp(x3, x2); break; case STM32_SMC_WRITE_SHADOW: @@ -50,11 +483,23 @@ uint32_t bsec_main(uint32_t x1, uint32_t x2, uint32_t x3, result = bsec_write_otp(tmp_data, x2); break; - - default: - result = BSEC_ERROR; + case STM32_SMC_READ_ALL: + result = bsec_read_all_bsec(otp_exch); + break; + case STM32_SMC_WRITE_ALL: + result = bsec_write_all_bsec(otp_exch, ret_otp_value); break; + case STM32_SMC_WRLOCK_OTP: + result = bsec_permanent_lock_otp(x2); + break; + default: + return STM32_SMC_INVALID_PARAMS; + } + + if ((x1 == STM32_SMC_READ_ALL) || (x1 == STM32_SMC_WRITE_ALL)) { + ret = mmap_remove_dynamic_region(map_begin, map_size); + assert(ret == 0); } - return result; + return (result == BSEC_OK) ? STM32_SMC_OK : STM32_SMC_FAILED; } diff --git a/plat/st/stm32mp1/services/low_power_svc.c b/plat/st/stm32mp1/services/low_power_svc.c new file mode 100644 index 0000000000..567a3c70f5 --- /dev/null +++ b/plat/st/stm32mp1/services/low_power_svc.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include + +#include "low_power_svc.h" + +uint32_t pm_domain_scv_handler(uint32_t x1, uint32_t x2) +{ + if (stm32mp1_set_pm_domain_state((enum stm32mp1_pm_domain)x1, + (bool)x2) < 0) { + return STM32_SMC_FAILED; + } + + return STM32_SMC_OK; +} diff --git a/plat/st/stm32mp1/services/low_power_svc.h b/plat/st/stm32mp1/services/low_power_svc.h new file mode 100644 index 0000000000..eb98e92252 --- /dev/null +++ b/plat/st/stm32mp1/services/low_power_svc.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef LOW_POWER_SVC_H +#define LOW_POWER_SVC_H + +#include + +uint32_t pm_domain_scv_handler(uint32_t x1, uint32_t x2); + +#endif /* LOW_POWER_SVC_H */ diff --git a/plat/st/stm32mp1/services/pwr_svc.c b/plat/st/stm32mp1/services/pwr_svc.c new file mode 100644 index 0000000000..1213d7ef64 --- /dev/null +++ b/plat/st/stm32mp1/services/pwr_svc.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include + +#include +#include + +#include "pwr_svc.h" + +static void access_allowed_mask(uint32_t request, uint32_t offset, + uint32_t value, uint32_t allowed_mask) +{ + uint32_t addr = stm32mp_pwr_base() + offset; + uint32_t masked_value = value & allowed_mask; + + stm32mp_pwr_regs_lock(); + + switch (request) { + case STM32_SMC_REG_WRITE: + mmio_clrsetbits_32(addr, allowed_mask, masked_value); + VERBOSE("wrt 0x%x = 0x%x => 0x%x\n", offset, value, + mmio_read_32(addr)); + break; + + case STM32_SMC_REG_SET: + mmio_setbits_32(addr, masked_value); + VERBOSE("set 0x%x = 0x%x => 0x%x\n", offset, value, + mmio_read_32(addr)); + break; + + case STM32_SMC_REG_CLEAR: + mmio_clrbits_32(addr, masked_value); + VERBOSE("clear 0x%x = 0x%x => 0x%x\n", offset, value, + mmio_read_32(addr)); + break; + + default: + break; + } + + stm32mp_pwr_regs_unlock(); +} + +static void raw_allowed_access_request(uint32_t request, + uint32_t offset, uint32_t value) +{ + uint32_t allowed_mask = 0; + + switch (offset) { + case PWR_CR3: + allowed_mask |= PWR_CR3_VBE | PWR_CR3_VBRS | PWR_CR3_USB33DEN | + PWR_CR3_REG18EN | PWR_CR3_REG11EN; + break; + + case PWR_WKUPCR: + allowed_mask |= PWR_WKUPCR_MASK; + break; + + case PWR_MPUWKUPENR: + allowed_mask |= PWR_MPUWKUPENR_MASK; + break; + + default: + return; + } + + if (allowed_mask != 0U) { + access_allowed_mask(request, offset, value, allowed_mask); + } +} + +uint32_t pwr_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3) +{ + uint32_t request = x1; + uint32_t offset = x2; + uint32_t value = x3; + + /* + * x2 may be either the PWR register offset or the register + * full physical address. + */ + if ((offset & ~PWR_OFFSET_MASK) != 0) { + if ((offset & ~PWR_OFFSET_MASK) != stm32mp_pwr_base()) { + return STM32_SMC_INVALID_PARAMS; + } + + offset &= PWR_OFFSET_MASK; + } + + /* PWR controls for non secure resource may be accessed straight */ + raw_allowed_access_request(request, offset, value); + + return STM32_SMC_OK; +} diff --git a/plat/st/stm32mp1/services/pwr_svc.h b/plat/st/stm32mp1/services/pwr_svc.h new file mode 100644 index 0000000000..6dacdf80d7 --- /dev/null +++ b/plat/st/stm32mp1/services/pwr_svc.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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/plat/st/stm32mp1/services/rcc_svc.c b/plat/st/stm32mp1/services/rcc_svc.c new file mode 100644 index 0000000000..0be76bbda4 --- /dev/null +++ b/plat/st/stm32mp1/services/rcc_svc.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "rcc_svc.h" + +static bool offset_is_clear_register(uint32_t __unused offset) +{ + /* All currently allowed registers are non set/clear registers */ + return false; +} + +static void access_allowed_mask(uint32_t request, uint32_t offset, + uint32_t value, uint32_t allowed_mask) +{ + uint32_t addr = stm32mp_rcc_base() + offset; + uint32_t masked_value = value & allowed_mask; + + switch (request) { + case STM32_SMC_REG_WRITE: + if (offset_is_clear_register(offset)) { + mmio_write_32(addr, masked_value); + } else { + stm32mp_mmio_clrsetbits_32_shregs(addr, allowed_mask, + masked_value); + } + VERBOSE("wrt 0x%x = 0x%x => 0x%x\n", offset, value, + mmio_read_32(addr)); + break; + + case STM32_SMC_REG_SET: + if (offset_is_clear_register(offset)) { + mmio_write_32(addr, masked_value); + } else { + stm32mp_mmio_setbits_32_shregs(addr, masked_value); + } + VERBOSE("set 0x%x = 0x%x => 0x%x\n", offset, value, + mmio_read_32(addr)); + break; + + case STM32_SMC_REG_CLEAR: + if (offset_is_clear_register(offset)) { + /* Nothing to do on CLR registers */ + } else { + stm32mp_mmio_clrbits_32_shregs(addr, masked_value); + } + VERBOSE("clear 0x%x = 0x%x => 0x%x\n", offset, value, + mmio_read_32(addr)); + break; + + default: + break; + } +} + +static uint32_t raw_allowed_access_request(uint32_t request, + uint32_t offset, uint32_t value) +{ + uint32_t allowed_mask = 0; + + switch (offset) { + case RCC_MP_CIER: + case RCC_MP_CIFR: + allowed_mask = RCC_MP_CIFR_WKUPF; + break; + default: + return STM32_SMC_INVALID_PARAMS; + } + + if (allowed_mask != 0U) { + access_allowed_mask(request, offset, value, allowed_mask); + } + + return STM32_SMC_OK; +} + +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; + + /* + * x2 may be either the RCC register offset or the register + * full physical address. + */ + if ((offset & ~RCC_OFFSET_MASK) != 0) { + if ((offset & ~RCC_OFFSET_MASK) != stm32mp_rcc_base()) { + return STM32_SMC_INVALID_PARAMS; + } + + offset &= RCC_OFFSET_MASK; + } + + return raw_allowed_access_request(request, offset, value); +} + +uint32_t rcc_cal_scv_handler(uint32_t x1) +{ + uint32_t ret = STM32_SMC_FAILED; + + switch (x1) { + case CK_CSI: + if (stm32mp1_calib_start_csi_cal() == 0) { + ret = STM32_SMC_OK; + } + break; + + case CK_HSI: + if (stm32mp1_calib_start_hsi_cal() == 0) { + ret = STM32_SMC_OK; + } + break; + + default: + ret = STM32_SMC_INVALID_PARAMS; + break; + } + + return ret; +} + +uint32_t rcc_opp_scv_handler(uint32_t x1, uint32_t x2, uint32_t *res) +{ + uint32_t cmd = x1; + uint32_t opp = x2 / 1000U; /* KHz */ + + switch (cmd) { + case STM32_SMC_RCC_OPP_SET: + if (stm32mp1_set_opp_khz(opp) != 0) { + return STM32_SMC_FAILED; + } + break; + + case STM32_SMC_RCC_OPP_ROUND: + if (stm32mp1_round_opp_khz(&opp) != 0) { + return STM32_SMC_FAILED; + } + + if (opp > (UINT32_MAX / 1000U)) { + return STM32_SMC_FAILED; + } + + *res = opp * 1000U; + break; + + default: + return STM32_SMC_INVALID_PARAMS; + } + + return STM32_SMC_OK; +} diff --git a/plat/st/stm32mp1/services/rcc_svc.h b/plat/st/stm32mp1/services/rcc_svc.h new file mode 100644 index 0000000000..23c75824f0 --- /dev/null +++ b/plat/st/stm32mp1/services/rcc_svc.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCC_SVC_H +#define RCC_SVC_H + +uint32_t rcc_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3); +uint32_t rcc_cal_scv_handler(uint32_t x1); +uint32_t rcc_opp_scv_handler(uint32_t x1, uint32_t x2, uint32_t *res); + +#endif /* RCC_SVC_H */ diff --git a/plat/st/stm32mp1/services/stm32mp1_svc_setup.c b/plat/st/stm32mp1/services/stm32mp1_svc_setup.c index 49375a62d0..037a0b40b8 100644 --- a/plat/st/stm32mp1/services/stm32mp1_svc_setup.c +++ b/plat/st/stm32mp1/services/stm32mp1_svc_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2014-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,9 +13,13 @@ #include #include +#include #include #include "bsec_svc.h" +#include "low_power_svc.h" +#include "pwr_svc.h" +#include "rcc_svc.h" /* STM32 SiP Service UUID */ DEFINE_SVC_UUID2(stm32_sip_svc_uid, @@ -66,6 +70,32 @@ static uintptr_t stm32mp1_svc_smc_handler(uint32_t smc_fid, u_register_t x1, ret2_enabled = true; break; + case STM32_SMC_RCC: + ret1 = rcc_scv_handler(x1, x2, x3); + break; + + case STM32_SMC_RCC_CAL: + ret1 = rcc_cal_scv_handler(x1); + break; + + case STM32_SMC_RCC_OPP: + ret1 = rcc_opp_scv_handler(x1, x2, &ret2); + ret2_enabled = true; + break; + + case STM32_SMC_PWR: + ret1 = pwr_scv_handler(x1, x2, x3); + break; + + case STM32_SMC_PD_DOMAIN: + ret1 = pm_domain_scv_handler(x1, x2); + break; + + case STM32_SMC_AUTO_STOP: + stm32_auto_stop(); + ret1 = STM32_SMC_OK; + break; + case STM32_SIP_SMC_SCMI_AGENT0: scmi_smt_fastcall_smc_entry(0); break; @@ -75,7 +105,7 @@ static uintptr_t stm32mp1_svc_smc_handler(uint32_t smc_fid, u_register_t x1, default: WARN("Unimplemented STM32MP1 Service Call: 0x%x\n", smc_fid); - ret1 = SMC_UNK; + ret1 = STM32_SMC_NOT_SUPPORTED; break; } diff --git a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk index 8866fb556c..dd81d13a08 100644 --- a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk +++ b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk @@ -1,17 +1,31 @@ # -# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # SP_MIN_WITH_SECURE_FIQ := 1 +# Allow SP_min to be placed in DDR +STM32MP_SP_MIN_IN_DDR ?= 0 + +$(eval $(call assert_booleans, STM32MP_SP_MIN_IN_DDR)) + +$(eval $(call add_defines, STM32MP_SP_MIN_IN_DDR)) + BL32_CFLAGS += -DSTM32MP_SHARED_RESOURCES -BL32_SOURCES += drivers/st/etzpc/etzpc.c \ +BL32_SOURCES += drivers/st/clk/stm32mp1_calib.c \ + drivers/st/etzpc/etzpc.c \ + drivers/st/rng/stm32_rng.c \ + drivers/st/rtc/stm32_rtc.c \ + drivers/st/tamper/stm32_tamp.c \ + drivers/st/timer/stm32_timer.c \ plat/common/aarch32/platform_mp_stack.S \ plat/st/stm32mp1/sp_min/sp_min_setup.c \ + plat/st/stm32mp1/stm32mp1_low_power.c \ plat/st/stm32mp1/stm32mp1_pm.c \ + plat/st/stm32mp1/stm32mp1_power_config.c \ plat/st/stm32mp1/stm32mp1_shared_resources.c \ plat/st/stm32mp1/stm32mp1_topology.c @@ -20,11 +34,14 @@ include drivers/arm/gic/v2/gicv2.mk BL32_SOURCES += ${GICV2_SOURCES} \ plat/common/plat_gicv2.c \ - plat/st/stm32mp1/stm32mp1_gic.c + plat/st/common/stm32_gic.c # Generic PSCI BL32_SOURCES += plat/common/plat_psci_common.c +# Generic FDT +BL32_SOURCES += common/fdt_fixup.c + # SCMI server drivers BL32_SOURCES += drivers/st/scmi-msg/base.c \ drivers/st/scmi-msg/clock.c \ @@ -34,8 +51,15 @@ BL32_SOURCES += drivers/st/scmi-msg/base.c \ # stm32mp1 specific services BL32_SOURCES += plat/st/stm32mp1/services/bsec_svc.c \ + plat/st/stm32mp1/services/low_power_svc.c \ + plat/st/stm32mp1/services/pwr_svc.c \ + plat/st/stm32mp1/services/rcc_svc.c \ plat/st/stm32mp1/services/stm32mp1_svc_setup.c \ plat/st/stm32mp1/stm32mp1_scmi.c # Arm Archtecture services BL32_SOURCES += services/arm_arch_svc/arm_arch_svc_setup.c + +ifneq ($(STM32MP_SP_MIN_IN_DDR),1) +BL32_SOURCES += plat/st/stm32mp1/stm32mp1_critic_power.c +endif diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c index b639fcb358..0065be44fd 100644 --- a/plat/st/stm32mp1/sp_min/sp_min_setup.c +++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,16 +12,27 @@ #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 @@ -29,6 +40,9 @@ #include #include +#include +#include +#include /****************************************************************************** * Placeholder variables for copying the arguments that have been passed to @@ -37,6 +51,132 @@ static entry_point_info_t bl33_image_ep_info; static console_t console; +static void stm32mp1_tamper_action(int id); + +static const char *tamper_name[PLAT_MAX_TAMP_INT] = { + "RTC power domain", + "Temperature monitoring", + "LSE monitoring", + "HSE monitoring", + "RTC calendar overflow", + "Monotonic counter" +}; + +static struct stm32_tamp_int int_tamp[PLAT_MAX_TAMP_INT] = { + { + .id = ITAMP1, + .func = stm32mp1_tamper_action, + }, + { + .id = ITAMP2, + .func = stm32mp1_tamper_action, + }, + { + .id = ITAMP3, + .func = stm32mp1_tamper_action, + }, + { + .id = ITAMP4, + .func = stm32mp1_tamper_action, + }, + TAMP_UNUSED, + TAMP_UNUSED, +}; + +static struct stm32_tamp_ext ext_tamp[PLAT_MAX_TAMP_EXT] = { + TAMP_UNUSED, + TAMP_UNUSED, + TAMP_UNUSED, +}; + +static void stm32_sgi1_it_handler(void) +{ + uint32_t id; + + stm32mp_mask_timer(); + +#if DEBUG + stm32mp_dump_core_registers(false); +#endif + + gicv2_end_of_interrupt(ARM_IRQ_SEC_SGI_1); + + do { + id = plat_ic_get_pending_interrupt_id(); + + if (id <= MAX_SPI_ID) { + gicv2_end_of_interrupt(id); + + plat_ic_disable_interrupt(id); + } + } while (id <= MAX_SPI_ID); + + stm32mp_wait_cpu_reset(); +} + +static void stm32mp1_tamper_action(int id) +{ + ERROR("Tamper %s occurs\n", tamper_name[id]); + stm32mp_system_reset(); +} + +static void configure_wakeup_interrupt(void) +{ + int irq_num = fdt_rcc_enable_it("wakeup"); + + if (irq_num < 0) { + ERROR("irq_num = %d\n", irq_num); + panic(); + } + + plat_ic_set_interrupt_priority(irq_num, STM32MP1_IRQ_RCC_SEC_PRIO); +} + +static void initialize_pll1_settings(void) +{ + uint32_t cpu_voltage = 0U; + + if (stm32_are_pll1_settings_valid_in_context()) { + return; + } + + if (dt_pmic_status() > 0) { + const char *name = stm32mp_get_cpu_supply_name(); + int ret; + + if (name == NULL) { + panic(); + } + + ret = stpmic1_regulator_voltage_get(name); + if (ret < 0) { + panic(); + } + + cpu_voltage = (uint32_t)ret; + } + + if (stm32mp1_clk_compute_all_pll1_settings(cpu_voltage) != 0) { + panic(); + } +} + +static void disable_usb_phy_regulator(void) +{ + if (dt_pmic_status() > 0) { + const char *name = stm32mp_get_usb_phy_supply_name(); + int ret; + + if (name == NULL) { + return; + } + + ret = stpmic1_regulator_disable(name); + if (ret < 0) { + WARN("USBPHYC phy-supply (%s) disable failed\n", name); + } + } +} /******************************************************************************* * Interrupt handler for FIQ (secure IRQ) @@ -44,20 +184,75 @@ static console_t console; void sp_min_plat_fiq_handler(uint32_t id) { switch (id & INT_ID_MASK) { + case ARM_IRQ_SEC_PHY_TIMER: + case STM32MP1_IRQ_MCU_SEV: + case STM32MP1_IRQ_RCC_WAKEUP: + stm32mp1_calib_it_handler(id); + break; case STM32MP1_IRQ_TZC400: - ERROR("STM32MP1_IRQ_TZC400 generated\n"); + tzc400_init(STM32MP1_TZC_BASE); + tzc400_it_handler(); panic(); break; + case STM32MP1_IRQ_TAMPSERRS: + stm32_tamp_it_handler(); + break; + case ARM_IRQ_SEC_SGI_1: + stm32_sgi1_it_handler(); + break; + case ARM_IRQ_SEC_SGI_6: + /* tell the primary cpu to exit from stm32_pwr_down_wfi() */ + if (plat_my_core_pos() == STM32MP_PRIMARY_CPU) { + stm32mp1_calib_set_wakeup(true); + } + gicv2_end_of_interrupt(ARM_IRQ_SEC_SGI_6); + break; + case STM32MP1_IRQ_IWDG1: + case STM32MP1_IRQ_IWDG2: + stm32_iwdg_it_handler(id); + break; case STM32MP1_IRQ_AXIERRIRQ: ERROR("STM32MP1_IRQ_AXIERRIRQ generated\n"); panic(); break; default: - ERROR("SECURE IT handler not define for it : %u", id); + ERROR("SECURE IT handler not define for it : %u\n", id); break; } } +/******************************************************************************* + * Return the value of the saved PC from the backup register if present + ******************************************************************************/ +static uintptr_t get_saved_pc(void) +{ + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t saved_pc; + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + uint32_t magic_nb; + + clk_enable(RTCAPB); + + magic_nb = mmio_read_32(bkpr_core1_magic); + saved_pc = mmio_read_32(bkpr_core1_addr); + + clk_disable(RTCAPB); + + if (magic_nb != BOOT_API_A7_CORE0_MAGIC_NUMBER) { + return 0U; + } + + /* BL33 return address should be in DDR */ + if ((saved_pc < STM32MP_DDR_BASE) || + (saved_pc > (STM32MP_DDR_BASE + (dt_get_ddr_size() - 1U)))) { + panic(); + } + + return saved_pc; +} + /******************************************************************************* * Return a pointer to the 'entry_point_info' structure of the next image for * the security state specified. BL33 corresponds to the non-secure image type @@ -66,12 +261,36 @@ void sp_min_plat_fiq_handler(uint32_t id) ******************************************************************************/ entry_point_info_t *sp_min_plat_get_bl33_ep_info(void) { - entry_point_info_t *next_image_info; - - next_image_info = &bl33_image_ep_info; + entry_point_info_t *next_image_info = &bl33_image_ep_info; + /* + * PC is set to 0 when resetting after STANDBY + * The context should be restored, and the image information + * should be filled with what was saved + */ if (next_image_info->pc == 0U) { - return NULL; + void *cpu_context; + uintptr_t saved_pc; + + if (stm32_restore_context() != 0) { + panic(); + } + + cpu_context = cm_get_context(NON_SECURE); + + next_image_info->spsr = read_ctx_reg(get_regs_ctx(cpu_context), + CTX_SPSR); + + /* PC should be retrieved in backup register if OK, else it can + * be retrieved from non-secure context + */ + saved_pc = get_saved_pc(); + if (saved_pc != 0U) { + next_image_info->pc = saved_pc; + } else { + next_image_info->pc = + read_ctx_reg(get_regs_ctx(cpu_context), CTX_LR); + } } return next_image_info; @@ -108,15 +327,107 @@ static void stm32mp1_etzpc_early_setup(void) etzpc_configure_tzma(STM32MP1_ETZPC_TZMA_SYSRAM, TZMA1_SECURE_RANGE); } +#if !STM32MP_USE_STM32IMAGE +static void populate_ns_dt(u_register_t ns_dt_addr, uintptr_t sec_base, size_t sec_size) +{ + void *external_fdt = (void *)ns_dt_addr; + int ret; + + if (sec_base < STM32MP_DDR_BASE) { + /* No need to reserve memory if secure monitor is not in DDR */ + return; + } + + /* Map Base Non Secure DDR for Non secure DT update */ + ret = mmap_add_dynamic_region(ns_dt_addr, ns_dt_addr, STM32MP_HW_CONFIG_MAX_SIZE, + MT_NON_CACHEABLE | MT_EXECUTE_NEVER | MT_RW | MT_NS); + assert(ret == 0); + + if (fdt_check_header(external_fdt) != 0) { + INFO("Non-secure device tree not found\n"); + + goto out; + } + + ret = fdt_open_into(external_fdt, external_fdt, STM32MP_HW_CONFIG_MAX_SIZE); + if (ret < 0) { + WARN("Error opening DT %i\n", ret); + goto out; + } + + ret = fdt_add_reserved_memory(external_fdt, "tf-a", sec_base, sec_size); + if (ret < 0) { + WARN("Error updating DT %i\n", ret); + goto out; + } + + ret = fdt_pack(external_fdt); + if (ret < 0) { + WARN("Error packing DT %i\n", ret); + } + +out: + ret = mmap_remove_dynamic_region(ns_dt_addr, STM32MP_HW_CONFIG_MAX_SIZE); + assert(ret == 0); +} +#endif + +/******************************************************************************* + * Setup UART console using device tree information. + ******************************************************************************/ +static void setup_uart_console(void) +{ + struct dt_node_info dt_uart_info; + unsigned int console_flags; + int result; + uint32_t boot_itf; + uint32_t boot_instance; + + result = dt_get_stdout_uart_info(&dt_uart_info); + if ((result <= 0) || (dt_uart_info.status == DT_DISABLED)) { + return; + } + + stm32_get_boot_interface(&boot_itf, &boot_instance); + + if ((boot_itf == BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART) && + (get_uart_address(boot_instance) == dt_uart_info.base)) { + return; + } + + if (console_stm32_register(dt_uart_info.base, 0, + STM32MP_UART_BAUDRATE, &console) == 0U) { + panic(); + } + + console_flags = CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH | + CONSOLE_FLAG_TRANSLATE_CRLF; +#ifdef DEBUG + console_flags |= CONSOLE_FLAG_RUNTIME; +#endif + console_set_scope(&console, console_flags); +} + /******************************************************************************* * Perform any BL32 specific platform actions. ******************************************************************************/ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { - struct dt_node_info dt_uart_info; - int result; bl_params_t *params_from_bl2 = (bl_params_t *)arg0; +#if STM32MP_USE_STM32IMAGE + uintptr_t dt_addr = STM32MP_DTB_BASE; +#else + uintptr_t dt_addr = arg1; + uintptr_t sec_base = 0U; + size_t sec_size = 0U; +#endif +#if STM32MP_SP_MIN_IN_DDR + uintptr_t bl2_code_base = 0U; + uintptr_t bl2_code_end = 0U; + uintptr_t bl2_end = 0U; + int result __unused; +#endif /* Imprecise aborts can be masked in NonSecure */ write_scr(read_scr() | SCR_AW_BIT); @@ -125,59 +436,152 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, BL_CODE_END - BL_CODE_BASE, MT_CODE | MT_SECURE); +#if STM32MP_SP_MIN_IN_DDR + /* BL32 data*/ + mmap_add_region(BL_CODE_END, BL_CODE_END, + BL_END - BL_CODE_END, + MT_RW_DATA | MT_SECURE); + + /* BL32 Device Tree Blob */ + mmap_add_region(dt_addr, dt_addr, + STM32MP_BL32_DTB_SIZE, + MT_RO_DATA | MT_SECURE); + + /* Map SCMI shared buffers */ + mmap_add_region(STM32MP_SCMI_NS_SHM_BASE, STM32MP_SCMI_NS_SHM_BASE, + STM32MP_SCMI_NS_SHM_SIZE, + MT_DEVICE | MT_RW | MT_NS | MT_EXECUTE_NEVER); +#endif + configure_mmu(); + if (dt_open_and_check(dt_addr) < 0) { + panic(); + } + + if (bsec_probe() != 0) { + panic(); + } + + if (stm32mp1_clk_probe() < 0) { + panic(); + } + + setup_uart_console(); + + stm32mp1_etzpc_early_setup(); + +#if STM32MP_SP_MIN_IN_DDR + stm32_context_get_bl2_low_power_params(&bl2_code_base, &bl2_code_end, &bl2_end); + + /* BL2 Code */ + result = mmap_add_dynamic_region(bl2_code_base, bl2_code_base, + bl2_code_end - bl2_code_base, + MT_CODE | MT_SECURE); + assert(result == 0); + + /* BL2 RW memory */ + result = mmap_add_dynamic_region(bl2_code_end, bl2_code_end, + bl2_end - bl2_code_end, + MT_RW_DATA | MT_SECURE); + assert(result == 0); +#endif + assert(params_from_bl2 != NULL); assert(params_from_bl2->h.type == PARAM_BL_PARAMS); assert(params_from_bl2->h.version >= VERSION_2); bl_params_node_t *bl_params = params_from_bl2->head; - /* - * Copy BL33 entry point information. - * They are stored in Secure RAM, in BL2's address space. - */ while (bl_params != NULL) { + /* + * Copy BL33 entry point information. + * They are stored in Secure RAM, in BL2's address space. + */ if (bl_params->image_id == BL33_IMAGE_ID) { bl33_image_ep_info = *bl_params->ep_info; - break; + /* + * Check if hw_configuration is given to BL32 and + * share it to BL33 + */ + if (arg2 != 0U) { + bl33_image_ep_info.args.arg0 = 0U; + bl33_image_ep_info.args.arg1 = 0U; + bl33_image_ep_info.args.arg2 = arg2; + } } +#if !STM32MP_USE_STM32IMAGE + if (bl_params->image_id == BL32_IMAGE_ID) { + sec_base = bl_params->image_info->image_base; + sec_size = bl_params->image_info->image_max_size; + } +#endif + bl_params = bl_params->next_params_info; } - if (dt_open_and_check() < 0) { - panic(); - } +#if !STM32MP_USE_STM32IMAGE + if (arg2 != 0U) { + /* This will expect the BL32 DT and BL32 are grouped */ + if (dt_addr < sec_base) { + sec_size = sec_size + sec_base - dt_addr; + sec_base = dt_addr; + } else { + sec_size = dt_addr - sec_base + STM32MP_BL32_DTB_SIZE; + } - if (bsec_probe() != 0) { - panic(); + populate_ns_dt(arg2, sec_base, sec_size); + } else { + INFO("Non-secure device tree not found\n"); } +#endif - if (stm32mp1_clk_probe() < 0) { - panic(); + if (dt_pmic_status() > 0) { + initialize_pmic(); } - result = dt_get_stdout_uart_info(&dt_uart_info); + disable_usb_phy_regulator(); - if ((result > 0) && (dt_uart_info.status != 0U)) { - unsigned int console_flags; + initialize_pll1_settings(); - if (console_stm32_register(dt_uart_info.base, 0, - STM32MP_UART_BAUDRATE, &console) == - 0) { - panic(); - } + stm32mp1_init_lp_states(); +} - console_flags = CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH | - CONSOLE_FLAG_TRANSLATE_CRLF; -#ifdef DEBUG - console_flags |= CONSOLE_FLAG_RUNTIME; -#endif - console_set_scope(&console, console_flags); +static void init_sec_peripherals(void) +{ + uint32_t filter_conf = 0; + uint32_t active_conf = 0; + int ret; + + /* Disable MCU subsystem protection */ + stm32mp1_clk_mcuss_protect(false); + + /* Init rtc driver */ + ret = stm32_rtc_init(); + if (ret < 0) { + WARN("RTC driver init error %i\n", ret); } - stm32mp1_etzpc_early_setup(); + /* Init rng driver */ + ret = stm32_rng_init(); + if (ret < 0) { + WARN("RNG driver init error %i\n", ret); + } + + /* Init tamper */ + if (stm32_tamp_init() > 0) { + stm32_tamp_configure_internal(int_tamp, PLAT_MAX_TAMP_INT); + stm32_tamp_configure_external(ext_tamp, PLAT_MAX_TAMP_EXT, + filter_conf, active_conf); + + /* Enable timestamp for tamper */ + stm32_rtc_set_tamper_timestamp(); + } + + if (stm32_timer_init() == 0) { + stm32mp1_calib_init(); + } } /******************************************************************************* @@ -185,17 +589,22 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, ******************************************************************************/ void sp_min_platform_setup(void) { - /* Initialize tzc400 after DDR initialization */ - stm32mp1_security_setup(); + stm32_init_low_power(); + + ddr_save_sr_mode(); generic_delay_timer_init(); - stm32mp1_gic_init(); + stm32_gic_init(); + + init_sec_peripherals(); if (stm32_iwdg_init() < 0) { panic(); } + configure_wakeup_interrupt(); + stm32mp_lock_periph_registering(); stm32mp1_init_scmi_server(); diff --git a/plat/st/stm32mp1/stm32mp1.S b/plat/st/stm32mp1/stm32mp1.S index 7255fe5aa9..1b7e9e7eff 100644 --- a/plat/st/stm32mp1/stm32mp1.S +++ b/plat/st/stm32mp1/stm32mp1.S @@ -1,13 +1,15 @@ /* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#if STM32MP_USE_STM32IMAGE #ifdef BL32_BIN_PATH .section .bl32_image .incbin BL32_BIN_PATH #endif +#endif .section .bl2_image .incbin BL2_BIN_PATH diff --git a/plat/st/stm32mp1/stm32mp1.ld.S b/plat/st/stm32mp1/stm32mp1.ld.S index b347baddf7..43e2733397 100644 --- a/plat/st/stm32mp1/stm32mp1.ld.S +++ b/plat/st/stm32mp1/stm32mp1.ld.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -16,7 +16,7 @@ OUTPUT_ARCH(PLATFORM_LINKER_ARCH) ENTRY(__BL2_IMAGE_START__) MEMORY { - HEADER (rw) : ORIGIN = 0x00000000, LENGTH = 0x3000 + HEADER (rw) : ORIGIN = 0x00000000, LENGTH = STM32MP_HEADER_RESERVED_SIZE RAM (rwx) : ORIGIN = STM32MP_BINARY_BASE, LENGTH = STM32MP_BINARY_SIZE } @@ -43,7 +43,11 @@ SECTIONS * The strongest and only alignment contraint is MMU 4K page. * Indeed as images below will be removed, 4K pages will be re-used. */ +#if STM32MP_USE_STM32IMAGE . = ( STM32MP_DTB_BASE - STM32MP_BINARY_BASE ); +#else + . = ( STM32MP_BL2_DTB_BASE - STM32MP_BINARY_BASE ); +#endif __DTB_IMAGE_START__ = .; *(.dtb_image*) __DTB_IMAGE_END__ = .; @@ -53,12 +57,16 @@ SECTIONS * The strongest and only alignment contraint is MMU 4K page. * Indeed as images below will be removed, 4K pages will be re-used. */ +#if SEPARATE_CODE_AND_RODATA + . = ( STM32MP_BL2_RO_BASE - STM32MP_BINARY_BASE ); +#else . = ( STM32MP_BL2_BASE - STM32MP_BINARY_BASE ); +#endif __BL2_IMAGE_START__ = .; *(.bl2_image*) __BL2_IMAGE_END__ = .; -#ifndef AARCH32_SP_OPTEE +#if STM32MP_USE_STM32IMAGE && !defined(AARCH32_SP_OPTEE) /* * bl32 will be settled by bl2. * The strongest and only alignment constraint is 8 words to simplify diff --git a/plat/st/stm32mp1/stm32mp1_boot_device.c b/plat/st/stm32mp1/stm32mp1_boot_device.c index 997335d0da..690d8f04c9 100644 --- a/plat/st/stm32mp1/stm32mp1_boot_device.c +++ b/plat/st/stm32mp1/stm32mp1_boot_device.c @@ -19,13 +19,11 @@ #if STM32MP_RAW_NAND || STM32MP_SPI_NAND static int get_data_from_otp(struct nand_device *nand_dev, bool is_slc) { - int result; uint32_t nand_param; /* Check if NAND parameters are stored in OTP */ - result = bsec_shadow_read_otp(&nand_param, NAND_OTP); - if (result != BSEC_OK) { - ERROR("BSEC: NAND_OTP Error %i\n", result); + if (stm32_get_otp_value(NAND_OTP, &nand_param) != 0) { + ERROR("BSEC: NAND_OTP Error\n"); return -EACCES; } diff --git a/plat/st/stm32mp1/stm32mp1_context.c b/plat/st/stm32mp1/stm32mp1_context.c index cf8a91eb41..027475ed40 100644 --- a/plat/st/stm32mp1/stm32mp1_context.c +++ b/plat/st/stm32mp1/stm32mp1_context.c @@ -1,35 +1,492 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include +#include +#include #include -#define TAMP_BOOT_ITF_BACKUP_REG_ID U(20) -#define TAMP_BOOT_ITF_MASK U(0x0000FF00) -#define TAMP_BOOT_ITF_SHIFT 8 +#include +#include -int stm32_save_boot_interface(uint32_t interface, uint32_t instance) +#define TRAINING_AREA_SIZE 64 + +#define BL32_CANARY_ID U(0x424c3332) + +/* + * MAILBOX_MAGIC relates to struct backup_data_s as defined + * + * MAILBOX_MAGIC_V1: + * Context provides magic, resume entry, zq0cr0 zdata and DDR training buffer. + * + * MAILBOX_MAGIC_V2: + * Context provides magic, resume entry, zq0cr0 zdata, DDR training buffer + * and PLL1 dual OPP settings structure (86 bytes). + * + * MAILBOX_MAGIC_V3: + * Context provides magic, resume entry, zq0cr0 zdata, DDR training buffer + * and PLL1 dual OPP settings structure, low power entry point, BL2 code start, end and BL2_END + * (102 bytes). + */ +#define MAILBOX_MAGIC_V1 (0x0001 << 16) +#define MAILBOX_MAGIC_V2 (0x0002 << 16) +#define MAILBOX_MAGIC_V3 (0x0003 << 16) +#define MAILBOX_MAGIC (MAILBOX_MAGIC_V3 | \ + TRAINING_AREA_SIZE) + +#define MAGIC_ID(magic) ((magic) & GENMASK_32(31, 16)) +#define MAGIC_AREA_SIZE(magic) ((magic) & GENMASK_32(15, 0)) + +#if (PLAT_MAX_OPP_NB != 2) || (PLAT_MAX_PLLCFG_NB != 6) +#error MAILBOX_MAGIC_V2/_V3 does not support expected PLL1 settings +#endif + +/* pll_settings structure size definitions (reference to clock driver) */ +#define PLL1_SETTINGS_SIZE (((PLAT_MAX_OPP_NB * \ + (PLAT_MAX_PLLCFG_NB + 3)) + 1) * \ + sizeof(uint32_t)) + +/* Set to 600 bytes to be a bit flexible but could be optimized if needed */ +#define CLOCK_CONTEXT_SIZE 600 + +/* SCMI needs only 24 bits to save the state of the 24 exposed clocks */ +#define SCMI_CONTEXT_SIZE (sizeof(uint8_t) * 4) + +struct backup_data_s { + uint32_t magic; + uint32_t core0_resume_hint; + uint32_t zq0cr0_zdata; + uint8_t ddr_training_backup[TRAINING_AREA_SIZE]; + uint8_t pll1_settings[PLL1_SETTINGS_SIZE]; + uint32_t low_power_ep; + uint32_t bl2_code_base; + uint32_t bl2_code_end; + uint32_t bl2_end; +}; + +#if defined(IMAGE_BL32) +struct backup_bl32_data_s { + uint32_t canary_id; + smc_ctx_t saved_smc_context[PLATFORM_CORE_COUNT]; + cpu_context_t saved_cpu_context[PLATFORM_CORE_COUNT]; + struct stm32_rtc_calendar rtc; + unsigned long long stgen; + uint8_t clock_cfg[CLOCK_CONTEXT_SIZE]; + uint8_t scmi_context[SCMI_CONTEXT_SIZE]; +}; + +static struct backup_bl32_data_s *get_bl32_backup_data(void) +{ + return (struct backup_bl32_data_s *)(STM32MP_BACKUP_RAM_BASE + + sizeof(struct backup_data_s)); +} + +#if STM32MP_SP_MIN_IN_DDR +void (*stm32_pwr_down_wfi)(bool is_cstop); +#endif +#endif + +uint32_t stm32_pm_get_optee_ep(void) +{ + struct backup_data_s *backup_data; + uint32_t ep; + + clk_enable(BKPSRAM); + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + switch (MAGIC_ID(backup_data->magic)) { + case MAILBOX_MAGIC_V1: + case MAILBOX_MAGIC_V2: + case MAILBOX_MAGIC_V3: + if (MAGIC_AREA_SIZE(backup_data->magic) != TRAINING_AREA_SIZE) { + panic(); + } + break; + default: + ERROR("PM context: bad magic\n"); + panic(); + } + + ep = backup_data->core0_resume_hint; + + clk_disable(BKPSRAM); + + return ep; +} + +void stm32_clean_context(void) +{ + clk_enable(BKPSRAM); + +#if defined(IMAGE_BL2) + zeromem((void *)STM32MP_BACKUP_RAM_BASE, sizeof(struct backup_data_s)); +#elif defined(IMAGE_BL32) + zeromem((void *)get_bl32_backup_data(), sizeof(struct backup_bl32_data_s)); +#endif + + clk_disable(BKPSRAM); +} + +#if defined(IMAGE_BL32) +void stm32mp1_pm_save_clock_cfg(size_t offset, uint8_t *data, size_t size) +{ + struct backup_bl32_data_s *backup_data = get_bl32_backup_data(); + + if (offset + size > sizeof(backup_data->clock_cfg)) { + panic(); + } + + clk_enable(BKPSRAM); + + memcpy(backup_data->clock_cfg + offset, data, size); + + clk_disable(BKPSRAM); +} + +void stm32mp1_pm_restore_clock_cfg(size_t offset, uint8_t *data, size_t size) +{ + struct backup_bl32_data_s *backup_data = get_bl32_backup_data(); + + if (offset + size > sizeof(backup_data->clock_cfg)) + panic(); + + clk_enable(BKPSRAM); + + memcpy(data, backup_data->clock_cfg + offset, size); + + clk_disable(BKPSRAM); +} + +int stm32_save_context(uint32_t zq0cr0_zdata, + struct stm32_rtc_calendar *rtc_time, + unsigned long long stgen_cnt) { - uint32_t bkpr_itf_idx = tamp_bkpr(TAMP_BOOT_ITF_BACKUP_REG_ID); + void *smc_context; + void *cpu_context; + struct backup_data_s *backup_data; + struct backup_bl32_data_s *backup_bl32_data; + + stm32mp1_clock_suspend(); + + clk_enable(BKPSRAM); + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + /* Save BL32 context data provided to BL2 */ + backup_data->magic = MAILBOX_MAGIC; + + backup_data->zq0cr0_zdata = zq0cr0_zdata; + + stm32mp1_clk_lp_save_opp_pll1_settings(backup_data->pll1_settings, + sizeof(backup_data->pll1_settings)); + + /* Save the BL32 specific data */ + backup_bl32_data = get_bl32_backup_data(); + + backup_bl32_data->canary_id = BL32_CANARY_ID; + + /* Retrieve smc context struct address */ + smc_context = smc_get_ctx(NON_SECURE); + + /* Retrieve smc context struct address */ + cpu_context = cm_get_context(NON_SECURE); + + /* Save context in Backup SRAM */ + memcpy(&backup_bl32_data->saved_smc_context[0], smc_context, + sizeof(smc_ctx_t) * PLATFORM_CORE_COUNT); + memcpy(&backup_bl32_data->saved_cpu_context[0], cpu_context, + sizeof(cpu_context_t) * PLATFORM_CORE_COUNT); + + memcpy(&backup_bl32_data->rtc, rtc_time, sizeof(struct stm32_rtc_calendar)); + backup_bl32_data->stgen = stgen_cnt; - stm32mp_clk_enable(RTCAPB); + stm32mp1_pm_save_scmi_state(backup_bl32_data->scmi_context, + sizeof(backup_bl32_data->scmi_context)); - mmio_clrsetbits_32(bkpr_itf_idx, - TAMP_BOOT_ITF_MASK, - ((interface << 4) | (instance & 0xFU)) << - TAMP_BOOT_ITF_SHIFT); + save_clock_pm_context(); - stm32mp_clk_disable(RTCAPB); + clk_disable(BKPSRAM); return 0; } + +int stm32_restore_context(void) +{ + void *smc_context; + void *cpu_context; + struct backup_data_s *backup_data; + struct backup_bl32_data_s *backup_bl32_data; + struct stm32_rtc_calendar current_calendar; + unsigned long long stdby_time_in_ms; + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + clk_enable(BKPSRAM); + + stm32mp1_clk_lp_load_opp_pll1_settings(backup_data->pll1_settings, + sizeof(backup_data->pll1_settings)); + + backup_bl32_data = get_bl32_backup_data(); + + if (backup_bl32_data->canary_id != BL32_CANARY_ID) { + ERROR("Incorrect BL32 backup data\n"); + return -EINVAL; + } + + /* Retrieve smc context struct address */ + smc_context = smc_get_ctx(NON_SECURE); + + /* Retrieve smc context struct address */ + cpu_context = cm_get_context(NON_SECURE); + + restore_clock_pm_context(); + + stm32mp1_pm_restore_scmi_state(backup_bl32_data->scmi_context, + sizeof(backup_bl32_data->scmi_context)); + + /* Restore data from Backup SRAM */ + memcpy(smc_context, backup_bl32_data->saved_smc_context, + sizeof(smc_ctx_t) * PLATFORM_CORE_COUNT); + memcpy(cpu_context, backup_bl32_data->saved_cpu_context, + sizeof(cpu_context_t) * PLATFORM_CORE_COUNT); + + /* Restore STGEN counter with standby mode length */ + stm32_rtc_get_calendar(¤t_calendar); + stdby_time_in_ms = stm32_rtc_diff_calendar(¤t_calendar, + &backup_bl32_data->rtc); + stm32mp_stgen_restore_counter(backup_bl32_data->stgen, stdby_time_in_ms); + + clk_disable(BKPSRAM); + + stm32mp1_clock_resume(); + + return 0; +} + +unsigned long long stm32_get_stgen_from_context(void) +{ + struct backup_bl32_data_s *backup_data; + unsigned long long stgen_cnt; + + clk_enable(BKPSRAM); + + backup_data = get_bl32_backup_data(); + + stgen_cnt = backup_data->stgen; + + clk_disable(BKPSRAM); + + return stgen_cnt; +} + +void stm32_context_get_bl2_low_power_params(uintptr_t *bl2_code_base, + uintptr_t *bl2_code_end, + uintptr_t *bl2_end) +{ + struct backup_data_s *backup_data; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + if (MAGIC_ID(backup_data->magic) != MAILBOX_MAGIC_V3) { + panic(); + } + +#if STM32MP_SP_MIN_IN_DDR + stm32_pwr_down_wfi = (void (*)(bool))backup_data->low_power_ep; +#endif + *bl2_code_base = (uintptr_t)backup_data->bl2_code_base; + *bl2_code_end = (uintptr_t)backup_data->bl2_code_end; + *bl2_end = (uintptr_t)backup_data->bl2_end; + + clk_disable(BKPSRAM); +} + +#endif /* IMAGE_BL32 */ + +#if defined(IMAGE_BL2) +void stm32_context_save_bl2_param(void) +{ + struct backup_data_s *backup_data; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + backup_data->low_power_ep = (uint32_t)&stm32_pwr_down_wfi_wrapper; + backup_data->bl2_code_base = BL_CODE_BASE; + backup_data->bl2_code_end = BL_CODE_END; + backup_data->bl2_end = BL2_END; + backup_data->magic = MAILBOX_MAGIC_V3; + + clk_disable(BKPSRAM); +} +#endif + +uint32_t stm32_get_zdata_from_context(void) +{ + struct backup_data_s *backup_data; + uint32_t zdata; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + zdata = (backup_data->zq0cr0_zdata >> DDRPHYC_ZQ0CRN_ZDATA_SHIFT) & + DDRPHYC_ZQ0CRN_ZDATA_MASK; + + clk_disable(BKPSRAM); + + return zdata; +} + +static int pll1_settings_in_context(struct backup_data_s *backup_data) +{ + switch (MAGIC_ID(backup_data->magic)) { + case MAILBOX_MAGIC_V1: + return -ENOENT; + case MAILBOX_MAGIC_V2: + case MAILBOX_MAGIC_V3: + assert(MAGIC_AREA_SIZE(backup_data->magic) == + TRAINING_AREA_SIZE); + return 0; + default: + panic(); + } +} + +int stm32_get_pll1_settings_from_context(void) +{ + struct backup_data_s *backup_data; + int ret; + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + clk_enable(BKPSRAM); + + ret = pll1_settings_in_context(backup_data); + if (ret == 0) { + uint8_t *data = (uint8_t *)backup_data->pll1_settings; + size_t size = sizeof(backup_data->pll1_settings); + + stm32mp1_clk_lp_load_opp_pll1_settings(data, size); + } + + clk_disable(BKPSRAM); + + return ret; +} + +bool stm32_are_pll1_settings_valid_in_context(void) +{ + struct backup_data_s *backup_data; + uint32_t *data; + bool is_valid; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + data = (uint32_t *)backup_data->pll1_settings; + + is_valid = (data[0] == PLL1_SETTINGS_VALID_ID); + + clk_disable(BKPSRAM); + + return is_valid; +} + +bool stm32_pm_context_is_valid(void) +{ + struct backup_data_s *backup_data; + bool ret; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + switch (MAGIC_ID(backup_data->magic)) { + case MAILBOX_MAGIC_V1: + case MAILBOX_MAGIC_V2: + case MAILBOX_MAGIC_V3: + ret = true; + break; + default: + ret = false; + break; + } + + clk_disable(BKPSRAM); + + return ret; +} + +#if defined(IMAGE_BL32) +/* + * 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, and will be restored after + */ +void stm32_save_ddr_training_area(void) +{ + struct backup_data_s *backup_data; + int ret __unused; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + PAGE_SIZE, MT_MEMORY | MT_RW | MT_NS); + assert(ret == 0); + + memcpy(&backup_data->ddr_training_backup, + (const uint32_t *)STM32MP_DDR_BASE, + TRAINING_AREA_SIZE); + dsb(); + + ret = mmap_remove_dynamic_region(STM32MP_DDR_BASE, PAGE_SIZE); + assert(ret == 0); + + clk_disable(BKPSRAM); +} +#endif + +void stm32_restore_ddr_training_area(void) +{ + struct backup_data_s *backup_data; + + clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + memcpy((uint32_t *)STM32MP_DDR_BASE, + &backup_data->ddr_training_backup, + TRAINING_AREA_SIZE); + dsb(); + + clk_disable(BKPSRAM); +} diff --git a/plat/st/stm32mp1/stm32mp1_critic_power.c b/plat/st/stm32mp1/stm32mp1_critic_power.c new file mode 100644 index 0000000000..583cf93fad --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_critic_power.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include +#include +#include + +#include + +static void cstop_critic_enter(void) +{ + /* + * Set DDR in Self-refresh,. + * This is also the procedure awaited when switching off power supply. + */ + if (ddr_standby_sr_entry() != 0) { + ERROR("Unable to put DDR in SR\n"); + panic(); + } +} + +/* + * stm32_exit_cstop_critic - Exit from CSTOP mode reenable DDR + */ +static void cstop_critic_exit(void) +{ + if (ddr_sw_self_refresh_exit() != 0) { + panic(); + } +} + +void stm32_pwr_down_wfi_load(bool is_cstop) +{ + if (is_cstop) { + cstop_critic_enter(); + } + + /* + * Synchronize on memory accesses and instruction flow before the WFI + * instruction. + */ + dsb(); + isb(); + wfi(); + + stm32_iwdg_refresh(); + + if (is_cstop) { + cstop_critic_exit(); + } +} + +#if defined(IMAGE_BL32) && !STM32MP_SP_MIN_IN_DDR +extern void wfi_svc_int_enable(uintptr_t stack_addr); +static uint32_t int_stack[STM32MP_INT_STACK_SIZE]; + +void stm32_pwr_down_wfi(bool is_cstop) +{ + uint32_t interrupt = GIC_SPURIOUS_INTERRUPT; + + if (is_cstop) { + cstop_critic_enter(); + } + + stm32mp1_calib_set_wakeup(false); + + while (interrupt == GIC_SPURIOUS_INTERRUPT && + !stm32mp1_calib_get_wakeup()) { + wfi_svc_int_enable((uintptr_t)&int_stack[0]); + + interrupt = gicv2_acknowledge_interrupt(); + + if (interrupt != GIC_SPURIOUS_INTERRUPT) { + gicv2_end_of_interrupt(interrupt); + } + + stm32_iwdg_refresh(); + } + + if (is_cstop) { + cstop_critic_exit(); + } +} +#endif diff --git a/plat/st/stm32mp1/stm32mp1_critic_power_wrapper.S b/plat/st/stm32mp1/stm32mp1_critic_power_wrapper.S new file mode 100644 index 0000000000..2c4dd844d2 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_critic_power_wrapper.S @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include +#include +#include + + .global stm32_pwr_down_wfi_load + .global stm32_pwr_down_wfi_wrapper + .global bl2_vector_table + .global disable_mmu_secure + .global __STACKS_END__ + +func stm32_pwr_down_wfi_wrapper + push {r4,r5,r6,lr} + + # Save current sp in r4 + mov r4, sp + # Save current VBAR in r5 + ldcopr r5, VBAR + # Save current MVBAR in r6 + ldcopr r6, MVBAR + + # Reuse BL2 vector table for VBAR and MVBAR + ldr r1, =bl2_vector_table + stcopr r1, VBAR + stcopr r1, MVBAR + + # Set sp to BL2 STACK (as BL2 is not using it anymore) + ldr sp, =__STACKS_END__ + + # Disable MMU as TLB are still stored in DDR, + # and in few instructions DDR won't be readable + bl disable_mmu_secure + + # dsb is done in disable mmu + # isb is done in disable mmu + + # We didn't change R0 to keep it as first parameter + bl stm32_pwr_down_wfi_load + + # Restore stack + mov sp, r4 + # Restore VBAR + stcopr r5, VBAR + # Restore MVBAR + stcopr r6, MVBAR + + # Synchronize on memory access and instruction + # after resetting stack/IT handler + dsb + isb + + pop {r4,r5,r6,pc} +endfunc stm32_pwr_down_wfi_wrapper diff --git a/plat/st/stm32mp1/stm32mp1_dbgmcu.c b/plat/st/stm32mp1/stm32mp1_dbgmcu.c index d0264968c6..eac2cf68c5 100644 --- a/plat/st/stm32mp1/stm32mp1_dbgmcu.c +++ b/plat/st/stm32mp1/stm32mp1_dbgmcu.c @@ -1,15 +1,17 @@ /* - * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include #include #include +#include #include #include #include @@ -25,36 +27,27 @@ #define DBGMCU_APB4FZ1_IWDG2 BIT(2) -static uintptr_t get_rcc_base(void) -{ - /* This is called before stm32mp_rcc_base() is available */ - return RCC_BASE; -} - static int stm32mp1_dbgmcu_init(void) { - uint32_t dbg_conf; - uintptr_t rcc_base = get_rcc_base(); - - dbg_conf = bsec_read_debug_conf(); - - if ((dbg_conf & BSEC_DBGSWGEN) == 0U) { - uint32_t result = bsec_write_debug_conf(dbg_conf | - BSEC_DBGSWGEN); - - if (result != BSEC_OK) { - ERROR("Error enabling DBGSWGEN\n"); - return -1; - } + if ((bsec_read_debug_conf() & BSEC_DBGSWGEN) == 0U) { + INFO("Software access to all debug components is disabled\n"); + return -1; } - mmio_setbits_32(rcc_base + RCC_DBGCFGR, RCC_DBGCFGR_DBGCKEN); + mmio_setbits_32(RCC_BASE + RCC_DBGCFGR, RCC_DBGCFGR_DBGCKEN); return 0; } +/* + * @brief Get silicon revision from DBGMCU registers. + * @param chip_version: pointer to the read value. + * @retval 0 on success, negative value on failure. + */ int stm32mp1_dbgmcu_get_chip_version(uint32_t *chip_version) { + assert(chip_version != NULL); + if (stm32mp1_dbgmcu_init() != 0) { return -EPERM; } @@ -65,18 +58,29 @@ int stm32mp1_dbgmcu_get_chip_version(uint32_t *chip_version) return 0; } +/* + * @brief Get device ID from DBGMCU registers. + * @param chip_dev_id: pointer to the read value. + * @retval 0 on success, negative value on failure. + */ int stm32mp1_dbgmcu_get_chip_dev_id(uint32_t *chip_dev_id) { + assert(chip_dev_id != NULL); + if (stm32mp1_dbgmcu_init() != 0) { return -EPERM; } *chip_dev_id = mmio_read_32(DBGMCU_BASE + DBGMCU_IDC) & - DBGMCU_IDC_DEV_ID_MASK; + DBGMCU_IDC_DEV_ID_MASK; return 0; } +/* + * @brief Freeze IWDG2 in debug mode. + * @retval None. + */ int stm32mp1_dbgmcu_freeze_iwdg2(void) { uint32_t dbg_conf; diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index ee04a23fd9..027dba1bde 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,18 +11,22 @@ #include #include #include +#include #include #include #ifndef __ASSEMBLER__ #include +#include #include +#include +#include #include -#include #include #include #include +#include #include #include #include @@ -31,6 +35,8 @@ /******************************************************************************* * CHIP ID ******************************************************************************/ +#define STM32MP1_CHIP_ID U(0x500) + #define STM32MP157C_PART_NB U(0x05000000) #define STM32MP157A_PART_NB U(0x05000001) #define STM32MP153C_PART_NB U(0x05000024) @@ -55,15 +61,27 @@ #define PKG_AC_TFBGA361 U(2) #define PKG_AD_TFBGA257 U(1) +/******************************************************************************* + * BOOT PARAM + ******************************************************************************/ +#define BOOT_PARAM_ADDR U(0x2FFC0078) + /******************************************************************************* * STM32MP1 memory map related constants ******************************************************************************/ #define STM32MP_ROM_BASE U(0x00000000) #define STM32MP_ROM_SIZE U(0x00020000) +#define STM32MP_ROM_SIZE_2MB_ALIGNED U(0x00200000) #define STM32MP_SYSRAM_BASE U(0x2FFC0000) #define STM32MP_SYSRAM_SIZE U(0x00040000) +#define STM32MP_RETRAM_BASE U(0x38000000) +#define STM32MP_RETRAM_SIZE U(0x00010000) + +#define STM32MP_BACKUP_RAM_BASE U(0x54000000) +#define STM32MP_BACKUP_RAM_SIZE U(0x00001000) + #define STM32MP_NS_SYSRAM_SIZE PAGE_SIZE #define STM32MP_NS_SYSRAM_BASE (STM32MP_SYSRAM_BASE + \ STM32MP_SYSRAM_SIZE - \ @@ -79,12 +97,13 @@ /* DDR configuration */ #define STM32MP_DDR_BASE U(0xC0000000) #define STM32MP_DDR_MAX_SIZE U(0x40000000) /* Max 1GB */ -#ifdef AARCH32_SP_OPTEE +#if STM32MP_USE_STM32IMAGE #define STM32MP_DDR_S_SIZE U(0x01E00000) /* 30 MB */ +#ifdef AARCH32_SP_OPTEE #define STM32MP_DDR_SHMEM_SIZE U(0x00200000) /* 2 MB */ +#endif #else -#define STM32MP_DDR_S_SIZE U(0) -#define STM32MP_DDR_SHMEM_SIZE U(0) +#define STM32MP_DDR_S_SIZE U(0x02000000) /* 32 MB */ #endif /* DDR power initializations */ @@ -100,6 +119,8 @@ enum ddr_type { #define STM32MP_PARAM_LOAD_SIZE U(0x00002400) /* 9 KB for param */ /* 256 Octets reserved for header */ #define STM32MP_HEADER_SIZE U(0x00000100) +/* round_up(STM32MP_PARAM_LOAD_SIZE + STM32MP_HEADER_SIZE, PAGE_SIZE) */ +#define STM32MP_HEADER_RESERVED_SIZE U(0x3000) #define STM32MP_BINARY_BASE (STM32MP_SEC_SYSRAM_BASE + \ STM32MP_PARAM_LOAD_SIZE + \ @@ -109,83 +130,214 @@ enum ddr_type { (STM32MP_PARAM_LOAD_SIZE + \ STM32MP_HEADER_SIZE)) -#ifdef AARCH32_SP_OPTEE -#define STM32MP_BL32_SIZE U(0) +#if !STM32MP_SSP +#if STM32MP_USE_STM32IMAGE +#define STM32MP_BL2_SIZE U(0x0001C000) /* 112 KB for BL2 */ +#ifdef AARCH32_SP_OPTEE #define STM32MP_OPTEE_BASE STM32MP_SEC_SYSRAM_BASE -#define STM32MP_OPTEE_SIZE (STM32MP_DTB_BASE - \ +#define STM32MP_OPTEE_SIZE (TF_A_MAPPING_START - \ STM32MP_OPTEE_BASE) -#else -#if STACK_PROTECTOR_ENABLED -#define STM32MP_BL32_SIZE U(0x00012000) /* 72 KB for BL32 */ -#else -#define STM32MP_BL32_SIZE U(0x00011000) /* 68 KB for BL32 */ -#endif -#endif + +#define STM32MP_BL2_BASE (STM32MP_SEC_SYSRAM_BASE + \ + STM32MP_SEC_SYSRAM_SIZE - \ + STM32MP_BL2_SIZE) + +#define STM32MP_BL32_BASE STM32MP_SEC_SYSRAM_BASE + +#else /* AARCH32_SP_OPTEE */ +#define STM32MP_BL32_SIZE U(0x0001A000) /* 104 KB for BL32 */ +#define STM32MP_BL32_BIN_SIZE STM32MP_BL32_SIZE #define STM32MP_BL32_BASE (STM32MP_SEC_SYSRAM_BASE + \ STM32MP_SEC_SYSRAM_SIZE - \ STM32MP_BL32_SIZE) -#ifdef AARCH32_SP_OPTEE -#if STACK_PROTECTOR_ENABLED -#define STM32MP_BL2_SIZE U(0x0001A000) /* 100 KB for BL2 */ +#define STM32MP_BL2_BASE (STM32MP_BL32_BASE - \ + STM32MP_BL2_SIZE) +#endif /* AARCH32_SP_OPTEE */ +#else /* STM32MP_USE_STM32IMAGE */ +#if TRUSTED_BOARD_BOOT && !STM32MP_USE_EXTERNAL_HEAP +#define STM32MP_BL2_RO_SIZE U(0x00014000) /* 80 KB */ +#define STM32MP_BL2_SIZE U(0x0001B000) /* 108 KB for BL2 */ #else -#define STM32MP_BL2_SIZE U(0x00018000) /* 92 KB for BL2 */ +#define STM32MP_BL2_RO_SIZE U(0x00010000) /* 64 KB */ +#define STM32MP_BL2_SIZE U(0x00015000) /* 84 KB for BL2 */ #endif + +#define STM32MP_BL2_BASE (STM32MP_SEC_SYSRAM_BASE + \ + STM32MP_SEC_SYSRAM_SIZE - \ + STM32MP_BL2_SIZE) + +#define STM32MP_BL2_RO_BASE STM32MP_BL2_BASE + +#define STM32MP_BL2_RW_BASE (STM32MP_BL2_RO_BASE + \ + STM32MP_BL2_RO_SIZE) + +#define STM32MP_BL2_RW_SIZE STM32MP_SEC_SYSRAM_BASE + \ + STM32MP_SEC_SYSRAM_SIZE - \ + STM32MP_BL2_RW_BASE + +#if STM32MP_SP_MIN_IN_DDR +#define STM32MP_BL32_SIZE U(0x00025000) /* 148 KB for BL32 */ #else -#if STACK_PROTECTOR_ENABLED -#define STM32MP_BL2_SIZE U(0x00019000) /* 96 KB for BL2 */ -#else -#define STM32MP_BL2_SIZE U(0x00017000) /* 88 KB for BL2 */ +#define STM32MP_BL32_SIZE U(0x0001A000) /* 100 KB for BL32 */ #endif +#endif /* STM32MP_USE_STM32IMAGE */ +#endif /* STM32MP_SSP */ + +#if defined(IMAGE_BL2) + #define STM32MP_DEFAULT_XLAT U(2) /* 8 KB for mapping */ #endif -#define STM32MP_BL2_BASE (STM32MP_BL32_BASE - \ - STM32MP_BL2_SIZE) +/* BL32/sp_min require finer granularity tables */ +#if defined(IMAGE_BL32) + #define STM32MP_DEFAULT_XLAT U(4) /* 16 KB for mapping */ +#endif -/* BL2 and BL32/sp_min require 4 tables */ -#define MAX_XLAT_TABLES U(4) /* 16 KB for mapping */ +#if STM32MP_SP_MIN_IN_DDR && defined(IMAGE_BL32) + #define STM32MP_SP_MIN_IN_DDR_XLAT U(3) /* 12KB for mapping + * (BL32 data, BL32 DT, SCMI buffers) + */ +#else + #define STM32MP_SP_MIN_IN_DDR_XLAT U(0) +#endif +#define MAX_XLAT_TABLES (STM32MP_DEFAULT_XLAT + \ + STM32MP_SP_MIN_IN_DDR_XLAT) /* * MAX_MMAP_REGIONS is usually: * BL stm32mp1_mmap size + mmap regions in *_plat_arch_setup */ #if defined(IMAGE_BL2) - #define MAX_MMAP_REGIONS 11 + #if STM32MP_USB_PROGRAMMER + #define MAX_MMAP_REGIONS 8 + #else + #if STM32MP_USE_STM32IMAGE + #define MAX_MMAP_REGIONS 8 + #else + #define MAX_MMAP_REGIONS 7 + #endif + #endif #endif #if defined(IMAGE_BL32) - #define MAX_MMAP_REGIONS 6 + #if STM32MP_USE_STM32IMAGE + #define MAX_MMAP_REGIONS 6 + #else + #define MAX_MMAP_REGIONS 10 + #endif #endif +#if STM32MP_SSP +#define STM32MP_BL2_DTB_SIZE U(0x00004000) /* 16 KB for DTB */ + +#define STM32MP_BL2_DTB_BASE (STM32MP_SYSRAM_BASE + \ + STM32MP_HEADER_RESERVED_SIZE) + +#define STM32MP_BL2_RO_SIZE U(0x0000C000) /* 48 Ko for BL2 */ + +#define STM32MP_BL2_RO_BASE STM32MP_BL2_DTB_BASE + \ + STM32MP_BL2_DTB_SIZE + +#define STM32MP_BL2_RW_BASE (STM32MP_BL2_RO_BASE + \ + STM32MP_BL2_RO_SIZE) + +#define STM32MP_BL2_RW_SIZE (STM32MP_SYSRAM_BASE + \ + STM32MP_SYSRAM_SIZE - \ + STM32MP_BL2_RW_BASE) + +#define STM32MP_DTB_SIZE STM32MP_BL2_DTB_SIZE +#define STM32MP_DTB_BASE STM32MP_BL2_DTB_BASE + +#define TF_A_MAPPING_START STM32MP_BL2_DTB_BASE +#else +#if STM32MP_USE_STM32IMAGE /* DTB initialization value */ -#define STM32MP_DTB_SIZE U(0x00005000) /* 20 KB for DTB */ +#define STM32MP_DTB_SIZE U(0x00006000) /* 24 KB for DTB */ -#define STM32MP_DTB_BASE (STM32MP_BL2_BASE - \ +#define STM32MP_DTB_BASE (STM32MP_BL2_BASE - \ STM32MP_DTB_SIZE) +#define TF_A_MAPPING_START STM32MP_DTB_BASE +#else /* STM32MP_USE_STM32IMAGE */ +#define STM32MP_BL2_DTB_SIZE U(0x00007000) /* 28 KB for DTB */ +#define STM32MP_BL2_DTB_BASE (STM32MP_BL2_BASE - \ + STM32MP_BL2_DTB_SIZE) +#define STM32MP_BL32_DTB_SIZE U(0x00005000) /* 20 KB for DTB */ +#define STM32MP_BL32_DTB_BASE STM32MP_SYSRAM_BASE + +#define STM32MP_BL32_BASE (STM32MP_BL32_DTB_BASE + \ + STM32MP_BL32_DTB_SIZE) + +#if defined(IMAGE_BL2) +#define STM32MP_DTB_SIZE STM32MP_BL2_DTB_SIZE +#define STM32MP_DTB_BASE STM32MP_BL2_DTB_BASE +#endif +#if defined(IMAGE_BL32) +#define STM32MP_DTB_SIZE STM32MP_BL32_DTB_SIZE +#define STM32MP_DTB_BASE STM32MP_BL32_DTB_BASE +#endif +#define TF_A_MAPPING_START STM32MP_BL2_DTB_BASE +#endif /* STM32MP_USE_STM32IMAGE */ +#endif /* STM32MP_SSP */ + +#define STM32MP_FW_CONFIG_BASE (STM32MP_SYSRAM_BASE + \ + STM32MP_SYSRAM_SIZE - \ + PAGE_SIZE) +#define STM32MP_FW_CONFIG_MAX_SIZE PAGE_SIZE #define STM32MP_BL33_BASE (STM32MP_DDR_BASE + U(0x100000)) +#define STM32MP_BL33_MAX_SIZE U(0x400000) +#define STM32MP_HW_CONFIG_BASE (STM32MP_BL33_BASE + \ + STM32MP_BL33_MAX_SIZE) +#define STM32MP_HW_CONFIG_MAX_SIZE U(0x20000) + +/* Define Temporary Stack size use during low power mode */ +#define STM32MP_INT_STACK_SIZE 0x200 /* Define maximum page size for NAND devices */ #define PLATFORM_MTD_MAX_PAGE_SIZE U(0x1000) +/* + * Only used for MTD devices that need some backup blocks. + * Must define a number of reserved blocks (depends on devices). + */ +#define PLATFORM_MTD_BACKUP_BLOCKS U(20) /* (20 * MTD block size) */ + /******************************************************************************* * STM32MP1 RAW partition offset for MTD devices ******************************************************************************/ -#define STM32MP_NOR_BL33_OFFSET U(0x00080000) +#if STM32MP_USE_STM32IMAGE +#ifndef STM32MP_NOR_BASE_OFFSET +#define STM32MP_NOR_BASE_OFFSET U(0x00080000) +#endif +#define STM32MP_NOR_BL33_OFFSET STM32MP_NOR_BASE_OFFSET #ifdef AARCH32_SP_OPTEE -#define STM32MP_NOR_TEEH_OFFSET U(0x00280000) -#define STM32MP_NOR_TEED_OFFSET U(0x002C0000) -#define STM32MP_NOR_TEEX_OFFSET U(0x00300000) +#define STM32MP_NOR_TEEH_OFFSET U(0x00300000) +#define STM32MP_NOR_TEED_OFFSET U(0x00340000) +#define STM32MP_NOR_TEEX_OFFSET U(0x003C0000) #endif -#define STM32MP_NAND_BL33_OFFSET U(0x00200000) +#ifndef STM32MP_NAND_BASE_OFFSET +#define STM32MP_NAND_BASE_OFFSET U(0x00200000) +#endif +#define STM32MP_NAND_BL33_OFFSET STM32MP_NAND_BASE_OFFSET #ifdef AARCH32_SP_OPTEE -#define STM32MP_NAND_TEEH_OFFSET U(0x00600000) -#define STM32MP_NAND_TEED_OFFSET U(0x00680000) -#define STM32MP_NAND_TEEX_OFFSET U(0x00700000) +#define STM32MP_NAND_TEEH_OFFSET (STM32MP_NAND_BASE_OFFSET + \ + U(0x00400000)) +#define STM32MP_NAND_TEED_OFFSET (STM32MP_NAND_BASE_OFFSET + \ + U(0x00480000)) +#define STM32MP_NAND_TEEX_OFFSET (STM32MP_NAND_BASE_OFFSET + \ + U(0x00500000)) #endif +#else /* STM32MP_USE_STM32IMAGE */ +#ifndef STM32MP_NOR_FIP_OFFSET +#define STM32MP_NOR_FIP_OFFSET U(0x00080000) +#endif +#ifndef STM32MP_NAND_FIP_OFFSET +#define STM32MP_NAND_FIP_OFFSET U(0x00200000) +#endif +#endif /* STM32MP_USE_STM32IMAGE */ /******************************************************************************* * STM32MP1 device/io map related constants (used for MMU) @@ -206,6 +358,22 @@ enum ddr_type { ******************************************************************************/ #define PWR_BASE U(0x50001000) +/******************************************************************************* + * STM32MP1 EXTI + ******************************************************************************/ +#define EXTI_BASE U(0x5000D000) +#define EXTI_TZENR1 U(0x14) +#define EXTI_RPR3 U(0x4C) +#define EXTI_FPR3 U(0x50) +#define EXTI_C1IMR1 U(0x80) +#define EXTI_C2IMR1 U(0xC0) +#define EXTI_C2IMR2 U(0xD0) +#define EXTI_C2IMR3 U(0xE0) +#define EXTI_TZENR1_TZEN18 BIT(18) +#define EXTI_IMR1_IM18 BIT(18) +#define EXTI_RPR3_RPIF65 BIT(1) +#define EXTI_FPR3_FPIF65 BIT(1) + /******************************************************************************* * STM32MP1 GPIO ******************************************************************************/ @@ -277,111 +445,12 @@ enum ddr_type { #define STM32MP1_ETZPC_TZMA_ALL_SECURE GENMASK_32(9, 0) -/* ETZPC DECPROT IDs */ -#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_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_SEC_ID_LIMIT 13 - -#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_RSV_ID 95 - -#define STM32MP_ETZPC_MAX_ID 96 - /******************************************************************************* * STM32MP1 TZC (TZ400) ******************************************************************************/ #define STM32MP1_TZC_BASE U(0x5C006000) -#define STM32MP1_TZC_A7_ID U(0) -#define STM32MP1_TZC_M4_ID U(1) -#define STM32MP1_TZC_LCD_ID U(3) -#define STM32MP1_TZC_GPU_ID U(4) -#define STM32MP1_TZC_MDMA_ID U(5) -#define STM32MP1_TZC_DMA_ID U(6) -#define STM32MP1_TZC_USB_HOST_ID U(7) -#define STM32MP1_TZC_USB_OTG_ID U(8) -#define STM32MP1_TZC_SDMMC_ID U(9) -#define STM32MP1_TZC_ETH_ID U(10) -#define STM32MP1_TZC_DAP_ID U(15) - -#define STM32MP1_FILTER_BIT_ALL U(3) +#define STM32MP1_FILTER_BIT_ALL (BIT(1) | BIT(0)) /******************************************************************************* * STM32MP1 SDMMC @@ -404,16 +473,44 @@ enum ddr_type { #define OTP_MAX_SIZE (STM32MP1_OTP_MAX_ID + 1U) -/* OTP offsets */ -#define DATA0_OTP U(0) -#define PART_NUMBER_OTP U(1) -#define NAND_OTP U(9) -#define PACKAGE_OTP U(16) -#define HW2_OTP U(18) +/* OTP labels */ +#define CFG0_OTP "cfg0_otp" +#define PART_NUMBER_OTP "part_number_otp" +#define PACKAGE_OTP "package_otp" +#define HW2_OTP "hw2_otp" +#define NAND_OTP "nand_otp" +#define MONOTONIC_OTP "monotonic_otp" +#define UID_OTP "uid_otp" +#define PKH_OTP "pkh_otp" +#define BOARD_ID_OTP "board_id" +#define CFG2_OTP "cfg2_otp" +#define SSP_OTP "ssp_otp" +#define CHIP_CERTIFICATE_OTP "chip_otp" +#define RMA_OTP "rma_otp" /* OTP mask */ -/* DATA0 */ -#define DATA0_OTP_SECURED BIT(6) +/* CFG0 */ +#define CFG0_CLOSED_DEVICE BIT(6) + +/* CFG2 */ +#define OTP_CFG2_SEC_COUNTER_MASK GENMASK_32(27, 20) +#define OTP_CFG2_SEC_COUNTER_SHIFT U(20) +#define OTP_CFG2_ST_KEY_MASK GENMASK_32(31, 28) +#define OTP_CFG2_ST_KEY_SHIFT U(28) + +/* SSP */ +#define SSP_OTP_REQ BIT(BOOT_API_OTP_SSP_REQ_BIT_POS) +#define SSP_OTP_SUCCESS BIT(BOOT_API_OTP_SSP_SUCCESS_BIT_POS) +#define SSP_OTP_MASK GENMASK_32(BOOT_API_OTP_SSP_SUCCESS_BIT_POS, \ + BOOT_API_OTP_SSP_REQ_BIT_POS) +#define SSP_OTP_SECRET_BASE U(59) +#define SSP_OTP_SECRET_END U(95) + +/* CHIP_CERT */ +#define CHIP_CERTIFICATE_MAX_SIZE U(0x40) + +/* RMA */ +#define RMA_OTP_MASK GENMASK_32(29, 0) /* PART NUMBER */ #define PART_NUMBER_OTP_PART_MASK GENMASK_32(7, 0) @@ -470,11 +567,24 @@ enum ddr_type { /* NAND number of planes */ #define NAND_PLANE_BIT_NB_MASK BIT(14) +/* MONOTONIC OTP */ +#define MAX_MONOTONIC_VALUE 32 + +/* UID OTP */ +#define UID_WORD_NB 3 + /******************************************************************************* * STM32MP1 TAMP ******************************************************************************/ +#define PLAT_MAX_TAMP_INT U(6) +#define PLAT_MAX_TAMP_EXT U(3) #define TAMP_BASE U(0x5C00A000) +#define TAMP_SMCR (TAMP_BASE + U(0x20)) #define TAMP_BKP_REGISTER_BASE (TAMP_BASE + U(0x100)) +#define TAMP_BKP_SEC_NUMBER U(10) +#define TAMP_BKP_SEC_WDPROT_SHIFT U(16) +#define TAMP_BKP_SEC_RWDPROT_SHIFT U(0) + #if !(defined(__LINKER__) || defined(__ASSEMBLER__)) static inline uint32_t tamp_bkpr(uint32_t idx) @@ -483,6 +593,11 @@ static inline uint32_t tamp_bkpr(uint32_t idx) } #endif +/******************************************************************************* + * STM32MP1 USB + ******************************************************************************/ +#define USB_OTG_BASE U(0x49000000) + /******************************************************************************* * STM32MP1 DDRCTRL ******************************************************************************/ @@ -518,12 +633,41 @@ static inline uint32_t tamp_bkpr(uint32_t idx) #define STGEN_BASE U(0x5c008000) #define SYSCFG_BASE U(0x50020000) +/******************************************************************************* + * STM32MP1 TIMERS + ******************************************************************************/ +#define TIM12_BASE U(0x40006000) +#define TIM15_BASE U(0x44006000) +#define TIM_MAX_INSTANCE U(2) + +/******************************************************************************* + * STM32MP1 OPP + ******************************************************************************/ +#define PLAT_OPP_ID1 U(1) +#define PLAT_OPP_ID2 U(2) +#define PLAT_MAX_OPP_NB U(2) +#define PLAT_MAX_PLLCFG_NB U(6) + +/******************************************************************************* + * DEBUG + ******************************************************************************/ +/*#define ICACHE_OFF*/ +/*#define DCACHE_OFF*/ +/*#define MMU_OFF*/ + /******************************************************************************* * Device Tree defines ******************************************************************************/ #define DT_BSEC_COMPAT "st,stm32mp15-bsec" +#define DT_DDR_COMPAT "st,stm32mp1-ddr" #define DT_IWDG_COMPAT "st,stm32mp1-iwdg" +#define DT_NVMEM_LAYOUT_COMPAT "st,stm32-nvmem-layout" +#define DT_OPP_COMPAT "operating-points-v2" #define DT_PWR_COMPAT "st,stm32mp1,pwr-reg" #define DT_RCC_CLK_COMPAT "st,stm32mp1-rcc" +#define DT_RCC_SEC_CLK_COMPAT "st,stm32mp1-rcc-secure" +#define DT_USBPHYC_COMPAT "st,stm32mp1-usbphyc" + +#define DT_PLL1_NODE_NAME "st,pll@0" #endif /* STM32MP1_DEF_H */ diff --git a/plat/st/stm32mp1/stm32mp1_fconf_firewall.c b/plat/st/stm32mp1/stm32mp1_fconf_firewall.c new file mode 100644 index 0000000000..16aee904e7 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_fconf_firewall.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define STM32MP_REGION_PARAMS 4 +#define STM32MP_MAX_REGIONS 8 +#define FORCE_SEC_REGION BIT(31) + +static uint32_t nb_regions; + +struct dt_id_attr { + fdt32_t id_attr[STM32MP_MAX_REGIONS]; +}; + +void stm32mp1_arch_security_setup(void) +{ + clk_enable(TZC1); + clk_enable(TZC2); + + tzc400_init(STM32MP1_TZC_BASE); + tzc400_disable_filters(); + + /* + * Region 0 set to cover all DRAM at 0xC000_0000 + * Only secure access is granted in read/write. + */ + tzc400_configure_region0(TZC_REGION_S_RDWR, 0); + + tzc400_set_action(TZC_ACTION_ERR); + tzc400_enable_filters(); +} + +void stm32mp1_security_setup(void) +{ + uint8_t i; + + assert(nb_regions > 0U); + + tzc400_init(STM32MP1_TZC_BASE); + tzc400_disable_filters(); + + /* + * Region 0 set to cover all DRAM at 0xC000_0000 + * No access is allowed. + */ + tzc400_configure_region0(TZC_REGION_S_NONE, 0); + + for (i = 1U; i <= nb_regions; i++) { + tzc400_update_filters(i, STM32MP1_FILTER_BIT_ALL); + } + + tzc400_set_action(TZC_ACTION_INT); + tzc400_enable_filters(); +} + +static int fconf_populate_stm32mp1_firewall(uintptr_t config) +{ + int node, len; + unsigned int i; + const struct dt_id_attr *conf_list; + const void *dtb = (const void *)config; + + /* Assert the node offset point to "st,mem-firewall" compatible property */ + const char *compatible_str = "st,mem-firewall"; + + node = fdt_node_offset_by_compatible(dtb, -1, compatible_str); + if (node < 0) { + ERROR("FCONF: Can't find %s compatible in dtb\n", compatible_str); + return node; + } + + conf_list = (const struct dt_id_attr *)fdt_getprop(dtb, node, "memory-ranges", &len); + if (conf_list == NULL) { + WARN("FCONF: Read cell failed for %s\n", "memory-ranges"); + return -1; + } + + /* Locate the memory cells and read all values */ + for (i = 0U; i < (unsigned int)(len / (sizeof(uint32_t) * STM32MP_REGION_PARAMS)); i++) { + uint32_t base; + uint32_t size; + uint32_t sec_attr; + uint32_t nsaid; + + base = fdt32_to_cpu(conf_list->id_attr[i * STM32MP_REGION_PARAMS]); + size = fdt32_to_cpu(conf_list->id_attr[i * STM32MP_REGION_PARAMS + 1]); + sec_attr = fdt32_to_cpu(conf_list->id_attr[i * STM32MP_REGION_PARAMS + 2]); + nsaid = fdt32_to_cpu(conf_list->id_attr[i * STM32MP_REGION_PARAMS + 3]); + + VERBOSE("FCONF: stm32mp1-firewall cell found with value = 0x%x 0x%x 0x%x 0x%x\n", + base, size, sec_attr, nsaid); + + nb_regions++; + + /* Configure region but keep disabled for secure access for BL2 load */ + tzc400_configure_region(0, nb_regions, (unsigned long long)base, + (unsigned long long)base + size - 1ULL, sec_attr, nsaid); + } + + /* Force flush as the value will be used cache off */ + flush_dcache_range((uintptr_t)&nb_regions, sizeof(uint32_t)); + + return 0; +} + +FCONF_REGISTER_POPULATOR(FW_CONFIG, stm32mp1_firewall, fconf_populate_stm32mp1_firewall); diff --git a/plat/st/stm32mp1/stm32mp1_gic.c b/plat/st/stm32mp1/stm32mp1_gic.c deleted file mode 100644 index 851a9cf0c4..0000000000 --- a/plat/st/stm32mp1/stm32mp1_gic.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include - -struct stm32_gic_instance { - uint32_t cells; - uint32_t phandle_node; -}; - -/****************************************************************************** - * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0 - * interrupts. - *****************************************************************************/ -static const interrupt_prop_t stm32mp1_interrupt_props[] = { - PLATFORM_G1S_PROPS(GICV2_INTR_GROUP0), - PLATFORM_G0_PROPS(GICV2_INTR_GROUP0) -}; - -/* Fix target_mask_array as secondary core is not able to initialize it */ -static unsigned int target_mask_array[PLATFORM_CORE_COUNT] = {1, 2}; - -static gicv2_driver_data_t platform_gic_data = { - .interrupt_props = stm32mp1_interrupt_props, - .interrupt_props_num = ARRAY_SIZE(stm32mp1_interrupt_props), - .target_masks = target_mask_array, - .target_masks_num = ARRAY_SIZE(target_mask_array), -}; - -static struct stm32_gic_instance stm32_gic; - -void stm32mp1_gic_init(void) -{ - int node; - void *fdt; - const fdt32_t *cuint; - struct dt_node_info dt_gic; - - if (fdt_get_address(&fdt) == 0) { - panic(); - } - - node = dt_get_node(&dt_gic, -1, "arm,cortex-a7-gic"); - if (node < 0) { - panic(); - } - - platform_gic_data.gicd_base = dt_gic.base; - - cuint = fdt_getprop(fdt, node, "reg", NULL); - if (cuint == NULL) { - panic(); - } - - platform_gic_data.gicc_base = fdt32_to_cpu(*(cuint + 2)); - - cuint = fdt_getprop(fdt, node, "#interrupt-cells", NULL); - if (cuint == NULL) { - panic(); - } - - stm32_gic.cells = fdt32_to_cpu(*cuint); - - stm32_gic.phandle_node = fdt_get_phandle(fdt, node); - if (stm32_gic.phandle_node == 0U) { - panic(); - } - - gicv2_driver_init(&platform_gic_data); - gicv2_distif_init(); - - stm32mp1_gic_pcpu_init(); -} - -void stm32mp1_gic_pcpu_init(void) -{ - gicv2_pcpu_distif_init(); - gicv2_set_pe_target_mask(plat_my_core_pos()); - gicv2_cpuif_enable(); -} diff --git a/plat/st/stm32mp1/stm32mp1_helper.S b/plat/st/stm32mp1/stm32mp1_helper.S index 3021362365..bc2d73cebb 100644 --- a/plat/st/stm32mp1/stm32mp1_helper.S +++ b/plat/st/stm32mp1/stm32mp1_helper.S @@ -10,11 +10,17 @@ #include #include #include +#include #define GPIO_TX_SHIFT (DEBUG_UART_TX_GPIO_PORT << 1) .globl platform_mem_init .globl plat_report_exception +#if AARCH32_EXCEPTION_DEBUG + .globl plat_report_undef_inst + .globl plat_report_prefetch_abort + .globl plat_report_data_abort +#endif .globl plat_get_my_entrypoint .globl plat_secondary_cold_boot_setup .globl plat_reset_handler @@ -24,6 +30,7 @@ .globl plat_crash_console_flush .globl plat_crash_console_putc .globl plat_panic_handler + .globl wfi_svc_int_enable func platform_mem_init /* Nothing to do, don't need to init SYSRAM */ @@ -75,6 +82,96 @@ print_exception_info: #endif endfunc plat_report_exception +#if AARCH32_EXCEPTION_DEBUG +func plat_report_undef_inst +#if DEBUG + mov r8, lr + + mov r9, r0 + + ldr r4, =undefined_str + bl asm_print_str + + mov r4, r9 + sub r4, r4, #4 + bl asm_print_hex + + ldr r4, =end_error_str + bl asm_print_str + + bx r8 +#else + bx lr +#endif +endfunc plat_report_undef_inst + +func plat_report_prefetch_abort +#if DEBUG + mov r8, lr + mov r9, r0 + + ldr r4, =prefetch_abort_str + bl asm_print_str + + mov r4, r9 + sub r4, r4, #4 + bl asm_print_hex + + ldr r4, =ifsr_str + bl asm_print_str + + ldcopr r4, IFSR + bl asm_print_hex + + ldr r4, =ifar_str + bl asm_print_str + + ldcopr r4, IFAR + bl asm_print_hex + + ldr r4, =end_error_str + bl asm_print_str + + bx r8 +#else + bx lr +#endif +endfunc plat_report_prefetch_abort + +func plat_report_data_abort +#if DEBUG + mov r8, lr + mov r9, r0 + + ldr r4, =data_abort_str + bl asm_print_str + + mov r4, r9 + sub r4, r4, #8 + bl asm_print_hex + + ldr r4, =dfsr_str + bl asm_print_str + + ldcopr r4, DFSR + bl asm_print_hex + + ldr r4, =dfar_str + bl asm_print_str + + ldcopr r4, DFAR + bl asm_print_hex + + ldr r4, =end_error_str + bl asm_print_str + + bx r8 +#else + bx lr +#endif +endfunc plat_report_data_abort +#endif + func plat_reset_handler bx lr endfunc plat_reset_handler @@ -204,7 +301,7 @@ endfunc plat_crash_console_init * --------------------------------------------- */ func plat_crash_console_flush - ldr r1, =STM32MP_DEBUG_USART_BASE + ldr r0, =STM32MP_DEBUG_USART_BASE b console_stm32_core_flush endfunc plat_crash_console_flush @@ -245,12 +342,48 @@ endfunc plat_panic_handler .section .rodata.rev_err_str, "aS" abort_str: .asciz "\nAbort at: 0x" +#if AARCH32_EXCEPTION_DEBUG +prefetch_abort_str: + .asciz "\nPrefetch Abort at: 0x" +data_abort_str: + .asciz "\nData Abort at: 0x" +#endif undefined_str: .asciz "\nUndefined instruction at: 0x" exception_start_str: .asciz "\nException mode=0x" exception_end_str: .asciz " at: 0x" +#if AARCH32_EXCEPTION_DEBUG +dfsr_str: + .asciz " DFSR = 0x" +dfar_str: + .asciz " DFAR = 0x" +ifsr_str: + .asciz " IFSR = 0x" +ifar_str: + .asciz " IFAR = 0x" +#endif end_error_str: .asciz "\n\r" #endif + +func wfi_svc_int_enable + push {r4,r8,lr} + ldcopr r4, SCR + mov r8, sp + mov sp, r0 + add r0, r0, #STM32MP_INT_STACK_SIZE + str r0, [sp, #SMC_CTX_SP_MON] + str r4, [sp, #SMC_CTX_SCR] + cps #MODE32_svc + cpsie af + dsb + isb + wfi + cpsid af + cps #MODE32_mon + mov sp, r8 + pop {r4,r8,lr} + bx lr +endfunc wfi_svc_int_enable diff --git a/plat/st/stm32mp1/stm32mp1_low_power.c b/plat/st/stm32mp1/stm32mp1_low_power.c new file mode 100644 index 0000000000..ae6fd88c88 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_low_power.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 + +static unsigned int gicc_pmr; +static struct stm32_rtc_calendar sleep_time; +static bool enter_cstop_done; +static unsigned long long stgen_cnt; + +struct pwr_lp_config { + uint32_t pwr_cr1; + uint32_t pwr_mpucr; + const char *regul_suspend_node_name; +}; + +#define PWRLP_TEMPO_5_HSI 5 + +#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 = PWR_MPUCR_CSSF, + .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", + }, +}; + +#define GICC_PMR_PRIORITY_8 U(0x8) + +enum { + STATE_NONE = 0, + STATE_AUTOSTOP_ENTRY, + STATE_AUTOSTOP_EXIT, +}; + +static struct spinlock lp_lock; +static volatile int cpu0_state = STATE_NONE; +static volatile int cpu1_state = STATE_NONE; + +void stm32_apply_pmic_suspend_config(uint32_t mode) +{ + const char *node_name; + + assert(mode < ARRAY_SIZE(config_pwr)); + + node_name = config_pwr[mode].regul_suspend_node_name; + + if (node_name != NULL) { + if (!initialize_pmic_i2c()) { + panic(); + } + + if (pmic_set_lp_config(node_name) < 0) { + panic(); + } + + if (pmic_configure_boot_on_regulators() < 0) { + panic(); + } + } +} + +/* + * stm32_enter_cstop - Prepare CSTOP mode + * + * @mode - Target low power mode + * @nsec_addr - Non secure resume entry point + * Return 0 if succeed to suspend, non 0 else. + */ +static void enter_cstop(uint32_t mode, uint32_t nsec_addr) +{ + uint32_t zq0cr0_zdata; + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + uint32_t pwr_cr1 = config_pwr[mode].pwr_cr1; + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + stm32mp1_syscfg_disable_io_compensation(); + + /* Switch to Software Self-Refresh mode */ + ddr_set_sr_mode(DDR_SSR_MODE); + + dcsw_op_all(DC_OP_CISW); + + stm32_clean_context(); + + if (mode == STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR) { + /* + * The first 64 bytes of DDR need to be saved for DDR DQS + * training + */ + stm32_save_ddr_training_area(); + } + + if (dt_pmic_status() > 0) { + stm32_apply_pmic_suspend_config(mode); + + if (mode == STM32_PM_CSTOP_ALLOW_LP_STOP) { + pwr_cr1 |= PWR_CR1_LPCFG; + } + } + + /* 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, PWR_MPUCR_MASK, + config_pwr[mode].pwr_mpucr); + mmio_clrsetbits_32(pwr_base + PWR_CR1, 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(); + + gicc_pmr = plat_ic_set_priority_mask(GICC_PMR_PRIORITY_8); + + zq0cr0_zdata = ddr_get_io_calibration_val(); + + clk_enable(RTCAPB); + + mmio_write_32(bkpr_core1_addr, 0); + mmio_write_32(bkpr_core1_magic, 0); + + stm32mp1_clock_stopmode_save(); + + stm32_rtc_get_calendar(&sleep_time); + stgen_cnt = stm32mp_stgen_get_counter(); + + if (mode == STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR) { + /* + * Save non-secure world entrypoint after standby in Backup + * register + */ + mmio_write_32(bkpr_core1_addr, nsec_addr); + mmio_write_32(bkpr_core1_magic, + BOOT_API_A7_CORE0_MAGIC_NUMBER); + + if (stm32_save_context(zq0cr0_zdata, &sleep_time, + stgen_cnt) != 0) { + panic(); + } + + if (stm32mp1_get_retram_enabled()) { + mmio_setbits_32(pwr_base + PWR_CR2, PWR_CR2_RREN); + while ((mmio_read_32(pwr_base + PWR_CR2) & + PWR_CR2_RRRDY) == 0U) { + ; + } + } + + /* Keep backup RAM content in standby */ + mmio_setbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN); + while ((mmio_read_32(pwr_base + PWR_CR2) & + PWR_CR2_BRRDY) == 0U) { + ; + } + } + + clk_disable(RTCAPB); + + enter_cstop_done = true; +} + +bool stm32_is_cstop_done(void) +{ + return enter_cstop_done; +} + +/* + * stm32_exit_cstop - Exit from CSTOP mode + */ +void stm32_exit_cstop(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + unsigned long long stdby_time_in_ms; + struct stm32_rtc_calendar current_calendar; + + if (!enter_cstop_done) { + return; + } + + enter_cstop_done = false; + + stm32mp1_syscfg_enable_io_compensation_start(); + + plat_ic_set_priority_mask(gicc_pmr); + + /* Disable RCC Wake-up */ + mmio_clrbits_32(rcc_base + RCC_MP_CIER, RCC_MP_CIFR_WKUPF); + + /* Disable STOP request */ + mmio_setbits_32(rcc_base + RCC_MP_SREQCLRR, + RCC_MP_SREQSETR_STPREQ_P0 | RCC_MP_SREQSETR_STPREQ_P1); + + dsb(); + isb(); + + /* Disable retention and backup RAM content after stop */ + mmio_clrbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | PWR_CR2_RREN); + + /* Update STGEN counter with low power mode duration */ + stm32_rtc_get_calendar(¤t_calendar); + + stdby_time_in_ms = stm32_rtc_diff_calendar(¤t_calendar, + &sleep_time); + stm32mp_stgen_restore_counter(stgen_cnt, stdby_time_in_ms); + + if (stm32mp1_clock_stopmode_resume() != 0) { + panic(); + } + + stm32mp1_syscfg_enable_io_compensation_finish(); +} + +static int get_locked(volatile int *state) +{ + volatile int val; + + spin_lock(&lp_lock); + val = *state; + spin_unlock(&lp_lock); + + return val; +} + +static void set_locked(volatile int *state, int val) +{ + spin_lock(&lp_lock); + *state = val; + spin_unlock(&lp_lock); +} + +static void smp_synchro(int state, bool wake_up) +{ + /* if the other CPU is stopped, no need to synchronize */ + if (psci_is_last_on_cpu() == 1U) { + return; + } + + if (plat_my_core_pos() == STM32MP_PRIMARY_CPU) { + set_locked(&cpu0_state, state); + + while (get_locked(&cpu1_state) != state) { + if (wake_up) { + /* wakeup secondary CPU */ + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_6, + STM32MP_SECONDARY_CPU); + udelay(10); + } + }; + } else { + while (get_locked(&cpu0_state) != state) { + if (wake_up) { + /* wakeup primary CPU */ + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_6, + STM32MP_PRIMARY_CPU); + udelay(10); + } + }; + + set_locked(&cpu1_state, state); + } +} + +static void stm32_auto_stop_cpu0(void) +{ + smp_synchro(STATE_AUTOSTOP_ENTRY, false); + + enter_cstop(STM32_PM_CSTOP_ALLOW_LP_STOP, 0); + + stm32_pwr_down_wfi(true); + + stm32_exit_cstop(); + + smp_synchro(STATE_AUTOSTOP_EXIT, true); +} + +static void stm32_auto_stop_cpu1(void) +{ + unsigned int gicc_pmr_cpu1; + + /* clear cache before the DDR is being disabled by cpu0 */ + dcsw_op_all(DC_OP_CISW); + + smp_synchro(STATE_AUTOSTOP_ENTRY, false); + + gicc_pmr_cpu1 = plat_ic_set_priority_mask(GICC_PMR_PRIORITY_8); + wfi(); + plat_ic_set_priority_mask(gicc_pmr_cpu1); + + smp_synchro(STATE_AUTOSTOP_EXIT, true); +} + +void stm32_auto_stop(void) +{ + if (plat_my_core_pos() == STM32MP_PRIMARY_CPU) { + stm32_auto_stop_cpu0(); + } else { + stm32_auto_stop_cpu1(); + } +} + +static void enter_shutdown(void) +{ + /* Set DDR in Self-refresh before shutting down the platform */ + if (ddr_standby_sr_entry() != 0) { + WARN("DDR can't be set in Self-refresh mode\n"); + } + + if (dt_pmic_status() > 0) { + if (!initialize_pmic_i2c()) { + panic(); + } + + stpmic1_switch_off(); + + udelay(100); + + /* Shouldn't be reached */ + panic(); + } +} + +static void enter_csleep(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + + mmio_clrsetbits_32(pwr_base + PWR_MPUCR, PWR_MPUCR_MASK, + config_pwr[STM32_PM_CSLEEP_RUN].pwr_mpucr); + mmio_clrsetbits_32(pwr_base + PWR_CR1, PWR_CR1_MASK, + config_pwr[STM32_PM_CSLEEP_RUN].pwr_cr1); + + stm32_pwr_down_wfi(false); +} + +void stm32_enter_low_power(uint32_t mode, uint32_t nsec_addr) +{ + switch (mode) { + case STM32_PM_SHUTDOWN: + enter_shutdown(); + break; + + case STM32_PM_CSLEEP_RUN: + enter_csleep(); + break; + + default: + enter_cstop(mode, nsec_addr); + break; + } +} + +void stm32_init_low_power(void) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t rcc_base = stm32mp_rcc_base(); + + /* + * Configure Standby mode available for MCU by default + * and allow to switch in standby SoC in all case + */ + mmio_setbits_32(pwr_base + PWR_MCUCR, PWR_MCUCR_PDDS); + + /* Disable STOP request */ + mmio_setbits_32(rcc_base + RCC_MP_SREQCLRR, + RCC_MP_SREQSETR_STPREQ_P0 | RCC_MP_SREQSETR_STPREQ_P1); + + /* Disable retention and backup RAM content after standby */ + mmio_clrbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | PWR_CR2_RREN); + + /* Wait 5 HSI periods before re-enabling PLLs after STOP modes */ + mmio_clrsetbits_32(rcc_base + RCC_PWRLPDLYCR, + RCC_PWRLPDLYCR_PWRLP_DLY_MASK, + PWRLP_TEMPO_5_HSI); +} diff --git a/plat/st/stm32mp1/stm32mp1_pm.c b/plat/st/stm32mp1/stm32mp1_pm.c index 31a9ae7f13..962a992243 100644 --- a/plat/st/stm32mp1/stm32mp1_pm.c +++ b/plat/st/stm32mp1/stm32mp1_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -14,14 +14,21 @@ #include #include #include -#include +#include +#include +#include +#include #include #include #include #include +#include +#include + static uintptr_t stm32_sec_entrypoint; static uint32_t cntfrq_core0; +static uintptr_t saved_entrypoint; /******************************************************************************* * STM32MP1 handler called when a CPU is about to enter standby. @@ -34,11 +41,12 @@ static void stm32_cpu_standby(plat_local_state_t cpu_state) assert(cpu_state == ARM_LOCAL_STATE_RET); /* - * Enter standby state - * dsb is good practice before using wfi to enter low power states + * Enter standby state. + * Synchronize on memory accesses and instruction flow before the WFI + * instruction. */ - isb(); dsb(); + isb(); while (interrupt == GIC_SPURIOUS_INTERRUPT) { wfi(); @@ -65,16 +73,29 @@ static int stm32_pwr_domain_on(u_register_t mpidr) uint32_t bkpr_core1_magic = tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + if (stm32mp_is_single_core()) { + return PSCI_E_INTERN_FAIL; + } + if (mpidr == current_cpu_mpidr) { return PSCI_E_INVALID_PARAMS; } + /* Reset backup register content */ + mmio_write_32(bkpr_core1_magic, 0); + + /* Need to send additional IT 0 after individual core 1 reset */ + gicv2_raise_sgi(ARM_IRQ_NON_SEC_SGI_0, STM32MP_SECONDARY_CPU); + + /* Wait for this IT to be acknowledged by ROM code. */ + udelay(10); + /* Only one valid entry point */ if (stm32_sec_entrypoint != (uintptr_t)&sp_min_warm_entrypoint) { return PSCI_E_INVALID_ADDRESS; } - stm32mp_clk_enable(RTCAPB); + clk_enable(RTCAPB); cntfrq_core0 = read_cntfrq_el0(); @@ -84,7 +105,7 @@ static int stm32_pwr_domain_on(u_register_t mpidr) /* Write magic number in backup register */ mmio_write_32(bkpr_core1_magic, BOOT_API_A7_CORE1_MAGIC_NUMBER); - stm32mp_clk_disable(RTCAPB); + clk_disable(RTCAPB); /* Generate an IT to core 1 */ gicv2_raise_sgi(ARM_IRQ_SEC_SGI_0, STM32MP_SECONDARY_CPU); @@ -107,7 +128,9 @@ static void stm32_pwr_domain_off(const psci_power_state_t *target_state) ******************************************************************************/ static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state) { - /* Nothing to do, power domain is not disabled */ + uint32_t soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_SUSPEND); + + stm32_enter_low_power(soc_mode, saved_entrypoint); } /******************************************************************************* @@ -118,7 +141,7 @@ static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state) ******************************************************************************/ static void stm32_pwr_domain_on_finish(const psci_power_state_t *target_state) { - stm32mp1_gic_pcpu_init(); + stm32_gic_pcpu_init(); write_cntfrq_el0(cntfrq_core0); } @@ -134,28 +157,65 @@ static void stm32_pwr_domain_suspend_finish(const psci_power_state_t /* Nothing to do, power domain is not disabled */ } +/******************************************************************************* + * STM32MP1 handler called when a core tries to power itself down. If this + * call is made by core 0, it is a return from stop mode. In this case, we + * should restore previous context and jump to secure entrypoint. + ******************************************************************************/ static void __dead2 stm32_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) { - ERROR("stm32mpu1 Power Down WFI: operation not handled.\n"); + if (MPIDR_AFFLVL0_VAL(read_mpidr_el1()) == STM32MP_PRIMARY_CPU) { + void (*warm_entrypoint)(void) = + (void (*)(void))stm32_sec_entrypoint; + + stm32_pwr_down_wfi(stm32_is_cstop_done()); + + stm32_exit_cstop(); + + disable_mmu_icache_secure(); + + warm_entrypoint(); + } + + mmio_write_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, + RCC_MP_GRSTCSETR_MPUP1RST); + + /* + * Synchronize on memory accesses and instruction flow before + * auto-reset from the WFI instruction. + */ + dsb(); + isb(); + wfi(); + + /* This shouldn't be reached */ panic(); } static void __dead2 stm32_system_off(void) { - ERROR("stm32mpu1 System Off: operation not handled.\n"); + uint32_t soc_mode = stm32mp1_get_lp_soc_mode(PSCI_MODE_SYSTEM_OFF); + + if (!stm32mp_is_single_core()) { + /* Prepare Core 1 reset */ + mmio_setbits_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, + RCC_MP_GRSTCSETR_MPUP1RST); + /* Send IT to core 1 to put itself in WFI */ + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_1, STM32MP_SECONDARY_CPU); + } + + stm32_enter_low_power(soc_mode, 0); + + stm32_pwr_down_wfi(false); + + /* This shouldn't be reached */ panic(); } static void __dead2 stm32_system_reset(void) { - mmio_setbits_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, - RCC_MP_GRSTCSETR_MPSYSRST); - - /* Loop in case system reset is not immediately caught */ - for ( ; ; ) { - ; - } + stm32mp_system_reset(); } static int stm32_validate_power_state(unsigned int power_state, @@ -188,6 +248,8 @@ static int stm32_validate_ns_entrypoint(uintptr_t entrypoint) return PSCI_E_INVALID_ADDRESS; } + saved_entrypoint = entrypoint; + return PSCI_E_SUCCESS; } @@ -211,6 +273,12 @@ static int stm32_node_hw_state(u_register_t target_cpu, return (int)HW_ON; } +static void stm32_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + req_state->pwr_domain_state[0] = ARM_LOCAL_STATE_OFF; + req_state->pwr_domain_state[1] = ARM_LOCAL_STATE_OFF; +} + /******************************************************************************* * Export the platform handlers. The ARM Standard platform layer will take care * of registering the handlers with PSCI. @@ -227,7 +295,8 @@ static const plat_psci_ops_t stm32_psci_ops = { .system_reset = stm32_system_reset, .validate_power_state = stm32_validate_power_state, .validate_ns_entrypoint = stm32_validate_ns_entrypoint, - .get_node_hw_state = stm32_node_hw_state + .get_node_hw_state = stm32_node_hw_state, + .get_sys_suspend_power_state = stm32_get_sys_suspend_power_state, }; /******************************************************************************* diff --git a/plat/st/stm32mp1/stm32mp1_power_config.c b/plat/st/stm32mp1/stm32mp1_power_config.c new file mode 100644 index 0000000000..1b22b3122f --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_power_config.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#define SYSTEM_SUSPEND_SUPPORTED_MODES "system_suspend_supported_soc_modes" +#define SYSTEM_OFF_MODE "system_off_soc_mode" +#define RETRAM_ENABLED "st,retram-enabled-in-standby-ddr-sr" + +static uint32_t deepest_system_suspend_mode; +static uint32_t system_off_mode; +static bool retram_enabled; +static uint8_t stm32mp1_supported_soc_modes[STM32_PM_MAX_SOC_MODE]; + +static int dt_get_pwr_node(void *fdt) +{ + return fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); +} + +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, + count, &supported[0]) < 0) { + ERROR("PWR DT\n"); + panic(); + } + + for (i = 0; i < count; i++) { + if (supported[i] >= STM32_PM_MAX_SOC_MODE) { + ERROR("Invalid mode\n"); + panic(); + } + stm32mp1_supported_soc_modes[supported[i]] = 1U; + } + + /* Initialize to deepest possible mode */ + for (i = STM32_PM_MAX_SOC_MODE - 1U; i != STM32_PM_CSLEEP_RUN; i--) { + if (stm32mp1_supported_soc_modes[i] == 1U) { + deepest_system_suspend_mode = i; + break; + } + } +} + +static int dt_fill_lp_state(uint32_t *lp_state_config, const char *lp_state) +{ + int pwr_node; + void *fdt; + const fdt32_t *cuint; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + pwr_node = dt_get_pwr_node(fdt); + if (pwr_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, pwr_node, lp_state, NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + *lp_state_config = fdt32_to_cpu(*cuint); + + save_supported_mode(fdt, pwr_node); + + return 0; +} + +static int dt_fill_retram_enabled(void) +{ + int pwr_node; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + pwr_node = dt_get_pwr_node(fdt); + if (pwr_node < 0) { + return -ENOENT; + } + + if (fdt_getprop(fdt, pwr_node, RETRAM_ENABLED, NULL) == NULL) { + retram_enabled = false; + } else { + retram_enabled = true; + } + + return 0; +} + +void stm32mp1_init_lp_states(void) +{ + if (dt_fill_lp_state(&system_off_mode, SYSTEM_OFF_MODE) < 0) { + ERROR("Node %s not found\n", SYSTEM_OFF_MODE); + panic(); + } + + if (dt_fill_retram_enabled() < 0) { + ERROR("could not configure retram state\n"); + panic(); + } +} + +/* Init with all domains ON */ +static bool pm_dom[STM32MP1_PD_MAX_PM_DOMAIN] = { + [STM32MP1_PD_VSW] = false, + [STM32MP1_PD_CORE_RET] = false, + [STM32MP1_PD_CORE] = false +}; + +static bool stm32mp1_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 &= 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 -EINVAL; + } + + pm_dom[domain] = status; + + return 0; +} + +static bool is_allowed_mode(uint32_t soc_mode) +{ + assert(soc_mode < ARRAY_SIZE(stm32mp1_supported_soc_modes)); + + if ((soc_mode == STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR) && + !stm32mp1_get_pm_domain_state(STM32MP1_PD_CORE_RET)) { + return false; + } + + if ((soc_mode == STM32_PM_CSTOP_ALLOW_LPLV_STOP) && + !stm32mp1_get_pm_domain_state(STM32MP1_PD_CORE)) { + return false; + } + + return stm32mp1_supported_soc_modes[soc_mode] == 1U; +} + +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_system_suspend_mode; + + while ((mode > STM32_PM_CSLEEP_RUN) && !is_allowed_mode(mode)) { + mode--; + } + + 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 -EINVAL; + } + + if (psci_mode == PSCI_MODE_SYSTEM_SUSPEND) { + deepest_system_suspend_mode = soc_mode; + } + + if (psci_mode == PSCI_MODE_SYSTEM_OFF) { + system_off_mode = soc_mode; + } + + return 0; +} + +bool stm32mp1_get_retram_enabled(void) +{ + return retram_enabled; +} diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c index bc77ee3342..09afe1a6ea 100644 --- a/plat/st/stm32mp1/stm32mp1_private.c +++ b/plat/st/stm32mp1/stm32mp1_private.c @@ -10,8 +10,18 @@ #include +#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include +#include /* Internal layout of the 32bit OTP word board_id */ #define BOARD_ID_BOARD_NB_MASK GENMASK(31, 16) @@ -34,6 +44,10 @@ BOARD_ID_VARFG_SHIFT) #define BOARD_ID2BOM(_id) ((_id) & BOARD_ID_BOM_MASK) +#define TAMP_BOOT_ITF_BACKUP_REG_ID U(20) +#define TAMP_BOOT_ITF_MASK U(0x0000FF00) +#define TAMP_BOOT_ITF_SHIFT 8 + #if defined(IMAGE_BL2) #define MAP_SEC_SYSRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ STM32MP_SYSRAM_SIZE, \ @@ -58,6 +72,13 @@ MT_EXECUTE_NEVER) #endif +#define MAP_RETRAM MAP_REGION_FLAT(STM32MP_RETRAM_BASE, \ + STM32MP_RETRAM_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_NS | \ + MT_EXECUTE_NEVER) + #define MAP_DEVICE1 MAP_REGION_FLAT(STM32MP1_DEVICE1_BASE, \ STM32MP1_DEVICE1_SIZE, \ MT_DEVICE | \ @@ -76,14 +97,18 @@ static const mmap_region_t stm32mp1_mmap[] = { MAP_SEC_SYSRAM, MAP_DEVICE1, +#if STM32MP_RAW_NAND MAP_DEVICE2, +#endif {0} }; #endif #if defined(IMAGE_BL32) static const mmap_region_t stm32mp1_mmap[] = { +#if !STM32MP_SP_MIN_IN_DDR MAP_SEC_SYSRAM, MAP_NS_SYSRAM, +#endif MAP_DEVICE1, MAP_DEVICE2, {0} @@ -92,10 +117,156 @@ static const mmap_region_t stm32mp1_mmap[] = { void configure_mmu(void) { +#ifndef MMU_OFF + unsigned int flags = 0; + mmap_add(stm32mp1_mmap); init_xlat_tables(); +#ifdef DCACHE_OFF + flags |= DISABLE_DCACHE; +#endif + enable_mmu_svc_mon(flags); +#endif +} + +#define ARM_CNTXCTL_IMASK BIT(1) + +void stm32mp_mask_timer(void) +{ + /* Mask timer interrupts */ + write_cntp_ctl(read_cntp_ctl() | ARM_CNTXCTL_IMASK); + write_cntv_ctl(read_cntv_ctl() | ARM_CNTXCTL_IMASK); +} + +void __dead2 stm32mp_wait_cpu_reset(void) +{ + uint32_t id; + + dcsw_op_all(DC_OP_CISW); + write_sctlr(read_sctlr() & ~SCTLR_C_BIT); + dcsw_op_all(DC_OP_CISW); + __asm__("clrex"); + + dsb(); + isb(); + + for ( ; ; ) { + do { + id = plat_ic_get_pending_interrupt_id(); + + if (id <= MAX_SPI_ID) { + gicv2_end_of_interrupt(id); + + plat_ic_disable_interrupt(id); + } + } while (id <= MAX_SPI_ID); + + wfi(); + } +} + +/* + * tzc_source_ip contains the TZC transaction source IPs that need to be reset + * before a C-A7 subsystem is reset (i.e. independent reset): + * - C-A7 subsystem is reset separately later in the sequence, + * - C-M4 subsystem is not concerned here, + * - DAP is excluded for debug purpose, + * - IPs are stored with their ETZPC IDs (STM32MP1_ETZPC_MAX_ID if not + * applicable) because some of them need to be reset only if they are not + * configured in MCU isolation mode inside ETZPC device tree. + */ +struct tzc_source_ip { + uint32_t reset_id; + uint32_t clock_id; + uint32_t decprot_id; +}; + +#define _TZC_FIXED(res, clk) \ + { \ + .reset_id = (res), \ + .clock_id = (clk), \ + .decprot_id = STM32MP1_ETZPC_MAX_ID, \ + } + +#define _TZC_COND(res, clk, decprot) \ + { \ + .reset_id = (res), \ + .clock_id = (clk), \ + .decprot_id = (decprot), \ + } + +static const struct tzc_source_ip tzc_source_ip[] = { + _TZC_FIXED(LTDC_R, LTDC_PX), + _TZC_FIXED(GPU_R, GPU), + _TZC_FIXED(USBH_R, USBH), + _TZC_FIXED(SDMMC1_R, SDMMC1_K), + _TZC_FIXED(SDMMC2_R, SDMMC2_K), + _TZC_FIXED(MDMA_R, MDMA), + _TZC_COND(USBO_R, USBO_K, STM32MP1_ETZPC_OTG_ID), + _TZC_COND(SDMMC3_R, SDMMC3_K, STM32MP1_ETZPC_SDMMC3_ID), + _TZC_COND(ETHMAC_R, ETHMAC, STM32MP1_ETZPC_ETH_ID), + _TZC_COND(DMA1_R, DMA1, STM32MP1_ETZPC_DMA1_ID), + _TZC_COND(DMA2_R, DMA2, STM32MP1_ETZPC_DMA2_ID), +}; - enable_mmu_svc_mon(0); +#define TIMEOUT_US_1MS U(1000) + +void __dead2 stm32mp_plat_reset(int cpu) +{ + uint32_t reg = RCC_MP_GRSTCSETR_MPUP0RST; + uint32_t id; + + /* Mask timer interrupts */ + stm32mp_mask_timer(); + + for (id = 0U; id < ARRAY_SIZE(tzc_source_ip); id++) { + if ((!clk_is_enabled(tzc_source_ip[id].clock_id)) || + ((tzc_source_ip[id].decprot_id != STM32MP1_ETZPC_MAX_ID) && + (etzpc_get_decprot(tzc_source_ip[id].decprot_id) == + ETZPC_DECPROT_MCU_ISOLATION))) { + continue; + } + + if (tzc_source_ip[id].reset_id != GPU_R) { + uint32_t reset = tzc_source_ip[id].reset_id; + int ret; + + ret = stm32mp_reset_assert(reset, TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + ret = stm32mp_reset_deassert(reset, TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + } else { + /* GPU reset automatically cleared by hardware */ + mmio_setbits_32(stm32mp_rcc_base() + RCC_AHB6RSTSETR, + RCC_AHB6RSTSETR_GPURST); + } + } + + if (!stm32mp_is_single_core()) { + unsigned int sec_cpu = (cpu == STM32MP_PRIMARY_CPU) ? + STM32MP_SECONDARY_CPU : STM32MP_PRIMARY_CPU; + + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_1, sec_cpu); + reg |= RCC_MP_GRSTCSETR_MPUP1RST; + } + + do { + id = plat_ic_get_pending_interrupt_id(); + + if (id <= MAX_SPI_ID) { + gicv2_end_of_interrupt(id); + + plat_ic_disable_interrupt(id); + } + } while (id <= MAX_SPI_ID); + + mmio_write_32(stm32mp_rcc_base() + RCC_MP_GRSTCSETR, reg); + + stm32mp_wait_cpu_reset(); } uintptr_t stm32_get_gpio_bank_base(unsigned int bank) @@ -120,6 +291,32 @@ uint32_t stm32_get_gpio_bank_offset(unsigned int bank) return bank * GPIO_BANK_OFFSET; } +#if STM32MP_UART_PROGRAMMER || defined(IMAGE_BL32) +/* + * UART Management + */ +static const uintptr_t stm32mp1_uart_addresses[8] = { + USART1_BASE, + USART2_BASE, + USART3_BASE, + UART4_BASE, + UART5_BASE, + USART6_BASE, + UART7_BASE, + UART8_BASE, +}; + +uintptr_t get_uart_address(uint32_t instance_nb) +{ + if ((instance_nb == 0U) || + (instance_nb > ARRAY_SIZE(stm32mp1_uart_addresses))) { + return 0U; + } + + return stm32mp1_uart_addresses[instance_nb - 1U]; +} +#endif + unsigned long stm32_get_gpio_bank_clock(unsigned int bank) { if (bank == GPIO_BANK_Z) { @@ -153,63 +350,138 @@ int stm32_get_gpio_bank_pinctrl_node(void *fdt, unsigned int bank) } } -static int get_part_number(uint32_t *part_nb) +int stm32_get_otp_index(const char *otp_name, uint32_t *otp_idx, + uint32_t *otp_len) { - uint32_t part_number; - uint32_t dev_id; + assert(otp_name != NULL); + assert(otp_idx != NULL); + + if (bsec_find_otp_name_in_dt(otp_name, otp_idx, otp_len) != BSEC_OK) { + return -1; + } - assert(part_nb != NULL); + return 0; +} - if (stm32mp1_dbgmcu_get_chip_dev_id(&dev_id) < 0) { +int stm32_get_otp_value(const char *otp_name, uint32_t *otp_val) +{ + uint32_t otp_idx; + + assert(otp_name != NULL); + assert(otp_val != NULL); + + if (stm32_get_otp_index(otp_name, &otp_idx, NULL) != 0) { + return -1; + } + + if (stm32_get_otp_value_from_idx(otp_idx, otp_val) != 0) { + ERROR("BSEC: %s Read Error\n", otp_name); return -1; } - if (bsec_shadow_read_otp(&part_number, PART_NUMBER_OTP) != BSEC_OK) { - ERROR("BSEC: PART_NUMBER_OTP Error\n"); + return 0; +} + +int stm32_get_otp_value_from_idx(const uint32_t otp_idx, uint32_t *otp_val) +{ + int ret = BSEC_NOT_SUPPORTED; + + assert(otp_val != NULL); + +#if defined(IMAGE_BL2) + ret = bsec_shadow_read_otp(otp_val, otp_idx); +#elif defined(IMAGE_BL32) + ret = bsec_read_otp(otp_val, otp_idx); +#else +#error "Not supported" +#endif + if (ret != BSEC_OK) { + ERROR("BSEC: idx=%d Read Error\n", otp_idx); return -1; } + return 0; +} + +int stm32mp_get_chip_version(uint32_t *chip_version) +{ + return stm32mp1_dbgmcu_get_chip_version(chip_version); +} + +static uint32_t get_part_number(void) +{ + static uint32_t part_number; + uint32_t dev_id; + + if (part_number != 0U) { + return part_number; + } + + if (stm32mp1_dbgmcu_get_chip_dev_id(&dev_id) < 0) { + INFO("Use default chip ID, debug disabled\n"); + dev_id = STM32MP1_CHIP_ID; + } + + if (stm32_get_otp_value(PART_NUMBER_OTP, &part_number) != 0) { + panic(); + } + part_number = (part_number & PART_NUMBER_OTP_PART_MASK) >> PART_NUMBER_OTP_PART_SHIFT; - *part_nb = part_number | (dev_id << 16); + part_number |= dev_id << 16; - return 0; + return part_number; } -static int get_cpu_package(uint32_t *cpu_package) +static uint32_t get_cpu_package(void) { uint32_t package; - assert(cpu_package != NULL); - - if (bsec_shadow_read_otp(&package, PACKAGE_OTP) != BSEC_OK) { - ERROR("BSEC: PACKAGE_OTP Error\n"); - return -1; + if (stm32_get_otp_value(PACKAGE_OTP, &package) != 0) { + panic(); } - *cpu_package = (package & PACKAGE_OTP_PKG_MASK) >> + package = (package & PACKAGE_OTP_PKG_MASK) >> PACKAGE_OTP_PKG_SHIFT; - return 0; + return package; } -void stm32mp_print_cpuinfo(void) +bool stm32mp_supports_cpu_opp(uint32_t opp_id) +{ + uint32_t id; + + switch (opp_id) { + case PLAT_OPP_ID1: + case PLAT_OPP_ID2: + id = opp_id; + break; + default: + return false; + } + + switch (get_part_number()) { + case STM32MP157F_PART_NB: + case STM32MP157D_PART_NB: + case STM32MP153F_PART_NB: + case STM32MP153D_PART_NB: + case STM32MP151F_PART_NB: + case STM32MP151D_PART_NB: + return true; + default: + return id == PLAT_OPP_ID1; + } +} + +void stm32mp_get_soc_name(char name[STM32_SOC_NAME_SIZE]) { - const char *cpu_s, *cpu_r, *pkg; - uint32_t part_number; - uint32_t cpu_package; + char *cpu_s, *cpu_r, *pkg; uint32_t chip_dev_id; int ret; /* MPUs Part Numbers */ - ret = get_part_number(&part_number); - if (ret < 0) { - WARN("Cannot get part number\n"); - return; - } - - switch (part_number) { + switch (get_part_number()) { case STM32MP157C_PART_NB: cpu_s = "157C"; break; @@ -252,13 +524,7 @@ void stm32mp_print_cpuinfo(void) } /* Package */ - ret = get_cpu_package(&cpu_package); - if (ret < 0) { - WARN("Cannot get CPU package\n"); - return; - } - - switch (cpu_package) { + switch (get_cpu_package()) { case PKG_AA_LFBGA448: pkg = "AA"; break; @@ -277,10 +543,9 @@ void stm32mp_print_cpuinfo(void) } /* REVISION */ - ret = stm32mp1_dbgmcu_get_chip_version(&chip_dev_id); + ret = stm32mp_get_chip_version(&chip_dev_id); if (ret < 0) { - WARN("Cannot get CPU version\n"); - return; + INFO("Cannot get CPU version, debug disabled\n"); } switch (chip_dev_id) { @@ -295,40 +560,23 @@ void stm32mp_print_cpuinfo(void) break; } - NOTICE("CPU: STM32MP%s%s Rev.%s\n", cpu_s, pkg, cpu_r); + snprintf(name, STM32_SOC_NAME_SIZE, + "STM32MP%s%s Rev.%s", cpu_s, pkg, cpu_r); } -void stm32mp_print_boardinfo(void) +void stm32mp_print_cpuinfo(void) { - uint32_t board_id; - uint32_t board_otp; - int bsec_node, bsec_board_id_node; - void *fdt; - const fdt32_t *cuint; - - if (fdt_get_address(&fdt) == 0) { - panic(); - } + char name[STM32_SOC_NAME_SIZE]; - bsec_node = fdt_node_offset_by_compatible(fdt, -1, DT_BSEC_COMPAT); - if (bsec_node < 0) { - return; - } - - bsec_board_id_node = fdt_subnode_offset(fdt, bsec_node, "board_id"); - if (bsec_board_id_node <= 0) { - return; - } - - cuint = fdt_getprop(fdt, bsec_board_id_node, "reg", NULL); - if (cuint == NULL) { - panic(); - } + stm32mp_get_soc_name(name); + NOTICE("CPU: %s\n", name); +} - board_otp = fdt32_to_cpu(*cuint) / sizeof(uint32_t); +void stm32mp_print_boardinfo(void) +{ + uint32_t board_id = 0; - if (bsec_shadow_read_otp(&board_id, board_otp) != BSEC_OK) { - ERROR("BSEC: PART_NUMBER_OTP Error\n"); + if (stm32_get_otp_value(BOARD_ID_OTP, &board_id) != 0) { return; } @@ -349,20 +597,12 @@ void stm32mp_print_boardinfo(void) /* Return true when SoC provides a single Cortex-A7 core, and false otherwise */ bool stm32mp_is_single_core(void) { - uint32_t part_number; - - if (get_part_number(&part_number) < 0) { - ERROR("Invalid part number, assume single core chip"); - return true; - } - - switch (part_number) { + switch (get_part_number()) { case STM32MP151A_PART_NB: case STM32MP151C_PART_NB: case STM32MP151D_PART_NB: case STM32MP151F_PART_NB: return true; - default: return false; } @@ -373,12 +613,27 @@ bool stm32mp_is_closed_device(void) { uint32_t value; - if ((bsec_shadow_register(DATA0_OTP) != BSEC_OK) || - (bsec_read_otp(&value, DATA0_OTP) != BSEC_OK)) { + if (stm32_get_otp_value(CFG0_OTP, &value) != 0) { return true; } - return (value & DATA0_OTP_SECURED) == DATA0_OTP_SECURED; + return (value & CFG0_CLOSED_DEVICE) == CFG0_CLOSED_DEVICE; +} + +/* Return true when device supports secure boot */ +bool stm32mp_is_auth_supported(void) +{ + switch (get_part_number()) { + case STM32MP151C_PART_NB: + case STM32MP151F_PART_NB: + case STM32MP153C_PART_NB: + case STM32MP153F_PART_NB: + case STM32MP157C_PART_NB: + case STM32MP157F_PART_NB: + return true; + default: + return false; + } } uint32_t stm32_iwdg_get_instance(uintptr_t base) @@ -398,13 +653,7 @@ uint32_t stm32_iwdg_get_otp_config(uint32_t iwdg_inst) uint32_t iwdg_cfg = 0U; uint32_t otp_value; -#if defined(IMAGE_BL2) - if (bsec_shadow_register(HW2_OTP) != BSEC_OK) { - panic(); - } -#endif - - if (bsec_read_otp(&otp_value, HW2_OTP) != BSEC_OK) { + if (stm32_get_otp_value(HW2_OTP, &otp_value) != 0) { panic(); } @@ -426,29 +675,34 @@ uint32_t stm32_iwdg_get_otp_config(uint32_t iwdg_inst) #if defined(IMAGE_BL2) uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags) { + uint32_t otp_value; uint32_t otp; uint32_t result; - if (bsec_shadow_read_otp(&otp, HW2_OTP) != BSEC_OK) { + if (stm32_get_otp_index(HW2_OTP, &otp, NULL) != 0) { + panic(); + } + + if (stm32_get_otp_value(HW2_OTP, &otp_value) != 0) { panic(); } - if ((flags & IWDG_DISABLE_ON_STOP) != 0U) { - otp |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STOP_POS); + if ((flags & IWDG_DISABLE_ON_STOP) != 0) { + otp_value |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STOP_POS); } - if ((flags & IWDG_DISABLE_ON_STANDBY) != 0U) { - otp |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STANDBY_POS); + if ((flags & IWDG_DISABLE_ON_STANDBY) != 0) { + otp_value |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STANDBY_POS); } - result = bsec_write_otp(otp, HW2_OTP); + result = bsec_write_otp(otp_value, otp); if (result != BSEC_OK) { return result; } /* Sticky lock OTP_IWDG (read and write) */ - if (!bsec_write_sr_lock(HW2_OTP, 1U) || - !bsec_write_sw_lock(HW2_OTP, 1U)) { + if ((bsec_set_sr_lock(otp) != BSEC_OK) || + (bsec_set_sw_lock(otp) != BSEC_OK)) { return BSEC_LOCK_FAIL; } @@ -456,6 +710,52 @@ uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags) } #endif +/* + * 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 ETZPC_DECPROT_S_RW; + case DECPROT_NS_R_S_W: + return ETZPC_DECPROT_NS_R_S_W; + case DECPROT_MCU_ISOLATION: + return ETZPC_DECPROT_MCU_ISOLATION; + case DECPROT_NS_RW: + return ETZPC_DECPROT_NS_RW; + default: + panic(); + } +} + +int plat_bind_regulator(struct stm32mp_regulator *regu) +{ + void *fdt; + int regu_node; + + if (fdt_get_address(&fdt) == 0) { + return false; + } + + if ((dt_pmic_status() > 0) && is_pmic_regulator(regu)) { + bind_pmic_regulator(regu); + } else { + bind_dummy_regulator(regu); + } + + regu_node = fdt_node_offset_by_phandle(fdt, regu->id); + if (fdt_getprop(fdt, regu_node, "regulator-always-on", NULL) != NULL) { + regu->always_on = true; + } + + return 0; +} + +#if STM32MP_USE_STM32IMAGE /* Get the non-secure DDR size */ uint32_t stm32mp_get_ddr_ns_size(void) { @@ -467,12 +767,145 @@ uint32_t stm32mp_get_ddr_ns_size(void) } ddr_size = dt_get_ddr_size(); - if ((ddr_size <= (STM32MP_DDR_S_SIZE + STM32MP_DDR_SHMEM_SIZE)) || - (ddr_size > STM32MP_DDR_MAX_SIZE)) { + if ((ddr_size <= STM32MP_DDR_S_SIZE) || (ddr_size > STM32MP_DDR_MAX_SIZE)) { panic(); } +#if defined(AARCH32_SP_OPTEE) ddr_ns_size = ddr_size - (STM32MP_DDR_S_SIZE + STM32MP_DDR_SHMEM_SIZE); +#else + ddr_ns_size = ddr_size; +#endif return ddr_ns_size; } +#endif + +bool stm32mp1_addr_inside_backupsram(uintptr_t addr) +{ + return (addr >= STM32MP_BACKUP_RAM_BASE) && + (addr < (STM32MP_BACKUP_RAM_BASE + STM32MP_BACKUP_RAM_SIZE)); +} + +bool stm32mp1_is_wakeup_from_standby(void) +{ + uint32_t bkpr_core1_addr = tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t nsec_address; + + if (stm32mp_get_boot_action() != BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY) { + return false; + } + + clk_enable(RTCAPB); + nsec_address = mmio_read_32(bkpr_core1_addr); + clk_disable(RTCAPB); + + if (nsec_address == 0U) { + return false; + } + + return stm32_pm_context_is_valid(); +} + +int stm32_save_boot_interface(uint32_t interface, uint32_t instance) +{ + uint32_t bkpr_itf_idx = tamp_bkpr(TAMP_BOOT_ITF_BACKUP_REG_ID); + + clk_enable(RTCAPB); + + mmio_clrsetbits_32(bkpr_itf_idx, + TAMP_BOOT_ITF_MASK, + ((interface << 4) | (instance & 0xFU)) << + TAMP_BOOT_ITF_SHIFT); + + clk_disable(RTCAPB); + + return 0; +} + +int stm32_get_boot_interface(uint32_t *interface, uint32_t *instance) +{ + static uint32_t itf; + + if (itf == 0U) { + uint32_t bkpr = tamp_bkpr(TAMP_BOOT_ITF_BACKUP_REG_ID); + + clk_enable(RTCAPB); + + itf = (mmio_read_32(bkpr) & TAMP_BOOT_ITF_MASK) >> TAMP_BOOT_ITF_SHIFT; + + clk_disable(RTCAPB); + } + + *interface = itf >> 4; + *instance = itf & 0xFU; + + return 0; +} + +bool stm32_boot_is_serial(void) +{ + uint32_t boot_itf; + uint32_t boot_instance; + + stm32_get_boot_interface(&boot_itf, &boot_instance); + + if ((boot_itf == BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART) || + (boot_itf == BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB)) { + return true; + } + + return false; +} + +#if defined(IMAGE_BL32) && DEBUG +static const char *const dump_table[] = { + "sp_usr ", + "lr_usr ", + "spsr_irq", + "sp_irq ", + "lr_irq ", + "spsr_fiq", + "sp_fiq ", + "lr_fiq ", + "spsr_svc", + "sp_svc ", + "lr_svc ", + "spsr_abt", + "sp_abt ", + "lr_abt ", + "spsr_und", + "sp_und ", + "lr_und ", + "spsr_mon", + "sp_mon", + "lr_mon", + "scr", + "pmcr", +}; + +/* + * Dump CPU registers when entering in monitor. + */ +void stm32mp_dump_core_registers(bool fcore) +{ + static bool firstcore; + unsigned int i; + smc_ctx_t *ctx = smc_get_ctx(NON_SECURE); + uint32_t *reg = (uint32_t *)&ctx->sp_usr; + + if (fcore) { + firstcore = true; + } + + if (!firstcore) { + return; + } + + INFO("CPU : %i\n", plat_my_core_pos()); + + for (i = 0U; i < ARRAY_SIZE(dump_table); i++) { + INFO("%s : 0x%x\n", dump_table[i], reg[i]); + } +} +#endif diff --git a/plat/st/stm32mp1/stm32mp1_scmi.c b/plat/st/stm32mp1/stm32mp1_scmi.c index 80faf0c6e0..a6086f93fd 100644 --- a/plat/st/stm32mp1/stm32mp1_scmi.c +++ b/plat/st/stm32mp1/stm32mp1_scmi.c @@ -8,12 +8,14 @@ #include +#include #include #include #include #include #include #include +#include #define TIMEOUT_US_1MS 1000U @@ -124,6 +126,7 @@ static struct stm32_scmi_rstd stm32_scmi0_reset_domain[] = { RESET_CELL(RST_SCMI0_RNG1, RNG1_R, "rng1"), RESET_CELL(RST_SCMI0_MDMA, MDMA_R, "mdma"), RESET_CELL(RST_SCMI0_MCU, MCU_R, "mcu"), + RESET_CELL(RST_SCMI0_MCU_HOLD_BOOT, MCU_HOLD_BOOT_R, "mcu_hold_boot"), }; struct scmi_agent_resources { @@ -260,6 +263,17 @@ const char *plat_scmi_clock_get_name(unsigned int agent_id, int32_t plat_scmi_clock_rates_array(unsigned int agent_id, unsigned int scmi_id, unsigned long *array, size_t *nb_elts) +{ + /* + * Do not expose clock rates by array since not supported by + * Linux kernel + */ + return SCMI_NOT_SUPPORTED; +} + +int32_t plat_scmi_clock_rates_by_step(unsigned int agent_id, + unsigned int scmi_id, + unsigned long *array) { struct stm32_scmi_clk *clock = find_clock(agent_id, scmi_id); @@ -271,12 +285,50 @@ int32_t plat_scmi_clock_rates_array(unsigned int agent_id, unsigned int scmi_id, return SCMI_DENIED; } - if (array == NULL) { - *nb_elts = 1U; - } else if (*nb_elts == 1U) { - *array = stm32mp_clk_get_rate(clock->clock_id); - } else { - return SCMI_GENERIC_ERROR; + switch (scmi_id) { + case CK_SCMI0_MPU: + /* + * Pretend we support all rates for MPU clock, + * CLOCK_RATE_SET will reject unsupported rates. + */ + array[0] = 0U; + array[1] = UINT32_MAX; + array[2] = 1U; + break; + default: + array[0] = clk_get_rate(clock->clock_id); + array[1] = array[0]; + array[2] = 0U; + break; + } + return SCMI_SUCCESS; +} + +int32_t plat_scmi_clock_set_rate(unsigned int agent_id, + unsigned int scmi_id, + unsigned long rate) +{ + struct stm32_scmi_clk *clock = find_clock(agent_id, scmi_id); + + if (clock == NULL) { + return SCMI_NOT_FOUND; + } + + if (!stm32mp_nsec_can_access_clock(clock->clock_id)) { + return SCMI_DENIED; + } + + switch (scmi_id) { + case CK_SCMI0_MPU: + if (stm32mp1_set_opp_khz(rate / 1000UL) != 0) { + return SCMI_INVALID_PARAMETERS; + } + break; + default: + if (rate != clk_get_rate(clock->clock_id)) { + return SCMI_INVALID_PARAMETERS; + } + break; } return SCMI_SUCCESS; @@ -292,7 +344,7 @@ unsigned long plat_scmi_clock_get_rate(unsigned int agent_id, return 0U; } - return stm32mp_clk_get_rate(clock->clock_id); + return clk_get_rate(clock->clock_id); } int32_t plat_scmi_clock_get_state(unsigned int agent_id, unsigned int scmi_id) @@ -323,13 +375,13 @@ int32_t plat_scmi_clock_set_state(unsigned int agent_id, unsigned int scmi_id, if (enable_not_disable) { if (!clock->enabled) { VERBOSE("SCMI clock %u enable\n", scmi_id); - stm32mp_clk_enable(clock->clock_id); + clk_enable(clock->clock_id); clock->enabled = true; } } else { if (clock->enabled) { VERBOSE("SCMI clock %u disable\n", scmi_id); - stm32mp_clk_disable(clock->clock_id); + clk_disable(clock->clock_id); clock->enabled = false; } } @@ -388,6 +440,10 @@ int32_t plat_scmi_rstd_autonomous(unsigned int agent_id, unsigned int scmi_id, return SCMI_NOT_FOUND; } + if (rstd->reset_id == MCU_HOLD_BOOT_R) { + return SCMI_NOT_SUPPORTED; + } + if (!stm32mp_nsec_can_access_reset(rstd->reset_id)) { return SCMI_DENIED; } @@ -423,6 +479,13 @@ int32_t plat_scmi_rstd_set_state(unsigned int agent_id, unsigned int scmi_id, return SCMI_DENIED; } + if (rstd->reset_id == MCU_HOLD_BOOT_R) { + VERBOSE("SCMI MCU reset %s\n", + assert_not_deassert ? "set" : "release"); + stm32mp_reset_assert_deassert_to_mcu(assert_not_deassert); + return SCMI_SUCCESS; + } + if (assert_not_deassert) { VERBOSE("SCMI reset %lu set\n", rstd->reset_id); stm32mp_reset_set(rstd->reset_id); @@ -461,7 +524,7 @@ void stm32mp1_init_scmi_server(void) /* Sync SCMI clocks with their targeted initial state */ if (clk->enabled && stm32mp_nsec_can_access_clock(clk->clock_id)) { - stm32mp_clk_enable(clk->clock_id); + clk_enable(clk->clock_id); } } @@ -476,3 +539,51 @@ void stm32mp1_init_scmi_server(void) } } } + +/* + * Save and restore SCMI state since lost during suspend. + * Only clock enabled field needs to be updated. + */ +void stm32mp1_pm_save_scmi_state(uint8_t *state, size_t size) +{ + size_t i; + size_t j; + size_t cnt = 0U; + + zeromem(state, size); + + for (i = 0U; i < ARRAY_SIZE(agent_resources); i++) { + for (j = 0U; j < agent_resources[i].clock_count; j++) { + if ((cnt / 8) > size) { + VERBOSE("state table too small\n"); + panic(); + } + + if (agent_resources[i].clock[j].enabled) { + *(state + (cnt / 8)) |= (uint8_t)BIT(cnt % 8); + } + + cnt++; + } + } +} + +void stm32mp1_pm_restore_scmi_state(uint8_t *state, size_t size) +{ + size_t i; + size_t j; + size_t cnt = 0U; + + for (i = 0U; i < ARRAY_SIZE(agent_resources); i++) { + for (j = 0U; j < agent_resources[i].clock_count; j++) { + if ((*(state + (cnt / 8)) & BIT(cnt % 8)) == 0U) { + agent_resources[i].clock[j].enabled = 0; + } else { + agent_resources[i].clock[j].enabled = 1; + } + + assert((cnt / 8) <= size); + cnt++; + } + } +} diff --git a/plat/st/stm32mp1/stm32mp1_security.c b/plat/st/stm32mp1/stm32mp1_security.c index 3a29ba9661..b1838db26e 100644 --- a/plat/st/stm32mp1/stm32mp1_security.c +++ b/plat/st/stm32mp1/stm32mp1_security.c @@ -10,22 +10,50 @@ #include #include +#include #include #include +#include #include -#define TZC_REGION_NSEC_ALL_ACCESS_RDWR \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_GPU_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_LCD_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_MDMA_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_M4_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DMA_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_HOST_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_OTG_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_ETH_ID) | \ - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DAP_ID) +static unsigned int region_nb; + +static void init_tzc400_begin(unsigned int region0_attr) +{ + tzc400_init(STM32MP1_TZC_BASE); + tzc400_disable_filters(); + + /* Region 0 set to cover all DRAM at 0xC000_0000 */ + tzc400_configure_region0(region0_attr, 0); + + region_nb = 1U; +} + +static void init_tzc400_end(unsigned int action) +{ + tzc400_set_action(action); + tzc400_enable_filters(); +} + +static void tzc400_add_region(unsigned long long region_base, + unsigned long long region_top, bool sec) +{ + unsigned int sec_attr; + unsigned int nsaid_permissions; + + if (sec) { + sec_attr = TZC_REGION_S_RDWR; + nsaid_permissions = 0; + } else { + sec_attr = TZC_REGION_S_NONE; + nsaid_permissions = TZC_REGION_NSEC_ALL_ACCESS_RDWR; + } + + tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, region_nb, region_base, + region_top, sec_attr, nsaid_permissions); + + region_nb++; +} /******************************************************************************* * Initialize the TrustZone Controller. Configure Region 0 with Secure RW access @@ -38,10 +66,9 @@ static void init_tzc400(void) unsigned long long ddr_ns_size = (unsigned long long)stm32mp_get_ddr_ns_size(); unsigned long long ddr_ns_top = ddr_base + (ddr_ns_size - 1U); + unsigned long long ddr_top __unused; - tzc400_init(STM32MP1_TZC_BASE); - - tzc400_disable_filters(); + init_tzc400_begin(TZC_REGION_S_NONE); /* * Region 1 set to cover all non-secure DRAM at 0xC000_0000. Apply the @@ -49,36 +76,28 @@ static void init_tzc400(void) */ region_base = ddr_base; region_top = ddr_ns_top; - tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 1, - region_base, - region_top, - TZC_REGION_S_NONE, - TZC_REGION_NSEC_ALL_ACCESS_RDWR); + tzc400_add_region(region_base, region_top, false); -#ifdef AARCH32_SP_OPTEE +#if defined(AARCH32_SP_OPTEE) /* Region 2 set to cover all secure DRAM. */ region_base = region_top + 1U; region_top += STM32MP_DDR_S_SIZE; - tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 2, - region_base, - region_top, - TZC_REGION_S_RDWR, - 0); - - /* Region 3 set to cover non-secure shared memory DRAM. */ - region_base = region_top + 1U; - region_top += STM32MP_DDR_SHMEM_SIZE; - tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 3, - region_base, - region_top, - TZC_REGION_S_NONE, - TZC_REGION_NSEC_ALL_ACCESS_RDWR); + tzc400_add_region(region_base, region_top, true); + + ddr_top = STM32MP_DDR_BASE + dt_get_ddr_size() - 1U; + if (region_top < ddr_top) { + /* Region 3 set to cover non-secure memory DRAM after BL32. */ + region_base = region_top + 1U; + region_top = ddr_top; + tzc400_add_region(region_base, region_top, false); + } #endif - /* Raise an exception if a NS device tries to access secure memory */ - tzc400_set_action(TZC_ACTION_ERR); - - tzc400_enable_filters(); + /* + * Raise an interrupt (secure FIQ) if a NS device tries to access + * secure memory + */ + init_tzc400_end(TZC_ACTION_INT); } /******************************************************************************* @@ -88,26 +107,14 @@ static void init_tzc400(void) ******************************************************************************/ static void early_init_tzc400(void) { - stm32mp_clk_enable(TZC1); - stm32mp_clk_enable(TZC2); - - tzc400_init(STM32MP1_TZC_BASE); + clk_enable(TZC1); + clk_enable(TZC2); - tzc400_disable_filters(); - - /* Region 1 set to cover Non-Secure DRAM at 0xC000_0000 */ - tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 1, - STM32MP_DDR_BASE, - STM32MP_DDR_BASE + - (STM32MP_DDR_MAX_SIZE - 1U), - TZC_REGION_S_NONE, - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID) | - TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID)); + /* Region 0 set to cover all DRAM secure at 0xC000_0000 */ + init_tzc400_begin(TZC_REGION_S_RDWR); /* Raise an exception if a NS device tries to access secure memory */ - tzc400_set_action(TZC_ACTION_ERR); - - tzc400_enable_filters(); + init_tzc400_end(TZC_ACTION_ERR); } /******************************************************************************* diff --git a/plat/st/stm32mp1/stm32mp1_shared_resources.c b/plat/st/stm32mp1/stm32mp1_shared_resources.c index 208e34a8b0..01fb499c1a 100644 --- a/plat/st/stm32mp1/stm32mp1_shared_resources.c +++ b/plat/st/stm32mp1/stm32mp1_shared_resources.c @@ -87,6 +87,64 @@ static const char __unused *shres2str_state(unsigned int state) return shres2str_state_tbl[state]; } +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_DDRCTRL_ID, "DDRCTRL"), + SHRES2DECPROT(SHRES_INVALID, STM32MP1_ETZPC_DDRPHYC_ID, "DDRPHY"), +}; + +static unsigned int decprot2shres(unsigned int decprot_id) +{ + uint32_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; + } + } + + VERBOSE("No shared resource %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; + } + } + + ERROR("Invalid ID %u", decprot_id); + panic(); +} + /* Get resource state: these accesses lock the registering support */ static void lock_registering(void) { @@ -114,7 +172,7 @@ static unsigned int get_gpio_nbpin(unsigned int bank) if (bank != GPIO_BANK_Z) { int count = fdt_get_gpio_bank_pin_count(bank); - assert((count >= 0) || (count <= (GPIO_PIN_MAX + 1))); + assert((count >= 0) || ((unsigned int)count <= (GPIO_PIN_MAX + 1))); return (unsigned int)count; } @@ -163,7 +221,7 @@ static void register_periph(enum stm32mp_shres id, unsigned int state) if ((id >= STM32MP1_SHRES_GPIOZ(0)) && (id <= STM32MP1_SHRES_GPIOZ(7)) && - ((id - STM32MP1_SHRES_GPIOZ(0)) >= get_gpioz_nbpin())) { + ((unsigned int)(id - STM32MP1_SHRES_GPIOZ(0)) >= get_gpioz_nbpin())) { ERROR("Invalid GPIO pin %u, %u pin(s) available\n", id - STM32MP1_SHRES_GPIOZ(0), get_gpioz_nbpin()); panic(); @@ -334,6 +392,53 @@ void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin) } } +void stm32mp1_register_etzpc_decprot(unsigned int id, + enum etzpc_decprot_attributes attr) +{ + unsigned int state = SHRES_SECURE; + unsigned int id_shres; + + switch (attr) { + case ETZPC_DECPROT_S_RW: + break; + case ETZPC_DECPROT_NS_R_S_W: + case ETZPC_DECPROT_MCU_ISOLATION: + case ETZPC_DECPROT_NS_RW: + state = SHRES_NON_SECURE; + break; + default: + panic(); + } + + switch (id) { + case STM32MP1_ETZPC_STGENC_ID: + case STM32MP1_ETZPC_BKPSRAM_ID: + /* We assume these must always be assigned to secure world */ + if (state != SHRES_SECURE) { + panic(); + } + break; + case STM32MP1_ETZPC_DDRCTRL_ID: + case STM32MP1_ETZPC_DDRPHYC_ID: + /* allow write only for secure world */ + if ((attr != ETZPC_DECPROT_S_RW) && + (attr != ETZPC_DECPROT_NS_R_S_W)) { + panic(); + } + break; + default: + id_shres = decprot2shres(id); + if (id_shres == SHRES_INVALID) { + if (state == SHRES_SECURE) { + panic(); + } + } else { + register_periph(id_shres, state); + } + break; + } +} + static bool stm32mp_gpio_bank_is_non_secure(unsigned int bank) { unsigned int non_secure = 0U; @@ -379,12 +484,15 @@ bool stm32mp_nsec_can_access_clock(unsigned long clock_id) enum stm32mp_shres shres_id = STM32MP1_SHRES_COUNT; switch (clock_id) { + case BSEC: + case CK_AXI: case CK_CSI: case CK_HSE: case CK_HSE_DIV2: case CK_HSI: case CK_LSE: case CK_LSI: + case CK_MPU: case PLL1_P: case PLL1_Q: case PLL1_R: @@ -454,6 +562,7 @@ bool stm32mp_nsec_can_access_reset(unsigned int reset_id) shres_id = STM32MP1_SHRES_I2C6; break; case MCU_R: + case MCU_HOLD_BOOT_R: shres_id = STM32MP1_SHRES_MCU; break; case MDMA_R: @@ -499,33 +608,81 @@ static enum etzpc_decprot_attributes shres2decprot_attr(enum stm32mp_shres id) return ETZPC_DECPROT_S_RW; } -static void set_etzpc_secure_configuration(void) +static bool check_decprot(unsigned int id, enum etzpc_decprot_attributes exp) { - /* Some system peripherals shall be secure */ - etzpc_configure_decprot(STM32MP1_ETZPC_STGENC_ID, ETZPC_DECPROT_S_RW); - etzpc_configure_decprot(STM32MP1_ETZPC_BKPSRAM_ID, ETZPC_DECPROT_S_RW); - etzpc_configure_decprot(STM32MP1_ETZPC_DDRCTRL_ID, - ETZPC_DECPROT_NS_R_S_W); - etzpc_configure_decprot(STM32MP1_ETZPC_DDRPHYC_ID, - ETZPC_DECPROT_NS_R_S_W); - - /* Configure ETZPC with peripheral registering */ - etzpc_configure_decprot(STM32MP1_ETZPC_CRYP1_ID, - shres2decprot_attr(STM32MP1_SHRES_CRYP1)); - etzpc_configure_decprot(STM32MP1_ETZPC_HASH1_ID, - shres2decprot_attr(STM32MP1_SHRES_HASH1)); - etzpc_configure_decprot(STM32MP1_ETZPC_I2C4_ID, - shres2decprot_attr(STM32MP1_SHRES_I2C4)); - etzpc_configure_decprot(STM32MP1_ETZPC_I2C6_ID, - shres2decprot_attr(STM32MP1_SHRES_I2C6)); - etzpc_configure_decprot(STM32MP1_ETZPC_IWDG1_ID, - shres2decprot_attr(STM32MP1_SHRES_IWDG1)); - etzpc_configure_decprot(STM32MP1_ETZPC_RNG1_ID, - shres2decprot_attr(STM32MP1_SHRES_RNG1)); - etzpc_configure_decprot(STM32MP1_ETZPC_USART1_ID, + enum etzpc_decprot_attributes cur = etzpc_get_decprot(id); + + if (cur == exp) { + return true; + } + + switch (exp) { + case ETZPC_DECPROT_NS_RW: + if (cur == ETZPC_DECPROT_S_RW) { + INFO("ETZPC: %s (%d) could be non secure\n", + decprot2str(id), id); + } + return true; + + case ETZPC_DECPROT_S_RW: + ERROR("ETZPC: %s (%d) expected secure but DECPROT = %d\n", + decprot2str(id), id, cur); + break; + + case ETZPC_DECPROT_NS_R_S_W: + case ETZPC_DECPROT_MCU_ISOLATION: + break; + default: + panic(); + } + + return false; +} + +static void check_etzpc_secure_configuration(void) +{ + bool error = false; + + assert(registering_locked); + + error |= !check_decprot(STM32MP1_ETZPC_STGENC_ID, ETZPC_DECPROT_S_RW); + + error |= !check_decprot(STM32MP1_ETZPC_BKPSRAM_ID, ETZPC_DECPROT_S_RW); + + error |= !check_decprot(STM32MP1_ETZPC_USART1_ID, shres2decprot_attr(STM32MP1_SHRES_USART1)); - etzpc_configure_decprot(STM32MP1_ETZPC_SPI6_ID, + + error |= !check_decprot(STM32MP1_ETZPC_I2C4_ID, + shres2decprot_attr(STM32MP1_SHRES_I2C4)); + + error |= !check_decprot(STM32MP1_ETZPC_SPI6_ID, shres2decprot_attr(STM32MP1_SHRES_SPI6)); + + error |= !check_decprot(STM32MP1_ETZPC_RNG1_ID, + shres2decprot_attr(STM32MP1_SHRES_RNG1)); + + error |= !check_decprot(STM32MP1_ETZPC_HASH1_ID, + shres2decprot_attr(STM32MP1_SHRES_HASH1)); + + error |= !check_decprot(STM32MP1_ETZPC_CRYP1_ID, + shres2decprot_attr(STM32MP1_SHRES_CRYP1)); + + error |= !((check_decprot(STM32MP1_ETZPC_DDRCTRL_ID, + ETZPC_DECPROT_NS_R_S_W)) || + (check_decprot(STM32MP1_ETZPC_DDRCTRL_ID, + ETZPC_DECPROT_S_RW))); + + error |= !((check_decprot(STM32MP1_ETZPC_DDRPHYC_ID, + ETZPC_DECPROT_NS_R_S_W)) || + (check_decprot(STM32MP1_ETZPC_DDRPHYC_ID, + ETZPC_DECPROT_S_RW))); + + error |= !check_decprot(STM32MP1_ETZPC_I2C6_ID, + shres2decprot_attr(STM32MP1_SHRES_I2C6)); + + if (error) { + panic(); + } } static void check_rcc_secure_configuration(void) @@ -592,6 +749,6 @@ void stm32mp_lock_periph_registering(void) print_shared_resources_state(); check_rcc_secure_configuration(); - set_etzpc_secure_configuration(); + check_etzpc_secure_configuration(); set_gpio_secure_configuration(); } diff --git a/plat/st/stm32mp1/stm32mp1_ssp.c b/plat/st/stm32mp1/stm32mp1_ssp.c new file mode 100644 index 0000000000..bcbf374782 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_ssp.c @@ -0,0 +1,1039 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 + +#define CERT_CHIP_ID_LEN U(3) +#define CERT_SECURITY_COUNTER_LEN U(2) +#define CERT_SECURITY_COUNTER_SHIFT CERT_CHIP_ID_LEN +#define CERT_RFU_LEN U(1) +#define CERT_RFU_SHIFT (CERT_SECURITY_COUNTER_LEN + \ + CERT_SECURITY_COUNTER_SHIFT) +#define CERT_PRODUCT_KEY_LEN U(2) +#define CERT_PRODUCT_KEY_SHIFT (CERT_RFU_LEN + CERT_RFU_SHIFT) +#define CERT_PRODUCT_ID_SIZE (CERT_PRODUCT_KEY_LEN + \ + CERT_PRODUCT_KEY_SHIFT) +#define CERT_SIGNATURE_LEN CHIP_CERTIFICATE_MAX_SIZE +#define CERT_SIGNATURE_SHIFT (CERT_PRODUCT_ID_SIZE + \ + BOOT_API_SSP_PUBK_KEY_SIZE_BYTES) +#define CERTIFICATE_SIZE (CERT_PRODUCT_ID_SIZE + \ + BOOT_API_SSP_PUBK_KEY_SIZE_BYTES + \ + CERT_SIGNATURE_LEN) /* 136 bytes */ +#define RESET_TIMEOUT_US_1MS U(1000) +#define BLOB_FILE_MAX_ADDR BL2_RW_LIMIT + +/* Local status for SSP processing sequences */ +typedef enum { + SSP_NONE, + SSP_GET_CERT, + SSP_FLASH_OEM, + SSP_DONE, + SSP_ERROR +} ssp_result_e; + +struct otp_val { + uint32_t idx; + uint32_t nb; +}; + +static struct otp_val otp_ssp; +static struct otp_val otp_rma; +static struct otp_val otp_pubkey; + +#if DEBUG +static console_t console; +#endif + +/* Platform empty definition required */ +void bl2_platform_setup(void) {} +struct bl_params *plat_get_next_bl_params(void) { return NULL; } +void plat_flush_next_bl_params(void) {} +struct bl_load_info *plat_get_bl_image_load_info(void) { return NULL; } +int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, + uintptr_t *image_spec) +{ + return 0; +} + +/* + * Initialized OTP index from device tree. + */ +static int initialize_otp(void) +{ + uint32_t len; + + /* OTP SSP */ + if (stm32_get_otp_index(SSP_OTP, &otp_ssp.idx, NULL) != 0) { + VERBOSE("%s: get index error\n", __func__); + return -EINVAL; + } + + /* OTP public key */ + if (stm32_get_otp_index(PKH_OTP, &otp_pubkey.idx, &len) != 0) { + VERBOSE("%s: get index error\n", __func__); + return -EINVAL; + } + + if (len != (CHAR_BIT * BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES)) { + VERBOSE("%s: length Error\n", __func__); + return -EINVAL; + } + + otp_pubkey.nb = len / __WORD_BIT; + + /* OTP RMA */ + if (stm32_get_otp_index(RMA_OTP, &otp_rma.idx, NULL) != 0) { + VERBOSE("%s: get index error\n", __func__); + return -EINVAL; + } + + return 0; +} + +/* + * Compute HASH from public key and burn it in OTP. + */ +static int ssp_pub_key_prog(boot_api_context_t *boot_context) +{ + uint8_t key_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES] __aligned(4); + uint8_t *pubk = (uint8_t *) + boot_context->p_ssp_config->p_blob_payload->oem_ecdsa_pubk; + uint32_t *value = (uint32_t *)key_hash; + uint32_t i; + + if (stm32_hash_register() != 0) { + return -EINVAL; + } + + stm32_hash_init(HASH_SHA256); + + if (stm32_hash_final_update(pubk, BOOT_API_SSP_PUBK_KEY_SIZE_BYTES, + key_hash) != 0) { + ERROR("Hash of payload failed\n"); + return -EINVAL; + } + + for (i = otp_pubkey.idx; i < (otp_pubkey.idx + otp_pubkey.nb); i++) { + if (bsec_program_otp(bswap32(*value), i) != BSEC_OK) { + return -EINVAL; + } + + value++; + if (bsec_permanent_lock_otp(i) != BSEC_OK) { + ERROR("Error locking OTP %i\n", i); + panic(); + } + } + + return 0; +} + +/* + * Burn OTP to close device. + */ +static int ssp_close_device(void) +{ + uint32_t otp; + uint32_t value; + + if (stm32_get_otp_index(CFG0_OTP, &otp, NULL) != 0) { + return -EINVAL; + } + + if (bsec_read_otp(&value, otp) != BSEC_OK) { + return -EINVAL; + } + + if ((value & CFG0_CLOSED_DEVICE) != 0U) { + ERROR("Device already closed\n"); + return -EINVAL; + } + + value |= CFG0_CLOSED_DEVICE; + if (bsec_program_otp(value, otp) != BSEC_OK) { + return -EINVAL; + } + + return 0; +} + +/* + * OTP initial check to detect previous values. + */ +static int ssp_secrets_check(boot_api_context_t *boot_ctx) +{ + uint32_t i; + uint32_t check_val; + uint32_t otp_bytes = boot_ctx->p_ssp_config->p_blob_payload->oem_secret_size_bytes; + uint32_t otp_decrypted; + + if (otp_bytes == 0U) { + return -EINVAL; + } + + for (i = otp_pubkey.idx; i < (otp_pubkey.idx + otp_pubkey.nb); i++) { + if (stm32_get_otp_value_from_idx(i, &check_val) != 0) { + return -EINVAL; + } + + if (check_val != 0U) { + ERROR("OTP %u value already programmed\n", i); + return -EINVAL; + } + } + + otp_decrypted = round_up(otp_bytes, sizeof(uint32_t)) / sizeof(uint32_t); + + /* OTP decrypted include RMA password */ + if (otp_decrypted > (2U + SSP_OTP_SECRET_END - SSP_OTP_SECRET_BASE)) { + return -EINVAL; + } + + /* Check RMA password */ + if (stm32_get_otp_value_from_idx(otp_rma.idx, &check_val) != 0) { + return -EINVAL; + } + + if (check_val != 0U) { + ERROR("OTP %s value already programmed\n", RMA_OTP); + return -EINVAL; + } + + /* Check all OTP available */ + for (i = SSP_OTP_SECRET_BASE; i < SSP_OTP_SECRET_BASE + otp_decrypted - 1U; i++) { + if (stm32_get_otp_value_from_idx(i, &check_val) != 0) { + return -EINVAL; + } + + if (check_val != 0U) { + ERROR("OTP %u value already programmed\n", i); + return -EINVAL; + } + } + + return 0; +} + +/* + * Burn OTP with the decrypted secret received. + */ +static int ssp_secrets_flash(boot_api_context_t *boot_ctx) +{ + uint32_t i; + uint32_t *val; + uint32_t otp_bytes = + boot_ctx->p_ssp_config->p_blob_payload->oem_secret_size_bytes; + uint32_t otp_decrypted; + uint32_t otp_mask = 0U; + + if (otp_bytes == 0U) { + return -EINVAL; + } + + if (otp_bytes % sizeof(uint32_t) != 0U) { + otp_mask = GENMASK_32(((otp_bytes % sizeof(uint32_t)) * + sizeof(uint32_t)) - 1, 0); + } + + val = (uint32_t *)boot_ctx->p_ssp_config->p_ssp_oem_secrets_decrypted; + + otp_decrypted = round_up(otp_bytes, sizeof(uint32_t)) / sizeof(uint32_t); + + /* Burn RMA password */ + if (bsec_program_otp((*val & RMA_OTP_MASK), otp_rma.idx) != BSEC_OK) { + WARN("RMA programing failed\n"); + return -EINVAL; + } + + val++; + otp_decrypted--; + for (i = SSP_OTP_SECRET_BASE; i < (SSP_OTP_SECRET_BASE + otp_decrypted - 1U); i++) { + if (*val == 0U) { + val++; + continue; + } + + if (bsec_program_otp(*val, i) != BSEC_OK) { + WARN("Error writing OTP %i\n", i); + return -EINVAL; + } + + if (bsec_permanent_lock_otp(i) != BSEC_OK) { + WARN("Error locking OTP %i\n", i); + return -EINVAL; + } + + val++; + } + + if (*val == 0U) { + return 0; + } + + /* Mask the last OTP value if needed */ + if (otp_mask != 0U) { + *val &= otp_mask; + } + + if (bsec_program_otp(*val, i) != BSEC_OK) { + WARN("Error writing OTP %i\n", i); + return -EINVAL; + } + + if (bsec_permanent_lock_otp(i) != BSEC_OK) { + WARN("Error locking OTP %i\n", i); + return -EINVAL; + } + + return 0; +} + +/* + * Finish SSP processing by fusing OTP SSP success. + */ +static int ssp_finish_process(void) +{ + uint32_t val; + + if (stm32_get_otp_value_from_idx(otp_ssp.idx, &val) != 0) { + return -EINVAL; + } + + if ((val & SSP_OTP_SUCCESS) != 0U) { + WARN("Error while configuring OTP\n"); + return -EINVAL; + } + + val |= SSP_OTP_SUCCESS; + if (bsec_program_otp(val, otp_ssp.idx) != BSEC_OK) { + return -EINVAL; + } + + VERBOSE("Write OTP Success\n"); + + return 0; +} + +/* + * Transform integer to string. + */ +static void itoa(uint32_t num, char *str, int nb) +{ + if (num == 0U) { + while (nb-- != 0U) { + str[nb] = '0'; + } + + return; + } + + while (num != 0U) { + int rem = num % 16; + + str[--nb] = (rem > 9) ? (rem - 10) + 'A' : rem + '0'; + num /= 16; + } + + while (nb != 0) { + str[--nb] = '0'; + } +} + +/* + * Return chip product ID. + */ +static int ssp_get_product_id(char *msg) +{ + uint32_t otp; + uint32_t otp_idx; + uint32_t chip_id; + + if (stm32_get_otp_index(CFG2_OTP, &otp_idx, NULL) != 0) { + VERBOSE("Get index error\n"); + return -EINVAL; + } + + if (stm32_get_otp_value_from_idx(otp_idx, &otp) != 0) { + return -EINVAL; + } + + if (stm32mp1_dbgmcu_get_chip_dev_id(&chip_id) < 0) { + return -EINVAL; + } + + itoa(chip_id, msg, CERT_CHIP_ID_LEN); + itoa((otp & OTP_CFG2_SEC_COUNTER_MASK) >> OTP_CFG2_SEC_COUNTER_SHIFT, + msg + CERT_SECURITY_COUNTER_SHIFT, + CERT_SECURITY_COUNTER_LEN); + + itoa(0, msg + CERT_RFU_SHIFT, CERT_RFU_LEN); + itoa((otp & OTP_CFG2_ST_KEY_MASK) >> OTP_CFG2_ST_KEY_SHIFT, + msg + CERT_PRODUCT_KEY_SHIFT, + CERT_PRODUCT_KEY_LEN); + + return 0; +} + +/* + * Construct SSP certificate. + */ +static int prepare_certificate(uint8_t *cert, const uint8_t *pubkey) +{ + uint32_t i; + uint32_t j; + uint32_t otp; + uint32_t otp_idx; + uint32_t otp_len; + + /* Prepare the ROM Security constant */ + if (ssp_get_product_id((char *)cert) != 0) { + return -EINVAL; + } + + /* Prepare public key and certificate for flashloader */ + /* Read Public Key from boot_context */ + memcpy(cert + CERT_PRODUCT_ID_SIZE, pubkey, BOOT_API_SSP_PUBK_KEY_SIZE_BYTES); + + if (stm32_get_otp_index(CHIP_CERTIFICATE_OTP, + &otp_idx, &otp_len) != 0) { + VERBOSE("Get index error\n"); + return -EINVAL; + } + + if (otp_len != (CHAR_BIT * CHIP_CERTIFICATE_MAX_SIZE)) { + VERBOSE("Length error\n"); + return -EINVAL; + } + + otp_len /= __WORD_BIT; + + /* Read Certificat from OTP */ + for (i = otp_idx, j = 0U; i < (otp_idx + otp_len); i++, j++) { + uint32_t otp_s; + + if (stm32_get_otp_value_from_idx(i, &otp) != 0) { + return -EINVAL; + } + + otp_s = bswap32(otp); + memcpy(&cert[CERT_SIGNATURE_SHIFT + (sizeof(uint32_t) * j)], + &otp_s, sizeof(uint32_t)); + } + + return 0; +} + +/* + * Clean external data and bootrom context secret values. + */ +static void ssp_cleanup(boot_api_context_t *boot_context) +{ + boot_api_ssp_config_t *ssp_config = boot_context->p_ssp_config; + + /* Cleanup boot_context */ + if (ssp_config->p_ssp_oem_secrets_decrypted != NULL) { + zeromem(ssp_config->p_ssp_oem_secrets_decrypted, + BOOT_API_SSP_OEM_SECRETS_MAX_SIZE_BYTES); +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)ssp_config->p_ssp_oem_secrets_decrypted, + BOOT_API_SSP_OEM_SECRETS_MAX_SIZE_BYTES); +#endif + ssp_config->p_ssp_oem_secrets_decrypted = NULL; + } + + if (ssp_config->p_chip_pubk != NULL) { + zeromem(ssp_config->p_chip_pubk, + BOOT_API_SSP_PUBK_KEY_SIZE_BYTES); +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)ssp_config->p_chip_pubk, + BOOT_API_SSP_PUBK_KEY_SIZE_BYTES); +#endif + ssp_config->p_chip_pubk = NULL; + } + + if (ssp_config->p_blob_license != NULL) { + zeromem(ssp_config->p_blob_license, + sizeof(boot_api_ssp_blob_license_t)); +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)ssp_config->p_blob_license, + sizeof(boot_api_ssp_blob_license_t)); +#endif + ssp_config->p_blob_license = NULL; + } + + if (ssp_config->p_blob_payload != NULL) { + zeromem(ssp_config->p_blob_payload, + sizeof(boot_api_ssp_blob_payload_t)); +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)ssp_config->p_blob_payload, + sizeof(boot_api_ssp_blob_payload_t)); +#endif + ssp_config->p_blob_payload = NULL; + } + + ssp_config->ssp_cmd = 0U; + +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)boot_context->p_ssp_config, + sizeof(boot_api_ssp_config_t)); +#endif +} + +/* + * Send certificate to the programmer and retrieve the associated + * encrypted file. + */ +static int ssp_download_phase(boot_api_context_t *boot_ctx) +{ + uint8_t *blob_file; +#if STM32MP_USB_PROGRAMMER + usb_handle_t *pdev; +#endif +#if STM32MP_UART_PROGRAMMER + uintptr_t uart_base; +#endif + int result = 0; + uint8_t cert[CERTIFICATE_SIZE]; + + blob_file = (uint8_t *)page_align(BLOB_FILE_MAX_ADDR - + sizeof(boot_api_ssp_blob_license_t) - + sizeof(boot_api_ssp_blob_payload_t), + DOWN); + + if (prepare_certificate(cert, boot_ctx->p_ssp_config->p_chip_pubk) != 0) { + return -EINVAL; + } + + switch (boot_ctx->boot_interface_selected) { +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + + /* init USB on platform */ + pdev = usb_dfu_plat_init(); + + result = stm32cubeprog_usb_ssp(pdev, (uintptr_t)cert, + sizeof(cert), (uintptr_t)blob_file, + sizeof(boot_api_ssp_blob_license_t) + + sizeof(boot_api_ssp_blob_payload_t)); + if (result != 0) { + return -EINVAL; + } + + break; +#endif + +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + uart_base = get_uart_address(boot_ctx->boot_interface_instance); + + if (uart_base == 0U) { + return -EINVAL; + } + + result = stm32cubeprog_uart_ssp(uart_base, (uintptr_t)cert, sizeof(cert), + (uintptr_t)blob_file, + sizeof(boot_api_ssp_blob_license_t) + + sizeof(boot_api_ssp_blob_payload_t)); + if (result != 0) { + return -EINVAL; + } + break; +#endif + default: + return -EINVAL; + } + + boot_ctx->p_ssp_config->p_blob_license = + (boot_api_ssp_blob_license_t *)blob_file; + + /* Payload is concatened with license file */ + boot_ctx->p_ssp_config->p_blob_payload = + (boot_api_ssp_blob_payload_t *)(blob_file + + sizeof(boot_api_ssp_blob_license_t)); + +#ifndef DCACHE_OFF + flush_dcache_range((uintptr_t)blob_file, + sizeof(boot_api_ssp_blob_license_t) + + sizeof(boot_api_ssp_blob_payload_t)); +#endif + + /* Set return address for decrypted_secrets */ + boot_ctx->p_ssp_config->p_ssp_oem_secrets_decrypted = + boot_ctx->p_ssp_config->p_blob_payload->oem_encrypted_secrets; + + return result; +} + +/* + * Burn decrypted secrets into OTP, clean memory and close the device. + */ +static int ssp_secret_programming(boot_api_context_t *boot_context) +{ + int result; + + result = ssp_secrets_check(boot_context); + if (result != 0) { + ERROR("SSP ERROR checking OTP\n"); + goto clean; + } + + result = ssp_pub_key_prog(boot_context); + if (result != 0) { + ERROR("SSP ERROR writing HASH key\n"); + goto clean; + } + + result = ssp_close_device(); + if (result != 0) { + ERROR("SSP close device failed\n"); + goto clean; + } + + result = ssp_secrets_flash(boot_context); + if (result != 0) { + ERROR("SSP Secret flash failed\n"); + } + +clean: + ssp_cleanup(boot_context); + + if (result != 0) { + return result; + } + + return ssp_finish_process(); +} + +/* + * Enable the SSP processing. + */ +static int ssp_enable_processing(boot_api_context_t *boot_context) +{ + uint32_t val; + int result; +#if STM32MP_USB_PROGRAMMER + usb_handle_t *pdev; +#endif +#if STM32MP_UART_PROGRAMMER + uintptr_t uart_base; +#endif + + if (stm32_get_otp_value_from_idx(otp_ssp.idx, &val) != 0) { + return -EINVAL; + } + + if (((val & SSP_OTP_MASK) == SSP_OTP_MASK) || + ((val & SSP_OTP_MASK) == SSP_OTP_SUCCESS)) { + return -EINVAL; + } + + if ((val & SSP_OTP_MASK) == 0U) { + if (bsec_program_otp(SSP_OTP_REQ, otp_ssp.idx) != BSEC_OK) { + return -EINVAL; + } + } + + switch (boot_context->boot_interface_selected) { +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + /* init USB on platform */ + pdev = usb_dfu_plat_init(); + + result = stm32cubeprog_usb_ssp(pdev, (uintptr_t)-1, 0, + (uintptr_t)NULL, 0); + if (result != 0) { + return -EINVAL; + } + + break; +#endif + +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + uart_base = get_uart_address(boot_context->boot_interface_instance); + if (uart_base == 0U) { + return -EINVAL; + } + + result = stm32cubeprog_uart_ssp(uart_base, (uintptr_t)-1, 0, + (uintptr_t)NULL, 0); + if (result != 0) { + return -EINVAL; + } + break; +#endif + default: + return -EINVAL; + } + + boot_context->p_ssp_config->ssp_cmd = + BOOT_API_CTX_SSP_CMD_CALC_CHIP_PUBK; + + return 0; +} + +/* + * Retrieve the current status of the SSP from bootrom context and OTP value. + */ +static ssp_result_e ssp_check_status(boot_api_context_t *boot_context) +{ + uint32_t otp; + + if (initialize_otp() < 0) { + return SSP_ERROR; + } + + if (stm32_get_otp_value_from_idx(otp_ssp.idx, &otp) != 0) { + return SSP_ERROR; + } + + if ((otp & SSP_OTP_REQ) == 0U) { + return SSP_NONE; + } + + if ((otp & SSP_OTP_SUCCESS) != 0U) { + return SSP_DONE; + } + + VERBOSE("Start Get ssp_cmd : %x\n", + boot_context->p_ssp_config->ssp_cmd); + + switch (boot_context->p_ssp_config->ssp_cmd) { + case BOOT_API_CTX_SSP_CMD_CALC_CHIP_PUBK_ACK: + INFO("Detected start SSP Phase 2\n"); + return SSP_GET_CERT; + case BOOT_API_CTX_SSP_CMD_PROV_SECRET_ACK: + INFO("Detected start SSP Phase 3\n"); + return SSP_FLASH_OEM; + default: + return SSP_NONE; + } +} + +/* + * Start the SSP processing. + */ +static void ssp_start(boot_api_context_t *boot_context) +{ + int result; + uint8_t ssp_phase = ssp_check_status(boot_context); + + switch (ssp_phase) { + case SSP_GET_CERT: + result = ssp_download_phase(boot_context); + if (result != 0) { + /* + * Download Phase failed, clean, reset + */ + ssp_cleanup(boot_context); + + ERROR("SSP_Error: Resetting target\n"); + } else { + /* Process completed, go to Phase 3 */ + boot_context->p_ssp_config->ssp_cmd = + BOOT_API_CTX_SSP_CMD_PROV_SECRET; + } + + break; + + case SSP_FLASH_OEM: + result = ssp_secret_programming(boot_context); + if (result != 0) { + ERROR("Error during provisionning\n"); + } else { + NOTICE("Provisioning completed\n"); + } + + break; + + case SSP_ERROR: + /* + * Error during bootrom SSP processing + */ + result = -EINVAL; + ERROR("SSP_Error: Resetting target\n"); + break; + + case SSP_NONE: + default: + result = ssp_enable_processing(boot_context); + if (result != 0) { + ERROR("Start SSP Failed (%i)\n", result); + } + } + + if ((result != 0) || (ssp_phase == SSP_FLASH_OEM)) { + goto out; + } + + /* + * Keep VDDCORE && VDD enabled if pmic used to generate + * the required MPSYSRST. + */ + if (dt_pmic_status() > 0) { + const char *name; + + name = stm32mp_get_cpu_supply_name(); + if (name == NULL) { + goto out; + } + + if (stpmic1_regulator_mask_reset_set(name) != 0) { + WARN("Failed to write %s reset mask\n", name); + } + + name = stm32mp_get_vdd_supply_name(); + if (name == NULL) { + goto out; + } + + if (stpmic1_regulator_mask_reset_set(name) != 0) { + WARN("Failed to write %s reset mask\n", name); + } + } else { + static const char debug_msg[] = { + "SSP next step will be only guarantee if the VDD\n" + "domain is maintained during system reset\n" + }; + + NOTICE("%s", debug_msg); + } + +out: +#ifndef DCACHE_OFF + if (boot_context->p_ssp_config != NULL) { + flush_dcache_range((uintptr_t)boot_context->p_ssp_config, + sizeof(boot_api_ssp_config_t)); + } +#endif + + stm32mp_system_reset(); +} + +#if DEBUG +static void reset_uart(uint32_t reset) +{ + int ret; + + ret = stm32mp_reset_assert(reset, RESET_TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + + udelay(2); + + ret = stm32mp_reset_deassert(reset, RESET_TIMEOUT_US_1MS); + if (ret != 0) { + panic(); + } + + mdelay(1); +} +#endif + +void bl2_el3_early_platform_setup(u_register_t arg0, + u_register_t arg1 __unused, + u_register_t arg2 __unused, + u_register_t arg3 __unused) +{ + stm32mp_save_boot_ctx_address(arg0); +} + +void bl2_el3_plat_arch_setup(void) +{ +#if DEBUG + int32_t result; + struct dt_node_info dt_uart_info; + const char *board_model; + uint32_t clk_rate; +#endif + uintptr_t pwr_base; + uintptr_t rcc_base; + + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); + bool serial_uart_interface __unused = + (boot_context->boot_interface_selected == + BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART); + uintptr_t uart_prog_addr __unused; + + if (bsec_probe() != 0) { + panic(); + } + + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + +#if SEPARATE_CODE_AND_RODATA + mmap_add_region(BL_RO_DATA_BASE, BL_RO_DATA_BASE, + BL_RO_DATA_END - BL_RO_DATA_BASE, + MT_RO_DATA | MT_SECURE); +#endif + + /* Prevent corruption of preloaded Device Tree */ + mmap_add_region(DTB_BASE, DTB_BASE, + DTB_LIMIT - DTB_BASE, + MT_RO_DATA | MT_SECURE); + + configure_mmu(); + + if (dt_open_and_check(STM32MP_DTB_BASE) < 0) { + panic(); + } + + pwr_base = stm32mp_pwr_base(); + rcc_base = stm32mp_rcc_base(); + + /* + * Disable the backup domain write protection. + * The protection is enable at each reset by hardware + * and must be disabled by software. + */ + mmio_setbits_32(pwr_base + PWR_CR1, PWR_CR1_DBP); + + while ((mmio_read_32(pwr_base + PWR_CR1) & PWR_CR1_DBP) == 0U) { + ; + } + + /* Reset backup domain on cold boot cases */ + if ((mmio_read_32(rcc_base + RCC_BDCR) & RCC_BDCR_RTCSRC_MASK) == 0U) { + mmio_setbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); + + while ((mmio_read_32(rcc_base + RCC_BDCR) & RCC_BDCR_VSWRST) == + 0U) { + ; + } + + mmio_clrbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); + } + + + generic_delay_timer_init(); + +#if STM32MP_UART_PROGRAMMER + uart_prog_addr = get_uart_address(boot_context->boot_interface_instance); + + /* Disable programmer UART before changing clock tree */ + if (serial_uart_interface) { + stm32_uart_stop(uart_prog_addr); + } +#endif + + if (stm32mp1_clk_probe() < 0) { + panic(); + } + + if (dt_pmic_status() > 0) { + initialize_pmic(); + } + +#if DEBUG + result = dt_get_stdout_uart_info(&dt_uart_info); + + if ((result <= 0) || + (dt_uart_info.status == DT_DISABLED) || +#if STM32MP_UART_PROGRAMMER + (serial_uart_interface && + (uart_prog_addr == dt_uart_info.base)) || +#endif + (dt_uart_info.clock < 0) || + (dt_uart_info.reset < 0)) { + goto skip_console_init; + } + + if (dt_set_stdout_pinctrl() != 0) { + goto skip_console_init; + } + + if (dt_uart_info.status == DT_DISABLED) { + panic(); + } + + clk_enable((unsigned long)dt_uart_info.clock); + + reset_uart((uint32_t)dt_uart_info.reset); + + clk_rate = clk_get_rate((unsigned long)dt_uart_info.clock); + + if (console_stm32_register(dt_uart_info.base, clk_rate, + STM32MP_UART_BAUDRATE, &console) == 0) { + panic(); + } + + console_set_scope(&console, CONSOLE_FLAG_BOOT | + CONSOLE_FLAG_CRASH | CONSOLE_FLAG_TRANSLATE_CRLF); + + stm32mp_print_cpuinfo(); + + board_model = dt_get_board_model(); + if (board_model != NULL) { + NOTICE("Model: %s\n", board_model); + } + + if ((boot_context->p_ssp_config == NULL) || + (boot_context->p_ssp_config->ssp_cmd != + BOOT_API_CTX_SSP_CMD_PROV_SECRET_ACK)) { + stm32mp_print_cpuinfo(); + if (!stm32mp_is_auth_supported()) { + ERROR("Chip doesn't support SSP\n"); + panic(); + } + } + +skip_console_init: +#endif + if (stm32mp_is_closed_device()) { + /* Closed chip required authentication */ + ERROR("SSP not supported on closed chip\n"); + panic(); + } + + if (stm32_iwdg_init() < 0) { + panic(); + } + + stm32_iwdg_refresh(); + + if (dt_pmic_status() > 0) { + initialize_pmic(); + print_pmic_info_and_debug(); + } + + ssp_start(boot_context); + + /* This must not be reached */ + panic(); +} diff --git a/plat/st/stm32mp1/stm32mp1_ssp.mk b/plat/st/stm32mp1/stm32mp1_ssp.mk new file mode 100644 index 0000000000..9041e6a032 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_ssp.mk @@ -0,0 +1,84 @@ +# +# Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ST_VERSION := r1.0-ssp +VERSION_STRING := v${VERSION_MAJOR}.${VERSION_MINOR}-${ST_VERSION}(${BUILD_TYPE}):${BUILD_STRING} + +# Required to use BL2_IN_XIP_MEM +BL2_IN_XIP_MEM := 1 + +SEPARATE_CODE_AND_RODATA := 1 + +TRUSTED_BOARD_BOOT := 0 + +# Macros and rules to build TF-A binary +STM32_TF_STM32 := $(addprefix ${BUILD_PLAT}/tf-a-ssp-, $(patsubst %.dtb,%.stm32,$(DTB_FILE_NAME))) + +PLAT_BL_COMMON_SOURCES := common/fdt_wrappers.c \ + plat/st/common/stm32mp_common.c \ + plat/st/stm32mp1/stm32mp1_private.c + +PLAT_BL_COMMON_SOURCES += ${XLAT_TABLES_LIB_SRCS} + +PLAT_BL_COMMON_SOURCES += lib/cpus/aarch32/cortex_a7.S + +PLAT_BL_COMMON_SOURCES += drivers/st/uart/aarch32/stm32_console.S + +PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ + drivers/clk/clk.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + drivers/st/bsec/bsec2.c \ + drivers/st/clk/stm32mp_clkfunc.c \ + drivers/st/gpio/stm32_gpio.c \ + drivers/st/i2c/stm32_i2c.c \ + drivers/st/iwdg/stm32_iwdg.c \ + drivers/st/pmic/stm32mp_pmic.c \ + drivers/st/pmic/stpmic1.c \ + drivers/st/regulator/stm32mp_dummy_regulator.c \ + drivers/st/regulator/stm32mp_regulator.c \ + drivers/st/reset/stm32mp1_reset.c \ + plat/st/common/stm32mp_dt.c \ + plat/st/common/stm32mp_shres_helpers.c \ + plat/st/stm32mp1/stm32mp1_dbgmcu.c \ + plat/st/stm32mp1/stm32mp1_helper.S \ + plat/st/stm32mp1/stm32mp1_syscfg.c + +PLAT_BL_COMMON_SOURCES += drivers/st/clk/stm32mp1_clk.c + +BL2_SOURCES := drivers/io/io_storage.c \ + drivers/st/crypto/stm32_hash.c \ + plat/st/stm32mp1/stm32mp1_ssp.c + +ifeq (${STM32MP_UART_PROGRAMMER},1) +BL2_SOURCES += drivers/st/uart/stm32_uart.c \ + plat/st/common/stm32cubeprogrammer_uart.c +endif + +ifeq (${STM32MP_USB_PROGRAMMER},1) +BL2_SOURCES += drivers/st/usb_dwc2/usb_dwc2.c \ + lib/usb/usb_core.c \ + lib/usb/usb_st_dfu.c \ + plat/st/common/stm32cubeprogrammer_usb.c \ + plat/st/stm32mp1/stm32mp1_usb.c +endif + +BL2_DTSI := stm32mp15-ssp-bl2.dtsi + +check_boot_ssp: + @if ([ ${STM32MP_UART_PROGRAMMER} = 1 ] && [ ${STM32MP_USB_PROGRAMMER} = 1 ]) || \ + ([ ${STM32MP_UART_PROGRAMMER} = 0 ] && [ ${STM32MP_USB_PROGRAMMER} = 0 ]); then \ + echo "Error selecting serial boot device"; \ + false; \ + fi + +bl2: check_boot_ssp + +${BUILD_PLAT}/stm32mp1-ssp-%.o: ${BUILD_PLAT}/fdts/%-bl2.dtb plat/st/stm32mp1/stm32mp1.S bl2 + @echo " SSP AS stm32mp1.S" + ${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \ + -DDTB_BIN_PATH=\"$<\" \ + -c plat/st/stm32mp1/stm32mp1.S -o $@ diff --git a/plat/st/stm32mp1/stm32mp1_syscfg.c b/plat/st/stm32mp1/stm32mp1_syscfg.c index 109725c8ac..2c5fa082fc 100644 --- a/plat/st/stm32mp1/stm32mp1_syscfg.c +++ b/plat/st/stm32mp1/stm32mp1_syscfg.c @@ -7,21 +7,24 @@ #include #include -#include +#include +#include #include #include +#include #include #include /* - * SYSCFG REGISTER OFFSET (base relative) + * SYSCFG register offsets (base relative) */ #define SYSCFG_BOOTR 0x00U #define SYSCFG_IOCTRLSETR 0x18U #define SYSCFG_ICNR 0x1CU #define SYSCFG_CMPCR 0x20U #define SYSCFG_CMPENSETR 0x24U +#define SYSCFG_CMPENCLRR 0x28U /* * SYSCFG_BOOTR Register @@ -53,6 +56,8 @@ #define SYSCFG_CMPCR_RAPSRC GENMASK(23, 20) #define SYSCFG_CMPCR_ANSRC_SHIFT 24 +#define SYSCFG_CMPCR_READY_TIMEOUT_US 10000U + /* * SYSCFG_CMPENSETR Register */ @@ -61,8 +66,9 @@ void stm32mp1_syscfg_init(void) { uint32_t bootr; - uint32_t otp = 0; + uint32_t otp_value; uint32_t vdd_voltage; + bool product_below_2v5; /* * Interconnect update : select master using the port 1. @@ -91,18 +97,18 @@ void stm32mp1_syscfg_init(void) * => TF-A enables the low power mode only if VDD < 2.7V (in DT) * but this value needs to be consistent with board design. */ - if (bsec_read_otp(&otp, HW2_OTP) != BSEC_OK) { + if (stm32_get_otp_value(HW2_OTP, &otp_value) != 0) { panic(); } - otp = otp & HW2_OTP_PRODUCT_BELOW_2V5; + product_below_2v5 = (otp_value & HW2_OTP_PRODUCT_BELOW_2V5) != 0U; /* Get VDD supply */ vdd_voltage = dt_get_pwr_vdd_voltage(); /* Check if VDD is Low Voltage */ if (vdd_voltage == 0U) { - WARN("VDD unknown"); + WARN("VDD unknown\n"); } else if (vdd_voltage < 2700000U) { mmio_write_32(SYSCFG_BASE + SYSCFG_IOCTRLSETR, SYSCFG_IOCTRLSETR_HSLVEN_TRACE | @@ -111,11 +117,11 @@ void stm32mp1_syscfg_init(void) SYSCFG_IOCTRLSETR_HSLVEN_SDMMC | SYSCFG_IOCTRLSETR_HSLVEN_SPI); - if (otp == 0U) { + if (!product_below_2v5) { INFO("Product_below_2v5=0: HSLVEN protected by HW\n"); } } else { - if (otp != 0U) { + if (product_below_2v5) { ERROR("Product_below_2v5=1:\n"); ERROR("\tHSLVEN update is destructive,\n"); ERROR("\tno update as VDD > 2.7V\n"); @@ -123,24 +129,38 @@ void stm32mp1_syscfg_init(void) } } - stm32mp1_syscfg_enable_io_compensation(); + stm32mp1_syscfg_enable_io_compensation_start(); } -void stm32mp1_syscfg_enable_io_compensation(void) +void stm32mp1_syscfg_enable_io_compensation_start(void) { /* * Activate automatic I/O compensation. * Warning: need to ensure CSI enabled and ready in clock driver. * Enable non-secure clock, we assume non-secure is suspended. */ - stm32mp1_clk_enable_non_secure(SYSCFG); + stm32mp1_clk_force_enable(SYSCFG); mmio_setbits_32(SYSCFG_BASE + SYSCFG_CMPENSETR, SYSCFG_CMPENSETR_MPU_EN); +} + +void stm32mp1_syscfg_enable_io_compensation_finish(void) +{ + uint64_t start; + + start = timeout_init_us(SYSCFG_CMPCR_READY_TIMEOUT_US); while ((mmio_read_32(SYSCFG_BASE + SYSCFG_CMPCR) & SYSCFG_CMPCR_READY) == 0U) { - ; + if (timeout_elapsed(start)) { + /* + * Failure on IO compensation enable is not a issue: + * warn only. + */ + WARN("IO compensation cell not ready\n"); + break; + } } mmio_clrbits_32(SYSCFG_BASE + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); @@ -150,6 +170,8 @@ void stm32mp1_syscfg_disable_io_compensation(void) { uint32_t value; + stm32mp1_clk_force_enable(SYSCFG); + /* * Deactivate automatic I/O compensation. * Warning: CSI is disabled automatically in STOP if not @@ -167,8 +189,7 @@ void stm32mp1_syscfg_disable_io_compensation(void) mmio_write_32(SYSCFG_BASE + SYSCFG_CMPCR, value | SYSCFG_CMPCR_SW_CTRL); - mmio_clrbits_32(SYSCFG_BASE + SYSCFG_CMPENSETR, - SYSCFG_CMPENSETR_MPU_EN); + mmio_setbits_32(SYSCFG_BASE + SYSCFG_CMPENCLRR, SYSCFG_CMPENSETR_MPU_EN); - stm32mp1_clk_disable_non_secure(SYSCFG); + stm32mp1_clk_force_disable(SYSCFG); } diff --git a/plat/st/stm32mp1/stm32mp1_usb.c b/plat/st/stm32mp1/stm32mp1_usb.c new file mode 100644 index 0000000000..c63db4a2ff --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_usb.c @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* String size (1 byte) + type (1 byte) + 24 UTF16 characters */ +/* (2 bytes per character) */ +#define USB_SIZ_STRING_SERIAL (1 + 1 + (24 * 2)) +#define USBD_MAX_STR_DESC_SIZ 0x100 +#define USBD_VID 0x0483 +#define USBD_PID 0xDF11 +#define USBD_LANGID_STRING 0x409 +#define USBD_MANUFACTURER_STRING "STMicroelectronics" +#define USBD_CONFIGURATION_STRING "DFU Config" +#define USBD_INTERFACE_STRING "DFU Interface" + +#define USB_DFU_ITF_NUM 6 + +#define USB_DFU_CONFIG_DESC_SIZ USB_DFU_DESC_SIZ(USB_DFU_ITF_NUM) + +/* DFU devices */ +static usb_dfu_handle_t usb_dfu_handle; + +/* USB Standard Device Descriptor */ +static const uint8_t usb_stm32mp1_desc[USB_LEN_DEV_DESC] = { + USB_LEN_DEV_DESC, /* bLength */ + USB_DESC_TYPE_DEVICE, /* bDescriptorType */ + 0x00, /* bcdUSB */ + 0x02, /* version */ + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + USB_MAX_EP0_SIZE, /* bMaxPacketSize */ + LOBYTE(USBD_VID), /* idVendor */ + HIBYTE(USBD_VID), /* idVendor */ + LOBYTE(USBD_PID), /* idVendor */ + HIBYTE(USBD_PID), /* idVendor */ + 0x00, /* bcdDevice rel. 2.00 */ + 0x02, + USBD_IDX_MFC_STR, /* Index of manufacturer string */ + USBD_IDX_PRODUCT_STR, /* Index of product string */ + USBD_IDX_SERIAL_STR, /* Index of serial number string */ + USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ +}; /* USB_DeviceDescriptor */ + +/* USB Standard String Descriptor */ +static const uint8_t usb_stm32mp1_lang_id_desc[USB_LEN_LANGID_STR_DESC] = { + USB_LEN_LANGID_STR_DESC, + USB_DESC_TYPE_STRING, + LOBYTE(USBD_LANGID_STRING), + HIBYTE(USBD_LANGID_STRING), +}; + +/* USB Standard Device Descriptor */ +static const uint8_t +usbd_stm32mp1_qualifier_desc[USB_LEN_DEV_QUALIFIER_DESC] = { + USB_LEN_DEV_QUALIFIER_DESC, + USB_DESC_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x01, + 0x00, +}; + +static uint8_t usb_stm32mp1_serial[USB_SIZ_STRING_SERIAL + 1] = { + USB_SIZ_STRING_SERIAL, + USB_DESC_TYPE_STRING, +}; + +/* USB DFU device Configuration Descriptor */ +static uint8_t usb_stm32mp1_config_desc[USB_DFU_CONFIG_DESC_SIZ] = { + 0x09, /* bLength: Configuration Descriptor size */ + USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ + USB_DFU_CONFIG_DESC_SIZ, /* wTotalLength: Bytes returned */ + 0x00, + 0x01, /* bNumInterfaces: 1 interface */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x02, /* iConfiguration: Index of string descriptor + * describing the configuration + */ + 0xC0, /* bmAttributes: bus powered and Supprts Remote Wakeup */ + 0x32, /* MaxPower 100 mA: this current is used for detecting Vbus */ + /* 09 */ + + /* Descriptor of DFU interface 0 Alternate setting 0..N */ + USBD_DFU_IF_DESC(0), + USBD_DFU_IF_DESC(1), + USBD_DFU_IF_DESC(2), +#if USB_DFU_ITF_NUM > 3 + USBD_DFU_IF_DESC(3), + USBD_DFU_IF_DESC(4), + USBD_DFU_IF_DESC(5), +#endif + /* DFU Functional Descriptor */ + 0x09, /* blength = 9 Bytes */ + DFU_DESCRIPTOR_TYPE, /* DFU Functional Descriptor */ + DFU_BM_ATTRIBUTE, /* bmAttribute + * bitCanDnload = 1 (bit 0) + * bitCanUpload = 1 (bit 1) + * bitManifestationTolerant = 1 (bit 2) + * bitWillDetach = 1 (bit 3) + * Reserved (bit4-6) + * bitAcceleratedST = 0 (bit 7) + */ + 0xFF, /* DetachTimeOut = 255 ms */ + 0x00, + /* WARNING: In DMA mode the multiple MPS packets feature + * is still not supported ==> In this case, + * when using DMA USBD_DFU_XFER_SIZE should be set + * to 64 in usbd_conf.h + */ + TRANSFER_SIZE_BYTES(USBD_DFU_XFER_SIZE), /* TransferSize = 1024 Byte */ + ((USB_DFU_VERSION >> 0) & 0xFF), /* bcdDFUVersion */ + ((USB_DFU_VERSION >> 8) & 0xFF) +}; + +static uint8_t usb_local_string_dec[USBD_MAX_STR_DESC_SIZ]; + +/* + * Convert Hex 32Bits value into char + * value: value to convert + * pbuf: pointer to the buffer + * len: buffer length + */ +static void int_to_unicode(uint32_t value, uint8_t *pbuf, uint8_t len) +{ + uint8_t idx; + + for (idx = 0U; idx < len; idx++) { + if (((value >> 28)) < 0xA) { + pbuf[2U * idx] = (value >> 28) + '0'; + } else { + pbuf[2U * idx] = (value >> 28) + 'A' - 10U; + } + value = value << 4; + pbuf[(2U * idx) + 1U] = 0U; + } +} + +/* + * Convert Hex 32Bits value into string with a fixed length (as sprintf %0X) + * value: value to convert + * pstr: pointer to the string + * len: buffer length + */ +static void int_to_str(uint32_t value, uint8_t *pstr, uint8_t len) +{ + uint8_t idx, v; + + if (len > 9U) { + len = 9U; + } + + for (idx = 0U; idx < len - 1U; idx++) { + v = (value >> (4U * (len - 2U - idx))) & 0xFU; + if (v < 0xAU) { + pstr[idx] = '0' + v; + } else { + pstr[idx] = 'A' + v - 0xAU; + } + } + pstr[len - 1] = 0U; +} + +/* + * Create the serial number string descriptor + */ +static void update_serial_num_string(void) +{ + uint8_t i; + /* serial number is set to 0 */ + uint32_t deviceserial[UID_WORD_NB] = {0U, 0U, 0U}; + uint32_t otp; + uint32_t len; + + if (stm32_get_otp_index(UID_OTP, &otp, &len) != 0) { + ERROR("BSEC: Get UID_OTP number Error\n"); + return; + } + + if ((len / __WORD_BIT) != UID_WORD_NB) { + ERROR("BSEC: Get UID_OTP length Error\n"); + return; + } + + for (i = 0; i < UID_WORD_NB; i++) { + if (bsec_shadow_read_otp(&deviceserial[i], i + otp) != + BSEC_OK) { + ERROR("BSEC: UID%d Error\n", i); + return; + } + } + + int_to_unicode(deviceserial[0], (uint8_t *)&usb_stm32mp1_serial[2], 8); + int_to_unicode(deviceserial[1], (uint8_t *)&usb_stm32mp1_serial[18], 8); + int_to_unicode(deviceserial[2], (uint8_t *)&usb_stm32mp1_serial[34], 8); +} + +/* + * usb_get_qualifier_desc + * return Device Qualifier descriptor + * param : length : pointer data length + * return : pointer to descriptor buffer + */ +static uint8_t *stm32mp1_get_qualifier_desc(uint16_t *length) +{ + *length = sizeof(usbd_stm32mp1_qualifier_desc); + + return (uint8_t *)usbd_stm32mp1_qualifier_desc; +} + +/* + * stm32mp1_get_config_desc + * return configuration descriptor + * param : speed : current device speed + * param : length : pointer data length + * return : pointer to descriptor buffer + */ +static uint8_t *stm32mp1_get_config_desc(uint16_t *length) +{ + *length = sizeof(usb_stm32mp1_config_desc); + + return (uint8_t *)usb_stm32mp1_config_desc; +} + +/* + * stm32mp1_get_string + * Convert Ascii string into unicode one + * param : desc : descriptor buffer + * param : unicode : Formatted string buffer (unicode) + * param : len : descriptor length + * return : None + */ +static void stm32mp1_get_string(uint8_t *desc, uint8_t *unicode, uint16_t *len) +{ + uint8_t idx = 0; + + if (desc == NULL) { + return; + } + + *len = strlen((char *)desc) * 2 + 2; + unicode[idx++] = *len; + unicode[idx++] = USB_DESC_TYPE_STRING; + + while (*desc != '\0') { + unicode[idx++] = *desc++; + unicode[idx++] = 0x00; + } +} + +/* + * stm32mp1_device_desc + * Returns the device descriptor. + * length: Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_device_desc(uint16_t *length) +{ + *length = sizeof(usb_stm32mp1_desc); + + return (uint8_t *)usb_stm32mp1_desc; +} + +/* + * stm32mp1_lang_id_desc + * Returns the LangID string descriptor. + * speed: Current device speed + * length: Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_lang_id_desc(uint16_t *length) +{ + *length = sizeof(usb_stm32mp1_lang_id_desc); + + return (uint8_t *)usb_stm32mp1_lang_id_desc; +} + +/* + * stm32mp1_product_desc + * Returns the product string descriptor. + * length: Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_product_desc(uint16_t *length) +{ + char name[STM32_SOC_NAME_SIZE]; + char product[128]; + uint32_t chip_version; + char str_chip_id[4]; + char str_chip_version[5]; + + stm32mp_get_soc_name(name); + stm32mp_get_chip_version(&chip_version); + + int_to_str(STM32MP1_CHIP_ID, (uint8_t *)str_chip_id, 4); + int_to_str(chip_version, (uint8_t *)str_chip_version, 5); + snprintf(product, sizeof(product), + "DFU @Device ID /0x%s, @Revision ID /0x%s, @Name /%s,", + str_chip_id, str_chip_version, name); + + stm32mp1_get_string((uint8_t *)product, usb_local_string_dec, length); + + return usb_local_string_dec; +} + +/* + * stm32mp1_manufacturer_desc + * Returns the manufacturer string descriptor. + * length: Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_manufacturer_desc(uint16_t *length) +{ + stm32mp1_get_string((uint8_t *)USBD_MANUFACTURER_STRING, + usb_local_string_dec, length); + + return usb_local_string_dec; +} + +/* + * stm32mp1_serial_desc + * Returns the serial number string descriptor. + * length: Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_serial_desc(uint16_t *length) +{ + *length = USB_SIZ_STRING_SERIAL; + + /* Update the serial number string descriptor + * with the data from the unique ID + */ + update_serial_num_string(); + + return (uint8_t *)usb_stm32mp1_serial; +} + +/* + * stm32mp1_Config_desc + * Returns the configuration string descriptor. + * length: Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_config_desc(uint16_t *length) +{ + stm32mp1_get_string((uint8_t *)USBD_CONFIGURATION_STRING, + usb_local_string_dec, length); + + return usb_local_string_dec; +} + +/* + * stm32mp1_interface_desc + * Returns the interface string descriptor. + * length : Pointer to data length variable + * return : Pointer to descriptor buffer + */ +static uint8_t *stm32mp1_interface_desc(uint16_t *length) +{ + stm32mp1_get_string((uint8_t *)USBD_INTERFACE_STRING, + usb_local_string_dec, length); + + return usb_local_string_dec; +} + +/* + * stm32mp1_get_usr_desc + * Manages the transfer of memory interfaces string descriptors. + * param : index: descriptor index + * param : length : pointer data length + * return : pointer to the descriptor table or NULL if the descriptor + * is not supported. + */ +static uint8_t *stm32mp1_get_usr_desc(uint8_t index, uint16_t *length) +{ + uint8_t *ret; + + switch (index) { + case 0: + stm32mp1_get_string((uint8_t *)"@Partition0 /0x00/1*256Ke", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 1: + stm32mp1_get_string((uint8_t *)"@FSBL /0x01/1*1Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 2: + stm32mp1_get_string((uint8_t *)"@Partition2 /0x02/1*1Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 3: + stm32mp1_get_string((uint8_t *)"@Partition3 /0x03/1*16Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 4: + stm32mp1_get_string((uint8_t *)"@Partition4 /0x04/1*16Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 5: + stm32mp1_get_string((uint8_t *)"@virtual /0xF1/1*512Ba", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + default: + ret = NULL; + break; + } + + return ret; +} + +static const usb_desc_t dfu_desc = { + .get_device_desc = stm32mp1_device_desc, + .get_lang_id_desc = stm32mp1_lang_id_desc, + .get_manufacturer_desc = stm32mp1_manufacturer_desc, + .get_product_desc = stm32mp1_product_desc, + .get_configuration_desc = stm32mp1_config_desc, + .get_serial_desc = stm32mp1_serial_desc, + .get_interface_desc = stm32mp1_interface_desc, + .get_usr_desc = stm32mp1_get_usr_desc, + .get_config_desc = stm32mp1_get_config_desc, + .get_device_qualifier_desc = stm32mp1_get_qualifier_desc, +}; + +static usb_handle_t usb_core_handle; +static pcd_handle_t pcd_handle; + +usb_handle_t *usb_dfu_plat_init(void) +{ + /* prepare USB Driver */ + pcd_handle.in_ep[0].maxpacket = USB_MAX_EP0_SIZE; + pcd_handle.out_ep[0].maxpacket = USB_MAX_EP0_SIZE; + usb_dwc2_init_driver(&usb_core_handle, &pcd_handle, + (uint32_t *)USB_OTG_BASE); + + /* STM32MP15 = keep the configuration from ROM code */ + usb_core_handle.ep0_state = USBD_EP0_DATA_IN; + usb_core_handle.dev_state = USBD_STATE_CONFIGURED; + + /* prepare USB DFU stack */ + usb_dfu_register(&usb_core_handle, &usb_dfu_handle); + + /* register descriptor in USB stack */ + register_platform(&usb_core_handle, &dfu_desc); + + return &usb_core_handle; +} + +/* Link between USB alternate and STM32CubeProgramer phase */ +uint8_t usb_dfu_get_phase(uint8_t alt) +{ + switch (alt) { + case 0: +#if STM32MP_SSP + return PHASE_SSP; +#else + return PHASE_FLASHLAYOUT; +#endif + case 3: + return PHASE_SSBL; + case 5: + return PHASE_CMD; + default: + return PHASE_RESET; + } +} diff --git a/tools/cert_create/Makefile b/tools/cert_create/Makefile index 0ec08b0540..5ef8faf522 100644 --- a/tools/cert_create/Makefile +++ b/tools/cert_create/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -9,7 +9,7 @@ V ?= 0 DEBUG := 0 CRTTOOL ?= cert_create${BIN_EXT} BINARY := $(notdir ${CRTTOOL}) -OPENSSL_DIR := /usr +OPENSSL_DIR ?= /usr COT := tbbr MAKE_HELPERS_DIRECTORY := ../../make_helpers/ @@ -53,13 +53,13 @@ HOSTCCFLAGS += ${DEFINES} # could get pulled in from firmware tree. INC_DIR := -I ./include -I ${PLAT_INCLUDE} -I ${OPENSSL_DIR}/include LIB_DIR := -L ${OPENSSL_DIR}/lib -LIB := -lssl -lcrypto +LIB := -lssl -lcrypto -lpthread HOSTCC ?= gcc .PHONY: all clean realclean -all: clean ${BINARY} +all: ${BINARY} ${BINARY}: ${OBJECTS} Makefile @echo " HOSTLD $@" diff --git a/tools/cert_create/include/key.h b/tools/cert_create/include/key.h index d96d9839a2..3409502d82 100644 --- a/tools/cert_create/include/key.h +++ b/tools/cert_create/include/key.h @@ -22,7 +22,8 @@ enum { enum { KEY_ALG_RSA, /* RSA PSS as defined by PKCS#1 v2.1 (default) */ #ifndef OPENSSL_NO_EC - KEY_ALG_ECDSA, + KEY_ALG_ECDSA_NIST, + KEY_ALG_ECDSA_BRAINPOOL, #endif /* OPENSSL_NO_EC */ KEY_ALG_MAX_NUM }; @@ -42,7 +43,8 @@ enum{ static const unsigned int KEY_SIZES[KEY_ALG_MAX_NUM][KEY_SIZE_MAX_NUM] = { { 2048, 1024, 3072, 4096 }, /* KEY_ALG_RSA */ #ifndef OPENSSL_NO_EC - {} /* KEY_ALG_ECDSA */ + {}, /* KEY_ALG_ECDSA_NIST */ + {} /* KEY_ALG_ECDSA_BRAINPOOL */ #endif /* OPENSSL_NO_EC */ }; diff --git a/tools/cert_create/src/key.c b/tools/cert_create/src/key.c index fcc9d53162..25d7d4bd9b 100644 --- a/tools/cert_create/src/key.c +++ b/tools/cert_create/src/key.c @@ -76,11 +76,11 @@ err: } #ifndef OPENSSL_NO_EC -static int key_create_ecdsa(key_t *key, int key_bits) +static int key_create_ecdsa(key_t *key, int key_bits, int curve_id) { EC_KEY *ec; - ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + ec = EC_KEY_new_by_curve_name(curve_id); if (ec == NULL) { printf("Cannot create EC key\n"); goto err; @@ -101,13 +101,25 @@ err: EC_KEY_free(ec); return 0; } + +static int key_create_ecdsa_nist(key_t *key, int key_bits) +{ + return key_create_ecdsa(key, key_bits, NID_X9_62_prime256v1); +} + +static int key_create_ecdsa_brainpool(key_t *key, int key_bits) +{ + return key_create_ecdsa(key, key_bits, NID_brainpoolP256t1); +} + #endif /* OPENSSL_NO_EC */ typedef int (*key_create_fn_t)(key_t *key, int key_bits); static const key_create_fn_t key_create_fn[KEY_ALG_MAX_NUM] = { - key_create_rsa, /* KEY_ALG_RSA */ + [KEY_ALG_RSA] = key_create_rsa, #ifndef OPENSSL_NO_EC - key_create_ecdsa, /* KEY_ALG_ECDSA */ + [KEY_ALG_ECDSA_NIST] = key_create_ecdsa_nist, + [KEY_ALG_ECDSA_BRAINPOOL] = key_create_ecdsa_brainpool, #endif /* OPENSSL_NO_EC */ }; diff --git a/tools/cert_create/src/main.c b/tools/cert_create/src/main.c index 2ba110132a..8a1e02e62f 100644 --- a/tools/cert_create/src/main.c +++ b/tools/cert_create/src/main.c @@ -84,7 +84,8 @@ static char *strdup(const char *str) static const char *key_algs_str[] = { [KEY_ALG_RSA] = "rsa", #ifndef OPENSSL_NO_EC - [KEY_ALG_ECDSA] = "ecdsa" + [KEY_ALG_ECDSA_NIST] = "ecdsa", + [KEY_ALG_ECDSA_BRAINPOOL] = "ecdsa-brainpool" #endif /* OPENSSL_NO_EC */ }; diff --git a/tools/encrypt_fw/Makefile b/tools/encrypt_fw/Makefile index 6eb6fae7a8..7f959abeb3 100644 --- a/tools/encrypt_fw/Makefile +++ b/tools/encrypt_fw/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2019-2020, Linaro Limited. All rights reserved. +# Copyright (c) 2019-2021, Linaro Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -9,7 +9,7 @@ BUILD_INFO ?= 1 DEBUG := 0 ENCTOOL ?= encrypt_fw${BIN_EXT} BINARY := $(notdir ${ENCTOOL}) -OPENSSL_DIR := /usr +OPENSSL_DIR ?= /usr OBJECTS := src/encrypt.o \ src/cmd_opt.o \ @@ -40,13 +40,13 @@ endif # could get pulled in from firmware tree. INC_DIR := -I ./include -I ../../include/tools_share -I ${OPENSSL_DIR}/include LIB_DIR := -L ${OPENSSL_DIR}/lib -LIB := -lssl -lcrypto +LIB := -lssl -lcrypto -lpthread HOSTCC ?= gcc .PHONY: all clean realclean -all: clean ${BINARY} +all: ${BINARY} ${BINARY}: ${OBJECTS} Makefile @echo " HOSTLD $@" diff --git a/tools/fiptool/Makefile b/tools/fiptool/Makefile index df8ab5c7be..0ef5c42707 100644 --- a/tools/fiptool/Makefile +++ b/tools/fiptool/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2014-2021, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,6 +8,7 @@ MAKE_HELPERS_DIRECTORY := ../../make_helpers/ include ${MAKE_HELPERS_DIRECTORY}build_macros.mk include ${MAKE_HELPERS_DIRECTORY}build_env.mk +OPENSSL_DIR ?= /usr FIPTOOL ?= fiptool${BIN_EXT} PROJECT := $(notdir ${FIPTOOL}) OBJECTS := fiptool.o tbbr_config.o @@ -20,7 +21,8 @@ ifeq (${DEBUG},1) else HOSTCCFLAGS += -O2 endif -LDLIBS := -lcrypto +LIB_DIR := -L ${OPENSSL_DIR}/lib +LDLIBS := -lcrypto -lpthread ifeq (${V},0) Q := @ @@ -38,7 +40,7 @@ all: ${PROJECT} ${PROJECT}: ${OBJECTS} Makefile @echo " HOSTLD $@" - ${Q}${HOSTCC} ${OBJECTS} -o $@ ${LDLIBS} + ${Q}${HOSTCC} ${OBJECTS} -o $@ ${LIB_DIR} ${LDLIBS} @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @${ECHO_BLANK_LINE} diff --git a/tools/stm32image/stm32image.c b/tools/stm32image/stm32image.c index 41024e2866..209e0c9d80 100644 --- a/tools/stm32image/stm32image.c +++ b/tools/stm32image/stm32image.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -45,8 +45,6 @@ struct stm32_header { uint8_t binary_type; }; -static struct stm32_header stm32image_header; - static void stm32image_default_header(struct stm32_header *ptr) { if (!ptr) { @@ -54,10 +52,9 @@ static void stm32image_default_header(struct stm32_header *ptr) } ptr->magic_number = HEADER_MAGIC; - ptr->header_version[VER_MAJOR] = HEADER_VERSION_V1; ptr->option_flags = HEADER_DEFAULT_OPTION; - ptr->ecdsa_algorithm = 1; - ptr->version_number = 0; + ptr->ecdsa_algorithm = __cpu_to_le32(1); + ptr->version_number = __cpu_to_le32(0); ptr->binary_type = TF_BINARY_TYPE; } @@ -105,27 +102,33 @@ static void stm32image_print_header(const void *ptr) } static void stm32image_set_header(void *ptr, struct stat *sbuf, int ifd, - uint32_t loadaddr, uint32_t ep, uint32_t ver) + uint32_t loadaddr, uint32_t ep, uint32_t ver, + uint32_t major, uint32_t minor) { struct stm32_header *stm32hdr = (struct stm32_header *)ptr; stm32image_default_header(stm32hdr); + stm32hdr->header_version[VER_MAJOR] = major; + stm32hdr->header_version[VER_MINOR] = minor; stm32hdr->load_address = __cpu_to_le32(loadaddr); stm32hdr->image_entry_point = __cpu_to_le32(ep); stm32hdr->image_length = __cpu_to_le32((uint32_t)sbuf->st_size - sizeof(struct stm32_header)); - stm32hdr->image_checksum = stm32image_checksum(ptr, sbuf->st_size); + stm32hdr->image_checksum = + __cpu_to_le32(stm32image_checksum(ptr, sbuf->st_size)); stm32hdr->version_number = __cpu_to_le32(ver); } static int stm32image_create_header_file(char *srcname, char *destname, uint32_t loadaddr, uint32_t entry, - uint32_t version) + uint32_t version, uint32_t major, + uint32_t minor) { int src_fd, dest_fd; struct stat sbuf; unsigned char *ptr; + struct stm32_header stm32image_header; dest_fd = open(destname, O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0666); if (dest_fd == -1) { @@ -177,11 +180,12 @@ static int stm32image_create_header_file(char *srcname, char *destname, dest_fd, 0); if (ptr == MAP_FAILED) { - fprintf(stderr, "Can't read %s\n", srcname); + fprintf(stderr, "Can't write %s\n", destname); return -1; } - stm32image_set_header(ptr, &sbuf, dest_fd, loadaddr, entry, version); + stm32image_set_header(ptr, &sbuf, dest_fd, loadaddr, entry, version, + major, minor); stm32image_print_header(ptr); @@ -193,9 +197,11 @@ static int stm32image_create_header_file(char *srcname, char *destname, int main(int argc, char *argv[]) { int opt, loadaddr = -1, entry = -1, err = 0, version = 0; + int major = HEADER_VERSION_V1; + int minor = 0; char *dest = NULL, *src = NULL; - while ((opt = getopt(argc, argv, ":s:d:l:e:v:")) != -1) { + while ((opt = getopt(argc, argv, ":s:d:l:e:v:m:n:")) != -1) { switch (opt) { case 's': src = optarg; @@ -204,17 +210,23 @@ int main(int argc, char *argv[]) dest = optarg; break; case 'l': - loadaddr = strtol(optarg, NULL, 16); + loadaddr = strtol(optarg, NULL, 0); break; case 'e': - entry = strtol(optarg, NULL, 16); + entry = strtol(optarg, NULL, 0); break; case 'v': - version = strtol(optarg, NULL, 10); + version = strtol(optarg, NULL, 0); + break; + case 'm': + major = strtol(optarg, NULL, 0); + break; + case 'n': + minor = strtol(optarg, NULL, 0); break; default: fprintf(stderr, - "Usage : %s [-s srcfile] [-d destfile] [-l loadaddr] [-e entry_point]\n", + "Usage : %s [-s srcfile] [-d destfile] [-l loadaddr] [-e entry_point] [-m major] [-n minor]\n", argv[0]); return -1; } @@ -241,7 +253,7 @@ int main(int argc, char *argv[]) } err = stm32image_create_header_file(src, dest, loadaddr, - entry, version); + entry, version, major, minor); return err; } -- 2.17.1