From 334224a627331b7278c8bc71c10b32da2db14194 Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Tue, 9 Jun 2020 11:44:18 +0200 Subject: [PATCH] st-update-v2.2-r2.0.0 --- CONTRIBUTING.md | 30 + Makefile | 2 + bl1/aarch32/bl1_entrypoint.S | 9 + 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 | 9 + common/aarch32/debug.S | 54 +- common/bl_common.c | 2 +- docs/devicetree/bindings/arm/secure.txt | 53 + .../bindings/clock/st,stm32mp1-rcc.txt | 488 ++++ 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 | 42 + 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 | 30 + docs/plat/stm32mp1.rst | 23 +- drivers/arm/tzc/tzc400.c | 151 +- .../intel/soc/stratix10/io/s10_memmap_qspi.c | 18 +- drivers/io/io_block.c | 60 +- drivers/io/io_fip.c | 6 +- drivers/io/io_memmap.c | 30 +- drivers/io/io_mtd.c | 248 ++ drivers/io/io_semihosting.c | 6 +- drivers/io/io_storage.c | 2 +- drivers/mmc/mmc.c | 67 +- drivers/mtd/nand/core.c | 118 + drivers/mtd/nand/raw_nand.c | 446 ++++ drivers/mtd/nand/spi_nand.c | 320 +++ drivers/mtd/nor/spi_nor.c | 387 +++ drivers/mtd/spi-mem/spi_mem.c | 288 +++ drivers/renesas/rcar/io/io_emmcdrv.c | 20 +- drivers/renesas/rcar/io/io_memdrv.c | 19 +- drivers/st/bsec/{bsec.c => bsec2.c} | 460 ++-- drivers/st/clk/stm32mp1_calib.c | 529 +++++ drivers/st/clk/stm32mp1_clk.c | 2105 ++++++++++++++--- drivers/st/clk/stm32mp_clkfunc.c | 237 +- drivers/st/crypto/stm32_hash.c | 15 +- drivers/st/ddr/stm32mp1_ddr.c | 308 ++- drivers/st/ddr/stm32mp1_ddr_helpers.c | 572 ++++- drivers/st/ddr/stm32mp1_ram.c | 133 +- drivers/st/etzpc/etzpc.c | 310 +++ drivers/st/fmc/stm32_fmc2_nand.c | 883 +++++++ drivers/st/gpio/stm32_gpio.c | 8 + drivers/st/i2c/stm32_i2c.c | 441 +++- drivers/st/io/io_mmc.c | 26 +- drivers/st/io/io_programmer_st_usb.c | 381 +++ drivers/st/io/io_stm32image.c | 205 +- drivers/st/iwdg/stm32_iwdg.c | 140 ++ drivers/st/mmc/stm32_sdmmc2.c | 127 +- drivers/st/pmic/stm32mp_pmic.c | 522 +++- 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 | 44 +- drivers/st/rng/stm32_rng.c | 188 ++ drivers/st/rtc/stm32_rtc.c | 479 ++++ drivers/st/scmi-msg/base.c | 202 ++ drivers/st/scmi-msg/base.h | 75 + drivers/st/scmi-msg/clock.c | 388 +++ drivers/st/scmi-msg/clock.h | 150 ++ drivers/st/scmi-msg/common.h | 136 ++ drivers/st/scmi-msg/entry.c | 63 + drivers/st/scmi-msg/reset_domain.c | 202 ++ drivers/st/scmi-msg/reset_domain.h | 122 + drivers/st/scmi-msg/smt.c | 206 ++ drivers/st/spi/stm32_qspi.c | 507 ++++ drivers/st/tamper/stm32_tamp.c | 377 +++ drivers/st/timer/stm32_timer.c | 316 +++ drivers/st/uart/aarch32/stm32_console.S | 23 +- drivers/st/uart/io_programmer_uart.c | 587 +++++ drivers/st/uart/stm32mp1xx_hal_uart.c | 801 +++++++ drivers/st/usb_dwc2/usb_dwc2.c | 859 +++++++ fdts/stm32mp15-ddr.dtsi | 2 + fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi | 49 +- fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi | 49 +- fdts/stm32mp15-pinctrl.dtsi | 292 +++ fdts/stm32mp151.dtsi | 683 ++++++ fdts/stm32mp153.dtsi | 20 + fdts/stm32mp157-pinctrl.dtsi | 348 --- fdts/stm32mp157.dtsi | 7 + fdts/stm32mp157a-avenger96.dts | 255 +- fdts/stm32mp157a-dk1.dts | 308 +-- fdts/stm32mp157a-ed1.dts | 46 + fdts/stm32mp157a-ev1.dts | 23 + fdts/stm32mp157c-dk2.dts | 41 +- fdts/stm32mp157c-ed1.dts | 316 +-- fdts/stm32mp157c-ev1.dts | 51 +- fdts/stm32mp157c-security.dtsi | 41 - fdts/stm32mp157c.dtsi | 366 --- fdts/stm32mp157caa-pinctrl.dtsi | 90 - fdts/stm32mp157cac-pinctrl.dtsi | 78 - fdts/stm32mp157d-dk1.dts | 49 + fdts/stm32mp157d-ed1.dts | 46 + fdts/stm32mp157d-ev1.dts | 22 + fdts/stm32mp157f-dk2.dts | 55 + fdts/stm32mp157f-ed1.dts | 51 + fdts/stm32mp157f-ev1.dts | 22 + fdts/stm32mp15xa.dtsi | 13 + fdts/stm32mp15xc.dtsi | 21 + fdts/stm32mp15xd.dtsi | 19 + fdts/stm32mp15xf.dtsi | 21 + fdts/stm32mp15xx-dkx.dtsi | 470 ++++ fdts/stm32mp15xx-edx.dtsi | 479 ++++ fdts/stm32mp15xx-evx.dtsi | 71 + fdts/stm32mp15xxaa-pinctrl.dtsi | 86 + fdts/stm32mp15xxab-pinctrl.dtsi | 57 + fdts/stm32mp15xxac-pinctrl.dtsi | 74 + fdts/stm32mp15xxad-pinctrl.dtsi | 57 + include/arch/aarch32/arch.h | 18 +- include/arch/aarch32/arch_helpers.h | 4 + include/arch/aarch32/el3_common_macros.S | 10 +- include/arch/aarch64/arch.h | 6 +- include/common/confine_array_index.h | 145 ++ include/common/speculation_barrier.h | 508 ++++ include/drivers/arm/tzc400.h | 39 +- include/drivers/io/io_driver.h | 2 +- include/drivers/io/io_mtd.h | 59 + include/drivers/io/io_storage.h | 7 +- include/drivers/mmc.h | 27 +- include/drivers/nand.h | 55 + include/drivers/raw_nand.h | 188 ++ include/drivers/spi_mem.h | 130 + include/drivers/spi_nand.h | 49 + include/drivers/spi_nor.h | 58 + include/drivers/st/bsec.h | 152 +- include/drivers/st/bsec2_reg.h | 98 + include/drivers/st/etzpc.h | 32 + include/drivers/st/io_programmer.h | 51 + include/drivers/st/io_programmer_st_usb.h | 12 + include/drivers/st/io_stm32image.h | 2 +- include/drivers/st/io_uart.h | 12 + include/drivers/st/scmi-msg.h | 207 ++ include/drivers/st/scmi.h | 29 + include/drivers/st/stm32_fmc2_nand.h | 12 + include/drivers/st/stm32_i2c.h | 41 +- include/drivers/st/stm32_iwdg.h | 1 + include/drivers/st/stm32_qspi.h | 12 + 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/stm32mp1_calib.h | 20 + include/drivers/st/stm32mp1_clk.h | 56 +- include/drivers/st/stm32mp1_ddr.h | 7 +- include/drivers/st/stm32mp1_ddr_helpers.h | 19 +- include/drivers/st/stm32mp1_ddr_regs.h | 2 + include/drivers/st/stm32mp1_pwr.h | 21 +- include/drivers/st/stm32mp1_rcc.h | 29 +- include/drivers/st/stm32mp1xx_hal.h | 204 ++ include/drivers/st/stm32mp1xx_hal_uart.h | 1586 +++++++++++++ include/drivers/st/stm32mp1xx_hal_uart_ex.h | 88 + include/drivers/st/stm32mp_clkfunc.h | 16 +- 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 | 39 +- include/drivers/st/stpmic1.h | 29 +- include/drivers/st/usb_dwc2.h | 443 ++++ include/dt-bindings/clock/stm32mp1-clks.h | 35 +- include/dt-bindings/pinctrl/stm32-pinfunc.h | 9 +- include/dt-bindings/power/stm32mp1-power.h | 19 + include/dt-bindings/reset/stm32mp1-resets.h | 13 + include/dt-bindings/soc/st,stm32-etzpc.h | 107 + include/lib/optee_utils.h | 1 + include/lib/usb/usb_core.h | 353 +++ include/lib/usb/usb_st_dfu.h | 116 + include/lib/utils_def.h | 19 + .../lib/xlat_tables/xlat_tables_v2_helpers.h | 41 +- include/plat/common/platform.h | 7 +- lib/compiler-rt/builtins/arm/aeabi_ldivmod.S | 46 + lib/compiler-rt/builtins/divdi3.c | 29 + lib/compiler-rt/builtins/divmoddi4.c | 25 + lib/compiler-rt/builtins/popcountdi2.c | 36 + lib/compiler-rt/builtins/popcountsi2.c | 33 + lib/compiler-rt/compiler-rt.mk | 14 +- lib/optee/optee_utils.c | 30 + lib/usb/usb_core.c | 733 ++++++ lib/usb/usb_st_dfu.c | 865 +++++++ lib/xlat_tables/aarch32/xlat_tables.c | 9 + lib/xlat_tables/aarch64/xlat_tables.c | 4 + lib/xlat_tables/xlat_tables_common.c | 18 + lib/xlat_tables_v2/xlat_tables_core.c | 7 +- make_helpers/defaults.mk | 3 + plat/common/aarch32/platform_helpers.S | 34 + plat/common/plat_bl_common.c | 2 +- plat/st/common/bl2_io_storage.c | 434 +++- plat/st/common/include/stm32mp_auth.h | 19 - plat/st/common/include/stm32mp_common.h | 37 +- plat/st/common/include/stm32mp_dt.h | 20 +- .../st/common/include/stm32mp_shres_helpers.h | 65 +- plat/st/common/stm32_gic.c | 223 ++ plat/st/common/stm32mp_auth.c | 90 - plat/st/common/stm32mp_common.c | 148 +- plat/st/common/stm32mp_cot.c | 114 + plat/st/common/stm32mp_crypto_lib.c | 174 ++ plat/st/common/stm32mp_dt.c | 432 +++- plat/st/common/stm32mp_img_parser_lib.c | 75 + plat/st/common/stm32mp_shres_helpers.c | 63 + plat/st/common/stm32mp_trusted_boot.c | 76 + plat/st/stm32mp1/bl2_plat_setup.c | 374 ++- plat/st/stm32mp1/include/boot_api.h | 286 ++- plat/st/stm32mp1/include/platform_def.h | 15 + .../stm32mp1/include/stm32mp1_boot_device.h | 18 + plat/st/stm32mp1/include/stm32mp1_context.h | 20 +- plat/st/stm32mp1/include/stm32mp1_low_power.h | 19 + .../stm32mp1/include/stm32mp1_power_config.h | 28 + plat/st/stm32mp1/include/stm32mp1_private.h | 19 +- .../include/stm32mp1_shared_resources.h | 132 ++ plat/st/stm32mp1/include/stm32mp1_smc.h | 148 +- plat/st/stm32mp1/include/stm32mp1_usb_desc.h | 55 + plat/st/stm32mp1/include/usb_ctx.h | 17 + plat/st/stm32mp1/plat_image_load.c | 68 +- plat/st/stm32mp1/platform.mk | 141 +- plat/st/stm32mp1/services/bsec_svc.c | 466 +++- 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 | 172 ++ plat/st/stm32mp1/services/rcc_svc.h | 14 + .../st/stm32mp1/services/stm32mp1_svc_setup.c | 38 +- plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk | 24 +- plat/st/stm32mp1/sp_min/sp_min_setup.c | 318 ++- plat/st/stm32mp1/stm32mp1_boot_device.c | 168 ++ plat/st/stm32mp1/stm32mp1_context.c | 391 ++- plat/st/stm32mp1/stm32mp1_dbgmcu.c | 36 +- plat/st/stm32mp1/stm32mp1_def.h | 304 ++- plat/st/stm32mp1/stm32mp1_gic.c | 92 - plat/st/stm32mp1/stm32mp1_helper.S | 220 +- plat/st/stm32mp1/stm32mp1_low_power.c | 346 +++ plat/st/stm32mp1/stm32mp1_pm.c | 96 +- plat/st/stm32mp1/stm32mp1_power_config.c | 187 ++ plat/st/stm32mp1/stm32mp1_private.c | 534 ++++- plat/st/stm32mp1/stm32mp1_scmi.c | 582 +++++ plat/st/stm32mp1/stm32mp1_security.c | 7 +- plat/st/stm32mp1/stm32mp1_shared_resources.c | 743 ++++++ plat/st/stm32mp1/stm32mp1_syscfg.c | 63 +- plat/st/stm32mp1/stm32mp1_usb_desc.c | 418 ++++ tools/stm32image/stm32image.c | 46 +- 252 files changed, 34319 insertions(+), 3715 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/io/io_mtd.c create mode 100644 drivers/mtd/nand/core.c create mode 100644 drivers/mtd/nand/raw_nand.c create mode 100644 drivers/mtd/nand/spi_nand.c create mode 100644 drivers/mtd/nor/spi_nor.c create mode 100644 drivers/mtd/spi-mem/spi_mem.c rename drivers/st/bsec/{bsec.c => bsec2.c} (65%) create mode 100644 drivers/st/clk/stm32mp1_calib.c create mode 100644 drivers/st/etzpc/etzpc.c create mode 100644 drivers/st/fmc/stm32_fmc2_nand.c create mode 100644 drivers/st/io/io_programmer_st_usb.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/scmi-msg/base.c create mode 100644 drivers/st/scmi-msg/base.h create mode 100644 drivers/st/scmi-msg/clock.c create mode 100644 drivers/st/scmi-msg/clock.h create mode 100644 drivers/st/scmi-msg/common.h create mode 100644 drivers/st/scmi-msg/entry.c create mode 100644 drivers/st/scmi-msg/reset_domain.c create mode 100644 drivers/st/scmi-msg/reset_domain.h create mode 100644 drivers/st/scmi-msg/smt.c create mode 100644 drivers/st/spi/stm32_qspi.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/io_programmer_uart.c create mode 100644 drivers/st/uart/stm32mp1xx_hal_uart.c create mode 100644 drivers/st/usb_dwc2/usb_dwc2.c create mode 100644 fdts/stm32mp15-pinctrl.dtsi create mode 100644 fdts/stm32mp151.dtsi create mode 100644 fdts/stm32mp153.dtsi delete mode 100644 fdts/stm32mp157-pinctrl.dtsi create mode 100644 fdts/stm32mp157.dtsi create mode 100644 fdts/stm32mp157a-ed1.dts create mode 100644 fdts/stm32mp157a-ev1.dts delete mode 100644 fdts/stm32mp157c-security.dtsi delete mode 100644 fdts/stm32mp157c.dtsi delete mode 100644 fdts/stm32mp157caa-pinctrl.dtsi delete mode 100644 fdts/stm32mp157cac-pinctrl.dtsi create mode 100644 fdts/stm32mp157d-dk1.dts create mode 100644 fdts/stm32mp157d-ed1.dts create mode 100644 fdts/stm32mp157d-ev1.dts create mode 100644 fdts/stm32mp157f-dk2.dts create mode 100644 fdts/stm32mp157f-ed1.dts create mode 100644 fdts/stm32mp157f-ev1.dts create mode 100644 fdts/stm32mp15xa.dtsi create mode 100644 fdts/stm32mp15xc.dtsi create mode 100644 fdts/stm32mp15xd.dtsi create mode 100644 fdts/stm32mp15xf.dtsi create mode 100644 fdts/stm32mp15xx-dkx.dtsi create mode 100644 fdts/stm32mp15xx-edx.dtsi create mode 100644 fdts/stm32mp15xx-evx.dtsi create mode 100644 fdts/stm32mp15xxaa-pinctrl.dtsi create mode 100644 fdts/stm32mp15xxab-pinctrl.dtsi create mode 100644 fdts/stm32mp15xxac-pinctrl.dtsi create mode 100644 fdts/stm32mp15xxad-pinctrl.dtsi create mode 100644 include/common/confine_array_index.h create mode 100644 include/common/speculation_barrier.h create mode 100644 include/drivers/io/io_mtd.h create mode 100644 include/drivers/nand.h create mode 100644 include/drivers/raw_nand.h create mode 100644 include/drivers/spi_mem.h create mode 100644 include/drivers/spi_nand.h create mode 100644 include/drivers/spi_nor.h create mode 100644 include/drivers/st/bsec2_reg.h create mode 100644 include/drivers/st/etzpc.h create mode 100644 include/drivers/st/io_programmer.h create mode 100644 include/drivers/st/io_programmer_st_usb.h create mode 100644 include/drivers/st/io_uart.h create mode 100644 include/drivers/st/scmi-msg.h create mode 100644 include/drivers/st/scmi.h create mode 100644 include/drivers/st/stm32_fmc2_nand.h create mode 100644 include/drivers/st/stm32_qspi.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/stm32mp1_calib.h create mode 100644 include/drivers/st/stm32mp1xx_hal.h create mode 100644 include/drivers/st/stm32mp1xx_hal_uart.h create mode 100644 include/drivers/st/stm32mp1xx_hal_uart_ex.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/st,stm32-etzpc.h create mode 100644 include/lib/usb/usb_core.h create mode 100644 include/lib/usb/usb_st_dfu.h create mode 100644 lib/compiler-rt/builtins/arm/aeabi_ldivmod.S create mode 100644 lib/compiler-rt/builtins/divdi3.c create mode 100644 lib/compiler-rt/builtins/divmoddi4.c create mode 100644 lib/compiler-rt/builtins/popcountdi2.c create mode 100644 lib/compiler-rt/builtins/popcountsi2.c create mode 100644 lib/usb/usb_core.c create mode 100644 lib/usb/usb_st_dfu.c delete mode 100644 plat/st/common/include/stm32mp_auth.h create mode 100644 plat/st/common/stm32_gic.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_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/stm32mp1_boot_device.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/include/stm32mp1_shared_resources.h create mode 100644 plat/st/stm32mp1/include/stm32mp1_usb_desc.h create mode 100644 plat/st/stm32mp1/include/usb_ctx.h 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_boot_device.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_scmi.c create mode 100644 plat/st/stm32mp1/stm32mp1_shared_resources.c create mode 100644 plat/st/stm32mp1/stm32mp1_usb_desc.c diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3d1bacd78 --- /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 721246d51..864eda7fc 100644 --- a/Makefile +++ b/Makefile @@ -700,6 +700,7 @@ $(eval $(call assert_boolean,BL2_AT_EL3)) $(eval $(call assert_boolean,BL2_IN_XIP_MEM)) $(eval $(call assert_boolean,BL2_INV_DCACHE)) $(eval $(call assert_boolean,USE_SPINLOCK_CAS)) +$(eval $(call assert_boolean,AARCH32_EXCEPTION_DEBUG)) $(eval $(call assert_numeric,ARM_ARCH_MAJOR)) $(eval $(call assert_numeric,ARM_ARCH_MINOR)) @@ -766,6 +767,7 @@ $(eval $(call add_define,BL2_AT_EL3)) $(eval $(call add_define,BL2_IN_XIP_MEM)) $(eval $(call add_define,BL2_INV_DCACHE)) $(eval $(call add_define,USE_SPINLOCK_CAS)) +$(eval $(call add_define,AARCH32_EXCEPTION_DEBUG)) ifeq (${SANITIZE_UB},trap) $(eval $(call add_define,MONITOR_TRAPS)) diff --git a/bl1/aarch32/bl1_entrypoint.S b/bl1/aarch32/bl1_entrypoint.S index 6a155660b..e7e72049c 100644 --- a/bl1/aarch32/bl1_entrypoint.S +++ b/bl1/aarch32/bl1_entrypoint.S @@ -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 */ diff --git a/bl2/aarch32/bl2_el3_exceptions.S b/bl2/aarch32/bl2_el3_exceptions.S index 087b6656d..dff4e36a4 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 102fd2f51..bfd721ca1 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 dd53e1d2b..e89033b96 100644 --- a/bl2/bl2_image_load_v2.c +++ b/bl2/bl2_image_load_v2.c @@ -71,17 +71,17 @@ struct entry_point_info *bl2_load_images(void) ERROR("BL2: Failed to load image (%i)\n", 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) { + 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) { - 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 6391f537c..426176d98 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 0a684754c..c4a6e8e83 100644 --- a/bl32/sp_min/aarch32/entrypoint.S +++ b/bl32/sp_min/aarch32/entrypoint.S @@ -44,10 +44,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 */ diff --git a/common/aarch32/debug.S b/common/aarch32/debug.S index f50635691..a058645e3 100644 --- a/common/aarch32/debug.S +++ b/common/aarch32/debug.S @@ -7,9 +7,16 @@ #include #include + .globl asm_print_str + .globl asm_print_hex .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 @@ -66,6 +73,41 @@ func 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 + #if ENABLE_ASSERTIONS .section .rodata.assert_str, "aS" assert_msg1: @@ -151,10 +193,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 @@ -162,16 +204,16 @@ 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 r3, lr + mov r7, lr mov r5, #32 /* No of bits to convert to ascii */ 1: sub r5, r5, #4 @@ -188,5 +230,5 @@ func asm_print_hex bl plat_crash_console_putc cmp r5, #0 bne 1b - bx r3 + bx r7 endfunc asm_print_hex diff --git a/common/bl_common.c b/common/bl_common.c index e6f98029e..7f2342499 100644 --- a/common/bl_common.c +++ b/common/bl_common.c @@ -215,7 +215,7 @@ int load_auth_image(unsigned int image_id, image_info_t *image_data) do { err = load_auth_image_internal(image_id, image_data, 0); - } 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/docs/devicetree/bindings/arm/secure.txt b/docs/devicetree/bindings/arm/secure.txt new file mode 100644 index 000000000..e31303fb2 --- /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 000000000..a082706ee --- /dev/null +++ b/docs/devicetree/bindings/clock/st,stm32mp1-rcc.txt @@ -0,0 +1,488 @@ +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. + PLLx is off when the associated node is absent or deactivated. + + 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 000000000..68aefa6dc --- /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 000000000..ac6a7df43 --- /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 000000000..6d3c626e0 --- /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 000000000..51576a384 --- /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 000000000..bd56e1946 --- /dev/null +++ b/docs/devicetree/bindings/power/st,stm32mp1-pwr.txt @@ -0,0 +1,42 @@ +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 + +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 000000000..83307d23b --- /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 000000000..b4edaf7c7 --- /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 000000000..3c613d791 --- /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 000000000..08b499045 --- /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 000000000..a2ac263ce --- /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 000000000..c430fb84d --- /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 000000000..dbd962ebc --- /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 000000000..4d21c6b8a --- /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 000000000..2453603a1 --- /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 9cca75e92..e2db1bf9a 100644 --- a/docs/getting_started/porting-guide.rst +++ b/docs/getting_started/porting-guide.rst @@ -481,6 +481,36 @@ constants must also be defined: Defines the total size of the physical address space in bytes. For example, for a 32 bit physical address space, this value should be ``(1ULL << 32)``. +If the platform port uses the translation table library code, the following +constants can be defined: + +- **#define : PLAT\_BASE\_XLAT\_BASE** + + Defines the identity map base address of the translation level 1 base table + which memory location is common to all BLs. This feature is useful only for + AArch32 only architectures where all BL do share the very same MMU resources + since all core modes rely on a single MMU support contrary to AArch64 aware + architectures where EL3 and secure EL1 each rely on their specific MMU + configuration resources. + +- **#define : PLAT\_BASE\_XLAT\_SIZE** + + Defines the byte size reserved for translation level 1 base table + **PLAT\_BASE\_XLAT\_BASE**. + +- **#define : PLAT\_XLAT\_BASE** + + Defines the identity map base address of the translation table array which + is common to all BLs. This feature is useful only for AArch32 only + architectures where all BL do share the very same MMU resources since + all core modes rely on a single MMU support contrary to AArch64 aware + architectures where EL3 and secure EL1 each rely on their specific MMU + configuration resources. + +- **#define : PLAT\_XLAT\_SIZE** + + Defines the byte size reserved for translation tables **PLAT\__XLAT\_BASE**. + If the platform port uses the IO storage framework, the following constants must also be defined: diff --git a/docs/plat/stm32mp1.rst b/docs/plat/stm32mp1.rst index 88251d6af..2c372a6a3 100644 --- a/docs/plat/stm32mp1.rst +++ b/docs/plat/stm32mp1.rst @@ -76,21 +76,34 @@ ROM code -> BL2 (compiled with BL2_AT_EL3) -> OP-TEE -> BL33 (U-Boot) 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: +To build with SP_min and support for all bootable devices: .. code:: bash - make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min DTB_FILE_NAME=stm32mp157c-ev1.dtb + 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 with Op-TEE support: - +To build TF-A with OP-TEE support for all bootable devices: .. code:: bash - make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=optee + 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 + cd + make stm32mp15_optee_defconfig + make DEVICE_TREE=stm32mp157c-ev1 all + The following build options are supported: diff --git a/drivers/arm/tzc/tzc400.c b/drivers/arm/tzc/tzc400.c index 50d670139..801cf3046 100644 --- a/drivers/arm/tzc/tzc400.c +++ b/drivers/arm/tzc/tzc400.c @@ -1,5 +1,5 @@ /* - * 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 */ @@ -60,7 +60,6 @@ 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) @@ -70,8 +69,50 @@ DEFINE_TZC_COMMON_WRITE_REGION_ID_ACCESS(400, 400) DEFINE_TZC_COMMON_CONFIGURE_REGION0(400) DEFINE_TZC_COMMON_CONFIGURE_REGION(400) -static unsigned int _tzc400_get_gate_keeper(uintptr_t base, - unsigned int filter) +static inline void tzc400_clear_it(long base, uint32_t filter) +{ + mmio_write_32(base + INT_CLEAR, 1U << filter); +} + +static inline uint32_t tzc400_get_int_by_filter(long base, uint32_t filter) +{ + return (mmio_read_32(base + INT_STATUS) & (1U << filter)); +} + +#if DEBUG +static long tzc400_get_fail_address(long base, uint32_t filter) +{ + long fail_address; + + if (filter != 0U) { + fail_address = mmio_read_32(base + FAIL_ADDRESS_LOW_OFF + + FILTER_OFFSET); +#ifdef __aarch64__ + fail_address += mmio_read_32(base + FAIL_ADDRESS_HIGH_OFF + + FILTER_OFFSET) << 32; +#endif + } else { + fail_address = mmio_read_32(base + FAIL_ADDRESS_LOW_OFF); +#ifdef __aarch64__ + fail_address += + mmio_read_32(base + FAIL_ADDRESS_HIGH_OFF) << 32; +#endif + } + + return fail_address; +} +#endif + +static inline uint32_t tzc400_get_fail_control(long base, uint32_t filter) +{ + if (filter != 0U) { + return mmio_read_32(base + FAIL_CONTROL_OFF + FILTER_OFFSET); + } else { + return mmio_read_32(base + FAIL_CONTROL_OFF); + } +} + +static unsigned int _tzc400_get_gate_keeper(uintptr_t base, unsigned int filter) { unsigned int open_status; @@ -81,9 +122,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 +191,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 +208,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 +225,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 +233,7 @@ 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_enable_filters(void) @@ -217,8 +256,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 +277,79 @@ void tzc400_disable_filters(void) for (filter = 0; filter < tzc400.num_filters; filter++) _tzc400_set_gate_keeper(tzc400.base, filter, 0); } + +void tzc400_clear_all_interrupts(void) +{ + unsigned int filter; + + assert(tzc400.base != 0U); + for (filter = 0U; filter < tzc400.num_filters; filter++) { + mmio_write_32(tzc400.base + INT_CLEAR, 1U << filter); + } +} + +int tzc400_is_pending_interrupt(void) +{ + unsigned int filter; + + assert(tzc400.base != 0U); + for (filter = 0U; filter < tzc400.num_filters; filter++) { + if (mmio_read_32(tzc400.base + INT_STATUS) & (1U << filter)) { + return 1; + } + } + return 0; +} + +void tzc400_it_handler(void) +{ + uint32_t filter; + uint32_t filter_it_pending = tzc400.num_filters; + uint32_t control_fail; +#if DEBUG + 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 in :\n", address_fail); +#endif + + control_fail = tzc400_get_fail_control(tzc400.base, filter_it_pending); + + if ((control_fail & FAIL_CONTROL_NS_SHIFT) == FAIL_CONTROL_NS_SECURE) { + ERROR("\tNon-Secure\n"); + } else { + ERROR("\tSecure\n"); + } + + if ((control_fail & FAIL_CONTROL_PRIV_SHIFT) == + FAIL_CONTROL_PRIV_PRIV) { + ERROR("\tPrivilege\n"); + } else { + ERROR("\tUnprivilege\n"); + } + + if ((control_fail & FAIL_CONTROL_DIR_SHIFT) == FAIL_CONTROL_DIR_READ) { + ERROR("\tRead\n"); + } else { + ERROR("\tWrite\n"); + } + + tzc400_clear_it(tzc400.base, filter_it_pending); +} diff --git a/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c b/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c index a0fc034d8..dcd199148 100644 --- a/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c +++ b/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c @@ -26,9 +26,9 @@ typedef struct { * valid. */ int in_use; - uintptr_t base; - size_t file_pos; - size_t size; + uintptr_t base; + unsigned long long file_pos; + unsigned long long size; } file_state_t; static file_state_t current_file = {0}; @@ -44,7 +44,7 @@ static int memmap_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); static int memmap_block_seek(io_entity_t *entity, int mode, - ssize_t offset); + signed long long offset); static int memmap_block_len(io_entity_t *entity, size_t *length); static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); @@ -131,7 +131,8 @@ static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, /* Seek to a particular file offset on the memmap device */ -static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int memmap_block_seek(io_entity_t *entity, int mode, + signed long long offset) { int result = -ENOENT; file_state_t *fp; @@ -143,7 +144,8 @@ static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) fp = (file_state_t *) entity->info; /* Assert that new file position is valid */ - assert((offset >= 0) && (offset < fp->size)); + assert((offset >= 0) && + ((unsigned long long)offset < fp->size)); /* Reset file position */ fp->file_pos = offset; @@ -171,7 +173,7 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_read != NULL); @@ -198,7 +200,7 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, size_t length, size_t *length_written) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_written != NULL); diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c index f190a4307..5d45c2f17 100644 --- a/drivers/io/io_block.c +++ b/drivers/io/io_block.c @@ -19,17 +19,17 @@ typedef struct { io_block_dev_spec_t *dev_spec; uintptr_t base; - size_t file_pos; - size_t size; + unsigned long long file_pos; + unsigned long long size; } block_dev_state_t; -#define is_power_of_2(x) ((x != 0) && ((x & (x - 1)) == 0)) +#define is_power_of_2(x) (((x) != 0U) && (((x) & ((x) - 1U)) == 0U)) io_type_t device_type_block(void); static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); -static int block_seek(io_entity_t *entity, int mode, ssize_t offset); +static int block_seek(io_entity_t *entity, int mode, signed long long offset); static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); static int block_write(io_entity_t *entity, const uintptr_t buffer, @@ -148,21 +148,21 @@ static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, } /* parameter offset is relative address at here */ -static int block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int block_seek(io_entity_t *entity, int mode, signed long long offset) { block_dev_state_t *cur; assert(entity->info != (uintptr_t)NULL); cur = (block_dev_state_t *)entity->info; - assert((offset >= 0) && (offset < cur->size)); + assert((offset >= 0) && ((unsigned long long)offset < cur->size)); switch (mode) { case IO_SEEK_SET: - cur->file_pos = offset; + cur->file_pos = (unsigned long long)offset; break; case IO_SEEK_CUR: - cur->file_pos += offset; + cur->file_pos += (unsigned long long)offset; break; default: return -EINVAL; @@ -270,7 +270,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, buf = &(cur->dev_spec->buffer); block_size = cur->dev_spec->block_size; assert((length <= cur->size) && - (length > 0) && + (length > 0U) && (ops->read != 0)); /* @@ -279,7 +279,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * on the low level driver. */ count = 0; - for (left = length; left > 0; left -= nbytes) { + for (left = length; left > 0U; left -= nbytes) { /* * We must only request operations aligned to the block * size. Therefore if file_pos is not block-aligned, @@ -288,7 +288,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * similarly, the number of bytes requested must be a * block size multiple */ - skip = cur->file_pos & (block_size - 1); + skip = cur->file_pos & (block_size - 1U); /* * Calculate the block number containing file_pos @@ -296,7 +296,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, */ lba = (cur->file_pos + cur->base) / block_size; - if (skip + left > buf->length) { + if ((skip + left) > buf->length) { /* * The underlying read buffer is too small to * read all the required data - limit to just @@ -311,7 +311,8 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * block size. */ request = skip + left; - request = (request + (block_size - 1)) & ~(block_size - 1); + request = (request + (block_size - 1U)) & + ~(block_size - 1U); } request = ops->read(lba, buf->offset, request); @@ -330,7 +331,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * the read data when copying to the user buffer. */ nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; memcpy((void *)(buffer + count), @@ -381,7 +382,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, buf = &(cur->dev_spec->buffer); block_size = cur->dev_spec->block_size; assert((length <= cur->size) && - (length > 0) && + (length > 0U) && (ops->read != 0) && (ops->write != 0)); @@ -391,7 +392,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * on the low level driver. */ count = 0; - for (left = length; left > 0; left -= nbytes) { + for (left = length; left > 0U; left -= nbytes) { /* * We must only request operations aligned to the block * size. Therefore if file_pos is not block-aligned, @@ -400,7 +401,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * similarly, the number of bytes requested must be a * block size multiple */ - skip = cur->file_pos & (block_size - 1); + skip = cur->file_pos & (block_size - 1U); /* * Calculate the block number containing file_pos @@ -408,7 +409,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, */ lba = (cur->file_pos + cur->base) / block_size; - if (skip + left > buf->length) { + if ((skip + left) > buf->length) { /* * The underlying read buffer is too small to * read all the required data - limit to just @@ -423,7 +424,8 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * block size. */ request = skip + left; - request = (request + (block_size - 1)) & ~(block_size - 1); + request = (request + (block_size - 1U)) & + ~(block_size - 1U); } /* @@ -432,7 +434,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * of the current request. */ nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; /* @@ -440,14 +442,14 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * some content and it means that we have to read before * writing */ - if (skip > 0 || padding > 0) { + if ((skip > 0U) || (padding > 0U)) { request = ops->read(lba, buf->offset, request); /* * The read may return size less than * requested. Round down to the nearest block * boundary */ - request &= ~(block_size-1); + request &= ~(block_size - 1U); if (request <= skip) { /* * We couldn't read enough bytes to jump over @@ -458,7 +460,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, return -EIO; } nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; } @@ -477,7 +479,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * buffer */ nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; cur->file_pos += nbytes; @@ -505,7 +507,7 @@ static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) assert(dev_info != NULL); result = allocate_dev_info(&info); - if (result) + if (result != 0) return -ENOENT; cur = (block_dev_state_t *)info->info; @@ -513,10 +515,10 @@ static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) cur->dev_spec = (io_block_dev_spec_t *)dev_spec; buffer = &(cur->dev_spec->buffer); block_size = cur->dev_spec->block_size; - assert((block_size > 0) && - (is_power_of_2(block_size) != 0) && - ((buffer->offset % block_size) == 0) && - ((buffer->length % block_size) == 0)); + assert((block_size > 0U) && + (is_power_of_2(block_size) != 0U) && + ((buffer->offset % block_size) == 0U) && + ((buffer->length % block_size) == 0U)); *dev_info = info; /* cast away const */ (void)block_size; diff --git a/drivers/io/io_fip.c b/drivers/io/io_fip.c index 544b37dbe..5d49fffaa 100644 --- a/drivers/io/io_fip.c +++ b/drivers/io/io_fip.c @@ -304,7 +304,8 @@ static int fip_file_open(io_dev_info_t *dev_info, const uintptr_t spec, } /* Seek past the FIP header into the Table of Contents */ - result = io_seek(backend_handle, IO_SEEK_SET, sizeof(fip_toc_header_t)); + result = io_seek(backend_handle, IO_SEEK_SET, + (signed long long)sizeof(fip_toc_header_t)); if (result != 0) { WARN("fip_file_open: failed to seek\n"); result = -ENOENT; @@ -389,7 +390,8 @@ static int fip_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, /* Seek to the position in the FIP where the payload lives */ file_offset = fp->entry.offset_address + fp->file_pos; - result = io_seek(backend_handle, IO_SEEK_SET, file_offset); + result = io_seek(backend_handle, IO_SEEK_SET, + (signed long long)file_offset); if (result != 0) { WARN("fip_file_read: failed to seek\n"); result = -ENOENT; diff --git a/drivers/io/io_memmap.c b/drivers/io/io_memmap.c index 96590b6c0..eed50cc08 100644 --- a/drivers/io/io_memmap.c +++ b/drivers/io/io_memmap.c @@ -23,10 +23,10 @@ typedef struct { /* Use the 'in_use' flag as any value for base and file_pos could be * valid. */ - int in_use; - uintptr_t base; - size_t file_pos; - size_t size; + int in_use; + uintptr_t base; + unsigned long long file_pos; + unsigned long long size; } file_state_t; static file_state_t current_file = {0}; @@ -42,7 +42,7 @@ static int memmap_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); static int memmap_block_seek(io_entity_t *entity, int mode, - ssize_t offset); + signed long long offset); static int memmap_block_len(io_entity_t *entity, size_t *length); static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); @@ -129,7 +129,8 @@ static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, /* Seek to a particular file offset on the memmap device */ -static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int memmap_block_seek(io_entity_t *entity, int mode, + signed long long offset) { int result = -ENOENT; file_state_t *fp; @@ -141,10 +142,11 @@ static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) fp = (file_state_t *) entity->info; /* Assert that new file position is valid */ - assert((offset >= 0) && (offset < fp->size)); + assert((offset >= 0) && + ((unsigned long long)offset < fp->size)); /* Reset file position */ - fp->file_pos = offset; + fp->file_pos = (unsigned long long)offset; result = 0; } @@ -158,7 +160,7 @@ static int memmap_block_len(io_entity_t *entity, size_t *length) assert(entity != NULL); assert(length != NULL); - *length = ((file_state_t *)entity->info)->size; + *length = (size_t)((file_state_t *)entity->info)->size; return 0; } @@ -169,7 +171,7 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_read != NULL); @@ -180,7 +182,8 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, pos_after = fp->file_pos + length; assert((pos_after >= fp->file_pos) && (pos_after <= fp->size)); - memcpy((void *)buffer, (void *)(fp->base + fp->file_pos), length); + memcpy((void *)buffer, + (void *)((uintptr_t)(fp->base + fp->file_pos)), length); *length_read = length; @@ -196,7 +199,7 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, size_t length, size_t *length_written) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_written != NULL); @@ -207,7 +210,8 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, pos_after = fp->file_pos + length; assert((pos_after >= fp->file_pos) && (pos_after <= fp->size)); - memcpy((void *)(fp->base + fp->file_pos), (void *)buffer, length); + memcpy((void *)((uintptr_t)(fp->base + fp->file_pos)), + (void *)buffer, length); *length_written = length; diff --git a/drivers/io/io_mtd.c b/drivers/io/io_mtd.c new file mode 100644 index 000000000..7575fa250 --- /dev/null +++ b/drivers/io/io_mtd.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +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 */ +} mtd_dev_state_t; + +io_type_t device_type_mtd(void); + +static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int mtd_seek(io_entity_t *entity, int mode, signed long long offset); +static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *length_read); +static int mtd_close(io_entity_t *entity); +static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); +static int mtd_dev_close(io_dev_info_t *dev_info); + +static const io_dev_connector_t mtd_dev_connector = { + .dev_open = mtd_dev_open +}; + +static const io_dev_funcs_t mtd_dev_funcs = { + .type = device_type_mtd, + .open = mtd_open, + .seek = mtd_seek, + .read = mtd_read, + .close = mtd_close, + .dev_close = mtd_dev_close, +}; + +static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES]; +static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES]; + +io_type_t device_type_mtd(void) +{ + return IO_TYPE_MTD; +} + +/* Locate a MTD state in the pool, specified by address */ +static int find_first_mtd_state(const io_mtd_dev_spec_t *dev_spec, + unsigned int *index_out) +{ + unsigned int index; + int result = -ENOENT; + + for (index = 0U; index < MAX_IO_MTD_DEVICES; index++) { + /* dev_spec is used as identifier since it's unique */ + if (state_pool[index].dev_spec == dev_spec) { + result = 0; + *index_out = index; + break; + } + } + + return result; +} + +/* Allocate a device info from the pool */ +static int allocate_dev_info(io_dev_info_t **dev_info) +{ + unsigned int index = 0U; + int result; + + result = find_first_mtd_state(NULL, &index); + if (result != 0) { + return -ENOMEM; + } + + dev_info_pool[index].funcs = &mtd_dev_funcs; + dev_info_pool[index].info = (uintptr_t)&state_pool[index]; + *dev_info = &dev_info_pool[index]; + + return 0; +} + +/* Release a device info from the pool */ +static int free_dev_info(io_dev_info_t *dev_info) +{ + int result; + unsigned int index = 0U; + mtd_dev_state_t *state; + + state = (mtd_dev_state_t *)dev_info->info; + result = find_first_mtd_state(state->dev_spec, &index); + if (result != 0) { + return result; + } + + zeromem(state, sizeof(mtd_dev_state_t)); + zeromem(dev_info, sizeof(io_dev_info_t)); + + 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; + + assert((dev_info->info != 0UL) && (entity->info == 0UL)); + + cur = (mtd_dev_state_t *)dev_info->info; + entity->info = (uintptr_t)cur; + cur->offset = 0U; + + return 0; +} + +/* Seek to a specific position using offset */ +static int mtd_seek(io_entity_t *entity, int mode, signed long long offset) +{ + mtd_dev_state_t *cur; + + assert((entity->info != (uintptr_t)NULL) && (offset >= 0)); + + cur = (mtd_dev_state_t *)entity->info; + + switch (mode) { + case IO_SEEK_SET: + if ((offset >= 0) && + ((unsigned long long)offset >= cur->size)) { + return -EINVAL; + } + + cur->offset = offset; + break; + case IO_SEEK_CUR: + if (((cur->offset + (unsigned long long)offset) >= + cur->size) || + ((cur->offset + (unsigned long long)offset) < + cur->offset)) { + return -EINVAL; + } + + cur->offset += (unsigned long long)offset; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *out_length) +{ + mtd_dev_state_t *cur; + io_mtd_ops_t *ops; + int ret; + + assert(entity->info != (uintptr_t)NULL); + assert((length > 0U) && (buffer != (uintptr_t)NULL)); + + cur = (mtd_dev_state_t *)entity->info; + ops = &cur->dev_spec->ops; + 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) { + return -EINVAL; + } + + ret = ops->read(cur->offset, buffer, length, out_length); + if (ret < 0) { + return ret; + } + + assert(*out_length == length); + cur->offset += *out_length; + + return 0; +} + +static int mtd_close(io_entity_t *entity) +{ + entity->info = (uintptr_t)NULL; + + return 0; +} + +static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) +{ + mtd_dev_state_t *cur; + io_dev_info_t *info; + io_mtd_ops_t *ops; + int result; + + result = allocate_dev_info(&info); + if (result != 0) { + return -ENOENT; + } + + cur = (mtd_dev_state_t *)info->info; + cur->dev_spec = (io_mtd_dev_spec_t *)dev_spec; + *dev_info = info; + ops = &(cur->dev_spec->ops); + if (ops->init != NULL) { + result = ops->init(&cur->dev_spec->device_size, + &cur->dev_spec->erase_size); + } + + if (result == 0) { + cur->size = cur->dev_spec->device_size; + } else { + cur->size = 0ULL; + } + + return result; +} + +static int mtd_dev_close(io_dev_info_t *dev_info) +{ + return free_dev_info(dev_info); +} + +/* Exported functions */ + +/* Register the MTD driver in the IO abstraction */ +int register_io_dev_mtd(const io_dev_connector_t **dev_con) +{ + int result; + + result = io_register_device(&dev_info_pool[0]); + if (result == 0) { + *dev_con = &mtd_dev_connector; + } + + return result; +} diff --git a/drivers/io/io_semihosting.c b/drivers/io/io_semihosting.c index 23d09c118..4ceddc6cc 100644 --- a/drivers/io/io_semihosting.c +++ b/drivers/io/io_semihosting.c @@ -25,7 +25,7 @@ static io_type_t device_type_sh(void) static int sh_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); static int sh_file_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); -static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset); +static int sh_file_seek(io_entity_t *entity, int mode, signed long long offset); static int sh_file_len(io_entity_t *entity, size_t *length); static int sh_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); @@ -90,7 +90,7 @@ static int sh_file_open(io_dev_info_t *dev_info __unused, /* Seek to a particular file offset on the semi-hosting device */ -static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset) +static int sh_file_seek(io_entity_t *entity, int mode, signed long long offset) { long file_handle, sh_result; @@ -98,7 +98,7 @@ static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset) file_handle = (long)entity->info; - sh_result = semihosting_file_seek(file_handle, offset); + sh_result = semihosting_file_seek(file_handle, (ssize_t)offset); return (sh_result == 0) ? 0 : -ENOENT; } diff --git a/drivers/io/io_storage.c b/drivers/io/io_storage.c index e444f87f7..b8c1d6479 100644 --- a/drivers/io/io_storage.c +++ b/drivers/io/io_storage.c @@ -237,7 +237,7 @@ int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle) /* Seek to a specific position in an IO entity */ -int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset) +int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset) { int result = -ENODEV; assert(is_valid_entity(handle) && is_valid_seek_mode(mode)); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b5f6a10d3..42243ea09 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 new file mode 100644 index 000000000..44b001e35 --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* + * Define a single nand_device used by specific NAND frameworks. + */ +static struct nand_device nand_dev; +static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE]; + +int nand_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read) +{ + unsigned int block = offset / nand_dev.block_size; + unsigned int end_block = (offset + length - 1U) / nand_dev.block_size; + unsigned int page_start = + (offset % nand_dev.block_size) / nand_dev.page_size; + unsigned int nb_pages = nand_dev.block_size / nand_dev.page_size; + unsigned int start_offset = offset % nand_dev.page_size; + unsigned int page; + unsigned int bytes_read; + int is_bad; + int ret; + + VERBOSE("Block %u - %u, page_start %u, nb %u, length %zu, offset %u\n", + block, end_block, page_start, nb_pages, length, offset); + + *length_read = 0UL; + + if (((start_offset != 0U) || (length % nand_dev.page_size) != 0U) && + (sizeof(scratch_buff) < nand_dev.page_size)) { + return -EINVAL; + } + + while (block <= end_block) { + is_bad = nand_dev.mtd_block_is_bad(block); + if (is_bad < 0) { + return is_bad; + } + + if (is_bad == 1) { + /* Skip the block */ + uint32_t max_block = + nand_dev.size / nand_dev.block_size; + + block++; + end_block++; + if ((block < max_block) && (end_block < max_block)) { + continue; + } + + return -EIO; + } + + for (page = page_start; page < nb_pages; page++) { + if ((start_offset != 0U) || + (length < nand_dev.page_size)) { + ret = nand_dev.mtd_read_page( + &nand_dev, + (block * nb_pages) + page, + (uintptr_t)scratch_buff); + if (ret != 0) { + return ret; + } + + bytes_read = MIN((size_t)(nand_dev.page_size - + start_offset), + length); + + memcpy((uint8_t *)buffer, + scratch_buff + start_offset, + bytes_read); + + start_offset = 0U; + } else { + ret = nand_dev.mtd_read_page(&nand_dev, + (block * nb_pages) + page, + buffer); + if (ret != 0) { + return ret; + } + + bytes_read = nand_dev.page_size; + } + + length -= bytes_read; + buffer += bytes_read; + *length_read += bytes_read; + + if (length == 0U) { + break; + } + } + + page_start = 0U; + block++; + } + + return 0; +} + +struct nand_device *get_nand_device(void) +{ + return &nand_dev; +} diff --git a/drivers/mtd/nand/raw_nand.c b/drivers/mtd/nand/raw_nand.c new file mode 100644 index 000000000..48131fcb2 --- /dev/null +++ b/drivers/mtd/nand/raw_nand.c @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define ONFI_SIGNATURE_ADDR 0x20U + +/* CRC calculation */ +#define CRC_POLYNOM 0x8005U +#define CRC_INIT_VALUE 0x4F4EU + +/* Status register */ +#define NAND_STATUS_READY BIT(6) + +#define SZ_128M 0x08000000U +#define SZ_512 0x200U + +static struct rawnand_device rawnand_dev; + +#pragma weak plat_get_raw_nand_data +int plat_get_raw_nand_data(struct rawnand_device *device) +{ + return 0; +} + +static int nand_send_cmd(uint8_t cmd, unsigned int tim) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_CMD | cmd; + req.inst_delay = tim; + + return rawnand_dev.ops->exec(&req); +} + +static int nand_send_addr(uint8_t addr, unsigned int tim) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_ADDR; + req.addr = &addr; + req.inst_delay = tim; + + return rawnand_dev.ops->exec(&req); +} + +static int nand_send_wait(unsigned int delay, unsigned int tim) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_WAIT; + req.inst_delay = tim; + req.delay_ms = delay; + + return rawnand_dev.ops->exec(&req); +} + + +static int nand_read_data(uint8_t *data, unsigned int length, bool use_8bit) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_DATAIN | (use_8bit ? NAND_REQ_BUS_WIDTH_8 : 0U); + req.addr = data; + req.length = length; + + return rawnand_dev.ops->exec(&req); +} + +int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer, + unsigned int len) +{ + int ret; + uint8_t addr[2]; + unsigned int i; + + ret = nand_send_cmd(NAND_CMD_CHANGE_1ST, 0U); + if (ret != 0) { + return ret; + } + + if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) { + offset /= 2U; + } + + addr[0] = offset; + addr[1] = offset >> 8; + + for (i = 0; i < 2U; i++) { + ret = nand_send_addr(addr[i], 0U); + if (ret != 0) { + return ret; + } + } + + ret = nand_send_cmd(NAND_CMD_CHANGE_2ND, NAND_TCCS_MIN); + if (ret != 0) { + return ret; + } + + return nand_read_data((uint8_t *)buffer, len, false); +} + +int nand_read_page_cmd(unsigned int page, unsigned int offset, + uintptr_t buffer, unsigned int len) +{ + uint8_t addr[5]; + uint8_t i = 0U; + uint8_t j; + int ret; + + VERBOSE(">%s page %u offset %u buffer 0x%lx\n", __func__, page, offset, + buffer); + + if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) { + offset /= 2U; + } + + addr[i++] = offset; + addr[i++] = offset >> 8; + + addr[i++] = page; + addr[i++] = page >> 8; + if (rawnand_dev.nand_dev->size > SZ_128M) { + addr[i++] = page >> 16; + } + + ret = nand_send_cmd(NAND_CMD_READ_1ST, 0U); + if (ret != 0) { + return ret; + } + + for (j = 0U; j < i; j++) { + ret = nand_send_addr(addr[j], 0U); + if (ret != 0) { + return ret; + } + } + + ret = nand_send_cmd(NAND_CMD_READ_2ND, NAND_TWB_MAX); + if (ret != 0) { + return ret; + } + + ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN); + if (ret != 0) { + return ret; + } + + if (buffer != 0U) { + ret = nand_read_data((uint8_t *)buffer, len, false); + } + + return ret; +} + +static int nand_status(uint8_t *status) +{ + int ret; + + ret = nand_send_cmd(NAND_CMD_STATUS, NAND_TWHR_MIN); + if (ret != 0) { + return ret; + } + + if (status != NULL) { + ret = nand_read_data(status, 1U, true); + } + + return ret; +} + +int nand_wait_ready(unsigned long delay) +{ + uint8_t status; + int ret; + uint64_t timeout; + + /* Wait before reading status */ + udelay(1); + + ret = nand_status(NULL); + if (ret != 0) { + return ret; + } + + timeout = timeout_init_us(delay); + while (!timeout_elapsed(timeout)) { + ret = nand_read_data(&status, 1U, true); + if (ret != 0) { + return ret; + } + + if ((status & NAND_STATUS_READY) != 0U) { + return nand_send_cmd(NAND_CMD_READ_1ST, 0U); + } + + udelay(10); + } + + return -ETIMEDOUT; +} + +#if NAND_ONFI_DETECT +static uint16_t nand_check_crc(uint16_t crc, uint8_t *data_in, + unsigned int data_len) +{ + uint32_t i; + uint32_t j; + uint32_t bit; + + for (i = 0U; i < data_len; i++) { + uint8_t cur_param = *data_in++; + + for (j = BIT(7); j != 0U; j >>= 1) { + bit = crc & BIT(15); + crc <<= 1; + + if ((cur_param & j) != 0U) { + bit ^= BIT(15); + } + + if (bit != 0U) { + crc ^= CRC_POLYNOM; + } + } + + crc &= GENMASK(15, 0); + } + + return crc; +} + +static int nand_read_id(uint8_t addr, uint8_t *id, unsigned int size) +{ + int ret; + + ret = nand_send_cmd(NAND_CMD_READID, 0U); + if (ret != 0) { + return ret; + } + + ret = nand_send_addr(addr, NAND_TWHR_MIN); + if (ret != 0) { + return ret; + } + + return nand_read_data(id, size, true); +} + +static int nand_reset(void) +{ + int ret; + + ret = nand_send_cmd(NAND_CMD_RESET, NAND_TWB_MAX); + if (ret != 0) { + return ret; + } + + return nand_send_wait(PSEC_TO_MSEC(NAND_TRST_MAX), 0U); +} + +static int nand_read_param_page(void) +{ + struct nand_param_page page; + uint8_t addr = 0U; + int ret; + + ret = nand_send_cmd(NAND_CMD_READ_PARAM_PAGE, 0U); + if (ret != 0) { + return ret; + } + + ret = nand_send_addr(addr, NAND_TWB_MAX); + if (ret != 0) { + return ret; + } + + ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN); + if (ret != 0) { + return ret; + } + + ret = nand_read_data((uint8_t *)&page, sizeof(page), true); + if (ret != 0) { + return ret; + } + + if (strncmp((char *)&page.page_sig, "ONFI", 4) != 0) { + WARN("Error ONFI detection\n"); + return -EINVAL; + } + + if (nand_check_crc(CRC_INIT_VALUE, (uint8_t *)&page, 254U) != + page.crc16) { + WARN("Error reading param\n"); + return -EINVAL; + } + + if ((page.features & ONFI_FEAT_BUS_WIDTH_16) != 0U) { + rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_16; + } else { + rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_8; + } + + rawnand_dev.nand_dev->block_size = page.num_pages_per_blk * + page.bytes_per_page; + rawnand_dev.nand_dev->page_size = page.bytes_per_page; + rawnand_dev.nand_dev->size = page.num_pages_per_blk * + page.bytes_per_page * + page.num_blk_in_lun * page.num_lun; + + if (page.nb_ecc_bits != GENMASK_32(7, 0)) { + rawnand_dev.nand_dev->ecc.max_bit_corr = page.nb_ecc_bits; + rawnand_dev.nand_dev->ecc.size = SZ_512; + } + + VERBOSE("Page size %u, block_size %u, Size %llu, ecc %u, buswidth %u\n", + rawnand_dev.nand_dev->page_size, + rawnand_dev.nand_dev->block_size, rawnand_dev.nand_dev->size, + rawnand_dev.nand_dev->ecc.max_bit_corr, + rawnand_dev.nand_dev->buswidth); + + return 0; +} + +static int detect_onfi(void) +{ + int ret; + char id[4]; + + ret = nand_reset(); + if (ret != 0) { + return ret; + } + + ret = nand_read_id(ONFI_SIGNATURE_ADDR, (uint8_t *)id, sizeof(id)); + if (ret != 0) { + return ret; + } + + if (strncmp(id, "ONFI", sizeof(id)) != 0) { + WARN("NAND Non ONFI detected\n"); + return -ENODEV; + } + + return nand_read_param_page(); +} +#endif + +static int nand_mtd_block_is_bad(unsigned int block) +{ + unsigned int nbpages_per_block = rawnand_dev.nand_dev->block_size / + rawnand_dev.nand_dev->page_size; + uint8_t bbm_marker[2]; + uint8_t page; + int ret; + + for (page = 0U; page < 2U; page++) { + ret = nand_read_page_cmd(block * nbpages_per_block, + rawnand_dev.nand_dev->page_size, + (uintptr_t)bbm_marker, + sizeof(bbm_marker)); + if (ret != 0) { + return ret; + } + + if ((bbm_marker[0] != GENMASK_32(7, 0)) || + (bbm_marker[1] != GENMASK_32(7, 0))) { + WARN("Block %u is bad\n", block); + return 1; + } + } + + return 0; +} + +static int nand_mtd_read_page_raw(struct nand_device *nand, unsigned int page, + uintptr_t buffer) +{ + return nand_read_page_cmd(page, 0U, buffer, + rawnand_dev.nand_dev->page_size); +} + +void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops) +{ + rawnand_dev.ops = ops; +} + +int nand_raw_init(unsigned long long *size, unsigned int *erase_size) +{ + rawnand_dev.nand_dev = get_nand_device(); + if (rawnand_dev.nand_dev == NULL) { + return -EINVAL; + } + + rawnand_dev.nand_dev->mtd_block_is_bad = nand_mtd_block_is_bad; + rawnand_dev.nand_dev->mtd_read_page = nand_mtd_read_page_raw; + rawnand_dev.nand_dev->ecc.mode = NAND_ECC_NONE; + + if ((rawnand_dev.ops->setup == NULL) || + (rawnand_dev.ops->exec == NULL)) { + return -ENODEV; + } + +#if NAND_ONFI_DETECT + if (detect_onfi() != 0) { + WARN("Detect ONFI failed\n"); + } +#endif + + if (plat_get_raw_nand_data(&rawnand_dev) != 0) { + return -EINVAL; + } + + assert((rawnand_dev.nand_dev->page_size != 0U) && + (rawnand_dev.nand_dev->block_size != 0U) && + (rawnand_dev.nand_dev->size != 0U)); + + *size = rawnand_dev.nand_dev->size; + *erase_size = rawnand_dev.nand_dev->block_size; + + rawnand_dev.ops->setup(rawnand_dev.nand_dev); + + return 0; +} diff --git a/drivers/mtd/nand/spi_nand.c b/drivers/mtd/nand/spi_nand.c new file mode 100644 index 000000000..d01a11963 --- /dev/null +++ b/drivers/mtd/nand/spi_nand.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define SPI_NAND_MAX_ID_LEN 4U +#define DELAY_US_400MS 400000U +#define MACRONIX_ID 0xC2U + +static struct spinand_device spinand_dev; + +#pragma weak plat_get_spi_nand_data +int plat_get_spi_nand_data(struct spinand_device *device) +{ + return 0; +} + +static int spi_nand_reg(bool read_reg, uint8_t reg, uint8_t *val, + enum spi_mem_data_dir dir) +{ + struct spi_mem_op op; + + zeromem(&op, sizeof(struct spi_mem_op)); + if (read_reg) { + op.cmd.opcode = SPI_NAND_OP_GET_FEATURE; + } else { + op.cmd.opcode = SPI_NAND_OP_SET_FEATURE; + } + + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.addr.val = reg; + op.addr.nbytes = 1U; + op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.dir = dir; + op.data.nbytes = 1U; + op.data.buf = val; + + return spi_mem_exec_op(&op); +} + +static int spi_nand_read_reg(uint8_t reg, uint8_t *val) +{ + return spi_nand_reg(true, reg, val, SPI_MEM_DATA_IN); +} + +static int spi_nand_write_reg(uint8_t reg, uint8_t val) +{ + return spi_nand_reg(false, reg, &val, SPI_MEM_DATA_OUT); +} + +static int spi_nand_update_cfg(uint8_t mask, uint8_t val) +{ + int ret; + uint8_t cfg = spinand_dev.cfg_cache; + + cfg &= ~mask; + cfg |= val; + + if (cfg == spinand_dev.cfg_cache) { + return 0; + } + + ret = spi_nand_write_reg(SPI_NAND_REG_CFG, cfg); + if (ret == 0) { + spinand_dev.cfg_cache = cfg; + } + + return ret; +} + +static int spi_nand_ecc_enable(bool enable) +{ + return spi_nand_update_cfg(SPI_NAND_CFG_ECC_EN, + enable ? SPI_NAND_CFG_ECC_EN : 0U); +} + +static int spi_nand_quad_enable(uint8_t manufacturer_id) +{ + bool enable = false; + + if (manufacturer_id != MACRONIX_ID) { + return 0; + } + + if (spinand_dev.spi_read_cache_op.data.buswidth == + SPI_MEM_BUSWIDTH_4_LINE) { + enable = true; + } + + return spi_nand_update_cfg(SPI_NAND_CFG_QE, + enable ? SPI_NAND_CFG_QE : 0U); +} + +static int spi_nand_wait_ready(uint8_t *status) +{ + int ret; + uint64_t timeout = timeout_init_us(DELAY_US_400MS); + + while (!timeout_elapsed(timeout)) { + ret = spi_nand_read_reg(SPI_NAND_REG_STATUS, status); + if (ret != 0) { + return ret; + } + + VERBOSE("%s Status %x\n", __func__, *status); + if ((*status & SPI_NAND_STATUS_BUSY) == 0U) { + return 0; + } + } + + return -ETIMEDOUT; +} + +static int spi_nand_reset(void) +{ + struct spi_mem_op op; + uint8_t status; + int ret; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = SPI_NAND_OP_RESET; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + ret = spi_mem_exec_op(&op); + if (ret != 0) { + return ret; + } + + return spi_nand_wait_ready(&status); +} + +static int spi_nand_read_id(uint8_t *id) +{ + struct spi_mem_op op; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = SPI_NAND_OP_READ_ID; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.dir = SPI_MEM_DATA_IN; + op.data.nbytes = SPI_NAND_MAX_ID_LEN; + op.data.buf = id; + op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + return spi_mem_exec_op(&op); +} + +static int spi_nand_load_page(unsigned int page) +{ + struct spi_mem_op op; + uint32_t block_nb = page / spinand_dev.nand_dev->block_size; + uint32_t page_nb = page - (block_nb * spinand_dev.nand_dev->page_size); + uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size / + spinand_dev.nand_dev->page_size; + uint32_t block_sh = __builtin_ctz(nbpages_per_block) + 1U; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = SPI_NAND_OP_LOAD_PAGE; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.addr.val = (block_nb << block_sh) | page_nb; + op.addr.nbytes = 3U; + op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + return spi_mem_exec_op(&op); +} + +static int spi_nand_read_from_cache(unsigned int page, unsigned int offset, + uint8_t *buffer, unsigned int len) +{ + uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size / + spinand_dev.nand_dev->page_size; + uint32_t block_nb = page / nbpages_per_block; + uint32_t page_sh = __builtin_ctz(spinand_dev.nand_dev->page_size) + 1U; + + spinand_dev.spi_read_cache_op.addr.val = offset; + + if ((spinand_dev.nand_dev->nb_planes > 1U) && ((block_nb % 2U) == 1U)) { + spinand_dev.spi_read_cache_op.addr.val |= 1U << page_sh; + } + + spinand_dev.spi_read_cache_op.data.buf = buffer; + spinand_dev.spi_read_cache_op.data.nbytes = len; + + return spi_mem_exec_op(&spinand_dev.spi_read_cache_op); +} + +static int spi_nand_read_page(unsigned int page, unsigned int offset, + uint8_t *buffer, unsigned int len, + bool ecc_enabled) +{ + uint8_t status; + int ret; + + ret = spi_nand_ecc_enable(ecc_enabled); + if (ret != 0) { + return ret; + } + + ret = spi_nand_load_page(page); + if (ret != 0) { + return ret; + } + + ret = spi_nand_wait_ready(&status); + if (ret != 0) { + return ret; + } + + ret = spi_nand_read_from_cache(page, offset, buffer, len); + if (ret != 0) { + return ret; + } + + if (ecc_enabled && ((status & SPI_NAND_STATUS_ECC_UNCOR) != 0U)) { + return -EBADMSG; + } + + return 0; +} + +static int spi_nand_mtd_block_is_bad(unsigned int block) +{ + unsigned int nbpages_per_block = spinand_dev.nand_dev->block_size / + spinand_dev.nand_dev->page_size; + uint8_t bbm_marker[2]; + int ret; + + ret = spi_nand_read_page(block * nbpages_per_block, + spinand_dev.nand_dev->page_size, + bbm_marker, sizeof(bbm_marker), false); + if (ret != 0) { + return ret; + } + + if ((bbm_marker[0] != GENMASK_32(7, 0)) || + (bbm_marker[1] != GENMASK_32(7, 0))) { + WARN("Block %i is bad\n", block); + return 1; + } + + return 0; +} + +static int spi_nand_mtd_read_page(struct nand_device *nand, unsigned int page, + uintptr_t buffer) +{ + return spi_nand_read_page(page, 0, (uint8_t *)buffer, + spinand_dev.nand_dev->page_size, true); +} + +int spi_nand_init(unsigned long long *size, unsigned int *erase_size) +{ + uint8_t id[SPI_NAND_MAX_ID_LEN]; + int ret; + + spinand_dev.nand_dev = get_nand_device(); + if (spinand_dev.nand_dev == NULL) { + return -EINVAL; + } + + spinand_dev.nand_dev->mtd_block_is_bad = spi_nand_mtd_block_is_bad; + spinand_dev.nand_dev->mtd_read_page = spi_nand_mtd_read_page; + spinand_dev.nand_dev->nb_planes = 1; + + spinand_dev.spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE; + spinand_dev.spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + spinand_dev.spi_read_cache_op.addr.nbytes = 2U; + spinand_dev.spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + spinand_dev.spi_read_cache_op.dummy.nbytes = 1U; + spinand_dev.spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + spinand_dev.spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + if (plat_get_spi_nand_data(&spinand_dev) != 0) { + return -EINVAL; + } + + ret = spi_nand_reset(); + if (ret != 0) { + return ret; + } + + ret = spi_nand_read_id(id); + if (ret != 0) { + return ret; + } + + ret = spi_nand_read_reg(SPI_NAND_REG_CFG, &spinand_dev.cfg_cache); + if (ret != 0) { + return ret; + } + + ret = spi_nand_quad_enable(id[0]); + if (ret != 0) { + return ret; + } + + VERBOSE("SPI_NAND Detected ID 0x%x 0x%x\n", id[0], id[1]); + + VERBOSE("Page size %i, Block size %i, size %lli\n", + spinand_dev.nand_dev->page_size, + spinand_dev.nand_dev->block_size, + spinand_dev.nand_dev->size); + + *size = spinand_dev.nand_dev->size; + *erase_size = spinand_dev.nand_dev->block_size; + + return 0; +} diff --git a/drivers/mtd/nor/spi_nor.c b/drivers/mtd/nor/spi_nor.c new file mode 100644 index 000000000..2b4a5d87d --- /dev/null +++ b/drivers/mtd/nor/spi_nor.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define SR_WIP BIT(0) /* Write in progress */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ +#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ +#define FSR_READY BIT(7) /* Device status, 0 = Busy, 1 = Ready */ + +/* Defined IDs for supported memories */ +#define SPANSION_ID 0x01U +#define MACRONIX_ID 0xC2U +#define MICRON_ID 0x2CU + +#define BANK_SIZE 0x1000000U + +#define SPI_READY_TIMEOUT_US 40000U + +static struct nor_device nor_dev; + +#pragma weak plat_get_nor_data +int plat_get_nor_data(struct nor_device *device) +{ + return 0; +} + +static int spi_nor_reg(uint8_t reg, uint8_t *buf, size_t len, + enum spi_mem_data_dir dir) +{ + struct spi_mem_op op; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = reg; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.dir = dir; + op.data.nbytes = len; + op.data.buf = buf; + + return spi_mem_exec_op(&op); +} + +static inline int spi_nor_read_id(uint8_t *id) +{ + return spi_nor_reg(SPI_NOR_OP_READ_ID, id, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_read_cr(uint8_t *cr) +{ + return spi_nor_reg(SPI_NOR_OP_READ_CR, cr, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_read_sr(uint8_t *sr) +{ + return spi_nor_reg(SPI_NOR_OP_READ_SR, sr, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_read_fsr(uint8_t *fsr) +{ + return spi_nor_reg(SPI_NOR_OP_READ_FSR, fsr, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_write_en(void) +{ + return spi_nor_reg(SPI_NOR_OP_WREN, NULL, 0U, SPI_MEM_DATA_OUT); +} + +/* + * Check if device is ready. + * + * Return 0 if ready, 1 if busy or a negative error code otherwise + */ +static int spi_nor_ready(void) +{ + uint8_t sr; + int ret; + + ret = spi_nor_read_sr(&sr); + if (ret != 0) { + return ret; + } + + if ((nor_dev.flags & SPI_NOR_USE_FSR) != 0U) { + uint8_t fsr; + + ret = spi_nor_read_fsr(&fsr); + if (ret != 0) { + return ret; + } + + return (((fsr & FSR_READY) != 0U) && ((sr & SR_WIP) == 0U)) ? + 0 : 1; + } + + return (((sr & SR_WIP) != 0U) ? 1 : 0); +} + +static int spi_nor_wait_ready(void) +{ + int ret; + uint64_t timeout = timeout_init_us(SPI_READY_TIMEOUT_US); + + while (!timeout_elapsed(timeout)) { + ret = spi_nor_ready(); + if (ret <= 0) { + return ret; + } + } + + return -ETIMEDOUT; +} + +static int spi_nor_macronix_quad_enable(void) +{ + uint8_t sr; + int ret; + + ret = spi_nor_read_sr(&sr); + if (ret != 0) { + return ret; + } + + if ((sr & SR_QUAD_EN_MX) == 0U) { + return 0; + } + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + sr |= SR_QUAD_EN_MX; + ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1, SPI_MEM_DATA_OUT); + if (ret != 0) { + return ret; + } + + ret = spi_nor_wait_ready(); + if (ret != 0) { + return ret; + } + + ret = spi_nor_read_sr(&sr); + if ((ret != 0) || ((sr & SR_QUAD_EN_MX) == 0U)) { + return -EINVAL; + } + + return 0; +} + +static int spi_nor_write_sr_cr(uint8_t *sr_cr) +{ + int ret; + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2, SPI_MEM_DATA_OUT); + if (ret != 0) { + return -EINVAL; + } + + ret = spi_nor_wait_ready(); + if (ret != 0) { + return ret; + } + + return 0; +} + +static int spi_nor_quad_enable(void) +{ + uint8_t sr_cr[2]; + int ret; + + ret = spi_nor_read_cr(&sr_cr[1]); + if (ret != 0) { + return ret; + } + + if ((sr_cr[1] & CR_QUAD_EN_SPAN) != 0U) { + return 0; + } + + sr_cr[1] |= CR_QUAD_EN_SPAN; + ret = spi_nor_read_sr(&sr_cr[0]); + if (ret != 0) { + return ret; + } + + ret = spi_nor_write_sr_cr(sr_cr); + if (ret != 0) { + return ret; + } + + ret = spi_nor_read_cr(&sr_cr[1]); + if ((ret != 0) || ((sr_cr[1] & CR_QUAD_EN_SPAN) == 0U)) { + return -EINVAL; + } + + return 0; +} + +static int spi_nor_clean_bar(void) +{ + int ret; + + if (nor_dev.selected_bank == 0U) { + return 0; + } + + nor_dev.selected_bank = 0U; + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + return spi_nor_reg(nor_dev.bank_write_cmd, &nor_dev.selected_bank, + 1, SPI_MEM_DATA_OUT); +} + +static int spi_nor_write_bar(uint32_t offset) +{ + uint8_t selected_bank = offset / BANK_SIZE; + int ret; + + if (selected_bank == nor_dev.selected_bank) { + return 0; + } + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + ret = spi_nor_reg(nor_dev.bank_write_cmd, &selected_bank, + 1, SPI_MEM_DATA_OUT); + if (ret != 0) { + return ret; + } + + nor_dev.selected_bank = selected_bank; + + return 0; +} + +static int spi_nor_read_bar(void) +{ + uint8_t selected_bank = 0; + int ret; + + ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank, + 1, SPI_MEM_DATA_IN); + if (ret != 0) { + return ret; + } + + nor_dev.selected_bank = selected_bank; + + return 0; +} + +int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read) +{ + size_t remain_len; + int ret; + + *length_read = 0; + nor_dev.read_op.addr.val = offset; + nor_dev.read_op.data.buf = (void *)buffer; + + VERBOSE("%s offset %i length %zu\n", __func__, offset, length); + + while (length != 0U) { + if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { + ret = spi_nor_write_bar(nor_dev.read_op.addr.val); + if (ret != 0) { + return ret; + } + + remain_len = (BANK_SIZE * (nor_dev.selected_bank + 1)) - + nor_dev.read_op.addr.val; + nor_dev.read_op.data.nbytes = MIN(length, remain_len); + } else { + nor_dev.read_op.data.nbytes = length; + } + + ret = spi_mem_exec_op(&nor_dev.read_op); + if (ret != 0) { + spi_nor_clean_bar(); + return ret; + } + + length -= nor_dev.read_op.data.nbytes; + nor_dev.read_op.addr.val += nor_dev.read_op.data.nbytes; + nor_dev.read_op.data.buf += nor_dev.read_op.data.nbytes; + *length_read += nor_dev.read_op.data.nbytes; + } + + if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { + ret = spi_nor_clean_bar(); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +int spi_nor_init(unsigned long long *size, unsigned int *erase_size) +{ + int ret = 0; + uint8_t id; + + /* Default read command used */ + nor_dev.read_op.cmd.opcode = SPI_NOR_OP_READ; + nor_dev.read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + nor_dev.read_op.addr.nbytes = 3U; + nor_dev.read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + nor_dev.read_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + nor_dev.read_op.data.dir = SPI_MEM_DATA_IN; + + if (plat_get_nor_data(&nor_dev) != 0) { + return -EINVAL; + } + + assert(nor_dev.size != 0); + + if (nor_dev.size > BANK_SIZE) { + nor_dev.flags |= SPI_NOR_USE_BANK; + } + + *size = nor_dev.size; + + ret = spi_nor_read_id(&id); + if (ret != 0) { + return ret; + } + + if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { + switch (id) { + case SPANSION_ID: + nor_dev.bank_read_cmd = SPINOR_OP_BRRD; + nor_dev.bank_write_cmd = SPINOR_OP_BRWR; + break; + default: + nor_dev.bank_read_cmd = SPINOR_OP_RDEAR; + nor_dev.bank_write_cmd = SPINOR_OP_WREAR; + break; + } + } + + if (nor_dev.read_op.data.buswidth == 4U) { + switch (id) { + case MACRONIX_ID: + INFO("Enable Macronix quad support\n"); + ret = spi_nor_macronix_quad_enable(); + break; + case MICRON_ID: + break; + default: + ret = spi_nor_quad_enable(); + break; + } + } + + if ((ret == 0) && ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U)) { + ret = spi_nor_read_bar(); + } + + return ret; +} diff --git a/drivers/mtd/spi-mem/spi_mem.c b/drivers/mtd/spi-mem/spi_mem.c new file mode 100644 index 000000000..63ea7699b --- /dev/null +++ b/drivers/mtd/spi-mem/spi_mem.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include + +#define SPI_MEM_DEFAULT_SPEED_HZ 100000U + +/* + * struct spi_slave - Representation of a SPI slave. + * + * @max_hz: Maximum speed for this slave in Hertz. + * @cs: ID of the chip select connected to the slave. + * @mode: SPI mode to use for this slave (see SPI mode flags). + * @ops: Ops defined by the bus. + */ +struct spi_slave { + unsigned int max_hz; + unsigned int cs; + unsigned int mode; + const struct spi_bus_ops *ops; +}; + +static struct spi_slave spi_slave; + +static bool spi_mem_check_buswidth_req(uint8_t buswidth, bool tx) +{ + switch (buswidth) { + case 1U: + return true; + + case 2U: + if ((tx && (spi_slave.mode & (SPI_TX_DUAL | SPI_TX_QUAD)) != + 0U) || + (!tx && (spi_slave.mode & (SPI_RX_DUAL | SPI_RX_QUAD)) != + 0U)) { + return true; + } + break; + + case 4U: + if ((tx && (spi_slave.mode & SPI_TX_QUAD) != 0U) || + (!tx && (spi_slave.mode & SPI_RX_QUAD) != 0U)) { + return true; + } + break; + + default: + break; + } + + return false; +} + +static bool spi_mem_supports_op(const struct spi_mem_op *op) +{ + if (!spi_mem_check_buswidth_req(op->cmd.buswidth, true)) { + return false; + } + + if ((op->addr.nbytes != 0U) && + !spi_mem_check_buswidth_req(op->addr.buswidth, true)) { + return false; + } + + if ((op->dummy.nbytes != 0U) && + !spi_mem_check_buswidth_req(op->dummy.buswidth, true)) { + return false; + } + + if ((op->data.nbytes != 0U) && + !spi_mem_check_buswidth_req(op->data.buswidth, + op->data.dir == SPI_MEM_DATA_OUT)) { + return false; + } + + return true; +} + +static int spi_mem_set_speed_mode(void) +{ + const struct spi_bus_ops *ops = spi_slave.ops; + int ret; + + ret = ops->set_speed(spi_slave.max_hz); + if (ret != 0) { + VERBOSE("Cannot set speed (err=%d)\n", ret); + return ret; + } + + ret = ops->set_mode(spi_slave.mode); + if (ret != 0) { + VERBOSE("Cannot set mode (err=%d)\n", ret); + return ret; + } + + return 0; +} + +static int spi_mem_check_bus_ops(const struct spi_bus_ops *ops) +{ + bool error = false; + + if (ops->claim_bus == NULL) { + VERBOSE("Ops claim bus is not defined\n"); + error = true; + } + + if (ops->release_bus == NULL) { + VERBOSE("Ops release bus is not defined\n"); + error = true; + } + + if (ops->exec_op == NULL) { + VERBOSE("Ops exec op is not defined\n"); + error = true; + } + + if (ops->set_speed == NULL) { + VERBOSE("Ops set speed is not defined\n"); + error = true; + } + + if (ops->set_mode == NULL) { + VERBOSE("Ops set mode is not defined\n"); + error = true; + } + + return error ? -EINVAL : 0; +} + +/* + * spi_mem_exec_op() - Execute a memory operation. + * @op: The memory operation to execute. + * + * This function first checks that @op is supported and then tries to execute + * it. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_mem_exec_op(const struct spi_mem_op *op) +{ + const struct spi_bus_ops *ops = spi_slave.ops; + int ret; + + VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addqr:%llx len:%x\n", + __func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, + op->addr.val, op->data.nbytes); + + if (!spi_mem_supports_op(op)) { + WARN("Error in spi_mem_support\n"); + return -ENOTSUP; + } + + ret = ops->claim_bus(spi_slave.cs); + if (ret != 0) { + WARN("Error claim_bus\n"); + return ret; + } + + ret = ops->exec_op(op); + + ops->release_bus(); + + return ret; +} + +/* + * spi_mem_init_slave() - SPI slave device initialization. + * @fdt: Pointer to the device tree blob. + * @bus_node: Offset of the bus node. + * @ops: The SPI bus ops defined. + * + * This function first checks that @ops are supported and then tries to find + * a SPI slave device. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_mem_init_slave(void *fdt, int bus_node, const struct spi_bus_ops *ops) +{ + int ret; + int mode = 0; + int nchips = 0; + int bus_subnode = 0; + const fdt32_t *cuint = NULL; + + ret = spi_mem_check_bus_ops(ops); + if (ret != 0) { + return ret; + } + + fdt_for_each_subnode(bus_subnode, fdt, bus_node) { + nchips++; + } + + if (nchips != 1) { + ERROR("Only one SPI device is currently supported\n"); + return -EINVAL; + } + + fdt_for_each_subnode(bus_subnode, fdt, bus_node) { + /* Get chip select */ + cuint = fdt_getprop(fdt, bus_subnode, "reg", NULL); + if (cuint == NULL) { + ERROR("Chip select not well defined\n"); + return -EINVAL; + } + spi_slave.cs = fdt32_to_cpu(*cuint); + + /* Get max slave frequency */ + spi_slave.max_hz = SPI_MEM_DEFAULT_SPEED_HZ; + cuint = fdt_getprop(fdt, bus_subnode, + "spi-max-frequency", NULL); + if (cuint != NULL) { + spi_slave.max_hz = fdt32_to_cpu(*cuint); + } + + /* Get mode */ + if ((fdt_getprop(fdt, bus_subnode, "spi-cpol", NULL)) != NULL) { + mode |= SPI_CPOL; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-cpha", NULL)) != NULL) { + mode |= SPI_CPHA; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-cs-high", NULL)) != + NULL) { + mode |= SPI_CS_HIGH; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-3wire", NULL)) != + NULL) { + mode |= SPI_3WIRE; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-half-duplex", NULL)) != + NULL) { + mode |= SPI_PREAMBLE; + } + + /* Get dual/quad mode */ + cuint = fdt_getprop(fdt, bus_subnode, "spi-tx-bus-width", NULL); + if (cuint != NULL) { + switch (fdt32_to_cpu(*cuint)) { + case 1U: + break; + case 2U: + mode |= SPI_TX_DUAL; + break; + case 4U: + mode |= SPI_TX_QUAD; + break; + default: + WARN("spi-tx-bus-width %d not supported\n", + fdt32_to_cpu(*cuint)); + return -EINVAL; + } + } + + cuint = fdt_getprop(fdt, bus_subnode, "spi-rx-bus-width", NULL); + if (cuint != NULL) { + switch (fdt32_to_cpu(*cuint)) { + case 1U: + break; + case 2U: + mode |= SPI_RX_DUAL; + break; + case 4U: + mode |= SPI_RX_QUAD; + break; + default: + WARN("spi-rx-bus-width %d not supported\n", + fdt32_to_cpu(*cuint)); + return -EINVAL; + } + } + + spi_slave.mode = mode; + spi_slave.ops = ops; + } + + return spi_mem_set_speed_mode(); +} diff --git a/drivers/renesas/rcar/io/io_emmcdrv.c b/drivers/renesas/rcar/io/io_emmcdrv.c index 4b464fb3e..84240d260 100644 --- a/drivers/renesas/rcar/io/io_emmcdrv.c +++ b/drivers/renesas/rcar/io/io_emmcdrv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,7 +25,7 @@ static int32_t emmcdrv_dev_close(io_dev_info_t *dev_info); typedef struct { uint32_t in_use; uintptr_t base; - ssize_t file_pos; + signed long long file_pos; EMMC_PARTITION_ID partition; } file_state_t; @@ -39,7 +39,7 @@ static io_type_t device_type_emmcdrv(void) } static int32_t emmcdrv_block_seek(io_entity_t *entity, int32_t mode, - ssize_t offset) + signed long long offset) { if (mode != IO_SEEK_SET) return IO_FAIL; @@ -59,12 +59,12 @@ static int32_t emmcdrv_block_read(io_entity_t *entity, uintptr_t buffer, sector_add = current_file.file_pos >> EMMC_SECTOR_SIZE_SHIFT; sector_num = (length + EMMC_SECTOR_SIZE - 1U) >> EMMC_SECTOR_SIZE_SHIFT; - NOTICE("BL2: Load dst=0x%lx src=(p:%d)0x%lx(%d) len=0x%lx(%d)\n", + NOTICE("BL2: Load dst=0x%lx src=(p:%d)0x%llx(%d) len=0x%lx(%d)\n", buffer, current_file.partition, current_file.file_pos, sector_add, length, sector_num); - if (buffer + length - 1 <= UINT32_MAX) + if ((buffer + length - 1U) <= (uintptr_t)UINT32_MAX) emmc_dma = LOADIMAGE_FLAGS_DMA_ENABLE; if (emmc_read_sector((uint32_t *) buffer, sector_add, sector_num, @@ -72,7 +72,7 @@ static int32_t emmcdrv_block_read(io_entity_t *entity, uintptr_t buffer, result = IO_FAIL; *length_read = length; - fp->file_pos += length; + fp->file_pos += (signed long long)length; return result; } @@ -82,7 +82,7 @@ static int32_t emmcdrv_block_open(io_dev_info_t *dev_info, { const io_drv_spec_t *block_spec = (io_drv_spec_t *) spec; - if (current_file.in_use) { + if (current_file.in_use != 0U) { WARN("mmc_block: Only one open spec at a time\n"); return IO_RESOURCES_EXHAUSTED; } @@ -103,9 +103,9 @@ static int32_t emmcdrv_block_open(io_dev_info_t *dev_info, return IO_FAIL; } - if (PARTITION_ID_USER == block_spec->partition || - PARTITION_ID_BOOT_1 == block_spec->partition || - PARTITION_ID_BOOT_2 == block_spec->partition) + if ((PARTITION_ID_USER == block_spec->partition) || + (PARTITION_ID_BOOT_1 == block_spec->partition) || + (PARTITION_ID_BOOT_2 == block_spec->partition)) current_file.partition = block_spec->partition; else current_file.partition = emmcdrv_bootpartition; diff --git a/drivers/renesas/rcar/io/io_memdrv.c b/drivers/renesas/rcar/io/io_memdrv.c index 3f6b4c71b..7e8c1d3a6 100644 --- a/drivers/renesas/rcar/io/io_memdrv.c +++ b/drivers/renesas/rcar/io/io_memdrv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -28,7 +28,7 @@ static int32_t memdrv_dev_close(io_dev_info_t *dev_info); typedef struct { uint32_t in_use; uintptr_t base; - ssize_t file_pos; + signed long long file_pos; } file_state_t; static file_state_t current_file = { 0 }; @@ -47,7 +47,7 @@ static int32_t memdrv_block_open(io_dev_info_t *dev_info, const uintptr_t spec, * spec at a time. When we have dynamic memory we can malloc and set * entity->info. */ - if (current_file.in_use) + if (current_file.in_use != 0U) return IO_RESOURCES_EXHAUSTED; /* File cursor offset for seek and incremental reads etc. */ @@ -61,7 +61,7 @@ static int32_t memdrv_block_open(io_dev_info_t *dev_info, const uintptr_t spec, } static int32_t memdrv_block_seek(io_entity_t *entity, int32_t mode, - ssize_t offset) + signed long long offset) { if (mode != IO_SEEK_SET) return IO_FAIL; @@ -78,16 +78,17 @@ static int32_t memdrv_block_read(io_entity_t *entity, uintptr_t buffer, fp = (file_state_t *) entity->info; - NOTICE("BL2: dst=0x%lx src=0x%lx len=%ld(0x%lx)\n", - buffer, fp->base + fp->file_pos, length, length); + NOTICE("BL2: dst=0x%lx src=0x%llx len=%ld(0x%lx)\n", + buffer, (unsigned long long)fp->base + + (unsigned long long)fp->file_pos, length, length); - if (FLASH_MEMORY_SIZE < fp->file_pos + length) { + if (FLASH_MEMORY_SIZE < (fp->file_pos + (signed long long)length)) { ERROR("BL2: check load image (source address)\n"); return IO_FAIL; } - rcar_dma_exec(buffer, fp->base + fp->file_pos, length); - fp->file_pos += length; + rcar_dma_exec(buffer, fp->base + (uintptr_t)fp->file_pos, length); + fp->file_pos += (signed long long)length; *cnt = length; return IO_SUCCESS; diff --git a/drivers/st/bsec/bsec.c b/drivers/st/bsec/bsec2.c similarity index 65% rename from drivers/st/bsec/bsec.c rename to drivers/st/bsec/bsec2.c index 01c369edc..9777e6721 100644 --- a/drivers/st/bsec/bsec.c +++ b/drivers/st/bsec/bsec2.c @@ -14,11 +14,12 @@ #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_ID_2 U(0x100032) #define OTP_ACCESS_SIZE (round_up(OTP_MAX_SIZE, __WORD_BIT) / __WORD_BIT) @@ -48,7 +49,7 @@ 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; } @@ -78,33 +79,59 @@ 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); } } @@ -121,19 +148,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; } @@ -158,6 +196,11 @@ uint32_t bsec_probe(void) bsec_base = bsec_info.base; + if (((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_1_1) || + (bsec_get_id() != BSEC_IP_ID_2)) { + panic(); + } + #if defined(IMAGE_BL32) bsec_dt_otp_nsec_access(fdt, node); #endif @@ -251,6 +294,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 = dt_get_node_by_compatible(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 +375,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 +401,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 +428,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 +447,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 +482,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; } - /* Check if programming of OTP is locked */ - if (bsec_read_sp_lock(otp)) { + 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; + } + + 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 +518,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 +529,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 +581,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 +593,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,7 +608,7 @@ 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. @@ -517,7 +632,7 @@ uint32_t bsec_write_debug_conf(uint32_t val) } /* - * bsec_read_debug_conf: read debug configuration. + * bsec_read_debug_conf: return debug configuration register value. */ uint32_t bsec_read_debug_conf(void) { @@ -533,7 +648,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 +656,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 +664,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 +672,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 +680,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; - return (bank_value & otp_mask) != 0U; + 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 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; - return (bank_value & otp_mask) != 0U; + 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 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 +863,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 +882,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 +904,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 +930,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 +940,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 000000000..b764c971c --- /dev/null +++ b/drivers/st/clk/stm32mp1_calib.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2019, 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 + +#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 = stm32mp_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 = stm32mp_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 != 0U) { + /* Load & enable timer */ + 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 0cc87cc71..5efe343b8 100644 --- a/drivers/st/clk/stm32mp1_clk.c +++ b/drivers/st/clk/stm32mp1_clk.c @@ -1,11 +1,12 @@ /* - * 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 */ #include #include +#include #include #include @@ -17,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -48,6 +49,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 */ @@ -105,10 +119,62 @@ enum stm32mp1_parent_sel { _MCUS_SEL, _USBPHY_SEL, _USBO_SEL, + _RTC_SEL, + _MPU_SEL, + _PER_SEL, _PARENT_SEL_NB, _UNKNOWN_SEL = 0xff, }; +/* State the parent clock ID straight related to a clock */ +static const uint8_t parent_id_clock_id[_PARENT_NB] = { + [_HSE] = CK_HSE, + [_HSI] = CK_HSI, + [_CSI] = CK_CSI, + [_LSE] = CK_LSE, + [_LSI] = CK_LSI, + [_I2S_CKIN] = _UNKNOWN_ID, + [_USB_PHY_48] = _UNKNOWN_ID, + [_HSI_KER] = CK_HSI, + [_HSE_KER] = CK_HSE, + [_HSE_KER_DIV2] = CK_HSE_DIV2, + [_CSI_KER] = CK_CSI, + [_PLL1_P] = PLL1_P, + [_PLL1_Q] = PLL1_Q, + [_PLL1_R] = PLL1_R, + [_PLL2_P] = PLL2_P, + [_PLL2_Q] = PLL2_Q, + [_PLL2_R] = PLL2_R, + [_PLL3_P] = PLL3_P, + [_PLL3_Q] = PLL3_Q, + [_PLL3_R] = PLL3_R, + [_PLL4_P] = PLL4_P, + [_PLL4_Q] = PLL4_Q, + [_PLL4_R] = PLL4_R, + [_ACLK] = CK_AXI, + [_PCLK1] = CK_AXI, + [_PCLK2] = CK_AXI, + [_PCLK3] = CK_AXI, + [_PCLK4] = CK_AXI, + [_PCLK5] = CK_AXI, + [_CK_PER] = CK_PER, + [_CK_MPU] = CK_MPU, + [_CK_MCU] = CK_MCU, +}; + +static unsigned int clock_id2parent_id(unsigned long id) +{ + unsigned int n = 0; + + for (n = 0; n < ARRAY_SIZE(parent_id_clock_id); n++) { + if (parent_id_clock_id[n] == id) { + return n; + } + } + + return _UNKNOWN_ID; +} + enum stm32mp1_pll_id { _PLL1, _PLL2, @@ -186,6 +252,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 */ }; @@ -210,46 +277,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), \ } @@ -258,7 +338,8 @@ struct stm32mp1_clk_pll { [_ ## _label ## _SEL] = { \ .offset = _rcc_selr, \ .src = _rcc_selr ## _ ## _label ## SRC_SHIFT, \ - .msk = _rcc_selr ## _ ## _label ## SRC_MASK, \ + .msk = (_rcc_selr ## _ ## _label ## SRC_MASK) >> \ + (_rcc_selr ## _ ## _label ## SRC_SHIFT), \ .parent = (_parents), \ .nb_parent = ARRAY_SIZE(_parents) \ } @@ -280,95 +361,109 @@ struct stm32mp1_clk_pll { .refclk[3] = (p4), \ } -static const uint8_t stm32mp1_clks[][2] = { - { CK_PER, _CK_PER }, - { CK_MPU, _CK_MPU }, - { CK_AXI, _ACLK }, - { CK_MCU, _CK_MCU }, - { CK_HSE, _HSE }, - { CK_CSI, _CSI }, - { CK_LSI, _LSI }, - { CK_LSE, _LSE }, - { CK_HSI, _HSI }, - { CK_HSE_DIV2, _HSE_KER_DIV2 }, -}; - #define NB_GATES ARRAY_SIZE(stm32mp1_clk_gate) +#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_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 + +#if defined(IMAGE_BL2) + _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), +#endif + + _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[] = { @@ -423,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[] = { @@ -439,6 +534,18 @@ static const uint8_t usbo_parents[] = { _PLL4_R, _USB_PHY_48 }; +static const uint8_t rtc_parents[] = { + _UNKNOWN_ID, _LSE, _LSI, _HSE +}; + +static const uint8_t mpu_parents[] = { + _HSI, _HSE, _PLL1_P, _PLL1_P /* specific div */ +}; + +static const uint8_t per_parents[] = { + _HSI, _HSE, _CSI, +}; + static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { _CLK_PARENT_SEL(I2C12, RCC_I2C12CKSELR, i2c12_parents), _CLK_PARENT_SEL(I2C35, RCC_I2C35CKSELR, i2c35_parents), @@ -447,6 +554,9 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { _CLK_PARENT_SEL(SPI6, RCC_SPI6CKSELR, spi6_parents), _CLK_PARENT_SEL(UART1, RCC_UART1CKSELR, usart1_parents), _CLK_PARENT_SEL(RNG1, RCC_RNG1CKSELR, rng1_parents), + _CLK_PARENT_SEL(RTC, RCC_BDCR, rtc_parents), + _CLK_PARENT_SEL(MPU, RCC_MPCKSELR, mpu_parents), + _CLK_PARENT_SEL(PER, RCC_CPERCKSELR, per_parents), _CLK_PARENT_SEL(UART6, RCC_UART6CKSELR, uart6_parents), _CLK_PARENT_SEL(UART24, RCC_UART24CKSELR, uart234578_parents), _CLK_PARENT_SEL(UART35, RCC_UART35CKSELR, uart234578_parents), @@ -455,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, @@ -520,17 +629,90 @@ static const uint8_t stm32mp1_axi_div[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE +static const char * const stm32mp1_clk_parent_name[_PARENT_NB] __unused = { + [_HSI] = "HSI", + [_HSE] = "HSE", + [_CSI] = "CSI", + [_LSI] = "LSI", + [_LSE] = "LSE", + [_I2S_CKIN] = "I2S_CKIN", + [_HSI_KER] = "HSI_KER", + [_HSE_KER] = "HSE_KER", + [_HSE_KER_DIV2] = "HSE_KER_DIV2", + [_CSI_KER] = "CSI_KER", + [_PLL1_P] = "PLL1_P", + [_PLL1_Q] = "PLL1_Q", + [_PLL1_R] = "PLL1_R", + [_PLL2_P] = "PLL2_P", + [_PLL2_Q] = "PLL2_Q", + [_PLL2_R] = "PLL2_R", + [_PLL3_P] = "PLL3_P", + [_PLL3_Q] = "PLL3_Q", + [_PLL3_R] = "PLL3_R", + [_PLL4_P] = "PLL4_P", + [_PLL4_Q] = "PLL4_Q", + [_PLL4_R] = "PLL4_R", + [_ACLK] = "ACLK", + [_PCLK1] = "PCLK1", + [_PCLK2] = "PCLK2", + [_PCLK3] = "PCLK3", + [_PCLK4] = "PCLK4", + [_PCLK5] = "PCLK5", + [_HCLK6] = "KCLK6", + [_HCLK2] = "HCLK2", + [_CK_PER] = "CK_PER", + [_CK_MPU] = "CK_MPU", + [_CK_MCU] = "CK_MCU", + [_USB_PHY_48] = "USB_PHY_48", +}; + +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", +}; +#endif + /* 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; 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]; @@ -559,15 +741,17 @@ static void stm32mp1_clk_unlock(struct spinlock *lock) bool stm32mp1_rcc_is_secure(void) { uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t mask = RCC_TZCR_TZEN; - return (mmio_read_32(rcc_base + RCC_TZCR) & RCC_TZCR_TZEN) != 0; + return (mmio_read_32(rcc_base + RCC_TZCR) & mask) == mask; } bool stm32mp1_rcc_is_mckprot(void) { uintptr_t rcc_base = stm32mp_rcc_base(); + uint32_t mask = RCC_TZCR_TZEN | RCC_TZCR_MCKPROT; - return (mmio_read_32(rcc_base + RCC_TZCR) & RCC_TZCR_MCKPROT) != 0; + return (mmio_read_32(rcc_base + RCC_TZCR) & mask) == mask; } void stm32mp1_clk_rcc_regs_lock(void) @@ -580,6 +764,28 @@ void stm32mp1_clk_rcc_regs_unlock(void) stm32mp1_clk_unlock(®_lock); } +static unsigned int get_id_from_rcc_bit(unsigned int offset, unsigned int bit) +{ + unsigned int idx; + + for (idx = 0U; idx < NB_GATES; idx++) { + const struct stm32mp1_clk_gate *gate = gate_ref(idx); + + if ((offset == gate->offset) && (bit == gate->bit)) { + return gate->index; + } + + if ((gate->set_clr != 0U) && + (offset == (gate->offset + RCC_MP_ENCLRR_OFFSET)) && + (bit == gate->bit)) { + return gate->index; + } + } + + /* Currently only supported gated clocks */ + return ~0U; +} + static unsigned long stm32mp1_clk_get_fixed(enum stm32mp_osc_id idx) { if (idx >= NB_OSC) { @@ -617,17 +823,16 @@ static enum stm32mp1_parent_id stm32mp1_clk_get_fixed_parent(int i) static int stm32mp1_clk_get_parent(unsigned long id) { const struct stm32mp1_clk_sel *sel; - uint32_t j, p_sel; + uint32_t p_sel; int i; enum stm32mp1_parent_id p; enum stm32mp1_parent_sel s; uintptr_t rcc_base = stm32mp_rcc_base(); - for (j = 0U; j < ARRAY_SIZE(stm32mp1_clks); j++) { - if (stm32mp1_clks[j][0] == id) { - return (int)stm32mp1_clks[j][1]; - } - } + /* Few non gateable clock have a static parent ID, find them */ + i = (int)clock_id2parent_id(id); + if (i != _UNKNOWN_ID) + return i; i = stm32mp1_clk_get_gated_id(id); if (i < 0) { @@ -648,8 +853,15 @@ static int stm32mp1_clk_get_parent(unsigned long id) } sel = clk_sel_ref(s); - p_sel = (mmio_read_32(rcc_base + sel->offset) & sel->msk) >> sel->src; + 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]; } @@ -750,9 +962,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; @@ -930,27 +1140,29 @@ static void __clk_enable(struct stm32mp1_clk_gate const *gate) { uintptr_t rcc_base = stm32mp_rcc_base(); + VERBOSE("Enable clock %d\n", gate->index); + if (gate->set_clr != 0U) { mmio_write_32(rcc_base + gate->offset, BIT(gate->bit)); } else { - mmio_setbits_32(rcc_base + gate->offset, BIT(gate->bit)); + stm32mp_mmio_setbits_32_shregs(rcc_base + gate->offset, + BIT(gate->bit)); } - - VERBOSE("Clock %d has been enabled", gate->index); } static void __clk_disable(struct stm32mp1_clk_gate const *gate) { uintptr_t rcc_base = stm32mp_rcc_base(); + VERBOSE("Disable clock %d\n", gate->index); + if (gate->set_clr != 0U) { mmio_write_32(rcc_base + gate->offset + RCC_MP_ENCLRR_OFFSET, BIT(gate->bit)); } else { - mmio_clrbits_32(rcc_base + gate->offset, BIT(gate->bit)); + stm32mp_mmio_clrbits_32_shregs(rcc_base + gate->offset, + BIT(gate->bit)); } - - VERBOSE("Clock %d has been disabled", gate->index); } static bool __clk_is_enabled(struct stm32mp1_clk_gate const *gate) @@ -960,57 +1172,119 @@ 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) +/* Oscillators and PLLs are not gated at runtime */ +static bool clock_is_always_on(unsigned long id) { - int i = stm32mp1_clk_get_gated_id(id); - - if (i < 0) { - panic(); + CASSERT((CK_HSE == 0) && + ((CK_HSE + 1) == CK_CSI) && + ((CK_HSE + 2) == CK_LSI) && + ((CK_HSE + 3) == CK_LSE) && + ((CK_HSE + 4) == CK_HSI) && + ((CK_HSE + 5) == CK_HSE_DIV2) && + ((PLL1_P + 1) == PLL1_Q) && + ((PLL1_P + 2) == PLL1_R) && + ((PLL1_P + 3) == PLL2_P) && + ((PLL1_P + 4) == PLL2_Q) && + ((PLL1_P + 5) == PLL2_R) && + ((PLL1_P + 6) == PLL3_P) && + ((PLL1_P + 7) == PLL3_Q) && + ((PLL1_P + 8) == PLL3_R), + assert_osc_and_pll_ids_are_contiguous); + + if ((id <= CK_HSE_DIV2) || ((id >= PLL1_P) && (id <= PLL3_R))) + return true; + + switch (id) { + case CK_AXI: + case CK_MPU: + case CK_MCU: + return true; + default: + return false; } - - return gate_refcounts[i]; } -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 = stm32mp1_clk_get_gated_id(id); - unsigned int *refcnt; + int i; + + if (clock_is_always_on(id)) { + return; + } + i = stm32mp1_clk_get_gated_id(id); if (i < 0) { ERROR("Clock %d can't be enabled\n", (uint32_t)id); panic(); } 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 = stm32mp1_clk_get_gated_id(id); - unsigned int *refcnt; + int i; + + if (clock_is_always_on(id)) { + return; + } + i = stm32mp1_clk_get_gated_id(id); if (i < 0) { ERROR("Clock %d can't be disabled\n", (uint32_t)id); panic(); } 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); } @@ -1027,10 +1301,25 @@ void stm32mp_clk_disable(unsigned long id) __stm32mp1_clk_disable(id, true); } +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); +} + bool stm32mp_clk_is_enabled(unsigned long id) { - int i = stm32mp1_clk_get_gated_id(id); + int i; + + if (clock_is_always_on(id)) { + return true; + } + i = stm32mp1_clk_get_gated_id(id); if (i < 0) { panic(); } @@ -1163,6 +1452,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) @@ -1359,11 +1655,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) & @@ -1372,21 +1665,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); @@ -1400,23 +1705,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); @@ -1523,53 +1844,68 @@ static void stm32mp1_set_rtcsrc(unsigned int clksrc, bool lse_css) } } -static void stm32mp1_stgen_config(void) +unsigned long stm32mp_clk_timer_get_rate(unsigned long id) { - uintptr_t stgen; - uint32_t cntfid0; - unsigned long rate; - unsigned long long counter; + unsigned long parent_rate; + uint32_t prescaler, timpre; + uintptr_t rcc_base = stm32mp_rcc_base(); - stgen = fdt_get_stgen_base(); - cntfid0 = mmio_read_32(stgen + CNTFID_OFF); - rate = get_clock_rate(stm32mp1_clk_get_parent(STGEN_K)); + parent_rate = stm32mp_clk_get_rate(id); - if (cntfid0 == rate) { - return; + if (id < TIM1_K) { + prescaler = mmio_read_32(rcc_base + RCC_APB1DIVR) & + RCC_APBXDIV_MASK; + timpre = mmio_read_32(rcc_base + RCC_TIMG1PRER) & + RCC_TIMGXPRER_TIMGXPRE; + } else { + prescaler = mmio_read_32(rcc_base + RCC_APB2DIVR) & + RCC_APBXDIV_MASK; + timpre = mmio_read_32(rcc_base + RCC_TIMG2PRER) & + RCC_TIMGXPRER_TIMGXPRE; } - mmio_clrbits_32(stgen + CNTCR_OFF, CNTCR_EN); - counter = (unsigned long long)mmio_read_32(stgen + CNTCVL_OFF); - counter |= ((unsigned long long)mmio_read_32(stgen + CNTCVU_OFF)) << 32; - counter = (counter * rate / cntfid0); - - mmio_write_32(stgen + CNTCVL_OFF, (uint32_t)counter); - mmio_write_32(stgen + CNTCVU_OFF, (uint32_t)(counter >> 32)); - mmio_write_32(stgen + CNTFID_OFF, rate); - mmio_setbits_32(stgen + CNTCR_OFF, CNTCR_EN); - - write_cntfrq((u_register_t)rate); + if (!prescaler) { + return parent_rate; + } - /* Need to update timer with new frequency */ - generic_delay_timer_init(); + return parent_rate * (timpre + 1) * 2; } -void stm32mp1_stgen_increment(unsigned long long offset_in_ms) +/******************************************************************************* + * 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) { - uintptr_t stgen; - unsigned long long cnt; - - stgen = fdt_get_stgen_base(); + unsigned long apb1_freq; + uint32_t rtc_freq; + uint32_t apb1_div; + uintptr_t rcc_base = stm32mp_rcc_base(); - cnt = ((unsigned long long)mmio_read_32(stgen + CNTCVU_OFF) << 32) | - mmio_read_32(stgen + CNTCVL_OFF); + 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(); + } - cnt += (offset_in_ms * mmio_read_32(stgen + 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 + CNTCR_OFF, CNTCR_EN); - mmio_write_32(stgen + CNTCVL_OFF, (uint32_t)cnt); - mmio_write_32(stgen + CNTCVU_OFF, (uint32_t)(cnt >> 32)); - mmio_setbits_32(stgen + CNTCR_OFF, CNTCR_EN); + return apb1_freq < (rtc_freq * 7U); } static void stm32mp1_pkcs_config(uint32_t pkcs) @@ -1586,96 +1922,673 @@ 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; + return pll1_settings.valid_id == PLL1_SETTINGS_VALID_ID; +} - /* Check status field to disable security */ - if (!fdt_get_rcc_secure_status()) { - mmio_write_32(rcc_base + RCC_TZCR, 0); +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; + + return 0; } - ret = fdt_rcc_read_uint32_array("st,clksrc", clksrc, - (uint32_t)CLKSRC_NB); - 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", clkdiv, - (uint32_t)CLKDIV_NB); - 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(plloff[i], "cfg", - pllcfg[i], (int)PLLCFG_NB); - 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); + + 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_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 (config_on_the_fly == 1) { + /* No need to reconfigure, setup already OK */ + return 0; + } + + 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; + } + + ret = stm32mp1_pll_stop(_PLL1); + if (ret != 0) { + return ret; + } + } + + ret = stm32mp1_pll_config(_PLL1, &pll1_settings.cfg[i][0], + pll1_settings.frac[i]); + if (ret != 0) { + return ret; + } + + 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) +{ + int ret; + + ret = fdt_read_uint32_array(plloff, "cfg", pllcfg, (uint32_t)PLLCFG_NB); + if (ret < 0) { + return -FDT_ERR_NOTFOUND; + } + + *fracv = fdt_read_uint32_default(plloff, "frac", 0); + + ret = fdt_read_uint32_array(plloff, "csg", csg, (uint32_t)PLLCSG_NB); + + *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; + int stgen_p = stm32mp1_clk_get_parent((int)STGEN_K); + int usbphy_p = stm32mp1_clk_get_parent((int)USBPHY_K); + + /* Check status field to disable security */ + if (!fdt_get_rcc_secure_status()) { + mmio_write_32(rcc_base + RCC_TZCR, 0); + } + + ret = fdt_rcc_read_uint32_array("st,clksrc", clksrc, + (uint32_t)CLKSRC_NB); + if (ret < 0) { + return -FDT_ERR_NOTFOUND; + } + + ret = fdt_rcc_read_uint32_array("st,clkdiv", clkdiv, + (uint32_t)CLKDIV_NB); + 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) { + 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); + } + 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); + } + /* + * 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; } @@ -1695,6 +2608,12 @@ int stm32mp1_clk_init(void) 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) || @@ -1714,7 +2633,8 @@ int stm32mp1_clk_init(void) if (ret != 0) { return ret; } - stm32mp1_stgen_config(); + + stm32mp_stgen_config(stm32mp_clk_get_rate(STGEN_K)); } /* Select DIV */ @@ -1776,15 +2696,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; } @@ -1794,25 +2711,20 @@ int stm32mp1_clk_init(void) continue; } - fracv = fdt_read_uint32_default(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(plloff[i], "csg", csg, - (uint32_t)PLLCSG_NB); - 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; } @@ -1846,6 +2758,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]); @@ -1866,13 +2783,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, @@ -1902,18 +2839,634 @@ static void stm32mp1_osc_init(void) } } +/* + * Lookup platform clock from enable bit location in RCC registers. + * Return a valid clock ID on success, return ~0 on error. + */ +unsigned long stm32mp1_clk_rcc2id(unsigned int offset, unsigned int bit) +{ + return get_id_from_rcc_bit(offset, bit); +} + +#ifdef IMAGE_BL32 +/* + * Get the parent ID of the target parent clock, for tagging as secure + * shared clock dependencies. + */ +static int get_parent_id_parent(unsigned int parent_id) +{ + enum stm32mp1_parent_sel s = _UNKNOWN_SEL; + enum stm32mp1_pll_id pll_id; + uint32_t p_sel; + + switch (parent_id) { + case _ACLK: + case _PCLK4: + case _PCLK5: + s = _AXIS_SEL; + break; + case _PLL1_P: + case _PLL1_Q: + case _PLL1_R: + pll_id = _PLL1; + break; + case _PLL2_P: + case _PLL2_Q: + case _PLL2_R: + pll_id = _PLL2; + break; + case _PLL3_P: + case _PLL3_Q: + case _PLL3_R: + pll_id = _PLL3; + break; + case _PLL4_P: + case _PLL4_Q: + case _PLL4_R: + pll_id = _PLL4; + break; + case _PCLK1: + case _PCLK2: + case _HCLK2: + case _HCLK6: + case _CK_PER: + case _CK_MPU: + case _CK_MCU: + case _USB_PHY_48: + /* We do not expected to access these */ + panic(); + break; + default: + /* Other parents have no parent */ + return -1; + } + + if (s != _UNKNOWN_SEL) { + const struct stm32mp1_clk_sel *sel = clk_sel_ref(s); + uintptr_t rcc_base = stm32mp_rcc_base(); + + p_sel = (mmio_read_32(rcc_base + sel->offset) >> sel->src) & + sel->msk; + + if (p_sel < sel->nb_parent) { + return (int)sel->parent[p_sel]; + } + } else { + const struct stm32mp1_clk_pll *pll = pll_ref(pll_id); + uintptr_t rcc_base = stm32mp_rcc_base(); + + p_sel = mmio_read_32(rcc_base + pll->rckxselr) & + RCC_SELR_REFCLK_SRC_MASK; + + if (pll->refclk[p_sel] != _UNKNOWN_OSC_ID) { + return (int)pll->refclk[p_sel]; + } + } + +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + VERBOSE("No parent selected for %s\n", + stm32mp1_clk_parent_name[parent_id]); +#endif + + return -1; +} + +static void secure_parent_clocks(unsigned long parent_id) +{ + int grandparent_id; + + switch (parent_id) { + case _PLL3_P: + case _PLL3_Q: + case _PLL3_R: + stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3); + break; + + /* These clocks are always secure when RCC is secure */ + case _ACLK: + case _HCLK2: + case _HCLK6: + case _PCLK4: + case _PCLK5: + case _PLL1_P: + case _PLL1_Q: + case _PLL1_R: + case _PLL2_P: + case _PLL2_Q: + case _PLL2_R: + case _HSI: + case _HSI_KER: + case _LSI: + case _CSI: + case _CSI_KER: + case _HSE: + case _HSE_KER: + case _HSE_KER_DIV2: + case _LSE: + break; + + default: +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + VERBOSE("Cannot secure parent clock %s\n", + stm32mp1_clk_parent_name[parent_id]); +#endif + panic(); + } + + grandparent_id = get_parent_id_parent(parent_id); + if (grandparent_id >= 0) { + secure_parent_clocks(grandparent_id); + } +} + +void stm32mp1_register_clock_parents_secure(unsigned long clock_id) +{ + int parent_id; + + if (!stm32mp1_rcc_is_secure()) { + return; + } + + switch (clock_id) { + case PLL1: + case PLL2: + /* PLL1/PLL2 are always secure: nothing to do */ + return; + case PLL3: + stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3); + return; + case PLL4: + ERROR("PLL4 cannot be secured\n"); + panic(); + break; + default: + /* Others are expected gateable clock */ + parent_id = stm32mp1_clk_get_parent(clock_id); + break; + } + + if (parent_id < 0) { + INFO("No parent for clock %lu\n", clock_id); + return; + } + + secure_parent_clocks(parent_id); +} +#else +void stm32mp1_register_clock_parents_secure(unsigned long clock_id) +{ +} +#endif /* IMAGE_BL32 */ + +/* + * 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; + 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; + } + + disable_kernel_clocks(); + + return 0; +} + +/* 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) { - if (!stm32mp_is_single_core()) { - stm32mp1_clk_enable_secure(RTCAPB); + unsigned int n; + + for (n = 0U; n < ARRAY_SIZE(stm32mp1_clk_gate); n++) { + const struct stm32mp1_clk_gate *gate = gate_ref(n); + + if (!gate_is_non_secure(gate)) + stm32mp1_register_clock_parents_secure(gate->index); } + + /* + * Register secure clock parents and init a refcount for + * secure only resources that are not registered from a driver probe. + * - DDR controller and phy clocks. + * - TZC400, ETZPC and STGEN clocks. + * - RTCAPB clocks on multi-core + */ + stm32mp_clk_enable(AXIDCG); + + stm32mp_clk_enable(DDRC1); + stm32mp_clk_enable(DDRC1LP); + stm32mp_clk_enable(DDRC2); + stm32mp_clk_enable(DDRC2LP); + stm32mp_clk_enable(DDRCAPB); + stm32mp_clk_enable(DDRPHYC); + stm32mp_clk_enable(DDRPHYCLP); + stm32mp_clk_enable(DDRPHYCAPB); + stm32mp_clk_enable(DDRPHYCAPBLP); + + stm32mp_clk_enable(TZPC); + stm32mp_clk_enable(TZC1); + stm32mp_clk_enable(TZC2); + stm32mp_clk_enable(STGEN_K); + + stm32mp_clk_enable(RTCAPB); } int stm32mp1_clk_probe(void) { + unsigned long freq_khz; + + assert(PLLCFG_NB == PLAT_MAX_PLLCFG_NB); + 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; + return 0; } diff --git a/drivers/st/clk/stm32mp_clkfunc.c b/drivers/st/clk/stm32mp_clkfunc.c index 87c8e2b84..d3ea010ff 100644 --- a/drivers/st/clk/stm32mp_clkfunc.c +++ b/drivers/st/clk/stm32mp_clkfunc.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 */ @@ -10,10 +10,13 @@ #include +#include +#include #include #include +#include -#define DT_STGEN_COMPAT "st,stm32-stgen" +#define DT_UART_COMPAT "st,stm32h7-uart" /* * Get the frequency of an oscillator from its name in device tree. @@ -44,7 +47,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", @@ -158,39 +162,11 @@ uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, /* * Get the RCC node offset from the device tree - * @param fdt: Device tree reference * @return: Node offset or a negative value on error */ -int fdt_get_rcc_node(void *fdt) +int fdt_get_rcc_node(void) { - return fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); -} - -/* - * Get the RCC base address from the device tree - * @return: RCC address or 0 on error - */ -uint32_t fdt_rcc_read_addr(void) -{ - int node; - void *fdt; - const fdt32_t *cuint; - - if (fdt_get_address(&fdt) == 0) { - return 0; - } - - node = fdt_get_rcc_node(fdt); - if (node < 0) { - return 0; - } - - cuint = fdt_getprop(fdt, node, "reg", NULL); - if (cuint == NULL) { - return 0; - } - - return fdt32_to_cpu(*cuint); + return dt_get_node_by_compatible(DT_RCC_CLK_COMPAT); } /* @@ -204,13 +180,8 @@ int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t *array, uint32_t count) { int node; - void *fdt; - if (fdt_get_address(&fdt) == 0) { - return -ENOENT; - } - - node = fdt_get_rcc_node(fdt); + node = fdt_get_rcc_node(); if (node < 0) { return -FDT_ERR_NOTFOUND; } @@ -218,6 +189,24 @@ int fdt_rcc_read_uint32_array(const char *prop_name, return fdt_read_uint32_array(node, prop_name, array, count); } +/******************************************************************************* + * 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; + + node = fdt_get_rcc_node(); + if (node < 0) { + return dflt_value; + } + + return fdt_read_uint32_default(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 @@ -232,7 +221,7 @@ int fdt_rcc_subnode_offset(const char *name) return -ENOENT; } - node = fdt_get_rcc_node(fdt); + node = fdt_get_rcc_node(); if (node < 0) { return -FDT_ERR_NOTFOUND; } @@ -261,7 +250,7 @@ const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) return NULL; } - node = fdt_get_rcc_node(fdt); + node = fdt_get_rcc_node(); if (node < 0) { return NULL; } @@ -282,13 +271,8 @@ const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) bool fdt_get_rcc_secure_status(void) { int node; - void *fdt; - if (fdt_get_address(&fdt) == 0) { - return false; - } - - node = fdt_get_rcc_node(fdt); + node = fdt_get_rcc_node(); if (node < 0) { return false; } @@ -297,30 +281,19 @@ bool fdt_get_rcc_secure_status(void) } /* - * Get the stgen base address. - * @return: address of stgen on success, and NULL value on failure. + * This function gets interrupt name. + * It reads the values indicated the enabling status. + * Returns 0 if success, and a negative value else. */ -uintptr_t fdt_get_stgen_base(void) +int fdt_rcc_enable_it(const char *name) { - int node; - const fdt32_t *cuint; - void *fdt; + int node = fdt_get_rcc_node(); - if (fdt_get_address(&fdt) == 0) { - return 0; - } - - node = fdt_node_offset_by_compatible(fdt, -1, DT_STGEN_COMPAT); if (node < 0) { - return 0; + return -ENODEV; } - cuint = fdt_getprop(fdt, node, "reg", NULL); - if (cuint == NULL) { - return 0; - } - - return fdt32_to_cpu(*cuint); + return stm32_gic_enable_spi(node, name); } /* @@ -345,3 +318,137 @@ 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); +} + +/******************************************************************************* + * This function gets the frequency of the specified uart instance. + * From this instance, all the uarts nodes in DT are parsed, and the register + * base is compared to the instance. If match between these two values, then + * the clock source is read from the DT and we deduce the frequency. + * Returns clock frequency on success, 0 value on failure. + ******************************************************************************/ +unsigned long fdt_get_uart_clock_freq(uintptr_t instance) +{ + void *fdt; + int node; + int clk_id; + + if (fdt_get_address(&fdt) == 0) { + return 0; + } + + /* 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 stm32mp_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) +{ + unsigned long long cnt; + + cnt = mmio_read_32(STGEN_BASE + CNTCVU_OFF); + cnt <<= 32; + cnt |= mmio_read_32(STGEN_BASE + CNTCVL_OFF); + + return cnt; +} + +/******************************************************************************* + * 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 = value; + + cnt += (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 f72787d33..fcc45fada 100644 --- a/drivers/st/crypto/stm32_hash.c +++ b/drivers/st/crypto/stm32_hash.c @@ -21,6 +21,8 @@ #include #include +#define TIMEOUT_US_1MS U(1000) + #define DT_HASH_COMPAT "st,stm32f756-hash" #define HASH_CR 0x00U @@ -251,6 +253,8 @@ int stm32_hash_final(uint8_t *digest) mmio_clrsetbits_32(hash_base() + HASH_STR, HASH_STR_NBLW_MASK, 8U * stm32_remain.length); zeromem(&stm32_remain, sizeof(stm32_remain)); + } else { + mmio_clrbits_32(hash_base() + HASH_STR, HASH_STR_NBLW_MASK); } mmio_setbits_32(hash_base() + HASH_STR, HASH_STR_DCAL); @@ -319,9 +323,16 @@ int stm32_hash_register(void) stm32mp_clk_enable(stm32_hash.clock); if (hash_info.reset >= 0) { - stm32mp_reset_assert((unsigned long)hash_info.reset); + uint32_t reset = hash_info.reset; + + if (stm32mp_reset_assert_to(reset, TIMEOUT_US_1MS)) { + panic(); + } + udelay(20); - stm32mp_reset_deassert((unsigned long)hash_info.reset); + if (stm32mp_reset_deassert_to(reset, TIMEOUT_US_1MS)) { + panic(); + } } stm32mp_clk_disable(stm32_hash.clock); diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c index 7d89d027e..db8247522 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 */ @@ -29,6 +29,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 +46,22 @@ 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 */ +#define DDRCTL_REG_PERF_SIZE 17 /* st,ctl-perf */ + +#define DDRPHY_REG_REG_SIZE 11 /* st,phy-reg */ +#define DDRPHY_REG_TIMING_SIZE 10 /* st,phy-timing */ +#define DDRPHY_REG_CAL_SIZE 12 /* st,phy-cal */ + #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 +90,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 +106,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 +119,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), @@ -125,7 +140,7 @@ static const struct reg_desc ddr_perf[] = { }; #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), @@ -140,7 +155,7 @@ static const struct reg_desc ddrphy_reg[] = { }; #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,7 +169,7 @@ 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), @@ -169,36 +184,9 @@ static const struct reg_desc ddrphy_cal[] = { DDRPHY_REG_CAL(dx3dqstr), }; -#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 +195,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 +215,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 }, }; @@ -675,7 +645,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 +664,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 +762,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 +782,27 @@ 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; + } + } else { + if (ddr_reten != 0U) { + 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 +863,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 +890,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 +924,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 +958,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 +972,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, diff --git a/drivers/st/ddr/stm32mp1_ddr_helpers.c b/drivers/st/ddr/stm32mp1_ddr_helpers.c index fcb4cfcfd..3b8af550f 100644 --- a/drivers/st/ddr/stm32mp1_ddr_helpers.c +++ b/drivers/st/ddr/stm32mp1_ddr_helpers.c @@ -1,14 +1,23 @@ /* - * 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(); @@ -22,3 +31,564 @@ void ddr_enable_clock(void) 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); + mmio_clrbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + /* Waits unit all AXI ports are idle + * Poll PSTAT.rd_port_busy_n = 0 + * Poll PSTAT.wr_port_busy_n = 0 + */ + 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); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX2DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_setbits_32(ddrphyc_base + DDRPHYC_DX3DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + 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 | + RCC_DDRITFCR_DDRC2EN | + 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); + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + 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); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX2DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + mmio_clrbits_32(ddrphyc_base + DDRPHYC_DX3DLLCR, + DDRPHYC_DXNDLLCR_DLLDIS); + + /* 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); + mmio_setbits_32(ddrctrl_base + DDRCTRL_PCTRL_1, + DDRCTRL_PCTRL_N_PORT_EN); + + stm32mp1_clk_rcc_regs_lock(); + + mmio_setbits_32(rcc_base + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_clk_rcc_regs_unlock(); + + return 0; +} + +int ddr_standby_sr_entry(uint32_t *zq0cr0_zdata) +{ + uintptr_t pwr_base = stm32mp_pwr_base(); + uintptr_t ddrphyc_base = stm32mp_ddrphyc_base(); + + /* Save IOs calibration values */ + if (zq0cr0_zdata != NULL) { + *zq0cr0_zdata = mmio_read_32(ddrphyc_base + DDRPHYC_ZQ0CR0) & + DDRPHYC_ZQ0CRN_ZDATA_MASK; + } + + /* Put DDR in Self-Refresh */ + if (ddr_sw_self_refresh_in() != 0) { + return -1; + } + + /* Enable I/O retention mode in standby */ + 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_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC1EN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2EN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCAPBLPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCAPBLPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCAPBEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCAPBEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_AXIDCGEN); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK); + + 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); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN); + + mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_ASR1); + + 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); + + mmio_clrbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRC2LPEN); + + mmio_setbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRPHYCLPEN); + + mmio_clrsetbits_32(rcc_ddritfcr, RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_HSR1); + + 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 4ae55fcc7..e6652e504 100644 --- a/drivers/st/ddr/stm32mp1_ram.c +++ b/drivers/st/ddr/stm32mp1_ram.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 */ @@ -49,6 +49,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 @@ -168,23 +188,30 @@ static int stm32mp1_ddr_setup(void) int ret; struct stm32mp1_ddr_config config; int node, len; - uint32_t uret, idx; + uint32_t magic, uret, idx; void *fdt; + 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); -#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), @@ -192,16 +219,15 @@ 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) { return -ENOENT; } - node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + node = dt_get_node_by_compatible(DT_DDR_COMPAT); if (node < 0) { - ERROR("%s: Cannot read DDR node in DT\n", __func__); return -EINVAL; } @@ -230,13 +256,34 @@ 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; + + stm32mp_clk_enable(RTCAPB); + + magic = mmio_read_32(bkpr_core1_magic); + if (magic == BOOT_API_A7_CORE0_MAGIC_NUMBER) { + config.self_refresh = true; + config.zdata = stm32_get_zdata_from_context(); + } + + stm32mp_clk_disable(RTCAPB); + /* Disable axidcg clock gating during init */ mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); @@ -245,36 +292,58 @@ static int stm32mp1_ddr_setup(void) /* Enable axidcg clock gating */ mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + /* check if DDR content is lost (self-refresh aborted) */ + if ((magic == BOOT_API_A7_CORE0_MAGIC_NUMBER) && !config.self_refresh) { + /* clear Backup register */ + mmio_write_32(bkpr_core1_addr, 0); + /* clear magic number */ + mmio_write_32(bkpr_core1_magic, 0); + } + priv->info.size = config.info.size; VERBOSE("%s : ram size(%x, %x)\n", __func__, (uint32_t)priv->info.base, (uint32_t)priv->info.size); - write_sctlr(read_sctlr() & ~SCTLR_C_BIT); - dcsw_op_all(DC_OP_CISW); + 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_data_bus(); - if (uret != 0U) { - ERROR("DDR data 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_test_addr_bus(); - if (uret != 0U) { - ERROR("DDR addr bus 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(); + } - 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_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(); + } } - write_sctlr(read_sctlr() | SCTLR_C_BIT); + /* + * 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()); return 0; } diff --git a/drivers/st/etzpc/etzpc.c b/drivers/st/etzpc/etzpc.c new file mode 100644 index 000000000..8795c6e00 --- /dev/null +++ b/drivers/st/etzpc/etzpc.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* Device Tree related definitions */ +#define ETZPC_COMPAT "st,stm32-etzpc" +#define ETZPC_LOCK_MASK 0x1U +#define ETZPC_MODE_SHIFT 8 +#define ETZPC_MODE_MASK GENMASK(1, 0) +#define ETZPC_ID_SHIFT 16 +#define ETZPC_ID_MASK GENMASK(7, 0) + +/* ID Registers */ +#define ETZPC_TZMA0_SIZE 0x000U +#define ETZPC_DECPROT0 0x010U +#define ETZPC_DECPROT_LOCK0 0x030U +#define ETZPC_HWCFGR 0x3F0U +#define ETZPC_VERR 0x3F4U + +/* ID Registers fields */ +#define ETZPC_TZMA0_SIZE_LOCK BIT(31) +#define ETZPC_DECPROT0_MASK GENMASK(1, 0) +#define ETZPC_HWCFGR_NUM_TZMA_SHIFT 0 +#define ETZPC_HWCFGR_NUM_PER_SEC_SHIFT 8 +#define ETZPC_HWCFGR_NUM_AHB_SEC_SHIFT 16 +#define ETZPC_HWCFGR_CHUNCKS1N4_SHIFT 24 + +#define DECPROT_SHIFT 1 +#define IDS_PER_DECPROT_REGS 16U +#define IDS_PER_DECPROT_LOCK_REGS 32U + +/* + * etzpc_instance. + * base : register base address set during init given by user + * chunk_size : supported TZMA size steps + * num_tzma: number of TZMA zone read from register at init + * num_ahb_sec : number of securable AHB master zone read from register + * num_per_sec : number of securable AHB & APB Peripherals read from register + * revision : IP revision read from register at init + */ +struct etzpc_instance { + uintptr_t base; + uint8_t chunck_size; + uint8_t num_tzma; + uint8_t num_per_sec; + uint8_t num_ahb_sec; + uint8_t revision; +}; + +/* 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. + */ +#define PERIPH_LOCK_BIT BIT(7) +#define PERIPH_ATTR_MASK GENMASK(2, 0) + +static bool valid_decprot_id(unsigned int id) +{ + return id < (unsigned int)etzpc_dev.num_per_sec; +} + +#if ENABLE_ASSERTIONS +static bool valid_tzma_id(unsigned int id) +{ + return id < (unsigned int)etzpc_dev.num_tzma; +} +#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); + if (!valid_decprot_id(id)) { + ERROR("Invalid DECPROT %d", id); + return -1; + } + + 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 + * decprot_attr : Restriction access attribute + */ +void etzpc_configure_decprot(uint32_t decprot_id, + enum etzpc_decprot_attributes decprot_attr) +{ + uintptr_t offset = 4U * (decprot_id / IDS_PER_DECPROT_REGS); + uint32_t shift = (decprot_id % IDS_PER_DECPROT_REGS) << DECPROT_SHIFT; + uint32_t masked_decprot = (uint32_t)decprot_attr & ETZPC_DECPROT0_MASK; + + assert(valid_decprot_id(decprot_id)); + + mmio_clrsetbits_32(etzpc_dev.base + ETZPC_DECPROT0 + offset, + (uint32_t)ETZPC_DECPROT0_MASK << shift, + masked_decprot << shift); +} + +/* + * etzpc_get_decprot : Get the DECPROT attribute + * decprot_id : ID of the IP + * return : Attribute of this DECPROT + */ +enum etzpc_decprot_attributes etzpc_get_decprot(uint32_t decprot_id) +{ + uintptr_t offset = 4U * (decprot_id / IDS_PER_DECPROT_REGS); + uint32_t shift = (decprot_id % IDS_PER_DECPROT_REGS) << DECPROT_SHIFT; + uintptr_t base_decprot = etzpc_dev.base + offset; + uint32_t value; + + assert(valid_decprot_id(decprot_id)); + + value = (mmio_read_32(base_decprot + ETZPC_DECPROT0) >> shift) & + ETZPC_DECPROT0_MASK; + + return (enum etzpc_decprot_attributes)value; +} + +/* + * etzpc_lock_decprot : Lock access to the DECPROT attribute + * decprot_id : ID of the IP + */ +void etzpc_lock_decprot(uint32_t decprot_id) +{ + uintptr_t offset = 4U * (decprot_id / IDS_PER_DECPROT_LOCK_REGS); + uint32_t shift = BIT(decprot_id % IDS_PER_DECPROT_LOCK_REGS); + uintptr_t base_decprot = etzpc_dev.base + offset; + + assert(valid_decprot_id(decprot_id)); + + mmio_write_32(base_decprot + ETZPC_DECPROT_LOCK0, shift); +} + +/* + * etzpc_configure_tzma : Configure the target TZMA read only size + * tzma_id : ID of the memory + * tzma_value : read-only size + */ +void etzpc_configure_tzma(uint32_t tzma_id, uint16_t tzma_value) +{ + assert(valid_tzma_id(tzma_id)); + + mmio_write_32(etzpc_dev.base + ETZPC_TZMA0_SIZE + + (sizeof(uint32_t) * tzma_id), tzma_value); +} + +/* + * etzpc_get_tzma : Get the target TZMA read only size + * tzma_id : TZMA ID + * return : Size of read only size + */ +uint16_t etzpc_get_tzma(uint32_t tzma_id) +{ + assert(valid_tzma_id(tzma_id)); + + return (uint16_t)mmio_read_32(etzpc_dev.base + ETZPC_TZMA0_SIZE + + (sizeof(uint32_t) * tzma_id)); +} + +/* + * etzpc_lock_tzma : Lock the target TZMA + * tzma_id : TZMA ID + */ +void etzpc_lock_tzma(uint32_t tzma_id) +{ + assert(valid_tzma_id(tzma_id)); + + mmio_setbits_32(etzpc_dev.base + ETZPC_TZMA0_SIZE + + (sizeof(uint32_t) * tzma_id), ETZPC_TZMA0_SIZE_LOCK); +} + +/* + * etzpc_get_lock_tzma : Return the lock status of the target TZMA + * tzma_id : TZMA ID + * return : True if TZMA is locked, false otherwise + */ +bool etzpc_get_lock_tzma(uint32_t tzma_id) +{ + uint32_t tzma_size; + + assert(valid_tzma_id(tzma_id)); + + tzma_size = mmio_read_32(etzpc_dev.base + ETZPC_TZMA0_SIZE + + (sizeof(uint32_t) * tzma_id)); + + return (tzma_size & ETZPC_TZMA0_SIZE_LOCK) != 0; +} + +/* + * etzpc_get_num_per_sec : Return the DECPROT ID limit value + */ +uint8_t etzpc_get_num_per_sec(void) +{ + return etzpc_dev.num_per_sec; +} + +/* + * etzpc_get_revision : Return the ETZPC IP revision + */ +uint8_t etzpc_get_revision(void) +{ + return etzpc_dev.revision; +} + +/* + * etzpc_get_base_address : Return the ETZPC IP base address + */ +uintptr_t etzpc_get_base_address(void) +{ + return etzpc_dev.base; +} + +/* + * etzpc_init : Initialize the ETZPC driver + * Return 0 on success else a non zero value + */ +int etzpc_init(void) +{ + uint32_t hwcfg; + int node; + struct dt_node_info etzpc_info; + + node = dt_get_node(&etzpc_info, -1, ETZPC_COMPAT); + if (node < 0) { + return -EIO; + } + + /* Check ETZPC is secure only */ + if (etzpc_info.status != DT_SECURE) { + return -EACCES; + } + + etzpc_dev.base = etzpc_info.base; + + hwcfg = mmio_read_32(etzpc_dev.base + ETZPC_HWCFGR); + + etzpc_dev.num_tzma = (uint8_t)(hwcfg >> ETZPC_HWCFGR_NUM_TZMA_SHIFT); + etzpc_dev.num_per_sec = (uint8_t)(hwcfg >> + ETZPC_HWCFGR_NUM_PER_SEC_SHIFT); + etzpc_dev.num_ahb_sec = (uint8_t)(hwcfg >> + ETZPC_HWCFGR_NUM_AHB_SEC_SHIFT); + etzpc_dev.chunck_size = (uint8_t)(hwcfg >> + ETZPC_HWCFGR_CHUNCKS1N4_SHIFT); + + etzpc_dev.revision = mmio_read_8(etzpc_dev.base + ETZPC_VERR); + + VERBOSE("ETZPC version 0x%x", etzpc_dev.revision); + + return etzpc_dt_conf_decprot(node); +} diff --git a/drivers/st/fmc/stm32_fmc2_nand.c b/drivers/st/fmc/stm32_fmc2_nand.c new file mode 100644 index 000000000..e976c3bdd --- /dev/null +++ b/drivers/st/fmc/stm32_fmc2_nand.c @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_US_1MS U(1000) + +/* FMC2 Compatibility */ +#define DT_FMC2_COMPAT "st,stm32mp15-fmc2" +#define MAX_CS 2U + +/* FMC2 Controller Registers */ +#define FMC2_BCR1 0x00U +#define FMC2_PCR 0x80U +#define FMC2_SR 0x84U +#define FMC2_PMEM 0x88U +#define FMC2_PATT 0x8CU +#define FMC2_HECCR 0x94U +#define FMC2_BCHISR 0x254U +#define FMC2_BCHDSR0 0x27CU +#define FMC2_BCHDSR1 0x280U +#define FMC2_BCHDSR2 0x284U +#define FMC2_BCHDSR3 0x288U +#define FMC2_BCHDSR4 0x28CU + +/* FMC2_BCR1 register */ +#define FMC2_BCR1_FMC2EN BIT(31) +/* FMC2_PCR register */ +#define FMC2_PCR_PWAITEN BIT(1) +#define FMC2_PCR_PBKEN BIT(2) +#define FMC2_PCR_PWID_MASK GENMASK_32(5, 4) +#define FMC2_PCR_PWID(x) (((x) << 4) & FMC2_PCR_PWID_MASK) +#define FMC2_PCR_PWID_8 0x0U +#define FMC2_PCR_PWID_16 0x1U +#define FMC2_PCR_ECCEN BIT(6) +#define FMC2_PCR_ECCALG BIT(8) +#define FMC2_PCR_TCLR_MASK GENMASK_32(12, 9) +#define FMC2_PCR_TCLR(x) (((x) << 9) & FMC2_PCR_TCLR_MASK) +#define FMC2_PCR_TCLR_DEFAULT 0xFU +#define FMC2_PCR_TAR_MASK GENMASK_32(16, 13) +#define FMC2_PCR_TAR(x) (((x) << 13) & FMC2_PCR_TAR_MASK) +#define FMC2_PCR_TAR_DEFAULT 0xFU +#define FMC2_PCR_ECCSS_MASK GENMASK_32(19, 17) +#define FMC2_PCR_ECCSS(x) (((x) << 17) & FMC2_PCR_ECCSS_MASK) +#define FMC2_PCR_ECCSS_512 0x1U +#define FMC2_PCR_ECCSS_2048 0x3U +#define FMC2_PCR_BCHECC BIT(24) +#define FMC2_PCR_WEN BIT(25) +/* FMC2_SR register */ +#define FMC2_SR_NWRF BIT(6) +/* FMC2_PMEM register*/ +#define FMC2_PMEM_MEMSET(x) (((x) & GENMASK_32(7, 0)) << 0) +#define FMC2_PMEM_MEMWAIT(x) (((x) & GENMASK_32(7, 0)) << 8) +#define FMC2_PMEM_MEMHOLD(x) (((x) & GENMASK_32(7, 0)) << 16) +#define FMC2_PMEM_MEMHIZ(x) (((x) & GENMASK_32(7, 0)) << 24) +#define FMC2_PMEM_DEFAULT 0x0A0A0A0AU +/* FMC2_PATT register */ +#define FMC2_PATT_ATTSET(x) (((x) & GENMASK_32(7, 0)) << 0) +#define FMC2_PATT_ATTWAIT(x) (((x) & GENMASK_32(7, 0)) << 8) +#define FMC2_PATT_ATTHOLD(x) (((x) & GENMASK_32(7, 0)) << 16) +#define FMC2_PATT_ATTHIZ(x) (((x) & GENMASK_32(7, 0)) << 24) +#define FMC2_PATT_DEFAULT 0x0A0A0A0AU +/* FMC2_BCHISR register */ +#define FMC2_BCHISR_DERF BIT(1) +/* FMC2_BCHDSR0 register */ +#define FMC2_BCHDSR0_DUE BIT(0) +#define FMC2_BCHDSR0_DEF BIT(1) +#define FMC2_BCHDSR0_DEN_MASK GENMASK_32(7, 4) +#define FMC2_BCHDSR0_DEN_SHIFT 4U +/* FMC2_BCHDSR1 register */ +#define FMC2_BCHDSR1_EBP1_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR1_EBP2_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR1_EBP2_SHIFT 16U +/* FMC2_BCHDSR2 register */ +#define FMC2_BCHDSR2_EBP3_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR2_EBP4_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR2_EBP4_SHIFT 16U +/* FMC2_BCHDSR3 register */ +#define FMC2_BCHDSR3_EBP5_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR3_EBP6_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR3_EBP6_SHIFT 16U +/* FMC2_BCHDSR4 register */ +#define FMC2_BCHDSR4_EBP7_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR4_EBP8_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR4_EBP8_SHIFT 16U + +/* Timings */ +#define FMC2_THIZ 0x01U +#define FMC2_TIO 8000U +#define FMC2_TSYNC 3000U +#define FMC2_PCR_TIMING_MASK GENMASK_32(3, 0) +#define FMC2_PMEM_PATT_TIMING_MASK GENMASK_32(7, 0) + +#define FMC2_BBM_LEN 2U +#define FMC2_MAX_ECC_BYTES 14U +#define TIMEOUT_US_10_MS 10000U +#define FMC2_PSEC_PER_MSEC (1000UL * 1000UL * 1000UL) + +enum stm32_fmc2_ecc { + FMC2_ECC_HAM = 1U, + FMC2_ECC_BCH4 = 4U, + FMC2_ECC_BCH8 = 8U +}; + +struct stm32_fmc2_cs_reg { + uintptr_t data_base; + uintptr_t cmd_base; + uintptr_t addr_base; +}; + +struct stm32_fmc2_nand_timings { + uint8_t tclr; + uint8_t tar; + uint8_t thiz; + uint8_t twait; + uint8_t thold_mem; + uint8_t tset_mem; + uint8_t thold_att; + uint8_t tset_att; +}; + +struct stm32_fmc2_nfc { + uintptr_t reg_base; + struct stm32_fmc2_cs_reg cs[MAX_CS]; + unsigned long clock_id; + unsigned int reset_id; + uint8_t cs_sel; +}; + +static struct stm32_fmc2_nfc stm32_fmc2; + +static uintptr_t fmc2_base(void) +{ + return stm32_fmc2.reg_base; +} + +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 hclkp = FMC2_PSEC_PER_MSEC / (hclk / 1000U); + unsigned long timing, tar, tclr, thiz, twait; + unsigned long tset_mem, tset_att, thold_mem, thold_att; + uint32_t pcr, pmem, patt; + + tar = MAX(hclkp, NAND_TAR_MIN); + timing = div_round_up(tar, hclkp) - 1U; + tims.tar = MIN(timing, (unsigned long)FMC2_PCR_TIMING_MASK); + + tclr = MAX(hclkp, NAND_TCLR_MIN); + timing = div_round_up(tclr, hclkp) - 1U; + tims.tclr = MIN(timing, (unsigned long)FMC2_PCR_TIMING_MASK); + + tims.thiz = FMC2_THIZ; + thiz = (tims.thiz + 1U) * hclkp; + + /* + * tWAIT > tRP + * tWAIT > tWP + * tWAIT > tREA + tIO + */ + twait = MAX(hclkp, NAND_TRP_MIN); + twait = MAX(twait, NAND_TWP_MIN); + twait = MAX(twait, NAND_TREA_MAX + FMC2_TIO); + timing = div_round_up(twait, hclkp); + tims.twait = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tSETUP_MEM > tCS - tWAIT + * tSETUP_MEM > tALS - tWAIT + * tSETUP_MEM > tDS - (tWAIT - tHIZ) + */ + tset_mem = hclkp; + if ((twait < NAND_TCS_MIN) && (tset_mem < (NAND_TCS_MIN - twait))) { + tset_mem = NAND_TCS_MIN - twait; + } + if ((twait < NAND_TALS_MIN) && (tset_mem < (NAND_TALS_MIN - twait))) { + tset_mem = NAND_TALS_MIN - twait; + } + if ((twait > thiz) && ((twait - thiz) < NAND_TDS_MIN) && + (tset_mem < (NAND_TDS_MIN - (twait - thiz)))) { + tset_mem = NAND_TDS_MIN - (twait - thiz); + } + timing = div_round_up(tset_mem, hclkp); + tims.tset_mem = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tHOLD_MEM > tCH + * tHOLD_MEM > tREH - tSETUP_MEM + * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) + */ + thold_mem = MAX(hclkp, NAND_TCH_MIN); + if ((tset_mem < NAND_TREH_MIN) && + (thold_mem < (NAND_TREH_MIN - tset_mem))) { + thold_mem = NAND_TREH_MIN - tset_mem; + } + if (((tset_mem + twait) < NAND_TRC_MIN) && + (thold_mem < (NAND_TRC_MIN - (tset_mem + twait)))) { + thold_mem = NAND_TRC_MIN - (tset_mem + twait); + } + if (((tset_mem + twait) < NAND_TWC_MIN) && + (thold_mem < (NAND_TWC_MIN - (tset_mem + twait)))) { + thold_mem = NAND_TWC_MIN - (tset_mem + twait); + } + timing = div_round_up(thold_mem, hclkp); + tims.thold_mem = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tSETUP_ATT > tCS - tWAIT + * tSETUP_ATT > tCLS - tWAIT + * tSETUP_ATT > tALS - tWAIT + * tSETUP_ATT > tRHW - tHOLD_MEM + * tSETUP_ATT > tDS - (tWAIT - tHIZ) + */ + tset_att = hclkp; + if ((twait < NAND_TCS_MIN) && (tset_att < (NAND_TCS_MIN - twait))) { + tset_att = NAND_TCS_MIN - twait; + } + if ((twait < NAND_TCLS_MIN) && (tset_att < (NAND_TCLS_MIN - twait))) { + tset_att = NAND_TCLS_MIN - twait; + } + if ((twait < NAND_TALS_MIN) && (tset_att < (NAND_TALS_MIN - twait))) { + tset_att = NAND_TALS_MIN - twait; + } + if ((thold_mem < NAND_TRHW_MIN) && + (tset_att < (NAND_TRHW_MIN - thold_mem))) { + tset_att = NAND_TRHW_MIN - thold_mem; + } + if ((twait > thiz) && ((twait - thiz) < NAND_TDS_MIN) && + (tset_att < (NAND_TDS_MIN - (twait - thiz)))) { + tset_att = NAND_TDS_MIN - (twait - thiz); + } + timing = div_round_up(tset_att, hclkp); + tims.tset_att = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tHOLD_ATT > tALH + * tHOLD_ATT > tCH + * tHOLD_ATT > tCLH + * tHOLD_ATT > tCOH + * tHOLD_ATT > tDH + * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM + * tHOLD_ATT > tADL - tSETUP_MEM + * tHOLD_ATT > tWH - tSETUP_MEM + * tHOLD_ATT > tWHR - tSETUP_MEM + * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) + * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) + */ + thold_att = MAX(hclkp, NAND_TALH_MIN); + thold_att = MAX(thold_att, NAND_TCH_MIN); + thold_att = MAX(thold_att, NAND_TCLH_MIN); + thold_att = MAX(thold_att, NAND_TCOH_MIN); + thold_att = MAX(thold_att, NAND_TDH_MIN); + if (((NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC) > tset_mem) && + (thold_att < (NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC - tset_mem))) { + thold_att = NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC - tset_mem; + } + if ((tset_mem < NAND_TADL_MIN) && + (thold_att < (NAND_TADL_MIN - tset_mem))) { + thold_att = NAND_TADL_MIN - tset_mem; + } + if ((tset_mem < NAND_TWH_MIN) && + (thold_att < (NAND_TWH_MIN - tset_mem))) { + thold_att = NAND_TWH_MIN - tset_mem; + } + if ((tset_mem < NAND_TWHR_MIN) && + (thold_att < (NAND_TWHR_MIN - tset_mem))) { + thold_att = NAND_TWHR_MIN - tset_mem; + } + if (((tset_att + twait) < NAND_TRC_MIN) && + (thold_att < (NAND_TRC_MIN - (tset_att + twait)))) { + thold_att = NAND_TRC_MIN - (tset_att + twait); + } + if (((tset_att + twait) < NAND_TWC_MIN) && + (thold_att < (NAND_TWC_MIN - (tset_att + twait)))) { + thold_att = NAND_TWC_MIN - (tset_att + twait); + } + timing = div_round_up(thold_att, hclkp); + tims.thold_att = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + VERBOSE("NAND timings: %u - %u - %u - %u - %u - %u - %u - %u\n", + tims.tclr, tims.tar, tims.thiz, tims.twait, + tims.thold_mem, tims.tset_mem, + tims.thold_att, tims.tset_att); + + /* Set tclr/tar timings */ + pcr = mmio_read_32(fmc2_base() + FMC2_PCR); + pcr &= ~FMC2_PCR_TCLR_MASK; + pcr |= FMC2_PCR_TCLR(tims.tclr); + pcr &= ~FMC2_PCR_TAR_MASK; + pcr |= FMC2_PCR_TAR(tims.tar); + + /* Set tset/twait/thold/thiz timings in common bank */ + pmem = FMC2_PMEM_MEMSET(tims.tset_mem); + pmem |= FMC2_PMEM_MEMWAIT(tims.twait); + pmem |= FMC2_PMEM_MEMHOLD(tims.thold_mem); + pmem |= FMC2_PMEM_MEMHIZ(tims.thiz); + + /* Set tset/twait/thold/thiz timings in attribute bank */ + patt = FMC2_PATT_ATTSET(tims.tset_att); + patt |= FMC2_PATT_ATTWAIT(tims.twait); + patt |= FMC2_PATT_ATTHOLD(tims.thold_att); + patt |= FMC2_PATT_ATTHIZ(tims.thiz); + + mmio_write_32(fmc2_base() + FMC2_PCR, pcr); + mmio_write_32(fmc2_base() + FMC2_PMEM, pmem); + mmio_write_32(fmc2_base() + FMC2_PATT, patt); +} + +static void stm32_fmc2_set_buswidth_16(bool set) +{ + mmio_clrsetbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_PWID_MASK, + (set ? FMC2_PCR_PWID(FMC2_PCR_PWID_16) : 0U)); +} + +static void stm32_fmc2_set_ecc(bool enable) +{ + mmio_clrsetbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_ECCEN, + (enable ? FMC2_PCR_ECCEN : 0U)); +} + +static int stm32_fmc2_ham_correct(uint8_t *buffer, uint8_t *eccbuffer, + uint8_t *ecc) +{ + uint8_t xor_ecc_ones; + uint16_t xor_ecc_1b, xor_ecc_2b, xor_ecc_3b; + union { + uint32_t val; + uint8_t bytes[4]; + } xor_ecc; + + /* Page size--------ECC_Code Size + * 256---------------22 bits LSB (ECC_CODE & 0x003FFFFF) + * 512---------------24 bits (ECC_CODE & 0x00FFFFFF) + * 1024--------------26 bits (ECC_CODE & 0x03FFFFFF) + * 2048--------------28 bits (ECC_CODE & 0x0FFFFFFF) + * 4096--------------30 bits (ECC_CODE & 0x3FFFFFFF) + * 8192--------------32 bits (ECC_CODE & 0xFFFFFFFF) + */ + + /* For Page size 512, ECC_Code size 24 bits */ + xor_ecc_1b = ecc[0] ^ eccbuffer[0]; + xor_ecc_2b = ecc[1] ^ eccbuffer[1]; + xor_ecc_3b = ecc[2] ^ eccbuffer[2]; + + xor_ecc.val = 0U; + xor_ecc.bytes[2] = xor_ecc_3b; + xor_ecc.bytes[1] = xor_ecc_2b; + xor_ecc.bytes[0] = xor_ecc_1b; + + if (xor_ecc.val == 0U) { + return 0; /* No Error */ + } + + xor_ecc_ones = __builtin_popcount(xor_ecc.val); + if (xor_ecc_ones < 23U) { + if (xor_ecc_ones == 12U) { + uint16_t bit_address, byte_address; + + /* Correctable ERROR */ + bit_address = ((xor_ecc_1b >> 1) & BIT(0)) | + ((xor_ecc_1b >> 2) & BIT(1)) | + ((xor_ecc_1b >> 3) & BIT(2)); + + byte_address = ((xor_ecc_1b >> 7) & BIT(0)) | + ((xor_ecc_2b) & BIT(1)) | + ((xor_ecc_2b >> 1) & BIT(2)) | + ((xor_ecc_2b >> 2) & BIT(3)) | + ((xor_ecc_2b >> 3) & BIT(4)) | + ((xor_ecc_3b << 4) & BIT(5)) | + ((xor_ecc_3b << 3) & BIT(6)) | + ((xor_ecc_3b << 2) & BIT(7)) | + ((xor_ecc_3b << 1) & BIT(8)); + + /* Correct bit error in the data */ + buffer[byte_address] = + buffer[byte_address] ^ BIT(bit_address); + VERBOSE("Hamming: 1 ECC error corrected\n"); + + return 0; + } + + /* Non Correctable ERROR */ + ERROR("%s: Uncorrectable ECC Errors\n", __func__); + return -1; + } + + /* ECC ERROR */ + ERROR("%s: Hamming correction error\n", __func__); + return -1; +} + + +static int stm32_fmc2_ham_calculate(uint8_t *buffer, uint8_t *ecc) +{ + uint32_t heccr; + uint64_t timeout = timeout_init_us(TIMEOUT_US_10_MS); + + while ((mmio_read_32(fmc2_base() + FMC2_SR) & FMC2_SR_NWRF) == 0U) { + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + } + + heccr = mmio_read_32(fmc2_base() + FMC2_HECCR); + + ecc[0] = heccr; + ecc[1] = heccr >> 8; + ecc[2] = heccr >> 16; + + /* Disable ECC */ + stm32_fmc2_set_ecc(false); + + return 0; +} + +static int stm32_fmc2_bch_correct(uint8_t *buffer, unsigned int eccsize) +{ + uint32_t bchdsr0, bchdsr1, bchdsr2, bchdsr3, bchdsr4; + uint16_t pos[8]; + int i, den; + uint64_t timeout = timeout_init_us(TIMEOUT_US_10_MS); + + while ((mmio_read_32(fmc2_base() + FMC2_BCHISR) & + FMC2_BCHISR_DERF) == 0U) { + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + } + + bchdsr0 = mmio_read_32(fmc2_base() + FMC2_BCHDSR0); + bchdsr1 = mmio_read_32(fmc2_base() + FMC2_BCHDSR1); + bchdsr2 = mmio_read_32(fmc2_base() + FMC2_BCHDSR2); + bchdsr3 = mmio_read_32(fmc2_base() + FMC2_BCHDSR3); + bchdsr4 = mmio_read_32(fmc2_base() + FMC2_BCHDSR4); + + /* Disable ECC */ + stm32_fmc2_set_ecc(false); + + /* No error found */ + if ((bchdsr0 & FMC2_BCHDSR0_DEF) == 0U) { + return 0; + } + + /* Too many errors detected */ + if ((bchdsr0 & FMC2_BCHDSR0_DUE) != 0U) { + return -EBADMSG; + } + + pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK; + pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT; + pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK; + pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT; + pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK; + pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT; + pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK; + pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT; + + den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT; + for (i = 0; i < den; i++) { + if (pos[i] < (eccsize * 8U)) { + uint8_t bitmask = BIT(pos[i] % 8U); + uint32_t offset = pos[i] / 8U; + + *(buffer + offset) ^= bitmask; + } + } + + return 0; +} + +static void stm32_fmc2_hwctl(struct nand_device *nand) +{ + stm32_fmc2_set_ecc(false); + + if (nand->ecc.max_bit_corr != FMC2_ECC_HAM) { + mmio_clrbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_WEN); + } + + stm32_fmc2_set_ecc(true); +} + +static int stm32_fmc2_read_page(struct nand_device *nand, + unsigned int page, uintptr_t buffer) +{ + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + unsigned int eccsteps = nand->page_size / eccsize; + uint8_t ecc_corr[FMC2_MAX_ECC_BYTES]; + uint8_t ecc_cal[FMC2_MAX_ECC_BYTES] = {0U}; + uint8_t *p; + unsigned int i; + unsigned int s; + int ret; + + VERBOSE(">%s page %i buffer %lx\n", __func__, page, buffer); + + ret = nand_read_page_cmd(page, 0U, 0U, 0U); + if (ret != 0) { + return ret; + } + + for (s = 0U, i = nand->page_size + FMC2_BBM_LEN, p = (uint8_t *)buffer; + s < eccsteps; + s++, i += eccbytes, p += eccsize) { + stm32_fmc2_hwctl(nand); + + /* Read the NAND page sector (512 bytes) */ + ret = nand_change_read_column_cmd(s * eccsize, (uintptr_t)p, + eccsize); + if (ret != 0) { + return ret; + } + + if (nand->ecc.max_bit_corr == FMC2_ECC_HAM) { + ret = stm32_fmc2_ham_calculate(p, ecc_cal); + if (ret != 0) { + return ret; + } + } + + /* Read the corresponding ECC bytes */ + ret = nand_change_read_column_cmd(i, (uintptr_t)ecc_corr, + eccbytes); + if (ret != 0) { + return ret; + } + + /* Correct the data */ + if (nand->ecc.max_bit_corr == FMC2_ECC_HAM) { + ret = stm32_fmc2_ham_correct(p, ecc_corr, ecc_cal); + } else { + ret = stm32_fmc2_bch_correct(p, eccsize); + } + + if (ret != 0) { + return ret; + } + } + + return 0; +} + +static void stm32_fmc2_read_data(struct nand_device *nand, + uint8_t *buff, unsigned int length, + bool use_bus8) +{ + uintptr_t data_base = stm32_fmc2.cs[stm32_fmc2.cs_sel].data_base; + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + stm32_fmc2_set_buswidth_16(false); + } + + if ((((uintptr_t)buff & BIT(0)) != 0U) && (length != 0U)) { + *buff = mmio_read_8(data_base); + buff += sizeof(uint8_t); + length -= sizeof(uint8_t); + } + + if ((((uintptr_t)buff & GENMASK_32(1, 0)) != 0U) && + (length >= sizeof(uint16_t))) { + *(uint16_t *)buff = mmio_read_16(data_base); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + /* 32bit aligned */ + while (length >= sizeof(uint32_t)) { + *(uint32_t *)buff = mmio_read_32(data_base); + buff += sizeof(uint32_t); + length -= sizeof(uint32_t); + } + + /* Read remaining bytes */ + if (length >= sizeof(uint16_t)) { + *(uint16_t *)buff = mmio_read_16(data_base); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + if (length != 0U) { + *buff = mmio_read_8(data_base); + } + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + /* Reconfigure bus width to 16-bit */ + stm32_fmc2_set_buswidth_16(true); + } +} + +static void stm32_fmc2_write_data(struct nand_device *nand, + uint8_t *buff, unsigned int length, + bool use_bus8) +{ + uintptr_t data_base = stm32_fmc2.cs[stm32_fmc2.cs_sel].data_base; + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + /* Reconfigure bus width to 8-bit */ + stm32_fmc2_set_buswidth_16(false); + } + + if ((((uintptr_t)buff & BIT(0)) != 0U) && (length != 0U)) { + mmio_write_8(data_base, *buff); + buff += sizeof(uint8_t); + length -= sizeof(uint8_t); + } + + if ((((uintptr_t)buff & GENMASK_32(1, 0)) != 0U) && + (length >= sizeof(uint16_t))) { + mmio_write_16(data_base, *(uint16_t *)buff); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + /* 32bits aligned */ + while (length >= sizeof(uint32_t)) { + mmio_write_32(data_base, *(uint32_t *)buff); + buff += sizeof(uint32_t); + length -= sizeof(uint32_t); + } + + /* Read remaining bytes */ + if (length >= sizeof(uint16_t)) { + mmio_write_16(data_base, *(uint16_t *)buff); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + if (length != 0U) { + mmio_write_8(data_base, *buff); + } + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + /* Reconfigure bus width to 16-bit */ + stm32_fmc2_set_buswidth_16(true); + } +} + +static void stm32_fmc2_ctrl_init(void) +{ + uint32_t pcr = mmio_read_32(fmc2_base() + FMC2_PCR); + uint32_t bcr1 = mmio_read_32(fmc2_base() + FMC2_BCR1); + + /* Enable wait feature and NAND flash memory bank */ + pcr |= FMC2_PCR_PWAITEN; + pcr |= FMC2_PCR_PBKEN; + + /* Set buswidth to 8 bits mode for identification */ + pcr &= ~FMC2_PCR_PWID_MASK; + + /* ECC logic is disabled */ + pcr &= ~FMC2_PCR_ECCEN; + + /* Default mode */ + pcr &= ~FMC2_PCR_ECCALG; + pcr &= ~FMC2_PCR_BCHECC; + pcr &= ~FMC2_PCR_WEN; + + /* Set default ECC sector size */ + pcr &= ~FMC2_PCR_ECCSS_MASK; + pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048); + + /* Set default TCLR/TAR timings */ + pcr &= ~FMC2_PCR_TCLR_MASK; + pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT); + pcr &= ~FMC2_PCR_TAR_MASK; + pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT); + + /* Enable FMC2 controller */ + bcr1 |= FMC2_BCR1_FMC2EN; + + mmio_write_32(fmc2_base() + FMC2_BCR1, bcr1); + mmio_write_32(fmc2_base() + FMC2_PCR, pcr); + mmio_write_32(fmc2_base() + FMC2_PMEM, FMC2_PMEM_DEFAULT); + mmio_write_32(fmc2_base() + FMC2_PATT, FMC2_PATT_DEFAULT); +} + +static int stm32_fmc2_exec(struct nand_req *req) +{ + int ret = 0; + + switch (req->type & NAND_REQ_MASK) { + case NAND_REQ_CMD: + VERBOSE("Write CMD %x\n", (uint8_t)req->type); + mmio_write_8(stm32_fmc2.cs[stm32_fmc2.cs_sel].cmd_base, + (uint8_t)req->type); + break; + case NAND_REQ_ADDR: + VERBOSE("Write ADDR %x\n", *(req->addr)); + mmio_write_8(stm32_fmc2.cs[stm32_fmc2.cs_sel].addr_base, + *(req->addr)); + break; + case NAND_REQ_DATAIN: + VERBOSE("Read data\n"); + stm32_fmc2_read_data(req->nand, req->addr, req->length, + ((req->type & NAND_REQ_BUS_WIDTH_8) != + 0U)); + break; + case NAND_REQ_DATAOUT: + VERBOSE("Write data\n"); + stm32_fmc2_write_data(req->nand, req->addr, req->length, + ((req->type & NAND_REQ_BUS_WIDTH_8) != + 0U)); + break; + case NAND_REQ_WAIT: + VERBOSE("WAIT Ready\n"); + ret = nand_wait_ready(req->delay_ms); + break; + default: + ret = -EINVAL; + break; + }; + + return ret; +} + +static void stm32_fmc2_setup(struct nand_device *nand) +{ + uint32_t pcr = mmio_read_32(fmc2_base() + FMC2_PCR); + + /* Set buswidth */ + pcr &= ~FMC2_PCR_PWID_MASK; + if (nand->buswidth == NAND_BUS_WIDTH_16) { + pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_16); + } + + if (nand->ecc.mode == NAND_ECC_HW) { + nand->mtd_read_page = stm32_fmc2_read_page; + + pcr &= ~FMC2_PCR_ECCALG; + pcr &= ~FMC2_PCR_BCHECC; + + pcr &= ~FMC2_PCR_ECCSS_MASK; + pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512); + + switch (nand->ecc.max_bit_corr) { + case FMC2_ECC_HAM: + nand->ecc.bytes = 3; + break; + case FMC2_ECC_BCH8: + pcr |= FMC2_PCR_ECCALG; + pcr |= FMC2_PCR_BCHECC; + nand->ecc.bytes = 13; + break; + default: + /* Use FMC2 ECC BCH4 */ + pcr |= FMC2_PCR_ECCALG; + nand->ecc.bytes = 7; + break; + } + + if ((nand->buswidth & NAND_BUS_WIDTH_16) != 0) { + nand->ecc.bytes++; + } + } + + mmio_write_32(stm32_fmc2.reg_base + FMC2_PCR, pcr); +} + +static const struct nand_ctrl_ops ctrl_ops = { + .setup = stm32_fmc2_setup, + .exec = stm32_fmc2_exec +}; + +int stm32_fmc2_init(void) +{ + int fmc_node; + int fmc_subnode = 0; + int nchips = 0; + unsigned int i; + void *fdt = NULL; + const fdt32_t *cuint; + struct dt_node_info info; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + fmc_node = dt_get_node(&info, -1, DT_FMC2_COMPAT); + if (fmc_node == -FDT_ERR_NOTFOUND) { + WARN("No FMC2 node found\n"); + return fmc_node; + } + + if (info.status == DT_DISABLED) { + return -FDT_ERR_NOTFOUND; + } + + stm32_fmc2.reg_base = info.base; + + if ((info.clock < 0) || (info.reset < 0)) { + return -FDT_ERR_BADVALUE; + } + + stm32_fmc2.clock_id = (unsigned long)info.clock; + stm32_fmc2.reset_id = (unsigned int)info.reset; + + cuint = fdt_getprop(fdt, fmc_node, "reg", NULL); + if (cuint == NULL) { + return -FDT_ERR_BADVALUE; + } + + cuint += 2; + + for (i = 0U; i < MAX_CS; i++) { + stm32_fmc2.cs[i].data_base = fdt32_to_cpu(*cuint); + stm32_fmc2.cs[i].cmd_base = fdt32_to_cpu(*(cuint + 2)); + stm32_fmc2.cs[i].addr_base = fdt32_to_cpu(*(cuint + 4)); + cuint += 6; + } + + /* Pinctrl initialization */ + if (dt_set_pinctrl_config(fmc_node) != 0) { + return -FDT_ERR_BADVALUE; + } + + /* Parse flash nodes */ + fdt_for_each_subnode(fmc_subnode, fdt, fmc_node) { + nchips++; + } + + if (nchips != 1) { + WARN("Only one SLC NAND device supported\n"); + return -FDT_ERR_BADVALUE; + } + + fdt_for_each_subnode(fmc_subnode, fdt, fmc_node) { + /* Get chip select */ + cuint = fdt_getprop(fdt, fmc_subnode, "reg", NULL); + if (cuint == NULL) { + WARN("Chip select not well defined\n"); + return -FDT_ERR_BADVALUE; + } + stm32_fmc2.cs_sel = fdt32_to_cpu(*cuint); + VERBOSE("NAND CS %i\n", stm32_fmc2.cs_sel); + } + + /* Enable Clock */ + stm32mp_clk_enable(stm32_fmc2.clock_id); + + /* Reset IP */ + if (stm32mp_reset_assert_to(stm32_fmc2.reset_id, TIMEOUT_US_1MS)) { + panic(); + } + if (stm32mp_reset_deassert_to(stm32_fmc2.reset_id, TIMEOUT_US_1MS)) { + panic(); + } + + /* Setup default IP registers */ + stm32_fmc2_ctrl_init(); + + /* Setup default timings */ + stm32_fmc2_nand_setup_timing(); + + /* Init NAND RAW framework */ + nand_raw_ctrl_init(&ctrl_ops); + + return 0; +} diff --git a/drivers/st/gpio/stm32_gpio.c b/drivers/st/gpio/stm32_gpio.c index a13c341a8..cdb56ffbe 100644 --- a/drivers/st/gpio/stm32_gpio.c +++ b/drivers/st/gpio/stm32_gpio.c @@ -254,6 +254,12 @@ void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, mmio_read_32(base + GPIO_AFRH_OFFSET)); stm32mp_clk_disable(clock); + + if (status == DT_SECURE) { + stm32mp_register_secure_gpio(bank, pin); + } else { + stm32mp_register_non_secure_gpio(bank, pin); + } } void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure) @@ -263,6 +269,8 @@ void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure) assert(pin <= GPIO_PIN_MAX); + assert(!(secure && stm32mp_gpio_bank_is_non_secure(bank))); + stm32mp_clk_enable(clock); if (secure) { diff --git a/drivers/st/i2c/stm32_i2c.c b/drivers/st/i2c/stm32_i2c.c index ed880522b..f12d40516 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 * - * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ #include +#include #include #include @@ -38,8 +39,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 +128,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) +{ + int i; + + for (i = 0; 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)stm32mp_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 +460,30 @@ 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; - - cuint = fdt_getprop(fdt, node, "i2c-scl-rising-time-ns", NULL); - if (cuint == NULL) { - init->rise_time = STM32_I2C_RISE_TIME_DEFAULT; - } else { - init->rise_time = fdt32_to_cpu(*cuint); - } - - cuint = fdt_getprop(fdt, node, "i2c-scl-falling-time-ns", NULL); - if (cuint == NULL) { - init->fall_time = STM32_I2C_FALL_TIME_DEFAULT; - } else { - init->fall_time = fdt32_to_cpu(*cuint); - } - - cuint = fdt_getprop(fdt, node, "clock-frequency", NULL); - if (cuint == NULL) { - init->speed_mode = STM32_I2C_SPEED_DEFAULT; - } else { - switch (fdt32_to_cpu(*cuint)) { - case STANDARD_RATE: - init->speed_mode = I2C_SPEED_STANDARD; - break; - case FAST_RATE: - init->speed_mode = I2C_SPEED_FAST; - break; - case FAST_PLUS_RATE: - init->speed_mode = I2C_SPEED_FAST_PLUS; - break; - default: - init->speed_mode = STM32_I2C_SPEED_DEFAULT; - break; - } + uint32_t read_val; + + init->rise_time = fdt_read_uint32_default(node, + "i2c-scl-rising-time-ns", + STM32_I2C_RISE_TIME_DEFAULT); + + init->fall_time = fdt_read_uint32_default(node, + "i2c-scl-falling-time-ns", + STM32_I2C_FALL_TIME_DEFAULT); + + read_val = fdt_read_uint32_default(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 +499,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,6 +511,11 @@ int stm32_i2c_init(struct i2c_handle_s *hi2c, hi2c->i2c_state = I2C_STATE_BUSY; + rc = i2c_setup_timing(hi2c, init_data, &timing); + if (rc != 0) { + return rc; + } + stm32mp_clk_enable(hi2c->clock); /* Disable the selected I2C peripheral */ @@ -978,4 +1336,3 @@ bail: return rc; } - diff --git a/drivers/st/io/io_mmc.c b/drivers/st/io/io_mmc.c index a239b5f3a..0b0e84ec1 100644 --- a/drivers/st/io/io_mmc.c +++ b/drivers/st/io/io_mmc.c @@ -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 */ @@ -20,14 +20,15 @@ static int mmc_dev_open(const uintptr_t init_params, io_dev_info_t **dev_info); static int mmc_block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); static int mmc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params); -static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset); +static int mmc_block_seek(io_entity_t *entity, int mode, + signed long long offset); static int mmc_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); static int mmc_block_close(io_entity_t *entity); static int mmc_dev_close(io_dev_info_t *dev_info); static io_type_t device_type_mmc(void); -static ssize_t seek_offset; +static signed long long seek_offset; static const io_dev_connector_t mmc_dev_connector = { .dev_open = mmc_dev_open @@ -85,7 +86,8 @@ static int mmc_block_open(io_dev_info_t *dev_info, const uintptr_t spec, } /* Seek to a particular file offset on the mmc device */ -static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int mmc_block_seek(io_entity_t *entity, int mode, + signed long long offset) { seek_offset = offset; return 0; @@ -95,12 +97,18 @@ static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset) static int mmc_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { - *length_read = mmc_read_blocks(seek_offset / MMC_BLOCK_SIZE, - buffer, length); + uint8_t retries = 3U; - if (*length_read != length) { - return -EIO; - } + do { + retries--; + if (retries == 0U) { + return -EIO; + } + + *length_read = mmc_read_blocks(seek_offset / MMC_BLOCK_SIZE, + buffer, length); + + } while (*length_read != length); return 0; } diff --git a/drivers/st/io/io_programmer_st_usb.c b/drivers/st/io/io_programmer_st_usb.c new file mode 100644 index 000000000..20da9656e --- /dev/null +++ b/drivers/st/io/io_programmer_st_usb.c @@ -0,0 +1,381 @@ +/* + * 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 + +#define USB_STATE_READY 0 +#define USB_STATE_WRITTEN 1 + +#define IO_USB_TIMEOUT_10_SEC U(10000000) +#define DETACH_TIMEOUT U(0x100) +#define USB_DFU_MAX_XFER_SIZE 1024 + +static uint8_t first_usb_buffer[USB_DFU_MAX_XFER_SIZE + 1] __aligned(4); +static usb_dfu_media_t usb_dfu_fops; +static uint8_t checksum_is_wrong; +static uint8_t usb_status; + +/* usb device functions */ +static int usb_dev_open(const uintptr_t init_params, + io_dev_info_t **dev_info); +static int usb_block_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int usb_dev_init(io_dev_info_t *dev_info, + const uintptr_t init_params); +static int usb_partition_size(io_entity_t *entity, size_t *length); +static int usb_block_seek(io_entity_t *entity, int mode, + signed long long offset); +static int usb_block_read(io_entity_t *entity, uintptr_t buffer, + size_t length, size_t *length_read); +static int usb_block_close(io_entity_t *entity); +static int usb_dev_close(io_dev_info_t *dev_info); +static io_type_t device_type_usb(void); + +static const io_dev_connector_t usb_dev_connector = { + .dev_open = usb_dev_open +}; + +static const io_dev_funcs_t usb_dev_funcs = { + .type = device_type_usb, + .open = usb_block_open, + .seek = usb_block_seek, + .size = usb_partition_size, + .read = usb_block_read, + .write = NULL, + .close = usb_block_close, + .dev_init = usb_dev_init, + .dev_close = usb_dev_close, +}; + +static io_dev_info_t usb_dev_info = { + .funcs = &usb_dev_funcs, + .info = (uintptr_t)0, +}; + +/* Identify the device type as usb */ +static io_type_t device_type_usb(void) +{ + return IO_TYPE_USB; +} + +/* Callback to notify that data has been written in memory + * ( set by USBD_DFU_SetDownloadAddr) + */ +static uint16_t usb_callback_write_done(uint32_t *written_in, uint32_t len) +{ + VERBOSE("%s Written_in 0x%lx len %i\n", __func__, (uintptr_t)written_in, + len); + + /* Update SRAM state machine when block writing is finished */ + usb_status = USB_STATE_WRITTEN; + + return 0; +} + +/* Call back to notify that a read memory is requested */ +static uint8_t *usb_callback_read(uint8_t *src, uint8_t *dest, uint32_t len) +{ + ERROR("%s read is not supported src 0x%lx dest 0x%lx len %i\n", + __func__, (uintptr_t)src, (uintptr_t)dest, len); + + /* Return a valid address to avoid HardFault */ + return (uint8_t *)(dest); +} + +/* Get the status to know if written operation has been checked */ +static uint16_t usb_callback_get_status(void) +{ + uint16_t status; + + /* According to SRAM state machine */ + switch (usb_status) { + case USB_STATE_WRITTEN: + /* The SRAM bloc writing has been done, change state machine + * to set SRAM in SRAM_STATE_READY + */ + usb_status = USB_STATE_READY; + + /* Notice caller that SRAM block writing is finished */ + status = DFU_MEDIA_STATE_WRITTEN; + + /* Checks checksum calculation result */ + if (checksum_is_wrong == 1) { + status = DFU_MEDIA_STATE_ERROR; + checksum_is_wrong = 0; + } + break; + + case USB_STATE_READY: + /* Notice caller that SRAM is ready to be written */ + status = DFU_MEDIA_STATE_READY; + break; + + default: + status = DFU_MEDIA_STATE_ERROR; + ERROR("USB unknown state\n"); + break; + } + VERBOSE("usb_callback_GetStatus status : %i\n", status); + return status; +} + +/* Open a connection to the usb device */ +static int usb_dev_open(const uintptr_t init_params, + io_dev_info_t **dev_info) +{ + usb_handle_t *usb_core_handle = (usb_handle_t *)init_params; + + assert(dev_info); + *dev_info = &usb_dev_info; + + usb_dfu_fops.write_done = usb_callback_write_done; + usb_dfu_fops.read = usb_callback_read; + usb_dfu_fops.get_status = usb_callback_get_status; + usb_status = USB_STATE_READY; + checksum_is_wrong = 0; + + usb_core_handle->user_data = &usb_dfu_fops; + + usb_dev_info.info = (uintptr_t)usb_core_handle; + + return 0; +} + +static int usb_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params) +{ + return 0; +} + +/* Close a connection to the usb device */ +static int usb_dev_close(io_dev_info_t *dev_info) +{ + return 0; +} + +/* Open a file on the usb device */ +static int usb_block_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity) +{ + int result; + uint32_t length = 0; + boot_api_image_header_t *header = + (boot_api_image_header_t *)first_usb_buffer; + + const struct stm32image_part_info *partition_spec = + (struct stm32image_part_info *)spec; + + /* Use PHASE_FSBL1 like init value*/ + if (current_phase.phase_id == PHASE_FSBL1) { + assert(partition_spec); + assert(entity); + + current_phase.current_packet = 0; + + if (!strcmp(partition_spec->name, BL33_IMAGE_NAME)) { + /* read flash layout first for U-boot */ + current_phase.phase_id = PHASE_FLASHLAYOUT; + current_phase.keep_header = 1; + + usb_dfu_set_phase_id(PHASE_FLASHLAYOUT); + usb_dfu_set_download_addr((uintptr_t) + &first_usb_buffer[0]); + + header->magic = 0; + + while (((header->magic != + BOOT_API_IMAGE_HEADER_MAGIC_NB) || + usb_dfu_get_current_req() == DFU_DNLOAD)) { + usb_core_handle_it((usb_handle_t *) + usb_dev_info.info); + } + result = usb_block_read(NULL, + FLASHLAYOUT_BASE, + 0, + &length); + if (result != 0) { + return result; + } + + flush_dcache_range((unsigned long)FLASHLAYOUT_BASE, + header->image_length + + sizeof(boot_api_image_header_t)); + + current_phase.current_packet = 0; + current_phase.keep_header = 0; + current_phase.phase_id = PHASE_SSBL; + current_phase.max_size = dt_get_ddr_size(); + } + entity->info = (uintptr_t)¤t_phase; + result = 0; + } else { + WARN("A UART device is already active. Close first.\n"); + result = -EIO; + } + + return result; +} + +/* Return the size of a partition */ +static int usb_partition_size(io_entity_t *entity, size_t *length) +{ + boot_api_image_header_t *header = + (boot_api_image_header_t *)first_usb_buffer; + int result = 0; + + usb_dfu_set_phase_id(current_phase.phase_id); + usb_dfu_set_download_addr((uintptr_t)&first_usb_buffer[0]); + + header->magic = 0; + + while ((header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) || + (usb_dfu_get_current_req() == DFU_DNLOAD)) { + usb_core_handle_it((usb_handle_t *)usb_dev_info.info); + } + + if (header->image_length > current_phase.max_size) + result = -EIO; + else + *length = header->image_length; + + INFO("%s: partition size : 0x%x\n", __func__, + header->image_length); + + return result; +} + +/* Seek to a particular file offset on the usb device */ +static int usb_block_seek(io_entity_t *entity, int mode, + signed long long offset) +{ + return 0; +} + +/* Read data from a file on the usb device */ +static int usb_block_read(io_entity_t *entity, uintptr_t buffer, + size_t length, size_t *length_read) +{ + uint8_t *local_ptr = (uint8_t *)buffer; + int result = 0; + boot_api_image_header_t *header = + (boot_api_image_header_t *)first_usb_buffer; + + INFO("Start Download partition %i to address 0x%lx length %i\n", + current_phase.phase_id, buffer, length); + + if (current_phase.keep_header) { + memcpy((uint8_t *)local_ptr, + (uint8_t *)&first_usb_buffer[0], + USB_DFU_MAX_XFER_SIZE); + + usb_dfu_set_download_addr((uintptr_t) + &local_ptr[USB_DFU_MAX_XFER_SIZE]); + } else { +#if TRUSTED_BOARD_BOOT + stm32mp_save_loaded_header(header); +#endif + memcpy((uint8_t *)local_ptr, + (uint8_t *) + &first_usb_buffer[sizeof(boot_api_image_header_t)], + USB_DFU_MAX_XFER_SIZE - + sizeof(boot_api_image_header_t)); + + usb_dfu_set_download_addr((uintptr_t) + &local_ptr[USB_DFU_MAX_XFER_SIZE - + sizeof(boot_api_image_header_t)]); + } + + while (!usb_dfu_download_is_completed()) { + /* Reload watchdog */ + stm32_iwdg_refresh(); + + usb_core_handle_it((usb_handle_t *)usb_dev_info.info); + } + + usb_core_handle_it((usb_handle_t *)usb_dev_info.info); + usb_core_handle_it((usb_handle_t *)usb_dev_info.info); + + if (current_phase.keep_header) + local_ptr += sizeof(boot_api_image_header_t); + + /* Verify header and checksum payload */ + result = stm32mp_check_header(header, (uintptr_t)local_ptr); + if (result) { + ERROR("Header check failed\n"); + return result; + } + + /* Wait Detach in case of bl33 */ + if (current_phase.phase_id == PHASE_SSBL) { + uint64_t timeout; + uint32_t detach_timeout = DETACH_TIMEOUT; + + usb_dfu_set_phase_id(0x0); + usb_dfu_set_download_addr(UNDEFINE_DOWN_ADDR); + usb_dfu_request_detach(); + timeout = timeout_init_us(IO_USB_TIMEOUT_10_SEC); + + while (detach_timeout != 0U) { + usb_core_handle_it((usb_handle_t *) + usb_dev_info.info); + + if (usb_dfu_detach_req() == 0U) { + /* + * Continue to handle usb core IT to assure + * complete data transmission + */ + detach_timeout--; + } + + if (timeout_elapsed(timeout)) { + return -EIO; + } + } + + /* STOP the USB Handler */ + usb_core_stop((usb_handle_t *)usb_dev_info.info); + } + + *length_read = length; + + return 0; +} + +/* Close a file on the usb device */ +static int usb_block_close(io_entity_t *entity) +{ + current_phase.phase_id = PHASE_FSBL1; + + return 0; +} + +/* Exported functions */ + +/* Register the usb driver with the IO abstraction */ +int register_io_dev_usb(const io_dev_connector_t **dev_con) +{ + int result; + + assert(dev_con); + + result = io_register_device(&usb_dev_info); + if (!result) + *dev_con = &usb_dev_connector; + + return result; +} diff --git a/drivers/st/io/io_stm32image.c b/drivers/st/io/io_stm32image.c index 413521b1e..a50a0df97 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,15 +212,21 @@ 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; - uint8_t *local_buffer = (uint8_t *)buffer; + 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; @@ -255,101 +234,55 @@ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, assert(buffer != 0U); assert(length_read != NULL); + 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; - } - } - - /* 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(); - } - - result = io_open(backend_dev_handle, backend_image_spec, - &backend_handle); - - 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); - - /* Adding part of size already read from header */ - *length_read += MAX_LBA_SIZE - sizeof(boot_api_image_header_t); - - if (result != 0) { - ERROR("%s: io_read (%i)\n", __func__, result); - *length_read = 0; - header->magic = 0; - continue; - } +#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(); + } - result = stm32mp_check_header(header, buffer); - if (result != 0) { - ERROR("Header check failed\n"); - *length_read = 0; - header->magic = 0; - } + result = io_open(backend_dev_handle, backend_image_spec, + &backend_handle); + if (result != 0) { + ERROR("%s: io_open (%i)\n", __func__, result); + return result; + } - result = stm32mp_auth_image(header, buffer); - if (result != 0) { - ERROR("Authentication Failed (%i)\n", result); - return result; - } + result = io_seek(backend_handle, IO_SEEK_SET, *stm32_img + offset); + if (result != 0) { + ERROR("%s: io_seek (%i)\n", __func__, result); + goto out; + } - io_close(backend_handle); + 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; } + /* Adding part of size already read from header */ + *length_read += MAX_LBA_SIZE - sizeof(boot_api_image_header_t); + +out: + io_close(backend_handle); return result; } diff --git a/drivers/st/iwdg/stm32_iwdg.c b/drivers/st/iwdg/stm32_iwdg.c index ea6fbb2b9..6055e4d84 100644 --- a/drivers/st/iwdg/stm32_iwdg.c +++ b/drivers/st/iwdg/stm32_iwdg.c @@ -22,11 +22,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 +71,109 @@ 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) +{ + unsigned int cpu = plat_my_core_pos(); + 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]; + + VERBOSE("CPU %x IT Watchdog %d\n", cpu, instance + 1); + + stm32_iwdg_refresh(); + + stm32mp_clk_enable(iwdg->clock); + + mmio_setbits_32(iwdg->base + IWDG_EWCR_OFFSET, IWDG_EWCR_EWIC); + + stm32mp_clk_disable(iwdg->clock); + + /* Ack interrupt as we do not return from next call */ + gicv2_end_of_interrupt(id); + + stm32mp_plat_reset(cpu); +} + +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 * + stm32mp_clk_get_rate(id_lsi); + reload = ((uint32_t)(reload_ll >> 8) - 1U) & IWDG_EWCR_EWIT_MASK; + + stm32mp_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(); + } + + stm32mp_clk_disable(iwdg->clock); + + return (timeout == 0U) ? -ETIMEDOUT : 0; +} +#endif + void stm32_iwdg_refresh(void) { uint8_t i; @@ -74,6 +196,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; @@ -137,6 +260,23 @@ int stm32_iwdg_init(void) ((dt_info.status & DT_NON_SECURE) != 0) ? "non-" : ""); + if ((dt_info.status & DT_NON_SECURE) != 0) { + stm32mp_register_non_secure_periph_iomem(iwdg->base); + } else { + stm32mp_register_secure_periph_iomem(iwdg->base); + } + + stm32mp_clk_enable(iwdg->clock); + stm32mp_clk_disable(iwdg->clock); + +#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 24e6efe98..782127c49 100644 --- a/drivers/st/mmc/stm32_sdmmc2.c +++ b/drivers/st/mmc/stm32_sdmmc2.c @@ -50,6 +50,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 */ @@ -116,6 +117,15 @@ #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 TIMEOUT_US_1MS U(1000) + #define DT_SDMMC2_COMPAT "st,stm32-sdmmc2" static void stm32_sdmmc2_init(void); @@ -137,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; @@ -153,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 | @@ -162,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) @@ -220,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; @@ -257,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)); + if ((cmd->resp_type & MMC_RSP_BUSY) != 0U) { mmio_write_32(base + SDMMC_DTIMER, UINT32_MAX); } @@ -550,6 +619,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); @@ -567,6 +637,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); @@ -615,6 +686,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; @@ -624,27 +696,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; } @@ -652,21 +711,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; @@ -701,6 +747,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; } @@ -719,6 +770,8 @@ 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; @@ -726,9 +779,13 @@ int stm32_sdmmc2_mmc_init(struct stm32_sdmmc2_params *params) stm32mp_clk_enable(sdmmc2_params.clock_id); - stm32mp_reset_assert(sdmmc2_params.reset_id); + if (stm32mp_reset_assert_to(sdmmc2_params.reset_id, TIMEOUT_US_1MS)) { + panic(); + } udelay(2); - stm32mp_reset_deassert(sdmmc2_params.reset_id); + if (stm32mp_reset_deassert_to(sdmmc2_params.reset_id, TIMEOUT_US_1MS)) { + panic(); + } mdelay(1); sdmmc2_params.clk_rate = stm32mp_clk_get_rate(sdmmc2_params.clock_id); diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index 9e9dddc4d..07249f607 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,156 +23,396 @@ #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) +static int dt_get_pmic_node(void) { - return fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1"); + static int node = -FDT_ERR_BADOFFSET; + + if (node == -FDT_ERR_BADOFFSET) { + node = dt_get_node_by_compatible("st,stpmic1"); + } + + return node; } int dt_pmic_status(void) { + static int status = -FDT_ERR_BADVALUE; int node; - void *fdt; - if (fdt_get_address(&fdt) == 0) { - return -ENOENT; + if (status != -FDT_ERR_BADVALUE) { + return status; } - node = dt_get_pmic_node(fdt); + node = dt_get_pmic_node(); 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; } -/* - * 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 bool dt_pmic_is_secure(void) +{ + int status = dt_pmic_status(); + + return (status >= 0) && + (status == DT_SECURE) && + (i2c_handle.dt_status == DT_SECURE); +} + +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; } -int dt_pmic_configure_boot_on_regulators(void) +#if defined(IMAGE_BL32) +static int pmic_config_lp(void *fdt, int node, const char *node_name, + const char *regu_name) { - int pmic_node, regulators_node, regulator_node; + 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 + +static int pmic_operate(uint8_t command, const char *node_name, + uint16_t *voltage_mv) +{ + int pmic_node, regulators_node, subnode; void *fdt; + int ret = -EIO; if (fdt_get_address(&fdt) == 0) { return -ENOENT; } - pmic_node = dt_get_pmic_node(fdt); + pmic_node = dt_get_pmic_node(); 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(); + 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(); + 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) @@ -195,6 +436,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; @@ -223,15 +465,104 @@ bool initialize_pmic_i2c(void) return true; } -void initialize_pmic(void) +static void register_non_secure_pmic(void) { - unsigned long pmic_version; + if (i2c_handle.i2c_base_addr == 0U) { + return; + } + + stm32mp_register_non_secure_periph_iomem(i2c_handle.i2c_base_addr); +} + +static void register_secure_pmic(void) +{ + stm32mp_register_secure_periph_iomem(i2c_handle.i2c_base_addr); +} +static int pmic_regulator_enable(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_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"); + register_non_secure_pmic(); return; } + if (dt_pmic_is_secure()) { + register_secure_pmic(); + } else { + VERBOSE("PMIC is not secure-only hence assumed non secure\n"); + register_non_secure_pmic(); + } +} + +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"); panic(); @@ -239,19 +570,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: @@ -271,7 +603,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; } @@ -321,7 +653,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; } @@ -331,7 +662,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 999963054..a1db120dc 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 000000000..1003aba05 --- /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 000000000..f0e4a4ae5 --- /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 fd3f93e01..a1e6e6b86 100644 --- a/drivers/st/reset/stm32mp1_reset.c +++ b/drivers/st/reset/stm32mp1_reset.c @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -15,8 +17,6 @@ #include #include -#define RESET_TIMEOUT_US_1MS U(1000) - static uint32_t id2reg_offset(unsigned int reset_id) { return ((reset_id & GENMASK(31, 5)) >> 5) * sizeof(uint32_t); @@ -27,36 +27,52 @@ static uint8_t id2reg_bit_pos(unsigned int reset_id) return (uint8_t)(reset_id & GENMASK(4, 0)); } -void stm32mp_reset_assert(uint32_t id) +int stm32mp_reset_assert_to(uint32_t id, unsigned int to_us) { uint32_t offset = id2reg_offset(id); uint32_t bitmsk = BIT(id2reg_bit_pos(id)); - uint64_t timeout_ref; uintptr_t rcc_base = stm32mp_rcc_base(); mmio_write_32(rcc_base + offset, bitmsk); - timeout_ref = timeout_init_us(RESET_TIMEOUT_US_1MS); - while ((mmio_read_32(rcc_base + offset) & bitmsk) == 0U) { - if (timeout_elapsed(timeout_ref)) { - panic(); + if (to_us != 0) { + uint64_t timeout_ref = timeout_init_us(to_us); + + while ((mmio_read_32(rcc_base + offset) & bitmsk) == 0U) { + if (timeout_elapsed(timeout_ref)) { + break; + } + } + + if ((mmio_read_32(rcc_base + offset) & bitmsk) == 0U) { + return -ETIMEDOUT; } } + + return 0; } -void stm32mp_reset_deassert(uint32_t id) +int stm32mp_reset_deassert_to(uint32_t id, unsigned int to_us) { uint32_t offset = id2reg_offset(id) + RCC_RSTCLRR_OFFSET; uint32_t bitmsk = BIT(id2reg_bit_pos(id)); - uint64_t timeout_ref; uintptr_t rcc_base = stm32mp_rcc_base(); mmio_write_32(rcc_base + offset, bitmsk); - timeout_ref = timeout_init_us(RESET_TIMEOUT_US_1MS); - while ((mmio_read_32(rcc_base + offset) & bitmsk) != 0U) { - if (timeout_elapsed(timeout_ref)) { - panic(); + if (to_us != 0) { + uint64_t timeout_ref = timeout_init_us(to_us); + + while ((mmio_read_32(rcc_base + offset) & bitmsk) != 0U) { + if (timeout_elapsed(timeout_ref)) { + break; + } + } + + if ((mmio_read_32(rcc_base + offset) & bitmsk) != 0U) { + return -ETIMEDOUT; } } + + return 0; } diff --git a/drivers/st/rng/stm32_rng.c b/drivers/st/rng/stm32_rng.c new file mode 100644 index 000000000..b22065616 --- /dev/null +++ b/drivers/st/rng/stm32_rng.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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; + } + + stm32mp_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: + stm32mp_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 == RNG1_BASE); + + 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; + + stm32mp_clk_enable(stm32_rng.clock); + stm32mp_clk_disable(stm32_rng.clock); + + if (dt_rng.reset >= 0) { + if (stm32mp_reset_assert_to((unsigned long)dt_rng.reset, + TIMEOUT_US_1MS)) + panic(); + + udelay(20); + if (stm32mp_reset_deassert_to((unsigned long)dt_rng.reset, + TIMEOUT_US_1MS)) + 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 000000000..3bfaed772 --- /dev/null +++ b/drivers/st/rtc/stm32_rtc.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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(); + stm32mp_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); + } + } + + stm32mp_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; + + stm32mp_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); + + stm32mp_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(); + stm32mp_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); + } + } + + stm32mp_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(); + stm32mp_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(); + + stm32mp_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; + + stm32mp_clk_enable(rtc_dev.clock); + + ret = (mmio_read_32(rtc_dev.base + RTC_CR) & RTC_CR_TAMPTS) != 0U; + + stm32mp_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/scmi-msg/base.c b/drivers/st/scmi-msg/base.c new file mode 100644 index 000000000..80ce190f7 --- /dev/null +++ b/drivers/st/scmi-msg/base.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" + +static bool message_id_is_supported(unsigned int message_id); + +static void report_version(struct scmi_msg *msg) +{ + struct scmi_protocol_version_p2a return_values = { + .status = SCMI_SUCCESS, + .version = SCMI_PROTOCOL_VERSION_BASE, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_attributes(struct scmi_msg *msg) +{ + size_t protocol_count = plat_scmi_protocol_count(); + struct scmi_protocol_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + /* Null agent count since agent discovery is not supported */ + .attributes = SCMI_BASE_PROTOCOL_ATTRIBUTES(protocol_count, 0U), + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_message_attributes(struct scmi_msg *msg) +{ + struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in; + struct scmi_protocol_message_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + /* For this protocol, attributes shall be zero */ + .attributes = 0U, + }; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (!message_id_is_supported(in_args->message_id)) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void discover_vendor(struct scmi_msg *msg) +{ + const char *name = plat_scmi_vendor_name(); + struct scmi_base_discover_vendor_p2a return_values = { + .status = SCMI_SUCCESS, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + COPY_NAME_IDENTIFIER(return_values.vendor_identifier, name); + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void discover_sub_vendor(struct scmi_msg *msg) +{ + const char *name = plat_scmi_sub_vendor_name(); + struct scmi_base_discover_sub_vendor_p2a return_values = { + .status = SCMI_SUCCESS, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + COPY_NAME_IDENTIFIER(return_values.sub_vendor_identifier, name); + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void discover_implementation_version(struct scmi_msg *msg) +{ + struct scmi_protocol_version_p2a return_values = { + .status = SCMI_SUCCESS, + .version = SCMI_IMPL_VERSION, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static unsigned int count_protocols_in_list(const uint8_t *protocol_list) +{ + unsigned int count = 0U; + + if (protocol_list != NULL) { + while (protocol_list[count] != 0U) { + count++; + } + } + + return count; +} + +#define MAX_PROTOCOL_IN_LIST 8U + +static void discover_list_protocols(struct scmi_msg *msg) +{ + const struct scmi_base_discover_list_protocols_a2p *a2p = NULL; + struct scmi_base_discover_list_protocols_p2a p2a = { + .status = SCMI_SUCCESS, + }; + uint8_t outargs[sizeof(p2a) + MAX_PROTOCOL_IN_LIST] = { 0U }; + const uint8_t *list = NULL; + unsigned int count = 0U; + + if (msg->in_size != sizeof(*a2p)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + assert(msg->out_size > sizeof(outargs)); + + a2p = (void *)msg->in; + + list = plat_scmi_protocol_list(msg->agent_id); + count = count_protocols_in_list(list); + if (count > a2p->skip) { + count = MIN(count - a2p->skip, MAX_PROTOCOL_IN_LIST); + } else { + count = 0U; + } + + p2a.num_protocols = count; + + memcpy(outargs, &p2a, sizeof(p2a)); + memcpy(outargs + sizeof(p2a), list + a2p->skip, count); + + scmi_write_response(msg, outargs, sizeof(outargs)); +} + +static const scmi_msg_handler_t scmi_base_handler_table[] = { + [SCMI_PROTOCOL_VERSION] = report_version, + [SCMI_PROTOCOL_ATTRIBUTES] = report_attributes, + [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes, + [SCMI_BASE_DISCOVER_VENDOR] = discover_vendor, + [SCMI_BASE_DISCOVER_SUB_VENDOR] = discover_sub_vendor, + [SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION] = + discover_implementation_version, + [SCMI_BASE_DISCOVER_LIST_PROTOCOLS] = discover_list_protocols, +}; + +static bool message_id_is_supported(unsigned int message_id) +{ + return (message_id < ARRAY_SIZE(scmi_base_handler_table)) && + (scmi_base_handler_table[message_id] != NULL); +} + +scmi_msg_handler_t scmi_msg_get_base_handler(struct scmi_msg *msg) +{ + const size_t array_size = ARRAY_SIZE(scmi_base_handler_table); + unsigned int message_id = 0U; + + if (msg->message_id >= array_size) { + VERBOSE("Base handle not found %u\n", msg->message_id); + return NULL; + } + + message_id = confine_array_index(msg->message_id, array_size); + + return scmi_base_handler_table[message_id]; +} diff --git a/drivers/st/scmi-msg/base.h b/drivers/st/scmi-msg/base.h new file mode 100644 index 000000000..689f30a8b --- /dev/null +++ b/drivers/st/scmi-msg/base.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ + +#ifndef SCMI_MSG_BASE_H +#define SCMI_MSG_BASE_H + +#include + +#define SCMI_PROTOCOL_VERSION_BASE 0x20000U + +#define SCMI_DEFAULT_STRING_LENGTH 16U + +enum scmi_base_message_id { + SCMI_BASE_DISCOVER_VENDOR = 0x003, + SCMI_BASE_DISCOVER_SUB_VENDOR = 0x004, + SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION = 0x005, + SCMI_BASE_DISCOVER_LIST_PROTOCOLS = 0x006, + SCMI_BASE_DISCOVER_AGENT = 0x007, + SCMI_BASE_NOTIFY_ERRORS = 0x008, +}; + +/* + * PROTOCOL_ATTRIBUTES + */ + +#define SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_POS 0 +#define SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_POS 8 + +#define SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_MASK 0xFFU +#define SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_MASK 0xFF00U + +#define SCMI_BASE_PROTOCOL_ATTRIBUTES(NUM_PROTOCOLS, NUM_AGENTS) \ + ((((NUM_PROTOCOLS) << SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_POS) & \ + SCMI_BASE_PROTOCOL_ATTRS_NUM_PROTOCOLS_MASK) | \ + (((NUM_AGENTS) << SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_POS) & \ + SCMI_BASE_PROTOCOL_ATTRS_NUM_AGENTS_MASK)) + +/* + * BASE_DISCOVER_VENDOR + */ +struct scmi_base_discover_vendor_p2a { + int32_t status; + char vendor_identifier[SCMI_DEFAULT_STRING_LENGTH]; +}; + +/* + * BASE_DISCOVER_SUB_VENDOR + */ +struct scmi_base_discover_sub_vendor_p2a { + int32_t status; + char sub_vendor_identifier[SCMI_DEFAULT_STRING_LENGTH]; +}; + +/* + * BASE_DISCOVER_IMPLEMENTATION_VERSION + * No special structure right now, see protocol_version. + */ + +/* + * BASE_DISCOVER_LIST_PROTOCOLS + */ +struct scmi_base_discover_list_protocols_a2p { + uint32_t skip; +}; + +struct scmi_base_discover_list_protocols_p2a { + int32_t status; + uint32_t num_protocols; + uint32_t protocols[]; +}; + +#endif /* SCMI_MSG_BASE_H */ diff --git a/drivers/st/scmi-msg/clock.c b/drivers/st/scmi-msg/clock.c new file mode 100644 index 000000000..72859cbc9 --- /dev/null +++ b/drivers/st/scmi-msg/clock.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, Linaro Limited + */ +#include +#include + +#include +#include +#include +#include + +#include "common.h" + +#pragma weak plat_scmi_clock_count +#pragma weak plat_scmi_clock_get_name +#pragma weak plat_scmi_clock_rates_array +#pragma weak plat_scmi_clock_rates_by_step +#pragma weak plat_scmi_clock_get_rate +#pragma weak plat_scmi_clock_set_rate +#pragma weak plat_scmi_clock_get_state +#pragma weak plat_scmi_clock_set_state + +static bool message_id_is_supported(unsigned int message_id); + +size_t plat_scmi_clock_count(unsigned int agent_id __unused) +{ + return 0U; +} + +const char *plat_scmi_clock_get_name(unsigned int agent_id __unused, + unsigned int scmi_id __unused) +{ + return NULL; +} + +int32_t plat_scmi_clock_rates_array(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + unsigned long *rates __unused, + size_t *nb_elts __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +int32_t plat_scmi_clock_rates_by_step(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + unsigned long *steps __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +unsigned long plat_scmi_clock_get_rate(unsigned int agent_id __unused, + unsigned int scmi_id __unused) +{ + return 0U; +} + +int32_t plat_scmi_clock_set_rate(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + unsigned long rate __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +int32_t plat_scmi_clock_get_state(unsigned int agent_id __unused, + unsigned int scmi_id __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +int32_t plat_scmi_clock_set_state(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + bool enable_not_disable __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +static void report_version(struct scmi_msg *msg) +{ + struct scmi_protocol_version_p2a return_values = { + .status = SCMI_SUCCESS, + .version = SCMI_PROTOCOL_VERSION_CLOCK, + }; + + if (msg->in_size != 0) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_attributes(struct scmi_msg *msg) +{ + size_t agent_count = plat_scmi_clock_count(msg->agent_id); + struct scmi_protocol_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + .attributes = SCMI_CLOCK_PROTOCOL_ATTRIBUTES(1U, agent_count), + }; + + if (msg->in_size != 0) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_message_attributes(struct scmi_msg *msg) +{ + struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in; + struct scmi_protocol_message_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + /* For this protocol, attributes shall be zero */ + .attributes = 0U, + }; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (!message_id_is_supported(in_args->message_id)) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void scmi_clock_attributes(struct scmi_msg *msg) +{ + const struct scmi_clock_attributes_a2p *in_args = (void *)msg->in; + struct scmi_clock_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + }; + const char *name = NULL; + unsigned int clock_id = 0U; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + clock_id = confine_array_index(in_args->clock_id, + plat_scmi_clock_count(msg->agent_id)); + + name = plat_scmi_clock_get_name(msg->agent_id, clock_id); + if (name == NULL) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + COPY_NAME_IDENTIFIER(return_values.clock_name, name); + + return_values.attributes = plat_scmi_clock_get_state(msg->agent_id, + clock_id); + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void scmi_clock_rate_get(struct scmi_msg *msg) +{ + const struct scmi_clock_rate_get_a2p *in_args = (void *)msg->in; + unsigned long rate = 0U; + struct scmi_clock_rate_get_p2a return_values = { + .status = SCMI_SUCCESS, + }; + unsigned int clock_id = 0U; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + clock_id = confine_array_index(in_args->clock_id, + plat_scmi_clock_count(msg->agent_id)); + + rate = plat_scmi_clock_get_rate(msg->agent_id, clock_id); + + return_values.rate[0] = (uint32_t)rate; + return_values.rate[1] = (uint32_t)((uint64_t)rate >> 32); + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void scmi_clock_rate_set(struct scmi_msg *msg) +{ + const struct scmi_clock_rate_set_a2p *in_args = (void *)msg->in; + unsigned long rate = 0U; + int32_t status = 0; + unsigned int clock_id = 0U; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + clock_id = confine_array_index(in_args->clock_id, + plat_scmi_clock_count(msg->agent_id)); + + rate = (unsigned long)(((uint64_t)in_args->rate[1] << 32) | + in_args->rate[0]); + + status = plat_scmi_clock_set_rate(msg->agent_id, clock_id, rate); + + scmi_status_response(msg, status); +} + +static void scmi_clock_config_set(struct scmi_msg *msg) +{ + const struct scmi_clock_config_set_a2p *in_args = (void *)msg->in; + int32_t status = SCMI_GENERIC_ERROR; + bool enable = false; + unsigned int clock_id = 0U; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + clock_id = confine_array_index(in_args->clock_id, + plat_scmi_clock_count(msg->agent_id)); + + enable = in_args->attributes & SCMI_CLOCK_CONFIG_SET_ENABLE_MASK; + + status = plat_scmi_clock_set_state(msg->agent_id, clock_id, enable); + + scmi_status_response(msg, status); +} + +#define RATES_ARRAY_SIZE_MAX (SCMI_PLAYLOAD_MAX - \ + sizeof(struct scmi_clock_describe_rates_p2a)) + +#define SCMI_RATES_BY_ARRAY(_nb_rates, _rem_rates) \ + SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS((_nb_rates), \ + SCMI_CLOCK_RATE_FORMAT_LIST, \ + (_rem_rates)) +#define SCMI_RATES_BY_STEP \ + SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(3U, \ + SCMI_CLOCK_RATE_FORMAT_RANGE, \ + 0U) + +#define RATE_DESC_SIZE sizeof(struct scmi_clock_rate) + +static void write_rate_desc_array_in_buffer(char *dest, unsigned long *rates, + size_t nb_elt) +{ + uint32_t *out = (uint32_t *)(uintptr_t)dest; + size_t n; + + ASSERT_SYM_PTR_ALIGN(out); + + for (n = 0U; n < nb_elt; n++) { + out[2 * n] = (uint32_t)rates[n]; + out[2 * n + 1] = (uint32_t)((uint64_t)rates[n] >> 32); + } +} + +static void scmi_clock_describe_rates(struct scmi_msg *msg) +{ + const struct scmi_clock_describe_rates_a2p *in_args = (void *)msg->in; + struct scmi_clock_describe_rates_p2a p2a = { + .status = SCMI_SUCCESS, + }; + size_t nb_rates; + int32_t status; + unsigned int clock_id; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + clock_id = confine_array_index(in_args->clock_id, + plat_scmi_clock_count(msg->agent_id)); + + /* Platform may support array rate description */ + status = plat_scmi_clock_rates_array(msg->agent_id, clock_id, NULL, + &nb_rates); + if (status == SCMI_SUCCESS) { + /* Currently 12 cells mex, so it's affordable for the stack */ + unsigned long plat_rates[RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE]; + size_t max_nb = RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE; + size_t ret_nb = MIN(nb_rates - in_args->rate_index, max_nb); + size_t rem_nb = nb_rates - in_args->rate_index - ret_nb; + + status = plat_scmi_clock_rates_array(msg->agent_id, clock_id, + plat_rates, &ret_nb); + if (status == SCMI_SUCCESS) { + write_rate_desc_array_in_buffer(msg->out + sizeof(p2a), + plat_rates, ret_nb); + + p2a.num_rates_flags = SCMI_RATES_BY_ARRAY(ret_nb, + rem_nb); + p2a.status = SCMI_SUCCESS; + + memcpy(msg->out, &p2a, sizeof(p2a)); + msg->out_size_out = sizeof(p2a) + + ret_nb * RATE_DESC_SIZE; + } + } else if (status == SCMI_NOT_SUPPORTED) { + unsigned long triplet[3] = { 0U, 0U, 0U }; + + /* Platform may support min§max/step triplet description */ + status = plat_scmi_clock_rates_by_step(msg->agent_id, clock_id, + triplet); + if (status == SCMI_SUCCESS) { + write_rate_desc_array_in_buffer(msg->out + sizeof(p2a), + triplet, 3U); + + p2a.num_rates_flags = SCMI_RATES_BY_STEP; + p2a.status = SCMI_SUCCESS; + + memcpy(msg->out, &p2a, sizeof(p2a)); + msg->out_size_out = sizeof(p2a) + (3U * RATE_DESC_SIZE); + } + } else { + /* Fallthrough generic exit sequence below with error status */ + } + + if (status != SCMI_SUCCESS) { + scmi_status_response(msg, status); + } else { + /* + * Message payload is already writen to msg->out, and + * msg->out_size_out updated. + */ + } +} + +static const scmi_msg_handler_t scmi_clock_handler_table[] = { + [SCMI_PROTOCOL_VERSION] = report_version, + [SCMI_PROTOCOL_ATTRIBUTES] = report_attributes, + [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes, + [SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes, + [SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates, + [SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set, + [SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get, + [SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set, +}; + +static bool message_id_is_supported(size_t message_id) +{ + return (message_id < ARRAY_SIZE(scmi_clock_handler_table)) && + (scmi_clock_handler_table[message_id] != NULL); +} + +scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg) +{ + const size_t array_size = ARRAY_SIZE(scmi_clock_handler_table); + unsigned int message_id = 0U; + + if (msg->message_id >= array_size) { + VERBOSE("Clock handle not found %u", msg->message_id); + return NULL; + } + + message_id = confine_array_index(msg->message_id, array_size); + + return scmi_clock_handler_table[message_id]; +} diff --git a/drivers/st/scmi-msg/clock.h b/drivers/st/scmi-msg/clock.h new file mode 100644 index 000000000..a637934ee --- /dev/null +++ b/drivers/st/scmi-msg/clock.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ + +#ifndef SCMI_MSG_CLOCK_H +#define SCMI_MSG_CLOCK_H + +#include + +#include + +#define SCMI_PROTOCOL_VERSION_CLOCK 0x20000U + +/* + * Identifiers of the SCMI Clock Management Protocol commands + */ +enum scmi_clock_command_id { + SCMI_CLOCK_ATTRIBUTES = 0x003, + SCMI_CLOCK_DESCRIBE_RATES = 0x004, + SCMI_CLOCK_RATE_SET = 0x005, + SCMI_CLOCK_RATE_GET = 0x006, + SCMI_CLOCK_CONFIG_SET = 0x007, +}; + +/* Protocol attributes */ +#define SCMI_CLOCK_CLOCK_COUNT_MASK GENMASK(15, 0) +#define SCMI_CLOCK_MAX_PENDING_TRANSITIONS_MASK GENMASK(23, 16) + +#define SCMI_CLOCK_PROTOCOL_ATTRIBUTES(_max_pending, _clk_count) \ + ((((_max_pending) << 16) & SCMI_CLOCK_MAX_PENDING_TRANSITIONS_MASK) | \ + (((_clk_count) & SCMI_CLOCK_CLOCK_COUNT_MASK))) + +struct scmi_clock_attributes_a2p { + uint32_t clock_id; +}; + +#define SCMI_CLOCK_NAME_LENGTH_MAX 16U + +struct scmi_clock_attributes_p2a { + int32_t status; + uint32_t attributes; + char clock_name[SCMI_CLOCK_NAME_LENGTH_MAX]; +}; + +/* + * Clock Rate Get + */ + +struct scmi_clock_rate_get_a2p { + uint32_t clock_id; +}; + +struct scmi_clock_rate_get_p2a { + int32_t status; + uint32_t rate[2]; +}; + +/* + * Clock Rate Set + */ + +/* If set, set the new clock rate asynchronously */ +#define SCMI_CLOCK_RATE_SET_ASYNC_POS 0 +/* If set, do not send a delayed asynchronous response */ +#define SCMI_CLOCK_RATE_SET_NO_DELAYED_RESPONSE_POS 1 +/* Round up, if set, otherwise round down */ +#define SCMI_CLOCK_RATE_SET_ROUND_UP_POS 2 +/* If set, the platform chooses the appropriate rounding mode */ +#define SCMI_CLOCK_RATE_SET_ROUND_AUTO_POS 3 + +#define SCMI_CLOCK_RATE_SET_ASYNC_MASK \ + BIT(SCMI_CLOCK_RATE_SET_ASYNC_POS) +#define SCMI_CLOCK_RATE_SET_NO_DELAYED_RESPONSE_MASK \ + BIT(SCMI_CLOCK_RATE_SET_NO_DELAYED_RESPONSE_POS) +#define SCMI_CLOCK_RATE_SET_ROUND_UP_MASK \ + BIT(SCMI_CLOCK_RATE_SET_ROUND_UP_POS) +#define SCMI_CLOCK_RATE_SET_ROUND_AUTO_MASK \ + BIT(SCMI_CLOCK_RATE_SET_ROUND_AUTO_POS) + +struct scmi_clock_rate_set_a2p { + uint32_t flags; + uint32_t clock_id; + uint32_t rate[2]; +}; + +struct scmi_clock_rate_set_p2a { + int32_t status; +}; + +/* + * Clock Config Set + */ + +#define SCMI_CLOCK_CONFIG_SET_ENABLE_POS 0 + +#define SCMI_CLOCK_CONFIG_SET_ENABLE_MASK \ + BIT(SCMI_CLOCK_CONFIG_SET_ENABLE_POS) + +struct scmi_clock_config_set_a2p { + uint32_t clock_id; + uint32_t attributes; +}; + +struct scmi_clock_config_set_p2a { + int32_t status; +}; + +/* + * Clock Describe Rates + */ + +#define SCMI_CLOCK_RATE_FORMAT_RANGE 1U +#define SCMI_CLOCK_RATE_FORMAT_LIST 0U + +#define SCMI_CLOCK_DESCRIBE_RATES_REMAINING_MASK GENMASK_32(31, 16) +#define SCMI_CLOCK_DESCRIBE_RATES_REMAINING_POS 16 + +#define SCMI_CLOCK_DESCRIBE_RATES_FORMAT_MASK BIT(12) +#define SCMI_CLOCK_DESCRIBE_RATES_FORMAT_POS 12 + +#define SCMI_CLOCK_DESCRIBE_RATES_COUNT_MASK GENMASK_32(11, 0) + +#define SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(_count, _fmt, _rem_rates) \ + ( \ + ((_count) & SCMI_CLOCK_DESCRIBE_RATES_COUNT_MASK) | \ + (((_rem_rates) << SCMI_CLOCK_DESCRIBE_RATES_REMAINING_POS) & \ + SCMI_CLOCK_DESCRIBE_RATES_REMAINING_MASK) | \ + (((_fmt) << SCMI_CLOCK_DESCRIBE_RATES_FORMAT_POS) & \ + SCMI_CLOCK_DESCRIBE_RATES_FORMAT_MASK) \ + ) + +struct scmi_clock_rate { + uint32_t low; + uint32_t high; +}; + +struct scmi_clock_describe_rates_a2p { + uint32_t clock_id; + uint32_t rate_index; +}; + +struct scmi_clock_describe_rates_p2a { + int32_t status; + uint32_t num_rates_flags; + struct scmi_clock_rate rates[]; +}; + +#endif /* SCMI_MSG_CLOCK_H */ diff --git a/drivers/st/scmi-msg/common.h b/drivers/st/scmi-msg/common.h new file mode 100644 index 000000000..bf073418f --- /dev/null +++ b/drivers/st/scmi-msg/common.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ +#ifndef SCMI_MSG_COMMON_H +#define SCMI_MSG_COMMON_H + +#include +#include +#include +#include + +#include "base.h" +#include "clock.h" +#include "reset_domain.h" + +#define SCMI_VERSION 0x20000U +#define SCMI_IMPL_VERSION 0U + +#define SCMI_PLAYLOAD_MAX 92U + +/* + * Copy name identifier in target buffer following the SCMI specification + * that state name identifier shall be a null terminated string. + */ +#define COPY_NAME_IDENTIFIER(_dst_array, _name) \ + do { \ + assert(strlen(_name) < sizeof(_dst_array)); \ + strlcpy((_dst_array), (_name), sizeof(_dst_array)); \ + } while (0) + +/* Common command identifiers shared by all procotols */ +enum scmi_common_message_id { + SCMI_PROTOCOL_VERSION = 0x000, + SCMI_PROTOCOL_ATTRIBUTES = 0x001, + SCMI_PROTOCOL_MESSAGE_ATTRIBUTES = 0x002 +}; + +/* Common platform-to-agent (p2a) PROTOCOL_VERSION structure */ +struct scmi_protocol_version_p2a { + int32_t status; + uint32_t version; +}; + +/* Generic platform-to-agent (p2a) PROTOCOL_ATTRIBUTES structure */ +struct scmi_protocol_attributes_p2a { + int32_t status; + uint32_t attributes; +}; + +/* Generic agent-to-platform (a2p) PROTOCOL_MESSAGE_ATTRIBUTES structure */ +struct scmi_protocol_message_attributes_a2p { + uint32_t message_id; +}; + +/* Generic platform-to-agent (p2a) PROTOCOL_MESSAGE_ATTRIBUTES structure */ +struct scmi_protocol_message_attributes_p2a { + int32_t status; + uint32_t attributes; +}; + +/* + * struct scmi_msg - SCMI message context + * + * @agent_id: SCMI agent ID, safely set from secure world + * @protocol_id: SCMI protocol ID for the related message, set by caller agent + * @message_id: SCMI message ID for the related message, set by caller agent + * @in: Address of the incoming message payload copied in secure memory + * @in_size: Byte length of the incoming message payload, set by caller agent + * @out: Address of of the output message payload message in non-secure memory + * @out_size: Byte length of the provisionned output buffer + * @out_size_out: Byte length of the output message payload + */ +struct scmi_msg { + unsigned int agent_id; + unsigned int protocol_id; + unsigned int message_id; + char *in; + size_t in_size; + char *out; + size_t out_size; + size_t out_size_out; +}; + +/* + * Type scmi_msg_handler_t is used by procotol drivers to safely find + * the handler function for the incoming message ID. + */ +typedef void (*scmi_msg_handler_t)(struct scmi_msg *msg); + +/* + * scmi_msg_get_base_handler - Return a handler for a base message + * @msg - message to process + * Return a function handler for the message or NULL + */ +scmi_msg_handler_t scmi_msg_get_base_handler(struct scmi_msg *msg); + +/* + * scmi_msg_get_clock_handler - Return a handler for a clock message + * @msg - message to process + * Return a function handler for the message or NULL + */ +scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg); + +/* + * scmi_msg_get_rd_handler - Return a handler for a reset domain message + * @msg - message to process + * Return a function handler for the message or NULL + */ +scmi_msg_handler_t scmi_msg_get_rd_handler(struct scmi_msg *msg); + +/* + * Process Read, process and write response for input SCMI message + * + * @msg: SCMI message context + */ +void scmi_process_message(struct scmi_msg *msg); + +/* + * Write SCMI response payload to output message shared memory + * + * @msg: SCMI message context + * @payload: Output message payload + * @size: Byte size of output message payload + */ +void scmi_write_response(struct scmi_msg *msg, void *payload, size_t size); + +/* + * Write status only SCMI response payload to output message shared memory + * + * @msg: SCMI message context + * @status: SCMI status value returned to caller + */ +void scmi_status_response(struct scmi_msg *msg, int32_t status); +#endif /* SCMI_MSG_COMMON_H */ diff --git a/drivers/st/scmi-msg/entry.c b/drivers/st/scmi-msg/entry.c new file mode 100644 index 000000000..ae1384e2b --- /dev/null +++ b/drivers/st/scmi-msg/entry.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, Linaro Limited + */ + +#include + +#include +#include + +#include "common.h" + +void scmi_status_response(struct scmi_msg *msg, int32_t status) +{ + assert(msg->out && msg->out_size >= sizeof(int32_t)); + + memcpy(msg->out, &status, sizeof(int32_t)); + msg->out_size_out = sizeof(int32_t); +} + +void scmi_write_response(struct scmi_msg *msg, void *payload, size_t size) +{ + /* + * Output payload shall be at least the size of the status + * Output buffer shall be at least be the size of the status + * Output paylaod shall fit in output buffer + */ + assert(payload && size >= sizeof(int32_t) && size <= msg->out_size && + msg->out && msg->out_size >= sizeof(int32_t)); + + memcpy(msg->out, payload, size); + msg->out_size_out = size; +} + +void scmi_process_message(struct scmi_msg *msg) +{ + scmi_msg_handler_t handler = NULL; + + switch (msg->protocol_id) { + case SCMI_PROTOCOL_ID_BASE: + handler = scmi_msg_get_base_handler(msg); + break; + case SCMI_PROTOCOL_ID_CLOCK: + handler = scmi_msg_get_clock_handler(msg); + break; + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + handler = scmi_msg_get_rd_handler(msg); + break; + default: + break; + } + + if (handler) { + handler(msg); + return; + } + + ERROR("Agent %u Protocol 0x%x Message 0x%x: not supported", + msg->agent_id, msg->protocol_id, msg->message_id); + + scmi_status_response(msg, SCMI_NOT_SUPPORTED); +} diff --git a/drivers/st/scmi-msg/reset_domain.c b/drivers/st/scmi-msg/reset_domain.c new file mode 100644 index 000000000..23e205b7a --- /dev/null +++ b/drivers/st/scmi-msg/reset_domain.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, Linaro Limited + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" + +static bool message_id_is_supported(unsigned int message_id); + +#pragma weak plat_scmi_rd_count +#pragma weak plat_scmi_rd_get_name +#pragma weak plat_scmi_rd_autonomous +#pragma weak plat_scmi_rd_set_state + +size_t plat_scmi_rd_count(unsigned int agent_id __unused) +{ + return 0U; +} + +const char *plat_scmi_rd_get_name(unsigned int agent_id __unused, + unsigned int scmi_id __unused) +{ + return NULL; +} + +int32_t plat_scmi_rd_autonomous(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + unsigned int state __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +int32_t plat_scmi_rd_set_state(unsigned int agent_id __unused, + unsigned int scmi_id __unused, + bool assert_not_deassert __unused) +{ + return SCMI_NOT_SUPPORTED; +} + +static void report_version(struct scmi_msg *msg) +{ + struct scmi_protocol_version_p2a return_values = { + .status = SCMI_SUCCESS, + .version = SCMI_PROTOCOL_VERSION_RESET_DOMAIN, + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_attributes(struct scmi_msg *msg) +{ + struct scmi_protocol_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + .attributes = plat_scmi_rd_count(msg->agent_id), + }; + + if (msg->in_size != 0U) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void report_message_attributes(struct scmi_msg *msg) +{ + struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in; + struct scmi_protocol_message_attributes_p2a return_values = { + .status = SCMI_SUCCESS, + .attributes = 0U, + }; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (!message_id_is_supported(in_args->message_id)) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void reset_domain_attributes(struct scmi_msg *msg) +{ + struct scmi_reset_domain_attributes_a2p *in_args = (void *)msg->in; + struct scmi_reset_domain_attributes_p2a return_values; + const char *name = NULL; + unsigned int domain_id = 0U; + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->domain_id >= plat_scmi_rd_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_INVALID_PARAMETERS); + return; + } + + domain_id = confine_array_index(in_args->domain_id, + plat_scmi_rd_count(msg->agent_id)); + + name = plat_scmi_rd_get_name(msg->agent_id, domain_id); + if (name == NULL) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + zeromem(&return_values, sizeof(return_values)); + return_values.status = SCMI_SUCCESS; + return_values.flags = 0U; /* Async and Notif are not supported */ + return_values.latency = SCMI_RESET_DOMAIN_ATTR_UNK_LAT; + COPY_NAME_IDENTIFIER(return_values.name, name); + + scmi_write_response(msg, &return_values, sizeof(return_values)); +} + +static void reset_request(struct scmi_msg *msg) +{ + struct scmi_reset_domain_request_a2p *in_args = (void *)msg->in; + struct scmi_reset_domain_request_p2a out_args = { + .status = SCMI_SUCCESS, + }; + unsigned int domain_id = 0U; + + domain_id = confine_array_index(in_args->domain_id, + plat_scmi_rd_count(msg->agent_id)); + + if (msg->in_size != sizeof(*in_args)) { + scmi_status_response(msg, SCMI_PROTOCOL_ERROR); + return; + } + + if (in_args->domain_id >= plat_scmi_rd_count(msg->agent_id)) { + scmi_status_response(msg, SCMI_NOT_FOUND); + return; + } + + if ((in_args->flags & SCMI_RESET_DOMAIN_AUTO) != 0U) { + out_args.status = plat_scmi_rd_autonomous(msg->agent_id, + domain_id, + in_args->reset_state); + } else if ((in_args->flags & SCMI_RESET_DOMAIN_EXPLICIT) != 0U) { + out_args.status = plat_scmi_rd_set_state(msg->agent_id, + domain_id, true); + } else { + out_args.status = plat_scmi_rd_set_state(msg->agent_id, + domain_id, false); + } + + if (out_args.status != SCMI_SUCCESS) { + scmi_status_response(msg, out_args.status); + } else { + scmi_write_response(msg, &out_args, sizeof(out_args)); + } +} + +static const scmi_msg_handler_t scmi_rd_handler_table[] = { + [SCMI_PROTOCOL_VERSION] = report_version, + [SCMI_PROTOCOL_ATTRIBUTES] = report_attributes, + [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes, + [SCMI_RESET_DOMAIN_ATTRIBUTES] = reset_domain_attributes, + [SCMI_RESET_DOMAIN_REQUEST] = reset_request, +}; + +static bool message_id_is_supported(unsigned int message_id) +{ + return (message_id < ARRAY_SIZE(scmi_rd_handler_table)) && + (scmi_rd_handler_table[message_id] != NULL); +} + +scmi_msg_handler_t scmi_msg_get_rd_handler(struct scmi_msg *msg) +{ + const size_t array_size = ARRAY_SIZE(scmi_rd_handler_table); + unsigned int message_id = 0U; + + if (msg->message_id >= array_size) { + VERBOSE("Reset domain handle not found %u\n", msg->message_id); + return NULL; + } + + message_id = confine_array_index(msg->message_id, array_size); + + return scmi_rd_handler_table[message_id]; +} diff --git a/drivers/st/scmi-msg/reset_domain.h b/drivers/st/scmi-msg/reset_domain.h new file mode 100644 index 000000000..47bee5e39 --- /dev/null +++ b/drivers/st/scmi-msg/reset_domain.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ +#ifndef SCMI_MSG_RESET_DOMAIN_H +#define SCMI_MSG_RESET_DOMAIN_H + +#include +#include + +#include + +#define SCMI_PROTOCOL_VERSION_RESET_DOMAIN 0x10000U + +#define SCMI_RESET_STATE_ARCH BIT(31) +#define SCMI_RESET_STATE_IMPL 0U + +/* + * Identifiers of the SCMI Reset Domain Management Protocol commands + */ +enum scmi_reset_domain_command_id { + SCMI_RESET_DOMAIN_ATTRIBUTES = 0x03, + SCMI_RESET_DOMAIN_REQUEST = 0x04, + SCMI_RESET_DOMAIN_NOTIFY = 0x05, +}; + +/* + * Identifiers of the SCMI Reset Domain Management Protocol responses + */ +enum scmi_reset_domain_response_id { + SCMI_RESET_ISSUED = 0x00, + SCMI_RESET_COMPLETE = 0x04, +}; + +/* + * PROTOCOL_ATTRIBUTES + */ + +#define SCMI_RESET_DOMAIN_COUNT_MASK GENMASK_32(15, 0) + +struct scmi_reset_domain_protocol_attributes_p2a { + int32_t status; + uint32_t attributes; +}; + +/* Value for scmi_reset_domain_attributes_p2a:flags */ +#define SCMI_RESET_DOMAIN_ATTR_ASYNC BIT(31) +#define SCMI_RESET_DOMAIN_ATTR_NOTIF BIT(30) + +/* Value for scmi_reset_domain_attributes_p2a:latency */ +#define SCMI_RESET_DOMAIN_ATTR_UNK_LAT 0x7fffffffU +#define SCMI_RESET_DOMAIN_ATTR_MAX_LAT 0x7ffffffeU + +/* Macro for scmi_reset_domain_attributes_p2a:name */ +#define SCMI_RESET_DOMAIN_ATTR_NAME_SZ 16U + +struct scmi_reset_domain_attributes_a2p { + uint32_t domain_id; +}; + +struct scmi_reset_domain_attributes_p2a { + int32_t status; + uint32_t flags; + uint32_t latency; + char name[SCMI_RESET_DOMAIN_ATTR_NAME_SZ]; +}; + +/* + * RESET + */ + +/* Values for scmi_reset_domain_request_a2p:flags */ +#define SCMI_RESET_DOMAIN_ASYNC BIT(2) +#define SCMI_RESET_DOMAIN_EXPLICIT BIT(1) +#define SCMI_RESET_DOMAIN_AUTO BIT(0) + +struct scmi_reset_domain_request_a2p { + uint32_t domain_id; + uint32_t flags; + uint32_t reset_state; +}; + +struct scmi_reset_domain_request_p2a { + int32_t status; +}; + +/* + * RESET_NOTIFY + */ + +/* Values for scmi_reset_notify_p2a:flags */ +#define SCMI_RESET_DOMAIN_DO_NOTIFY BIT(0) + +struct scmi_reset_domain_notify_a2p { + uint32_t domain_id; + uint32_t notify_enable; +}; + +struct scmi_reset_domain_notify_p2a { + int32_t status; +}; + +/* + * RESET_COMPLETE + */ + +struct scmi_reset_domain_complete_p2a { + int32_t status; + uint32_t domain_id; +}; + +/* + * RESET_ISSUED + */ + +struct scmi_reset_domain_issued_p2a { + uint32_t domain_id; + uint32_t reset_state; +}; + +#endif /* SCMI_MSG_RESET_DOMAIN_H */ diff --git a/drivers/st/scmi-msg/smt.c b/drivers/st/scmi-msg/smt.c new file mode 100644 index 000000000..9a0502a08 --- /dev/null +++ b/drivers/st/scmi-msg/smt.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, Linaro Limited + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +/* Legacy SMT/SCMI messages are 128 bytes at most including SMT header */ +#define SCMI_PLAYLOAD_MAX 92U +#define SCMI_PLAYLOAD_U32_MAX (SCMI_PLAYLOAD_MAX / sizeof(uint32_t)) + +/** + * struct smt_header - SMT formatted header for SMT base shared memory transfer + * + * @status: Bit flags, see SMT_STATUS_* + * @flags: Bit flags, see SMT_FLAG_* + * @length: Byte size of message payload (variable) + ::message_header (32bit) + * payload: SCMI message payload data + */ +struct smt_header { + uint32_t reserved0; + uint32_t status; + uint64_t reserved1; + uint32_t flags; + uint32_t length; /* message_header + payload */ + uint32_t message_header; + uint32_t payload[]; +}; + +CASSERT(SCMI_PLAYLOAD_MAX + sizeof(struct smt_header) <= SMT_BUF_SLOT_SIZE, + assert_scmi_message_max_length_fits_in_smt_buffer_slot); + +/* Flag set in smt_header::status when SMT does not contain pending message */ +#define SMT_STATUS_FREE BIT(0) +/* Flag set in smt_header::status when SMT reports an error */ +#define SMT_STATUS_ERROR BIT(1) + +/* Flag set in smt_header::flags when SMT uses interrupts */ +#define SMT_FLAG_INTR_ENABLED BIT(1) + +/* Bit fields packed in smt_header::message_header */ +#define SMT_MSG_ID_MASK GENMASK_32(7, 0) +#define SMT_HDR_MSG_ID(_hdr) ((_hdr) & SMT_MSG_ID_MASK) + +#define SMT_MSG_TYPE_MASK GENMASK_32(9, 8) +#define SMT_HDR_TYPE_ID(_hdr) (((_hdr) & SMT_MSG_TYPE_MASK) >> 8) + +#define SMT_MSG_PROT_ID_MASK GENMASK_32(17, 10) +#define SMT_HDR_PROT_ID(_hdr) (((_hdr) & SMT_MSG_PROT_ID_MASK) >> 10) + +/* + * Provision input message payload buffers for fastcall SMC context entries + * and for interrupt context execution entries. + */ +static uint32_t fast_smc_payload[PLATFORM_CORE_COUNT][SCMI_PLAYLOAD_U32_MAX]; +static uint32_t interrupt_payload[PLATFORM_CORE_COUNT][SCMI_PLAYLOAD_U32_MAX]; + +/* SMP protection on channel access */ +static struct spinlock smt_channels_lock; + +/* If channel is not busy, set busy and return true, otherwise return false */ +static bool channel_set_busy(struct scmi_msg_channel *chan) +{ + bool channel_is_busy; + + spin_lock(&smt_channels_lock); + + channel_is_busy = chan->busy; + + if (!channel_is_busy) { + chan->busy = true; + } + + spin_unlock(&smt_channels_lock); + + return !channel_is_busy; +} + +static void channel_release_busy(struct scmi_msg_channel *chan) +{ + chan->busy = false; +} + +static struct smt_header *channel_to_smt_hdr(struct scmi_msg_channel *chan) +{ + return (struct smt_header *)chan->shm_addr; +} + +/* + * Creates a SCMI message instance in secure memory and push it in the SCMI + * message drivers. Message structure contains SCMI protocol meta-data and + * references to input payload in secure memory and output message buffer + * in shared memory. + */ +static void scmi_proccess_smt(unsigned int agent_id, uint32_t *payload_buf) +{ + struct scmi_msg_channel *chan; + struct smt_header *smt_hdr; + size_t in_payload_size; + uint32_t smt_status; + struct scmi_msg msg; + bool error = true; + + chan = plat_scmi_get_channel(agent_id); + if (chan == NULL) { + return; + } + + smt_hdr = channel_to_smt_hdr(chan); + assert(smt_hdr); + + smt_status = __atomic_load_n(&smt_hdr->status, __ATOMIC_RELAXED); + + if (!channel_set_busy(chan)) { + VERBOSE("SCMI channel %u busy", agent_id); + goto out; + } + + in_payload_size = __atomic_load_n(&smt_hdr->length, __ATOMIC_RELAXED) - + sizeof(smt_hdr->message_header); + + if (in_payload_size > SCMI_PLAYLOAD_MAX) { + VERBOSE("SCMI payload too big %u", in_payload_size); + goto out; + } + + if (smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) { + VERBOSE("SCMI channel bad status 0x%x", + smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)); + goto out; + } + + /* Fill message */ + zeromem(&msg, sizeof(msg)); + msg.in = (char *)payload_buf; + msg.in_size = in_payload_size; + msg.out = (char *)smt_hdr->payload; + msg.out_size = chan->shm_size - sizeof(*smt_hdr); + + assert(msg.out && msg.out_size >= sizeof(int32_t)); + + /* Here the payload is copied in secure memory */ + memcpy(msg.in, smt_hdr->payload, in_payload_size); + + msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header); + msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header); + msg.agent_id = agent_id; + + scmi_process_message(&msg); + + /* Update message length with the length of the response message */ + smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header); + + channel_release_busy(chan); + error = false; + +out: + if (error) { + VERBOSE("SCMI error"); + smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE; + } else { + smt_hdr->status |= SMT_STATUS_FREE; + } +} + +void scmi_smt_fastcall_smc_entry(unsigned int agent_id) +{ + scmi_proccess_smt(agent_id, + fast_smc_payload[plat_my_core_pos()]); +} + +void scmi_smt_interrupt_entry(unsigned int agent_id) +{ + scmi_proccess_smt(agent_id, + interrupt_payload[plat_my_core_pos()]); +} + +/* Init a SMT header for a shared memory buffer: state it a free/no-error */ +void scmi_smt_init_agent_channel(struct scmi_msg_channel *chan) +{ + if (chan != NULL) { + struct smt_header *smt_header = channel_to_smt_hdr(chan); + + if (smt_header != NULL) { + memset(smt_header, 0, sizeof(*smt_header)); + smt_header->status = SMT_STATUS_FREE; + + return; + } + } + + panic(); +} diff --git a/drivers/st/spi/stm32_qspi.c b/drivers/st/spi/stm32_qspi.c new file mode 100644 index 000000000..f4a13386f --- /dev/null +++ b/drivers/st/spi/stm32_qspi.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_US_1MS U(1000) + +/* QUADSPI registers */ +#define QSPI_CR 0x00U +#define QSPI_DCR 0x04U +#define QSPI_SR 0x08U +#define QSPI_FCR 0x0CU +#define QSPI_DLR 0x10U +#define QSPI_CCR 0x14U +#define QSPI_AR 0x18U +#define QSPI_ABR 0x1CU +#define QSPI_DR 0x20U +#define QSPI_PSMKR 0x24U +#define QSPI_PSMAR 0x28U +#define QSPI_PIR 0x2CU +#define QSPI_LPTR 0x30U + +/* QUADSPI control register */ +#define QSPI_CR_EN BIT(0) +#define QSPI_CR_ABORT BIT(1) +#define QSPI_CR_DMAEN BIT(2) +#define QSPI_CR_TCEN BIT(3) +#define QSPI_CR_SSHIFT BIT(4) +#define QSPI_CR_DFM BIT(6) +#define QSPI_CR_FSEL BIT(7) +#define QSPI_CR_FTHRES_SHIFT 8U +#define QSPI_CR_TEIE BIT(16) +#define QSPI_CR_TCIE BIT(17) +#define QSPI_CR_FTIE BIT(18) +#define QSPI_CR_SMIE BIT(19) +#define QSPI_CR_TOIE BIT(20) +#define QSPI_CR_APMS BIT(22) +#define QSPI_CR_PMM BIT(23) +#define QSPI_CR_PRESCALER_MASK GENMASK_32(31, 24) +#define QSPI_CR_PRESCALER_SHIFT 24U + +/* QUADSPI device configuration register */ +#define QSPI_DCR_CKMODE BIT(0) +#define QSPI_DCR_CSHT_MASK GENMASK_32(10, 8) +#define QSPI_DCR_CSHT_SHIFT 8U +#define QSPI_DCR_FSIZE_MASK GENMASK_32(20, 16) +#define QSPI_DCR_FSIZE_SHIFT 16U + +/* QUADSPI status register */ +#define QSPI_SR_TEF BIT(0) +#define QSPI_SR_TCF BIT(1) +#define QSPI_SR_FTF BIT(2) +#define QSPI_SR_SMF BIT(3) +#define QSPI_SR_TOF BIT(4) +#define QSPI_SR_BUSY BIT(5) + +/* QUADSPI flag clear register */ +#define QSPI_FCR_CTEF BIT(0) +#define QSPI_FCR_CTCF BIT(1) +#define QSPI_FCR_CSMF BIT(3) +#define QSPI_FCR_CTOF BIT(4) + +/* QUADSPI communication configuration register */ +#define QSPI_CCR_DDRM BIT(31) +#define QSPI_CCR_DHHC BIT(30) +#define QSPI_CCR_SIOO BIT(28) +#define QSPI_CCR_FMODE_SHIFT 26U +#define QSPI_CCR_DMODE_SHIFT 24U +#define QSPI_CCR_DCYC_SHIFT 18U +#define QSPI_CCR_ABSIZE_SHIFT 16U +#define QSPI_CCR_ABMODE_SHIFT 14U +#define QSPI_CCR_ADSIZE_SHIFT 12U +#define QSPI_CCR_ADMODE_SHIFT 10U +#define QSPI_CCR_IMODE_SHIFT 8U +#define QSPI_CCR_IND_WRITE 0U +#define QSPI_CCR_IND_READ 1U +#define QSPI_CCR_MEM_MAP 3U + +#define QSPI_MAX_CHIP 2U + +#define QSPI_FIFO_TIMEOUT_US 30U +#define QSPI_CMD_TIMEOUT_US 1000U +#define QSPI_BUSY_TIMEOUT_US 100U +#define QSPI_ABT_TIMEOUT_US 100U + +#define DT_QSPI_COMPAT "st,stm32f469-qspi" + +#define FREQ_100MHZ 100000000U + +struct stm32_qspi_ctrl { + uintptr_t reg_base; + uintptr_t mm_base; + size_t mm_size; + unsigned long clock_id; + unsigned int reset_id; +}; + +static struct stm32_qspi_ctrl stm32_qspi; + +static uintptr_t qspi_base(void) +{ + return stm32_qspi.reg_base; +} + +static int stm32_qspi_wait_for_not_busy(void) +{ + uint64_t timeout = timeout_init_us(QSPI_BUSY_TIMEOUT_US); + + while ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_BUSY) != 0U) { + if (timeout_elapsed(timeout)) { + ERROR("%s: busy timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stm32_qspi_wait_cmd(const struct spi_mem_op *op) +{ + int ret = 0; + uint64_t timeout; + + if (op->data.nbytes == 0U) { + return stm32_qspi_wait_for_not_busy(); + } + + timeout = timeout_init_us(QSPI_CMD_TIMEOUT_US); + while ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_TCF) == 0U) { + if (timeout_elapsed(timeout)) { + ret = -ETIMEDOUT; + break; + } + } + + if (ret == 0) { + if ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_TEF) != 0U) { + ERROR("%s: transfer error\n", __func__); + ret = -EIO; + } + } else { + ERROR("%s: cmd timeout\n", __func__); + } + + /* Clear flags */ + mmio_write_32(qspi_base() + QSPI_FCR, QSPI_FCR_CTCF | QSPI_FCR_CTEF); + + return ret; +} + +static void stm32_qspi_read_fifo(uint8_t *val, uintptr_t addr) +{ + *val = mmio_read_8(addr); +} + +static void stm32_qspi_write_fifo(uint8_t *val, uintptr_t addr) +{ + mmio_write_8(addr, *val); +} + +static int stm32_qspi_poll(const struct spi_mem_op *op) +{ + void (*fifo)(uint8_t *val, uintptr_t addr); + uint32_t len; + uint8_t *buf; + + if (op->data.dir == SPI_MEM_DATA_IN) { + fifo = stm32_qspi_read_fifo; + } else { + fifo = stm32_qspi_write_fifo; + } + + buf = (uint8_t *)op->data.buf; + + for (len = op->data.nbytes; len != 0U; len--) { + uint64_t timeout = timeout_init_us(QSPI_FIFO_TIMEOUT_US); + + while ((mmio_read_32(qspi_base() + QSPI_SR) & + QSPI_SR_FTF) == 0U) { + if (timeout_elapsed(timeout)) { + ERROR("%s: fifo timeout\n", __func__); + return -ETIMEDOUT; + } + } + + fifo(buf++, qspi_base() + QSPI_DR); + } + + return 0; +} + +static int stm32_qspi_mm(const struct spi_mem_op *op) +{ + memcpy(op->data.buf, + (void *)(stm32_qspi.mm_base + (size_t)op->addr.val), + op->data.nbytes); + + return 0; +} + +static int stm32_qspi_tx(const struct spi_mem_op *op, uint8_t mode) +{ + if (op->data.nbytes == 0U) { + return 0; + } + + if (mode == QSPI_CCR_MEM_MAP) { + return stm32_qspi_mm(op); + } + + return stm32_qspi_poll(op); +} + +static unsigned int stm32_qspi_get_mode(uint8_t buswidth) +{ + if (buswidth == 4U) { + return 3U; + } + + return buswidth; +} + +static int stm32_qspi_exec_op(const struct spi_mem_op *op) +{ + uint64_t timeout; + uint32_t ccr; + size_t addr_max; + uint8_t mode = QSPI_CCR_IND_WRITE; + int ret; + + VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addr:%llx len:%x\n", + __func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, + op->addr.val, op->data.nbytes); + + ret = stm32_qspi_wait_for_not_busy(); + if (ret != 0) { + return ret; + } + + addr_max = op->addr.val + op->data.nbytes + 1U; + + if ((op->data.dir == SPI_MEM_DATA_IN) && (op->data.nbytes != 0U)) { + if ((addr_max < stm32_qspi.mm_size) && + (op->addr.buswidth != 0U)) { + mode = QSPI_CCR_MEM_MAP; + } else { + mode = QSPI_CCR_IND_READ; + } + } + + if (op->data.nbytes != 0U) { + mmio_write_32(qspi_base() + QSPI_DLR, op->data.nbytes - 1U); + } + + ccr = mode << QSPI_CCR_FMODE_SHIFT; + ccr |= op->cmd.opcode; + ccr |= stm32_qspi_get_mode(op->cmd.buswidth) << QSPI_CCR_IMODE_SHIFT; + + if (op->addr.nbytes != 0U) { + ccr |= (op->addr.nbytes - 1U) << QSPI_CCR_ADSIZE_SHIFT; + ccr |= stm32_qspi_get_mode(op->addr.buswidth) << + QSPI_CCR_ADMODE_SHIFT; + } + + if ((op->dummy.buswidth != 0U) && (op->dummy.nbytes != 0U)) { + ccr |= (op->dummy.nbytes * 8U / op->dummy.buswidth) << + QSPI_CCR_DCYC_SHIFT; + } + + if (op->data.nbytes != 0U) { + ccr |= stm32_qspi_get_mode(op->data.buswidth) << + QSPI_CCR_DMODE_SHIFT; + } + + mmio_write_32(qspi_base() + QSPI_CCR, ccr); + + if ((op->addr.nbytes != 0U) && (mode != QSPI_CCR_MEM_MAP)) { + mmio_write_32(qspi_base() + QSPI_AR, op->addr.val); + } + + ret = stm32_qspi_tx(op, mode); + + /* + * Abort in: + * - Error case. + * - Memory mapped read: prefetching must be stopped if we read the last + * byte of device (device size - fifo size). If device size is not + * known then prefetching is always stopped. + */ + if ((ret != 0) || (mode == QSPI_CCR_MEM_MAP)) { + goto abort; + } + + /* Wait end of TX in indirect mode */ + ret = stm32_qspi_wait_cmd(op); + if (ret != 0) { + goto abort; + } + + return 0; + +abort: + mmio_setbits_32(qspi_base() + QSPI_CR, QSPI_CR_ABORT); + + /* Wait clear of abort bit by hardware */ + timeout = timeout_init_us(QSPI_ABT_TIMEOUT_US); + while ((mmio_read_32(qspi_base() + QSPI_CR) & QSPI_CR_ABORT) != 0U) { + if (timeout_elapsed(timeout)) { + ret = -ETIMEDOUT; + break; + } + } + + mmio_write_32(qspi_base() + QSPI_FCR, QSPI_FCR_CTCF); + + if (ret != 0) { + ERROR("%s: exec op error\n", __func__); + } + + return ret; +} + +static int stm32_qspi_claim_bus(unsigned int cs) +{ + uint32_t cr; + + if (cs >= QSPI_MAX_CHIP) { + return -ENODEV; + } + + /* Set chip select and enable the controller */ + cr = QSPI_CR_EN; + if (cs == 1U) { + cr |= QSPI_CR_FSEL; + } + + mmio_clrsetbits_32(qspi_base() + QSPI_CR, QSPI_CR_FSEL, cr); + + return 0; +} + +static void stm32_qspi_release_bus(void) +{ + mmio_clrbits_32(qspi_base() + QSPI_CR, QSPI_CR_EN); +} + +static int stm32_qspi_set_speed(unsigned int hz) +{ + unsigned long qspi_clk = stm32mp_clk_get_rate(stm32_qspi.clock_id); + uint32_t prescaler = UINT8_MAX; + uint32_t csht; + int ret; + + if (qspi_clk == 0U) { + return -EINVAL; + } + + if (hz > 0U) { + prescaler = div_round_up(qspi_clk, hz) - 1U; + if (prescaler > UINT8_MAX) { + prescaler = UINT8_MAX; + } + } + + csht = div_round_up((5U * qspi_clk) / (prescaler + 1U), FREQ_100MHZ); + csht = ((csht - 1U) << QSPI_DCR_CSHT_SHIFT) & QSPI_DCR_CSHT_MASK; + + ret = stm32_qspi_wait_for_not_busy(); + if (ret != 0) { + return ret; + } + + mmio_clrsetbits_32(qspi_base() + QSPI_CR, QSPI_CR_PRESCALER_MASK, + prescaler << QSPI_CR_PRESCALER_SHIFT); + + mmio_clrsetbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CSHT_MASK, csht); + + VERBOSE("%s: speed=%lu\n", __func__, qspi_clk / (prescaler + 1U)); + + return 0; +} + +static int stm32_qspi_set_mode(unsigned int mode) +{ + int ret; + + ret = stm32_qspi_wait_for_not_busy(); + if (ret != 0) { + return ret; + } + + if ((mode & SPI_CS_HIGH) != 0U) { + return -ENODEV; + } + + if (((mode & SPI_CPHA) != 0U) && ((mode & SPI_CPOL) != 0U)) { + mmio_setbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CKMODE); + } else if (((mode & SPI_CPHA) == 0U) && ((mode & SPI_CPOL) == 0U)) { + mmio_clrbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CKMODE); + } else { + return -ENODEV; + } + + VERBOSE("%s: mode=0x%x\n", __func__, mode); + + if ((mode & SPI_RX_QUAD) != 0U) { + VERBOSE("rx: quad\n"); + } else if ((mode & SPI_RX_DUAL) != 0U) { + VERBOSE("rx: dual\n"); + } else { + VERBOSE("rx: single\n"); + } + + if ((mode & SPI_TX_QUAD) != 0U) { + VERBOSE("tx: quad\n"); + } else if ((mode & SPI_TX_DUAL) != 0U) { + VERBOSE("tx: dual\n"); + } else { + VERBOSE("tx: single\n"); + } + + return 0; +} + +static const struct spi_bus_ops stm32_qspi_bus_ops = { + .claim_bus = stm32_qspi_claim_bus, + .release_bus = stm32_qspi_release_bus, + .set_speed = stm32_qspi_set_speed, + .set_mode = stm32_qspi_set_mode, + .exec_op = stm32_qspi_exec_op, +}; + +int stm32_qspi_init(void) +{ + size_t size; + int qspi_node; + struct dt_node_info info; + void *fdt = NULL; + int ret; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + qspi_node = dt_get_node(&info, -1, DT_QSPI_COMPAT); + if (qspi_node < 0) { + ERROR("No QSPI ctrl found\n"); + return -FDT_ERR_NOTFOUND; + } + + if (info.status == DT_DISABLED) { + return -FDT_ERR_NOTFOUND; + } + + ret = fdt_get_reg_props_by_name(qspi_node, "qspi", + &stm32_qspi.reg_base, &size); + if (ret != 0) { + return ret; + } + + ret = fdt_get_reg_props_by_name(qspi_node, "qspi_mm", + &stm32_qspi.mm_base, + &stm32_qspi.mm_size); + if (ret != 0) { + return ret; + } + + if (dt_set_pinctrl_config(qspi_node) != 0) { + return -FDT_ERR_BADVALUE; + } + + if ((info.clock < 0) || (info.reset < 0)) { + return -FDT_ERR_BADVALUE; + } + + stm32_qspi.clock_id = (unsigned long)info.clock; + stm32_qspi.reset_id = (unsigned int)info.reset; + + stm32mp_clk_enable(stm32_qspi.clock_id); + + if (stm32mp_reset_assert_to(stm32_qspi.reset_id, TIMEOUT_US_1MS)) { + panic(); + } + if (stm32mp_reset_deassert_to(stm32_qspi.reset_id, TIMEOUT_US_1MS)) { + panic(); + } + + mmio_write_32(qspi_base() + QSPI_CR, QSPI_CR_SSHIFT); + mmio_write_32(qspi_base() + QSPI_DCR, QSPI_DCR_FSIZE_MASK); + + return spi_mem_init_slave(fdt, qspi_node, &stm32_qspi_bus_ops); +}; diff --git a/drivers/st/tamper/stm32_tamp.c b/drivers/st/tamper/stm32_tamp.c new file mode 100644 index 000000000..216e324d5 --- /dev/null +++ b/drivers/st/tamper/stm32_tamp.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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) { + if (int_list[tamp].func != NULL) { + int_list[tamp].func(int_id); + } + + mmio_setbits_32(stm32_tamp.base + STM32_TAMP_SCR, + BIT(int_id + 16U)); + it &= ~BIT(int_id + 16U); + } + 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 */ + stm32mp_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 (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 000000000..a4e178ec4 --- /dev/null +++ b/drivers/st/timer/stm32_timer.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 + +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) +{ + stm32mp_clk_enable(timer->clk); + + timer->freq = stm32mp_clk_timer_get_rate(timer->clk); + + if (timer->freq < TIM_MIN_FREQ_CALIB) { + WARN("Timer is not accurate enough for calibration\n"); + stm32mp_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); + } + + stm32mp_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 = 0U; + int twice = 0; + + if (stm32_timer_config(timer) < 0) { + return 0U; + } + + stm32mp_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); + + while ((twice < 2) || (old_counter != counter)) { + 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); + twice++; + } + +out: + stm32mp_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 = + stm32mp_clk_timer_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 = + stm32mp_clk_timer_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 ca3c1f618..0fabcc82e 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,14 +204,17 @@ endfunc console_stm32_core_getc * * In : r0 - console base address * Out : return -1 on error else return 0. - * Clobber list : r0, r1 + * Clobber list : r0, r1, r2 * --------------------------------------------------------------- */ func console_stm32_core_flush cmp r0, #0 beq flush_error /* Check Transmit Data Register Empty */ + mov r2, #USART_TIMEOUT txe_loop_3: + subs r2, r2, #1 + beq flush_error ldr r1, [r0, #USART_ISR] tst r1, #USART_ISR_TXE beq txe_loop_3 diff --git a/drivers/st/uart/io_programmer_uart.c b/drivers/st/uart/io_programmer_uart.c new file mode 100644 index 000000000..6d024e158 --- /dev/null +++ b/drivers/st/uart/io_programmer_uart.c @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2015-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* USART bootloader protocol version V4.0*/ +#define USART_BL_VERSION 0x40 + +#define UART_ISR_ERRORS (USART_ISR_ORE | USART_ISR_NE | \ + USART_ISR_FE | USART_ISR_PE) + +/* array of supported command */ +static const uint8_t command_tab[] = { + GET_CMD_COMMAND, + GET_VER_COMMAND, + GET_ID_COMMAND, + PHASE_COMMAND, + START_COMMAND, + DOWNLOAD_COMMAND +}; + +static uint8_t header_buffer[512] __aligned(4); +static int32_t header_length_read; + +/* UART device functions */ +static int uart_dev_open(const uintptr_t init_params, io_dev_info_t **dev_info); +static int uart_block_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int uart_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params); +static int uart_block_read(io_entity_t *entity, uintptr_t buffer, + size_t length, size_t *length_read); +static int uart_block_close(io_entity_t *entity); +static int uart_dev_close(io_dev_info_t *dev_info); +static int uart_block_len(io_entity_t *entity, size_t *length); +static io_type_t device_type_uart(void); + +/* variables */ +static const io_dev_connector_t uart_dev_connector = { + .dev_open = uart_dev_open +}; + +static const io_dev_funcs_t uart_dev_funcs = { + .type = device_type_uart, + .open = uart_block_open, + .seek = NULL, + .size = uart_block_len, + .read = uart_block_read, + .write = NULL, + .close = uart_block_close, + .dev_init = uart_dev_init, + .dev_close = uart_dev_close, +}; + +static const io_dev_info_t uart_dev_info = { + .funcs = &uart_dev_funcs, + .info = (uintptr_t)0, +}; + +static UART_HandleTypeDef *uart_handle_programmer; + +/* Identify the device type as memmap */ +static io_type_t device_type_uart(void) +{ + return IO_TYPE_UART; +} + +static int uart_write_byte(uint8_t byte) +{ + if (HAL_UART_GetState(uart_handle_programmer) == HAL_UART_STATE_RESET) + uart_handle_programmer->gState = HAL_UART_STATE_READY; + return HAL_UART_Transmit(uart_handle_programmer, (uint8_t *)&byte, 1, + PROGRAMMER_TIMEOUT); +} + +static int uart_write_uint32(uint32_t value) +{ + if (HAL_UART_GetState(uart_handle_programmer) == HAL_UART_STATE_RESET) + uart_handle_programmer->gState = HAL_UART_STATE_READY; + return HAL_UART_Transmit(uart_handle_programmer, (uint8_t *)&value, 4, + PROGRAMMER_TIMEOUT); +} + +static int uart_read_byte(uint8_t *byte) +{ + HAL_StatusTypeDef ret; + + if (HAL_UART_GetState(uart_handle_programmer) == HAL_UART_STATE_RESET) + uart_handle_programmer->RxState = HAL_UART_STATE_READY; + + ret = HAL_UART_Receive(uart_handle_programmer, byte, 1, + PROGRAMMER_TIMEOUT); + + if (ret || (uart_handle_programmer->Instance->ISReg & UART_ISR_ERRORS)) + return -EIO; + + return 0; +} + +static void uart_flush_rx_fifo(uint32_t timeout) +{ + uint8_t byte = 0; + + /* Clear all errors */ + __HAL_UART_CLEAR_FLAG(uart_handle_programmer, UART_ISR_ERRORS); + + while ((__HAL_UART_GET_FLAG(uart_handle_programmer, UART_FLAG_RXNE) != + RESET) && timeout) { + timeout--; + uart_read_byte(&byte); + } +} + +/* Open a connection to the uart device */ +static int uart_dev_open(const uintptr_t init_params, io_dev_info_t **dev_info) +{ + int result = 0; + + assert(dev_info); + *dev_info = (io_dev_info_t *)&uart_dev_info; + + uart_handle_programmer = (UART_HandleTypeDef *)init_params; + + /* Init UART to enable FIFO mode */ + if (HAL_UART_Init(uart_handle_programmer) != HAL_OK) { + return -EIO; + } + + uart_flush_rx_fifo(PROGRAMMER_TIMEOUT); + + uart_write_byte(NACK_BYTE); + + return result; +} + +static int uart_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params) +{ + return 0; +} + +/* Close a connection to the uart device */ +static int uart_dev_close(io_dev_info_t *dev_info) +{ + return 0; +} + +/* Return the size of a file on the uart device */ +static int uart_block_len(io_entity_t *entity, size_t *length) +{ + boot_api_image_header_t *header = + (boot_api_image_header_t *)&header_buffer[0]; + + assert(entity); + assert(length); + + header_length_read = 0; + header->magic = 0; + + uart_block_read(entity, (uintptr_t)&header_buffer[0], + sizeof(boot_api_image_header_t), + (size_t *)&header_length_read); + + if (header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) + return -EIO; + + if (header->image_length > current_phase.max_size) + return -EIO; + + *length = header->image_length; + + INFO("binary size 0x%x\n", header->image_length); + + return 0; +} + +/* Open a file on the uart device */ +static int uart_block_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity) +{ + int result = -EIO; + const struct stm32image_part_info *partition_spec = + (struct stm32image_part_info *)spec; + uint32_t length = 0; + uint32_t layout_length = 0; + + /* Use PHASE_FSBL1 like init value*/ + if (current_phase.phase_id == PHASE_FSBL1) { + assert(partition_spec); + assert(entity); + + current_phase.current_packet = 0; + + if (!strcmp(partition_spec->name, BL33_IMAGE_NAME)) { + /* read flashlayout first for U-boot */ + current_phase.phase_id = PHASE_FLASHLAYOUT; + current_phase.max_size = FLASHLAYOUT_LIMIT; + current_phase.keep_header = 1; + uart_block_len(entity, &layout_length); + uart_block_read(entity, FLASHLAYOUT_BASE, + layout_length, + &length); + + flush_dcache_range((unsigned long)FLASHLAYOUT_BASE, + layout_length + + sizeof(boot_api_image_header_t)); + + current_phase.current_packet = 0; + current_phase.phase_id = PHASE_SSBL; + current_phase.max_size = dt_get_ddr_size(); + current_phase.keep_header = 0; + } + entity->info = (uintptr_t)¤t_phase; + result = 0; + } else { + WARN("A UART device is already active. Close first.\n"); + result = -EIO; + } + return result; +} + +static int uart_receive_command(uint8_t *command) +{ + uint8_t byte = 0; + uint8_t xor = 0; + uint8_t counter = 0, found = 0; + int ret; + + /* check command */ + ret = uart_read_byte(&byte); + if (ret) + return ret; + + for (counter = 0; counter < sizeof(command_tab); counter++) { + if (command_tab[counter] == byte) { + found = 1; + break; + } + } + + if (found) { + ret = uart_read_byte(&xor); + if (ret) + return ret; + if ((byte ^ xor) != 0xFF) { + WARN("UART: Command XOR check fail (byte=0x%x, xor=0x%x)\n", + byte, xor); + return -EPROTO; + } + } else { + if (byte != INIT_BYTE) { + WARN("UART: Command unknown (byte=0x%x)\n", byte); + return -EPROTO; + } + } + + *command = byte; + + return 0; +} + +static int get_cmd_command(void) +{ + uint8_t counter = 0x0; + + uart_write_byte(sizeof(command_tab)); + uart_write_byte(USART_BL_VERSION); + + for (counter = 0; counter < sizeof(command_tab); counter++) + uart_write_byte(command_tab[counter]); + + return 0; +} + +static int get_version_command(void) +{ + uart_write_byte(STM32_TF_VERSION); + + return 0; +} + +static int get_id_command(void) +{ + /* Send Device IDCode */ + uart_write_byte(0x1); + uart_write_byte(DEVICE_ID_BYTE1); + uart_write_byte(DEVICE_ID_BYTE2); + + return 0; +} + +static int uart_send_phase(uint32_t address) +{ + uart_write_byte(0x05); /* length of data - 1 */ + /* Send the ID of next partition */ + uart_write_byte(current_phase.phase_id); /* partition ID */ + + uart_write_uint32(address); /* destination address */ + uart_write_byte(0x00); /* length of extra data */ + + return 0; +} + +static int uart_download_part(uint8_t *buffer, uint32_t *length_read) +{ + uint8_t byte = 0; + uint8_t xor = 0; + uint8_t operation = 0; + uint32_t packet_number = 0; + uint8_t packet_size = 0; + int i = 0; + volatile uint8_t *ptr = (uint8_t *)buffer; + + /* get operation number */ + if (uart_read_byte(&operation)) + return -EIO; + xor = operation; + + /* get packet Number */ + for (i = 3, byte = 0; i > 0; i--) { + if (uart_read_byte(&byte)) + return -EIO; + xor ^= byte; + packet_number = (packet_number << 8) | byte; + } + + if (packet_number != current_phase.current_packet) { + WARN("UART: Bad packet number receive: %i\n", packet_number); + return -EPROTO; + } + + /* checksum */ + if (uart_read_byte(&byte)) + return -EIO; + + if (xor != byte) { + WARN("UART: Download Command header checksum fail: calculated xor: %i, received xor:%i\n", + xor, byte); + return -EPROTO; + } + + uart_write_byte(ACK_BYTE); + + if (uart_read_byte(&packet_size)) + return -EIO; + + xor = packet_size; + + for (i = packet_size; i >= 0; i--) { + /* Reload watchdog, once every 8 loops */ + if (i % 8) + stm32_iwdg_refresh(); + + if (uart_read_byte(&byte)) + return -EIO; + *(volatile uint8_t *)ptr = byte; + xor ^= byte; + ptr++; + } + + /* checksum */ + if (uart_read_byte(&byte)) + return -EIO; + + if (xor != byte) { + WARN("UART: Download Command data checksum fail: calculated xor: 0x%x, received xor:0x%x\n", + xor, byte); + return -EPROTO; + } + + current_phase.current_packet++; + *length_read = (uint32_t)packet_size + 1; + + return 0; +} + +static int uart_start_cmd(boot_api_image_header_t *header, uintptr_t buffer) +{ + uint8_t byte = 0; + uint8_t xor = 0; + int i = 0; + uint32_t start_address = 0; + int result = 0; + + /* get address */ + for (i = 4; i > 0; i--) { + if (uart_read_byte(&byte)) + return -EIO; + xor ^= byte; + start_address = (start_address << 8) | byte; + } + + /* checksum */ + if (uart_read_byte(&byte)) + return -EIO; + + if (xor != byte) { + WARN("UART: Start command checksum fail: calculated xor: %i, received xor:%i\n", + xor, byte); + return -EPROTO; + } + + switch (current_phase.phase_id) { + case PHASE_FLASHLAYOUT: + result = stm32mp_check_header(header, buffer + + sizeof(boot_api_image_header_t)); + if (result) + return result; + break; + + case PHASE_SSBL: + if (start_address != BL33_BASE) { + VERBOSE("BL33 address provided: 0x%x, using: 0x%x\n", + start_address, BL33_BASE); + } + + return stm32mp_check_header(header, buffer); + + default: + ERROR("Invalid phase ID : %i\n", current_phase.phase_id); + return -EINVAL; + } + + return 0; +} + +/* Read data from the uart device */ +static int uart_block_read(io_entity_t *entity, uintptr_t buffer, + size_t length, size_t *length_read) +{ + uint32_t read_length = 0; + uint32_t total_length = 0; + uint32_t ptr_offset = 0; + uint8_t command = 0; + uint8_t all_commands_done = 0; + boot_api_image_header_t *header = + (boot_api_image_header_t *)&header_buffer[0]; + + if (header_length_read && current_phase.keep_header) { + memcpy((uint8_t *)buffer, (uint8_t *)&header_buffer[0], + header_length_read); + ptr_offset += header_length_read; + } else if (header_length_read && + ((header_length_read - + sizeof(boot_api_image_header_t)) > 0)) { +#if TRUSTED_BOARD_BOOT + stm32mp_save_loaded_header(header_buffer); +#endif + memcpy((uint8_t *)buffer, + (uint8_t *) + &header_buffer[sizeof(boot_api_image_header_t)], + header_length_read - + sizeof(boot_api_image_header_t)); + ptr_offset += header_length_read - + sizeof(boot_api_image_header_t); + } + + while (!all_commands_done) { + int result; + + /* Reload watchdog */ + stm32_iwdg_refresh(); + + result = uart_receive_command(&command); + if (result) { + if (result == -EIO) { + WARN("UART: device error on command receive\n"); + } + + mdelay(2); + + uart_flush_rx_fifo(PROGRAMMER_TIMEOUT); + uart_write_byte(NACK_BYTE); + continue; + } + + /* Send ack */ + uart_write_byte(ACK_BYTE); + + switch (command) { + case INIT_BYTE: + /* Nothing to do */ + continue; + case GET_CMD_COMMAND: + result = get_cmd_command(); + break; + + case GET_VER_COMMAND: + result = get_version_command(); + break; + + case GET_ID_COMMAND: + result = get_id_command(); + break; + + case PHASE_COMMAND: + result = uart_send_phase((uint32_t)buffer); + break; + + case DOWNLOAD_COMMAND: + result = uart_download_part((uint8_t *)(buffer + + ptr_offset), + &read_length); + if (!result) { + ptr_offset += read_length; + total_length += read_length; + if ((total_length >= length) && + !header_length_read) { + /* read header only go out*/ + all_commands_done = 1; + } + } + + break; + + case START_COMMAND: + result = uart_start_cmd(header, buffer); + if (!result) + all_commands_done = 1; + + break; + + default: + /* Not supported command */ + WARN("UART: Unknown command\n"); + uart_flush_rx_fifo(PROGRAMMER_TIMEOUT); + uart_write_byte(NACK_BYTE); + continue; + } + if (!result) { + /* Send ack */ + uart_write_byte(ACK_BYTE); + } else if (result == -EPROTO) { + uart_flush_rx_fifo(0xFFFF); + uart_write_byte(NACK_BYTE); + } else { + uart_flush_rx_fifo(PROGRAMMER_TIMEOUT); + uart_write_byte(NACK_BYTE); + continue; + } + } + + *length_read = total_length; + + INFO("Read block in buffer 0x%lx size 0x%x phase ID %i\n", + buffer, length, current_phase.phase_id); + + return 0; +} + +/* Close a file on the uart device */ +static int uart_block_close(io_entity_t *entity) +{ + current_phase.phase_id = PHASE_FSBL1; + + return 0; +} + +/* Exported functions */ + +/* Register the uart driver with the IO abstraction */ +int register_io_dev_uart(const io_dev_connector_t **dev_con) +{ + int result; + + assert(dev_con); + + result = io_register_device(&uart_dev_info); + if (!result) + *dev_con = &uart_dev_connector; + + return result; +} diff --git a/drivers/st/uart/stm32mp1xx_hal_uart.c b/drivers/st/uart/stm32mp1xx_hal_uart.c new file mode 100644 index 000000000..70ccc378e --- /dev/null +++ b/drivers/st/uart/stm32mp1xx_hal_uart.c @@ -0,0 +1,801 @@ +/** + ****************************************************************************** + * @file stm32mp1xx_hal_uart.c + * @author MCD Application Team + * @version $VERSION$ + * @date $DATE$ + * @brief UART HAL module driver. + * This file provides firmware functions to manage the following + * functionalities of the Universal Asynchronous Receiver Transmitter Peripheral (UART). + * + Initialization and de-initialization functions + * + IO operation functions + * + Peripheral Control functions + * + * + * @verbatim + * =============================================================================== + * ##### How to use this driver ##### + * =============================================================================== + * [..] + * The UART HAL driver can be used as follows: + * + * (#) Declare a UART_HandleTypeDef handle structure (eg. UART_HandleTypeDef huart). + * (#) Initialize the UART low level resources by implementing the HAL_UART_MspInit() API: + * (++) Enable the USARTx interface clock. + * (++) UART pins configuration: + * (+++) Enable the clock for the UART GPIOs. + * (+++) Configure these UART pins as alternate function pull-up. + * (++) NVIC configuration if you need to use interrupt process (HAL_UART_Transmit_IT() + * and HAL_UART_Receive_IT() APIs): + * (+++) Configure the USARTx interrupt priority. + * (+++) Enable the NVIC USART IRQ handle. + * (++) UART interrupts handling: + * -@@- The specific UART interrupts (Transmission complete interrupt, + * RXNE interrupt and Error Interrupts) are managed using the macros + * __HAL_UART_ENABLE_IT() and __HAL_UART_DISABLE_IT() inside the transmit and receive processes. + * (++) DMA Configuration if you need to use DMA process (HAL_UART_Transmit_DMA() + * and HAL_UART_Receive_DMA() APIs): + * (+++) Declare a DMA handle structure for the Tx/Rx channel. + * (+++) Enable the DMAx interface clock. + * (+++) Configure the declared DMA handle structure with the required Tx/Rx parameters. + * (+++) Configure the DMA Tx/Rx channel. + * (+++) Associate the initialized DMA handle to the UART DMA Tx/Rx handle. + * (+++) Configure the priority and enable the NVIC for the transfer complete interrupt on the DMA Tx/Rx channel. + * + * (#) Program the Baud Rate, Word Length, Stop Bit, Parity, Hardware + * flow control and Mode (Receiver/Transmitter) in the huart handle Init structure. + * + * (#) If required, program UART advanced features (TX/RX pins swap, auto Baud rate detection,...) + * in the huart handle AdvancedInit structure. + * + * (#) For the UART asynchronous mode, initialize the UART registers by calling + * the HAL_UART_Init() API. + * + * (#) For the UART Half duplex mode, initialize the UART registers by calling + * the HAL_HalfDuplex_Init() API. + * + * (#) For the UART LIN (Local Interconnection Network) mode, initialize the UART registers + * by calling the HAL_LIN_Init() API. + * + * (#) For the UART Multiprocessor mode, initialize the UART registers + * by calling the HAL_MultiProcessor_Init() API. + * + * (#) For the UART RS485 Driver Enabled mode, initialize the UART registers + * by calling the HAL_RS485Ex_Init() API. + * + * [..] + * (@) These API's (HAL_UART_Init(), HAL_HalfDuplex_Init(), HAL_LIN_Init(), HAL_MultiProcessor_Init(), + * also configure the low level Hardware GPIO, CLOCK, CORTEX...etc) by + * calling the customized HAL_UART_MspInit() API. + * + * @endverbatim + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015-2017 STMicroelectronics

+ * + * SPDX-License-Identifier: BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include +#include + +#include + +#include +#include +#include + +/** @addtogroup STM32MP1xx_HAL_Driver + * @{ + */ + +/** @defgroup UART UART + * @brief HAL UART module driver + * @{ + */ + +#ifdef HAL_UART_MODULE_ENABLED + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/** @defgroup UART_Private_Constants UART Private Constants + * @{ + */ +#define UART_CR1_FIELDS \ + ((uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | \ + USART_CR1_TE | USART_CR1_RE | USART_CR1_OVER8 | \ + USART_CR1_FIFOEN)) + +#define USART_CR3_FIELDS \ + ((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE | USART_CR3_ONEBIT | \ + USART_CR3_TXFTCFG | USART_CR3_RXFTCFG)) + + +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ + +/** @defgroup UART_Exported_Functions UART Exported Functions + * @{ + */ + +/** @defgroup UART_Exported_Functions_Group1 Initialization and de-initialization functions + * @brief Initialization and Configuration functions + * + * @verbatim + * =============================================================================== + * ##### Initialization and Configuration functions ##### + * =============================================================================== + * [..] + * This subsection provides a set of functions allowing to initialize the USARTx or the UARTy + * in asynchronous mode. + * (+) For the asynchronous mode the parameters below can be configured: + * (++) Baud Rate + * (++) Word Length + * (++) Stop Bit + * (++) Parity: If the parity is enabled, then the MSB bit of the data written + * in the data register is transmitted but is changed by the parity bit. + * (++) Hardware flow control + * (++) Receiver/transmitter modes + * (++) Over Sampling Method + * (++) One-Bit Sampling Method + * (+) For the asynchronous mode, the following advanced features can be configured as well: + * (++) TX and/or RX pin level inversion + * (++) data logical level inversion + * (++) RX and TX pins swap + * (++) RX overrun detection disabling + * (++) DMA disabling on RX error + * (++) MSB first on communication line + * (++) auto Baud rate detection + * [..] + * The HAL_UART_Init(), HAL_HalfDuplex_Init(), HAL_LIN_Init()and HAL_MultiProcessor_Init()API + * follow respectively the UART asynchronous, UART Half duplex, UART LIN mode + * and UART multiprocessor mode configuration procedures (details for the procedures + * are available in reference manual). + * + * @endverbatim + * + * Depending on the frame length defined by the M1 and M0 bits (7-bit, + * 8-bit or 9-bit), the possible UART formats are listed in the + * following table. + * + * Table 1. UART frame format. + * +-----------------------------------------------------------------------+ + * | M1 bit | M0 bit | PCE bit | UART frame | + * |---------|---------|-----------|---------------------------------------| + * | 0 | 0 | 0 | | SB | 8 bit data | STB | | + * |---------|---------|-----------|---------------------------------------| + * | 0 | 0 | 1 | | SB | 7 bit data | PB | STB | | + * |---------|---------|-----------|---------------------------------------| + * | 0 | 1 | 0 | | SB | 9 bit data | STB | | + * |---------|---------|-----------|---------------------------------------| + * | 0 | 1 | 1 | | SB | 8 bit data | PB | STB | | + * |---------|---------|-----------|---------------------------------------| + * | 1 | 0 | 0 | | SB | 7 bit data | STB | | + * |---------|---------|-----------|---------------------------------------| + * | 1 | 0 | 1 | | SB | 6 bit data | PB | STB | | + * +-----------------------------------------------------------------------+ + * + * @{ + */ + +/** + * @brief Initialize the UART mode according to the specified + * parameters in the UART_InitTypeDef and initialize the associated handle. + * @param huart: UART handle. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart) +{ + /* Check the UART handle allocation */ + if (!huart) + return HAL_ERROR; + + /* Check the parameters */ + if (huart->Init.HwFlowCtl != UART_HWCONTROL_NONE) + assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance)); + else + assert_param(IS_UART_INSTANCE(huart->Instance)); + + /* Allocate lock resource and initialize it */ + if (huart->gState == HAL_UART_STATE_RESET) + huart->Lock = HAL_UNLOCKED; + + huart->gState = HAL_UART_STATE_BUSY; + + /* Disable the Peripheral */ + __HAL_UART_DISABLE(huart); + + /* Set the UART Communication parameters */ + if (UART_SetConfig(huart) == HAL_ERROR) + return HAL_ERROR; + + if (huart->AdvancedInit.AdvFeatureInit != UART_ADVFEATURE_NO_INIT) + UART_AdvFeatureConfig(huart); + + /* In asynchronous mode, the following bits must be kept cleared: + - LINEN and CLKEN bits in the USART_CR2 register, + - SCEN, HDSEL and IREN bits in the USART_CR3 register.*/ + CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN)); + CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | + USART_CR3_IREN)); + + /* Enable the Peripheral */ + __HAL_UART_ENABLE(huart); + + /* TEACK and/or REACK to check before moving huart->gState and huart->RxState to Ready */ + return UART_CheckIdleState(huart); +} + + +/** + * @} + */ + +/** @defgroup UART_Exported_Functions_Group2 IO operation functions + * @brief UART Transmit/Receive functions + * + * @verbatim + * =============================================================================== + * ##### IO operation functions ##### + * =============================================================================== + * This subsection provides a set of functions allowing to manage the UART asynchronous + * and Half duplex data transfers. + * + * (#) There are two mode of transfer: + * (+) Blocking mode: The communication is performed in polling mode. + * The HAL status of all data processing is returned by the same function + * after finishing transfer. + * (+) Non-Blocking mode: The communication is performed using Interrupts + * or DMA, These API's return the HAL status. + * The end of the data processing will be indicated through the + * dedicated UART IRQ when using Interrupt mode or the DMA IRQ when + * using DMA mode. + * The HAL_UART_TxCpltCallback(), HAL_UART_RxCpltCallback() user callbacks + * will be executed respectively at the end of the transmit or Receive process + * The HAL_UART_ErrorCallback()user callback will be executed when a communication error is detected + * + * (#) Blocking mode API's are : + * (+) HAL_UART_Transmit() + * (+) HAL_UART_Receive() + * + * (#) Non-Blocking mode API's with Interrupt are : + * (+) HAL_UART_Transmit_IT() + * (+) HAL_UART_Receive_IT() + * (+) HAL_UART_IRQHandler() + * + * (#) Non-Blocking mode API's with DMA are : + * (+) HAL_UART_Transmit_DMA() + * (+) HAL_UART_Receive_DMA() + * (+) HAL_UART_DMAPause() + * (+) HAL_UART_DMAResume() + * (+) HAL_UART_DMAStop() + * + * (#) A set of Transfer Complete Callbacks are provided in Non_Blocking mode: + * (+) HAL_UART_TxHalfCpltCallback() + * (+) HAL_UART_TxCpltCallback() + * (+) HAL_UART_RxHalfCpltCallback() + * (+) HAL_UART_RxCpltCallback() + * (+) HAL_UART_ErrorCallback() + * + * -@- In the Half duplex communication, it is forbidden to run the transmit + * and receive process in parallel, the UART state HAL_UART_STATE_BUSY_TX_RX can't be useful. + * + * @endverbatim + * @{ + */ + +/** + * @brief Send an amount of data in blocking mode. + * @param huart: UART handle. + * @param pData: Pointer to data buffer. + * @param Size: Amount of data to be sent. + * @param Timeout: Timeout duration. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, + uint16_t Size, uint32_t Timeout) +{ + uint16_t *tmp; + uint32_t tickstart = 0U; + HAL_StatusTypeDef ret = HAL_OK; + + /* Check that a Tx process is not already ongoing */ + if (huart->gState != HAL_UART_STATE_READY) + return HAL_BUSY; + + if ((!pData) || (Size == 0U)) + return HAL_ERROR; + + /* Process Locked */ + __HAL_LOCK(huart); + + huart->ErrorCode = HAL_UART_ERROR_NONE; + huart->gState = HAL_UART_STATE_BUSY_TX; + + /* Init tickstart for timeout management*/ + tickstart = HAL_GetTick(); + + huart->TxXferSize = Size; + huart->TxXferCount = Size; + while (huart->TxXferCount > 0U) { + huart->TxXferCount--; + if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, + tickstart, Timeout) != HAL_OK) { + ret = HAL_TIMEOUT; + goto end; + } + + if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && + (huart->Init.Parity == UART_PARITY_NONE)) { + tmp = (uint16_t *)pData; + huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU); + pData += 2U; + } else { + huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU); + } + } + + if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, + Timeout) != HAL_OK) { + ret = HAL_TIMEOUT; + goto end; + } + +end: + /* At end of Tx process, restore huart->gState to Ready */ + huart->gState = HAL_UART_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK(huart); + + return ret; +} + +/** + * @brief Receive an amount of data in blocking mode. + * @param huart: UART handle. + * @param pData: pointer to data buffer. + * @param Size: amount of data to be received. + * @param Timeout: Timeout duration. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, + uint16_t Size, uint32_t Timeout) +{ + uint16_t *tmp; + uint16_t uhMask; + uint32_t tickstart = 0; + HAL_StatusTypeDef ret = HAL_OK; + + /* Check that a Rx process is not already ongoing */ + if (huart->RxState != HAL_UART_STATE_READY) + return HAL_BUSY; + + if ((!pData) || (Size == 0U)) + return HAL_ERROR; + + /* Process Locked */ + __HAL_LOCK(huart); + + huart->ErrorCode = HAL_UART_ERROR_NONE; + huart->RxState = HAL_UART_STATE_BUSY_RX; + + /* Init tickstart for timeout management*/ + tickstart = HAL_GetTick(); + + huart->RxXferSize = Size; + huart->RxXferCount = Size; + + /* Computation of UART mask to apply to RDR register */ + UART_MASK_COMPUTATION(huart); + uhMask = huart->Mask; + + /* as long as data have to be received */ + while (huart->RxXferCount > 0U) { + huart->RxXferCount--; + if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, + tickstart, Timeout) != HAL_OK) { + ret = HAL_TIMEOUT; + goto end; + } + + if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && + (huart->Init.Parity == UART_PARITY_NONE)) { + tmp = (uint16_t *)pData; + *tmp = (uint16_t)(huart->Instance->RDR & uhMask); + pData += 2U; + } else { + *pData++ = (uint8_t)(huart->Instance->RDR & + (uint8_t)uhMask); + } + } + +end: + /* At end of Rx process, restore huart->RxState to Ready */ + huart->RxState = HAL_UART_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK(huart); + + return ret; +} + + +/** + * @} + */ + +/** @defgroup UART_Exported_Functions_Group3 Peripheral Control functions + * @brief UART control functions + * +@verbatim + =============================================================================== + ##### Peripheral Control functions ##### + =============================================================================== + [..] + This subsection provides a set of functions allowing to control the UART. + (+) HAL_MultiProcessor_EnableMuteMode() API enables mute mode + (+) HAL_MultiProcessor_DisableMuteMode() API disables mute mode + (+) HAL_MultiProcessor_EnterMuteMode() API enters mute mode + (+) HAL_MultiProcessor_EnableMuteMode() API enables mute mode + (+) UART_SetConfig() API configures the UART peripheral + (+) UART_AdvFeatureConfig() API optionally configures the UART advanced features + (+) UART_CheckIdleState() API ensures that TEACK and/or REACK are set after initialization + (+) UART_Wakeup_AddressConfig() API configures the wake-up from stop mode parameters + (+) HAL_HalfDuplex_EnableTransmitter() API disables receiver and enables transmitter + (+) HAL_HalfDuplex_EnableReceiver() API disables transmitter and enables receiver + (+) HAL_LIN_SendBreak() API transmits the break characters +@endverbatim + * @{ + */ + + + +/** + * @} + */ + +/** @defgroup UART_Exported_Functions_Group4 Peripheral State and Error functions + * @brief UART Peripheral State functions + * +@verbatim + ============================================================================== + ##### Peripheral State and Error functions ##### + ============================================================================== + [..] + This subsection provides functions allowing to : + (+) Return the UART handle state. + (+) Return the UART handle error code + +@endverbatim + * @{ + */ + +/** + * @brief Return the UART handle state. + * @param huart Pointer to a UART_HandleTypeDef structure that contains + * the configuration information for the specified UART. + * @retval HAL state + */ +HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart) +{ + uint32_t temp1 = huart->gState; + uint32_t temp2 = huart->RxState; + + return (HAL_UART_StateTypeDef)(temp1 | temp2); +} + +/** + * @brief Return the UART handle error code. + * @param huart Pointer to a UART_HandleTypeDef structure that contains + * the configuration information for the specified UART. + * @retval UART Error Code +*/ +uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart) +{ + return huart->ErrorCode; +} + +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup UART_Private_Functions UART Private Functions + * @{ + */ + +static unsigned long uart_get_clock_freq(UART_HandleTypeDef *huart) +{ + return fdt_get_uart_clock_freq((uintptr_t)huart->Instance); +} + +/** + * @brief Configure the UART peripheral. + * @param huart: UART handle. + * @retval HAL status + */ +HAL_StatusTypeDef UART_SetConfig(UART_HandleTypeDef *huart) +{ + uint32_t tmpreg; + unsigned long clockfreq; + + /*---------------------- 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 huart->Init.WordLength value + * set PCE and PS bits according to huart->Init.Parity value + * set TE and RE bits according to huart->Init.Mode value + * set OVER8 bit according to huart->Init.OverSampling value + */ + tmpreg = (uint32_t)(huart->Init.WordLength | + huart->Init.Parity | + huart->Init.Mode | + huart->Init.OverSampling); + tmpreg |= (uint32_t)huart->Init.FIFOMode; + MODIFY_REG(huart->Instance->CR1, UART_CR1_FIELDS, tmpreg); + + /*--------------------- USART CR2 Configuration --------------------- + * Configure the UART Stop Bits: Set STOP[13:12] bits according + * to huart->Init.StopBits value + */ + MODIFY_REG(huart->Instance->CR2, USART_CR2_STOP, huart->Init.StopBits); + + /*--------------------- USART CR3 Configuration --------------------- + * Configure + * - UART HardWare Flow Control: set CTSE and RTSE bits according + * to huart->Init.HwFlowCtl value + * - one-bit sampling method versus three samples' majority rule + * according to huart->Init.OneBitSampling (not applicable to LPUART) + * - set TXFTCFG bit according to husart->Init.TXFIFOThreshold value + * - set RXFTCFG bit according to husart->Init.RXFIFOThreshold value + */ + tmpreg = (uint32_t)huart->Init.HwFlowCtl; + + tmpreg |= huart->Init.OneBitSampling; + + if (huart->Init.FIFOMode == UART_FIFOMODE_ENABLE) + tmpreg |= ((uint32_t)huart->Init.TXFIFOThreshold | + (uint32_t)huart->Init.RXFIFOThreshold); + + MODIFY_REG(huart->Instance->CR3, USART_CR3_FIELDS, tmpreg); + + /*--------------------- USART PRESC Configuration ------------------- + * Configure + * - UART Clock Prescaler : set PRESCALER according to + * huart->Init.Prescaler value + */ + MODIFY_REG(huart->Instance->PRESC, USART_PRESC_PRESCALER, + huart->Init.Prescaler); + + /*-------------------------- USART BRR Configuration -----------------------*/ + + clockfreq = uart_get_clock_freq(huart); + if (!clockfreq) + return HAL_ERROR; + + if (huart->Init.OverSampling == UART_OVERSAMPLING_8) { + uint16_t usartdiv = + (uint16_t)uart_div_sampling8(clockfreq, + huart->Init.BaudRate, + huart->Init.Prescaler); + + uint16_t brrtemp = usartdiv & 0xFFF0U; + + brrtemp |= (uint16_t)((usartdiv & (uint16_t)0x000FU) >> 1U); + huart->Instance->BRR = brrtemp; + } else { + huart->Instance->BRR = + (uint16_t)uart_div_sampling16(clockfreq, + huart->Init.BaudRate, + huart->Init.Prescaler); + } + + return HAL_OK; +} + +/** + * @brief Configure the UART peripheral advanced features. + * @param huart: UART handle. + * @retval None + */ +void UART_AdvFeatureConfig(UART_HandleTypeDef *huart) +{ + /* Check whether the set of advanced features to configure is properly set */ + assert_param( + IS_UART_ADVFEATURE_INIT(huart->AdvancedInit.AdvFeatureInit)); + + /* if required, configure TX pin active level inversion */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_TXINVERT_INIT)) { + assert_param( + IS_UART_ADVFEATURE_TXINV( + huart->AdvancedInit.TxPinLevelInvert)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_TXINV, + huart->AdvancedInit.TxPinLevelInvert); + } + + /* if required, configure RX pin active level inversion */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_RXINVERT_INIT)) { + assert_param( + IS_UART_ADVFEATURE_RXINV( + huart->AdvancedInit.RxPinLevelInvert)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_RXINV, + huart->AdvancedInit.RxPinLevelInvert); + } + + /* if required, configure data inversion */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_DATAINVERT_INIT)) { + assert_param( + IS_UART_ADVFEATURE_DATAINV( + huart->AdvancedInit.DataInvert)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_DATAINV, + huart->AdvancedInit.DataInvert); + } + + /* if required, configure RX/TX pins swap */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_SWAP_INIT)) { + assert_param(IS_UART_ADVFEATURE_SWAP(huart->AdvancedInit.Swap)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_SWAP, + huart->AdvancedInit.Swap); + } + + /* if required, configure RX overrun detection disabling */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_RXOVERRUNDISABLE_INIT)) { + assert_param(IS_UART_OVERRUN( + huart->AdvancedInit.OverrunDisable)); + MODIFY_REG(huart->Instance->CR3, USART_CR3_OVRDIS, + huart->AdvancedInit.OverrunDisable); + } + + /* if required, configure DMA disabling on reception error */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_DMADISABLEONERROR_INIT)) { + assert_param( + IS_UART_ADVFEATURE_DMAONRXERROR( + huart->AdvancedInit.DMADisableonRxError)); + MODIFY_REG(huart->Instance->CR3, USART_CR3_DDRE, + huart->AdvancedInit.DMADisableonRxError); + } + + /* if required, configure auto Baud rate detection scheme */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_AUTOBAUDRATE_INIT)) { + assert_param( + IS_USART_AUTOBAUDRATE_DETECTION_INSTANCE( + huart->Instance)); + assert_param( + IS_UART_ADVFEATURE_AUTOBAUDRATE( + huart->AdvancedInit.AutoBaudRateEnable)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_ABREN, + huart->AdvancedInit.AutoBaudRateEnable); + /* set auto Baudrate detection parameters if detection is enabled */ + if (huart->AdvancedInit.AutoBaudRateEnable == + UART_ADVFEATURE_AUTOBAUDRATE_ENABLE) { + assert_param( + IS_UART_ADVFEATURE_AUTOBAUDRATEMODE( + huart->AdvancedInit.AutoBaudRateMode)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_ABRMODE, + huart->AdvancedInit.AutoBaudRateMode); + } + } + + /* if required, configure MSB first on communication line */ + if (HAL_IS_BIT_SET(huart->AdvancedInit.AdvFeatureInit, + UART_ADVFEATURE_MSBFIRST_INIT)) { + assert_param( + IS_UART_ADVFEATURE_MSBFIRST( + huart->AdvancedInit.MSBFirst)); + MODIFY_REG(huart->Instance->CR2, USART_CR2_MSBFIRST, + huart->AdvancedInit.MSBFirst); + } +} + +/** + * @brief Check the UART Idle State. + * @param huart: UART handle. + * @retval HAL status + */ +HAL_StatusTypeDef UART_CheckIdleState(UART_HandleTypeDef *huart) +{ + uint32_t tickstart; + + /* Initialize the UART ErrorCode */ + huart->ErrorCode = HAL_UART_ERROR_NONE; + + /* Init tickstart for timeout management*/ + tickstart = HAL_GetTick(); + + /* Check if the Transmitter is enabled */ + if ((huart->Instance->CR1 & USART_CR1_TE) == USART_CR1_TE) { + /* Wait until TEACK flag is set */ + if (UART_WaitOnFlagUntilTimeout(huart, USART_ISR_TEACK, + RESET, tickstart, + HAL_UART_TIMEOUT_VALUE) != + HAL_OK) { + /* Timeout occurred */ + return HAL_TIMEOUT; + } + } + /* Check if the Receiver is enabled */ + if ((huart->Instance->CR1 & USART_CR1_RE) == USART_CR1_RE) { + /* Wait until REACK flag is set */ + if (UART_WaitOnFlagUntilTimeout(huart, USART_ISR_REACK, RESET, + tickstart, + HAL_UART_TIMEOUT_VALUE) != + HAL_OK) { + /* Timeout occurred */ + return HAL_TIMEOUT; + } + } + + /* Initialize the UART State */ + huart->gState = HAL_UART_STATE_READY; + huart->RxState = HAL_UART_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK(huart); + + return HAL_OK; +} + +/** + * @brief Handle UART Communication Timeout. + * @param huart: UART handle. + * @param Flag Specifies the UART flag to check + * @param Status Flag status (SET or RESET) + * @param Tickstart Tick start value + * @param Timeout Timeout duration + * @retval HAL status + */ +HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, + uint32_t Flag, FlagStatus Status, + uint32_t Tickstart, + uint32_t Timeout) +{ + /* Wait until flag is set */ + while ((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status) { + /* Check for the Timeout */ + if (Timeout != HAL_MAX_DELAY) { + if ((Timeout == 0U) || + ((HAL_GetTick() - Tickstart) > Timeout)) { + /* + * Disable TXE, RXNE, PE and ERR (Frame error, noise error, overrun error) + * interrupts for the interrupt process + */ + CLEAR_BIT(huart->Instance->CR1, + (USART_CR1_RXNEIE | USART_CR1_PEIE | + USART_CR1_TXEIE)); + CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE); + + huart->gState = HAL_UART_STATE_READY; + huart->RxState = HAL_UART_STATE_READY; + + /* Process Unlocked */ + __HAL_UNLOCK(huart); + + return HAL_TIMEOUT; + } + } + } + return HAL_OK; +} + +#endif /* HAL_UART_MODULE_ENABLED */ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/drivers/st/usb_dwc2/usb_dwc2.c b/drivers/st/usb_dwc2/usb_dwc2.c new file mode 100644 index 000000000..fc0b8625e --- /dev/null +++ b/drivers/st/usb_dwc2/usb_dwc2.c @@ -0,0 +1,859 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include + +static usb_dwc2_t dwc2_handle; + +static const usb_driver_t usb_dwc2driver = { + .disable_int = usb_dwc2_disable_int, + .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, + .stop_device = usb_dwc2_stop_device, + .set_address = usb_dwc2_set_address, + .dev_disconnect = usb_dwc2_dev_disconnect, + .write_empty_tx_fifo = usb_dwc2_write_empty_tx_fifo, + .it_handler = usb_dwc2_it_handler +}; + +/* + * USB_OTG_FlushTxFifo : Flush a Tx FIFO + * USBx : Selected device + * num : FIFO number + * This parameter can be a value from 1 to 15 + * 15 means Flush all Tx FIFOs + * return : status + */ +static usb_status_t usb_dwc2_flush_tx_fifo(usb_dwc2_global_t *usbx, + uint32_t num) +{ + uint32_t count = 0; + + usbx->grstctl = (USB_OTG_GRSTCTL_TXFFLSH | (uint32_t)(num << 6)); + + do { + if (++count > 200000) + return USBD_TIMEOUT; + } while ((usbx->grstctl & USB_OTG_GRSTCTL_TXFFLSH) == + USB_OTG_GRSTCTL_TXFFLSH); + + return USBD_OK; +} + +/* + * USB_FlushRxFifo : Flush Rx FIFO + * param : USBx : Selected device + * return : status + */ +static usb_status_t usb_dwc2_flush_rx_fifo(usb_dwc2_global_t *usbx) +{ + uint32_t count = 0; + + usbx->grstctl = USB_OTG_GRSTCTL_RXFFLSH; + + do { + if (++count > 200000) + return USBD_TIMEOUT; + } while ((usbx->grstctl & USB_OTG_GRSTCTL_RXFFLSH) == + USB_OTG_GRSTCTL_RXFFLSH); + + return USBD_OK; +} + +/* + * USB_ReadInterrupts: return the global USB interrupt status + * param USBx : Selected device + * return : interrupt register value + */ +static uint32_t usb_dwc2_read_int(usb_dwc2_global_t *usbx) +{ + uint32_t v = 0; + + v = usbx->gintsts; + v &= usbx->gintmsk; + + return v; +} + +/* + * usb_dwc2_all_out_ep_int : return the USB device OUT endpoints interrupt + * param : USBx : Selected device + * return : device OUT endpoint interrupts + */ +static uint32_t usb_dwc2_all_out_ep_int(usb_dwc2_global_t *usbx) +{ + uint32_t v = 0; + + v = dwc2_handle.usb_device->daint; + v &= dwc2_handle.usb_device->daintmsk; + + return ((v & 0xffff0000) >> 16); +} + +/* + * usb_dwc2_all_in_ep_int: return the USB device IN endpoints interrupt + * param : USBx : Selected device + * return : device IN endpoint interrupts + */ +static uint32_t usb_dwc2_all_in_ep_int(usb_dwc2_global_t *usbx) +{ + uint32_t v = 0; + + v = dwc2_handle.usb_device->daint; + v &= dwc2_handle.usb_device->daintmsk; + + return ((v & 0xFFFF)); +} + +/* + * usb_dwc2_out_ep_int : returns Device OUT EP Interrupt register + * USBx : Selected device + * epnum : endpoint number + * This parameter can be a value from 0 to 15 + * return : Device OUT EP Interrupt register + */ +static uint32_t usb_dwc2_out_ep_int(usb_dwc2_global_t *usbx, uint8_t epnum) +{ + uint32_t v = 0; + + v = dwc2_handle.usb_out_endpoint[epnum]->epint; + v &= dwc2_handle.usb_device->doepmsk; + + return v; +} + +/* + * usb_dwc2_in_ep_int : Returns Device IN EP Interrupt register + * param : USBx : Selected device + * param : epnum : endpoint number + * This parameter can be a value from 0 to 15 + * return : Device IN EP Interrupt register + */ +static uint32_t usb_dwc2_in_ep_int(usb_dwc2_global_t *usbx, uint8_t epnum) +{ + uint32_t msk, emp; + + msk = dwc2_handle.usb_device->diepmsk; + emp = dwc2_handle.usb_device->diepempmsk; + msk |= ((emp >> epnum) & 0x1) << 7; + + return (dwc2_handle.usb_in_endpoint[epnum]->epint & msk); +} + +/* + * usb_dwc2_get_mode : Returns USB core mode + * param : USBx : Selected device + * return : core mode : Host or Device + * This parameter can be one of the these values: + * 0 : Host + * 1 : Device + */ +static uint32_t usb_dwc2_get_mode(usb_dwc2_global_t *usbx) +{ + return ((usbx->gintsts) & 0x1); +} + +/* + * usb_dwc2_activate_setup : Activate EP0 for Setup transactions + * param : USBx : Selected device + * return : status + */ +static usb_status_t usb_dwc2_activate_setup(usb_dwc2_global_t *usbx) +{ + /* Set the MPS of the IN EP based on the enumeration speed */ + dwc2_handle.usb_in_endpoint[0]->epctl &= ~USB_OTG_DIEPCTL_MPSIZ; + + if ((dwc2_handle.usb_device->dsts & USB_OTG_DSTS_ENUMSPD) == + DSTS_ENUMSPD_LS_PHY_6MHZ) + dwc2_handle.usb_in_endpoint[0]->epctl |= 3; + + dwc2_handle.usb_device->dctl |= USB_OTG_DCTL_CGINAK; + + return USBD_OK; +} + +/* + * usb_dwc2_disable_int : + * Disable the controller's Global Int in the AHB Config reg + * param : handle : Selected device + * return : status + */ +usb_status_t usb_dwc2_disable_int(void *handle) +{ + usb_dwc2_global_t *usbx = ((usb_dwc2_t *)handle)->usb_global; + + usbx->gahbcfg &= ~USB_OTG_GAHBCFG_GINT; + return USBD_OK; +} + +/* + * usb_dwc2_ep0_out_start : Prepare the EP0 to start the first control setup + * param : handle : Selected device + * return : status + */ +usb_status_t usb_dwc2_ep0_out_start(void *handle) +{ + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + + dwc2_handle.usb_out_endpoint[0]->eptsiz = 0; + dwc2_handle.usb_out_endpoint[0]->eptsiz |= (USB_OTG_DOEPTSIZ_PKTCNT & + (1 << 19)); + dwc2_handle.usb_out_endpoint[0]->eptsiz |= (3 * 8); + dwc2_handle.usb_out_endpoint[0]->eptsiz |= USB_OTG_DOEPTSIZ_STUPCNT; + + return USBD_OK; +} + +/* + * usb_dwc2_ep_start_xfer : setup and starts a transfer over an EP + * param : handle : Selected device + * param : ep: pointer to endpoint structure + * return : status + */ +usb_status_t usb_dwc2_ep_start_xfer(void *handle, usb_otg_ep_t *ep) +{ + usb_dwc2_global_t *usbx = ((usb_dwc2_t *)handle)->usb_global; + + /* IN endpoint */ + if (ep->is_in == 1) { + /* Zero Length Packet? */ + if (ep->xfer_len == 0) { + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_PKTCNT); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_PKTCNT & (1 << 19)); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_XFRSIZ); + } else { + /* Program the transfer size and packet count + * as follows: xfersize = N * maxpacket + + * short_packet pktcnt = N + (short_packet + * exist ? 1 : 0) + */ + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_XFRSIZ); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_PKTCNT); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_PKTCNT & + (((ep->xfer_len + ep->maxpacket - 1) / + ep->maxpacket) << 19)); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_XFRSIZ & + ep->xfer_len); + + if (ep->type == EP_TYPE_ISOC) { + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_MULCNT); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_MULCNT & + (1 << 29)); + } + } + + if (ep->type != EP_TYPE_ISOC) { + /* Enable the Tx FIFO Empty Interrupt for this EP */ + if (ep->xfer_len > 0) + dwc2_handle.usb_device->diepempmsk |= + 1 << ep->num; + } + + if (ep->type == EP_TYPE_ISOC) { + if ((dwc2_handle.usb_device->dsts & (1 << 8)) == 0) { + dwc2_handle.usb_in_endpoint[ep->num]->epctl |= + USB_OTG_DIEPCTL_SODDFRM; + } else { + dwc2_handle.usb_in_endpoint[ep->num]->epctl |= + USB_OTG_DIEPCTL_SD0PID_SEVNFRM; + } + } + + /* EP enable, IN data in FIFO */ + dwc2_handle.usb_in_endpoint[ep->num]->epctl |= + (USB_OTG_DIEPCTL_CNAK | + USB_OTG_DIEPCTL_EPENA); + + if (ep->type == EP_TYPE_ISOC) + usb_dwc2_write_packet(usbx, ep->xfer_buff, + ep->num, ep->xfer_len); + } else { + /* Program the transfer size and packet count as follows: + * pktcnt = N + * xfersize = N * maxpacket + */ + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DOEPTSIZ_XFRSIZ); + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DOEPTSIZ_PKTCNT); + + if (ep->xfer_len == 0) { + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz |= + (USB_OTG_DOEPTSIZ_XFRSIZ & + ep->maxpacket); + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz |= + (USB_OTG_DOEPTSIZ_PKTCNT & (1 << 19)); + } else { + uint16_t pktcnt = (ep->xfer_len + ep->maxpacket - 1) / + ep->maxpacket; + + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz |= + (USB_OTG_DOEPTSIZ_PKTCNT & + (pktcnt << 19)); + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz |= + (USB_OTG_DOEPTSIZ_XFRSIZ & + (ep->maxpacket * pktcnt)); + } + + if (ep->type == EP_TYPE_ISOC) { + if ((dwc2_handle.usb_device->dsts & (1 << 8)) == 0) + dwc2_handle.usb_out_endpoint[ep->num]->epctl |= + USB_OTG_DOEPCTL_SODDFRM; + else + dwc2_handle.usb_out_endpoint[ep->num]->epctl |= + USB_OTG_DOEPCTL_SD0PID_SEVNFRM; + } + /* EP enable */ + dwc2_handle.usb_out_endpoint[ep->num]->epctl |= + (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA); + } + return USBD_OK; +} + +/* + * usb_dwc2_ep0_start_xfer : setup and starts a transfer over the EP 0 + * param : handle : Selected device + * param : ep: pointer to endpoint structure + * return : status + */ +usb_status_t usb_dwc2_ep0_start_xfer(void *handle, usb_otg_ep_t *ep) +{ + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + + /* IN endpoint */ + if (ep->is_in == 1) { + /* Zero Length Packet? */ + if (ep->xfer_len == 0) { + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_PKTCNT); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_PKTCNT & (1 << 19)); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_XFRSIZ); + } else { + /* Program the transfer size and packet count + * as follows: xfersize = N * maxpacket + + * short_packet pktcnt = N + (short_packet + * exist ? 1 : 0) + */ + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_XFRSIZ); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DIEPTSIZ_PKTCNT); + + if (ep->xfer_len > ep->maxpacket) + ep->xfer_len = ep->maxpacket; + + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_PKTCNT & (1 << 19)); + dwc2_handle.usb_in_endpoint[ep->num]->eptsiz |= + (USB_OTG_DIEPTSIZ_XFRSIZ & + ep->xfer_len); + } + + /* Enable the Tx FIFO Empty Interrupt for this EP */ + if (ep->xfer_len > 0) + dwc2_handle.usb_device->diepempmsk |= 1 << (ep->num); + + /* EP enable, IN data in FIFO */ + dwc2_handle.usb_in_endpoint[ep->num]->epctl |= + (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); + } else { + /* Program the transfer size and packet count as follows: + * pktcnt = N + * xfersize = N * maxpacket + */ + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DOEPTSIZ_XFRSIZ); + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz &= + ~(USB_OTG_DOEPTSIZ_PKTCNT); + + if (ep->xfer_len > 0) + ep->xfer_len = ep->maxpacket; + + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz |= + (USB_OTG_DOEPTSIZ_PKTCNT & + (1 << 19)); + dwc2_handle.usb_out_endpoint[ep->num]->eptsiz |= + (USB_OTG_DOEPTSIZ_XFRSIZ & + (ep->maxpacket)); + + /* EP enable */ + dwc2_handle.usb_out_endpoint[ep->num]->epctl |= + (USB_OTG_DOEPCTL_CNAK | + USB_OTG_DOEPCTL_EPENA); + } + return USBD_OK; +} + +/* + * usb_dwc2_write_packet : Writes 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 + * return : status + */ +usb_status_t usb_dwc2_write_packet(void *handle, uint8_t *src, + uint8_t ch_ep_num, uint16_t len) +{ + uint32_t count32b, i, j; + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + + count32b = (len + 3) / 4; + for (i = 0; i < count32b; i++, src += 4) { + uint32_t src_copy = 0; + + /* Data written to fifo need to be 4 bytes aligned */ + for (j = 0; j < 4; j++) + src_copy += (*(src + j)) << (8 * j); + + *dwc2_handle.usb_fifo[ch_ep_num] = src_copy; + } + + return USBD_OK; +} + +/* + * usb_dwc2_read_packet : read a packet from the Tx 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 + * return : pointer to destination buffer + */ +void *usb_dwc2_read_packet(void *handle, uint8_t *dest, uint16_t len) +{ + uint32_t i = 0; + uint32_t count32b = (len + 3) / 4; + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + + VERBOSE("read packet length %i to 0x%lx\n", len, (uintptr_t)dest); + + for (i = 0; i < count32b; i++, dest += 4) { + *(uint32_t *)dest = *dwc2_handle.usb_fifo[0]; + dsb(); + } + + return ((void *)dest); +} + +/* + * usb_dwc2_EPSetStall : set a stall condition over an EP + * param : handle : Selected device + * param : ep: pointer to endpoint structure + * return : status + */ +usb_status_t usb_dwc2_ep_set_stall(void *handle, usb_otg_ep_t *ep) +{ + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + if (ep->is_in == 1) { + if ((dwc2_handle.usb_in_endpoint[ep->num]->epctl & + USB_OTG_DIEPCTL_EPENA) == 0) + dwc2_handle.usb_in_endpoint[ep->num]->epctl &= + ~(USB_OTG_DIEPCTL_EPDIS); + dwc2_handle.usb_in_endpoint[ep->num]->epctl |= + USB_OTG_DIEPCTL_STALL; + } else { + if ((dwc2_handle.usb_out_endpoint[ep->num]->epctl & + USB_OTG_DOEPCTL_EPENA) == 0) + dwc2_handle.usb_out_endpoint[ep->num]->epctl &= + ~(USB_OTG_DOEPCTL_EPDIS); + dwc2_handle.usb_out_endpoint[ep->num]->epctl |= + USB_OTG_DOEPCTL_STALL; + } + return USBD_OK; +} + +/* + * usb_dwc2_stop_device : Stop the usb device mode + * param : handle : Selected device + * return : status + */ +usb_status_t usb_dwc2_stop_device(void *handle) +{ + uint32_t i = 0; + usb_dwc2_global_t *usbx = ((usb_dwc2_t *)handle)->usb_global; + + /* Clear Pending interrupt */ + for (i = 0; i < 15 ; i++) { + dwc2_handle.usb_in_endpoint[i]->epint = 0xFF; + dwc2_handle.usb_out_endpoint[i]->epint = 0xFF; + } + dwc2_handle.usb_device->daint = 0xFFFFFFFF; + + /* Clear interrupt masks */ + dwc2_handle.usb_device->diepmsk = 0; + dwc2_handle.usb_device->doepmsk = 0; + dwc2_handle.usb_device->daintmsk = 0; + + /* Flush the FIFO */ + usb_dwc2_flush_rx_fifo(usbx); + usb_dwc2_flush_tx_fifo(usbx, 0x10); + + return USBD_OK; +} + +/* + * usb_dwc2_set_address : 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 + * return : status + */ +usb_status_t usb_dwc2_set_address(void *handle, uint8_t address) +{ + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + + dwc2_handle.usb_device->dcfg &= ~(USB_OTG_DCFG_DAD); + dwc2_handle.usb_device->dcfg |= (address << 4) & USB_OTG_DCFG_DAD; + + return USBD_OK; +} + +/* + * usb_dwc2_dev_disconnect : + * Disconnect the USB device by disabling the pull-up/pull-down + * param : handle : Selected device + * return : status + */ +usb_status_t usb_dwc2_dev_disconnect(void *handle) +{ + /*usb_dwc2_global_t *USBx = (usb_dwc2_global_t *)handle;*/ + + dwc2_handle.usb_device->dctl |= USB_OTG_DCTL_SDIS; + + return USBD_OK; +} + +/* + * usb_dwc2_write_empty_tx_fifo + * 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 block + * param : maxpacket : max packet length + * param : xfer_buff : buffer pointer + * retval : status + */ +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) +{ + int32_t len = 0; + uint32_t len32b; + usb_dwc2_global_t *usbx = ((usb_dwc2_t *)handle)->usb_global; + + len = xfer_len - *xfer_count; + + if ((len > 0) && ((uint32_t)len > maxpacket)) + len = maxpacket; + + len32b = (len + 3) / 4; + + while ((dwc2_handle.usb_in_endpoint[epnum]->txfsts & + USB_OTG_DTXFSTS_INEPTFSAV) > len32b && + (*xfer_count < xfer_len) && (xfer_len != 0)) { + /* Write the FIFO */ + len = xfer_len - *xfer_count; + + if ((len > 0) && ((uint32_t)len > maxpacket)) + len = maxpacket; + + len32b = (len + 3) / 4; + + usb_dwc2_write_packet(usbx, *xfer_buff, epnum, len); + + *xfer_buff += len; + *xfer_count += len; + } + + if (len <= 0) { + uint32_t fifoemptymsk = 0x1 << epnum; + + dwc2_handle.usb_device->diepempmsk &= ~fifoemptymsk; + } + + return USBD_OK; +} + +/* + * @brief This function handles PCD interrupt request. + * @param hpcd: PCD handle + * @retval HAL status + */ +usb_action_t usb_dwc2_it_handler(void *handle, uint32_t *param) +{ + usb_dwc2_global_t *usbx = ((usb_dwc2_t *)handle)->usb_global; + uint32_t ep_intr, epint, epnum = 0; + uint32_t temp; + + /* ensure that we are in device mode */ + if (usb_dwc2_get_mode(usbx) != USB_OTG_MODE_DEVICE) + return USB_NOTHING; + + /* avoid spurious interrupt */ + if (!usb_dwc2_read_int(usbx)) + return USB_NOTHING; + + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_MMIS) + /* incorrect mode, acknowledge the interrupt */ + usbx->gintsts = USB_OTG_GINTSTS_MMIS; + + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_OEPINT) { + /* Read in the device interrupt bits */ + ep_intr = usb_dwc2_all_out_ep_int(usbx); + + while (!(ep_intr & 1)) { + epnum++; + ep_intr >>= 1; + } + + if (ep_intr & 1) { + epint = usb_dwc2_out_ep_int(usbx, epnum); + + if ((epint & USB_OTG_DOEPINT_XFRC) == + USB_OTG_DOEPINT_XFRC) { + dwc2_handle.usb_out_endpoint[epnum]->epint = + USB_OTG_DOEPINT_XFRC; + *param = epnum; + return USB_DATA_OUT; + } + if ((epint & USB_OTG_DOEPINT_STUP) == + USB_OTG_DOEPINT_STUP) { + /* Inform the upper layer that a setup packet + * is available + */ + dwc2_handle.usb_out_endpoint[epnum]->epint = + USB_OTG_DOEPINT_STUP; + return USB_SETUP; + } + if ((epint & USB_OTG_DOEPINT_OTEPDIS) == + USB_OTG_DOEPINT_OTEPDIS) + dwc2_handle.usb_out_endpoint[epnum]->epint = + USB_OTG_DOEPINT_OTEPDIS; + } + } + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_IEPINT) { + /* Read in the device interrupt bits */ + ep_intr = usb_dwc2_all_in_ep_int(usbx); + + while (!(ep_intr & 1)) { + epnum++; + ep_intr >>= 1; + } + /* In ITR */ + if (ep_intr & 0x1) { + epint = usb_dwc2_in_ep_int(usbx, epnum); + + if ((epint & USB_OTG_DIEPINT_XFRC) == + USB_OTG_DIEPINT_XFRC) { + uint32_t fifoemptymsk = 0x1 << epnum; + + dwc2_handle.usb_device->diepempmsk &= + ~fifoemptymsk; + + dwc2_handle.usb_in_endpoint[epnum]->epint = + USB_OTG_DIEPINT_XFRC; + + *param = epnum; + return USB_DATA_IN; + } + if ((epint & USB_OTG_DIEPINT_TOC) == + USB_OTG_DIEPINT_TOC) + dwc2_handle.usb_in_endpoint[epnum]->epint = + USB_OTG_DIEPINT_TOC; + + if ((epint & USB_OTG_DIEPINT_ITTXFE) == + USB_OTG_DIEPINT_ITTXFE) + dwc2_handle.usb_in_endpoint[epnum]->epint = + USB_OTG_DIEPINT_ITTXFE; + + if ((epint & USB_OTG_DIEPINT_INEPNE) == + USB_OTG_DIEPINT_INEPNE) + dwc2_handle.usb_in_endpoint[epnum]->epint = + USB_OTG_DIEPINT_INEPNE; + + if ((epint & USB_OTG_DIEPINT_EPDISD) == + USB_OTG_DIEPINT_EPDISD) + dwc2_handle.usb_in_endpoint[epnum]->epint = + USB_OTG_DIEPINT_EPDISD; + + if ((epint & USB_OTG_DIEPINT_TXFE) == + USB_OTG_DIEPINT_TXFE) { + *param = epnum; + return USB_WRITE_EMPTY; + } + } + } + + /* Handle Resume Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_WKUINT) { + INFO("handle USB : Resume\n"); + /* Clear the Remote Wake-up Signaling */ + dwc2_handle.usb_device->dctl &= ~USB_OTG_DCTL_RWUSIG; + usbx->gintsts = USB_OTG_GINTSTS_WKUINT; + return USB_RESUME; + } + + /* Handle Suspend Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_USBSUSP) { + INFO("handle USB : Suspend int\n"); + usbx->gintsts = USB_OTG_GINTSTS_USBSUSP; + if ((dwc2_handle.usb_device->dsts & USB_OTG_DSTS_SUSPSTS) == + USB_OTG_DSTS_SUSPSTS){ + return USB_SUSPEND; + } + } + + /* Handle LPM Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_LPMINT) { + INFO("handle USB : LPM int enter in suspend\n"); + usbx->gintsts = USB_OTG_GINTSTS_LPMINT; + *param = (usbx->glpmcfg & USB_OTG_GLPMCFG_BESL) >> 2; + return USB_LPM; + } + + /* Handle Reset Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_USBRST) { + INFO("handle USB : Reset\n"); + dwc2_handle.usb_device->dctl &= ~USB_OTG_DCTL_RWUSIG; + usb_dwc2_flush_tx_fifo(usbx, 0); + + dwc2_handle.usb_device->daint = 0xFFFFFFFF; + dwc2_handle.usb_device->daintmsk |= 0x10001; + + dwc2_handle.usb_device->doepmsk |= (USB_OTG_DOEPMSK_STUPM | + USB_OTG_DOEPMSK_XFRCM | + USB_OTG_DOEPMSK_EPDM); + dwc2_handle.usb_device->diepmsk |= (USB_OTG_DIEPMSK_TOM | + USB_OTG_DIEPMSK_XFRCM | + USB_OTG_DIEPMSK_EPDM); + + /* Set Default Address to 0 */ + dwc2_handle.usb_device->dcfg &= ~USB_OTG_DCFG_DAD; + + /* setup EP0 to receive SETUP packets */ + usb_dwc2_ep0_out_start(usbx); + + usbx->gintsts = USB_OTG_GINTSTS_USBRST; + } + + /* Handle Enumeration done Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_ENUMDNE) { + usb_dwc2_activate_setup(usbx); + usbx->gusbcfg &= ~USB_OTG_GUSBCFG_TRDT; + + usbx->gusbcfg |= (uint32_t)((USBD_HS_TRDT_VALUE << 10) & + USB_OTG_GUSBCFG_TRDT); + + usbx->gintsts = USB_OTG_GINTSTS_ENUMDNE; + return USB_ENUM_DONE; + } + + /* Handle RxQLevel Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_RXFLVL) { + usbx->gintmsk &= ~USB_OTG_GINTSTS_RXFLVL; + temp = usbx->grxstsp; + *param = (temp & USB_OTG_GRXSTSP_EPNUM); + *param |= ((temp & USB_OTG_GRXSTSP_BCNT) << 0xC); + + if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_DATA_UPDT) { + if ((temp & USB_OTG_GRXSTSP_BCNT) != 0) { + usbx->gintmsk |= USB_OTG_GINTSTS_RXFLVL; + return USB_READ_DATA_PACKET; + } + } else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17) == + STS_SETUP_UPDT) { + usbx->gintmsk |= USB_OTG_GINTSTS_RXFLVL; + return USB_READ_SETUP_PACKET; + } + usbx->gintmsk |= USB_OTG_GINTSTS_RXFLVL; + } + + /* Handle SOF Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_SOF) { + INFO("handle USB : SOF\n"); + usbx->gintsts = USB_OTG_GINTSTS_SOF; + return USB_SOF; + } + + /* Handle Incomplete ISO IN Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_IISOIXFR) { + INFO("handle USB : ISO IN\n"); + usbx->gintsts = USB_OTG_GINTSTS_IISOIXFR; + } + + /* Handle Incomplete ISO OUT Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_PXFR_INCOMPISOOUT) { + INFO("handle USB : ISO OUT\n"); + usbx->gintsts = USB_OTG_GINTSTS_PXFR_INCOMPISOOUT; + } + + /* Handle Connection event Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_SRQINT) { + INFO("handle USB : Connect\n"); + usbx->gintsts = USB_OTG_GINTSTS_SRQINT; + } + + /* Handle Disconnection event Interrupt */ + if (usb_dwc2_read_int(usbx) & USB_OTG_GINTSTS_OTGINT) { + INFO("handle USB : Disconnect\n"); + temp = usbx->gotgint; + if ((temp & USB_OTG_GOTGINT_SEDET) == USB_OTG_GOTGINT_SEDET) + return USB_DISCONNECT; + } + return USB_NOTHING; +} + +void usb_dwc2_init_driver(usb_handle_t *usb_core_handle, + uint32_t *base_register) +{ + uint32_t i = 0; + uintptr_t base = (uintptr_t)base_register; + + dwc2_handle.usb_global = (usb_dwc2_global_t *)base; + + dwc2_handle.usb_device = (usb_dwc2_device_t *) + (base + USB_OTG_DEVICE_BASE); + + for (i = 0; i < USB_MAX_ENDPOINT_NB; i++) { + dwc2_handle.usb_in_endpoint[i] = (usb_dwc2_endpoint_t *) + (base + USB_OTG_IN_ENDPOINT_BASE + + (i * sizeof(usb_dwc2_endpoint_t))); + dwc2_handle.usb_out_endpoint[i] = (usb_dwc2_endpoint_t *) + (base + USB_OTG_OUT_ENDPOINT_BASE + + (i * sizeof(usb_dwc2_endpoint_t))); + dwc2_handle.usb_fifo[i] = (uint32_t *)(base + + USB_OTG_FIFO_BASE + + (i * USB_OTG_FIFO_SIZE)); + } + + register_usb_driver(usb_core_handle, &usb_dwc2driver, + (void *)&dwc2_handle); +} diff --git a/fdts/stm32mp15-ddr.dtsi b/fdts/stm32mp15-ddr.dtsi index 4825691f9..f0657ad12 100644 --- a/fdts/stm32mp15-ddr.dtsi +++ b/fdts/stm32mp15-ddr.dtsi @@ -132,6 +132,7 @@ DDR_MR3 >; +#ifdef DDR_PHY_CAL_SKIP st,phy-cal = < DDR_DX0DLLCR DDR_DX0DQTR @@ -146,6 +147,7 @@ DDR_DX3DQTR DDR_DX3DQSTR >; +#endif status = "okay"; }; diff --git a/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi b/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi index 11e8f2bef..c0fc1f772 100644 --- a/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi +++ b/fdts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi @@ -1,24 +1,23 @@ // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause /* * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +/* + * File generated by STMicroelectronics STM32CubeMX DDR Tool for MPUs + * DDR type: DDR3 / DDR3L + * DDR width: 16bits + * DDR density: 4Gb + * System frequency: 533000Khz + * Relaxed Timing Mode: false + * Address mapping type: RBC * - * STM32MP157C DK1/DK2 BOARD configuration - * 1x DDR3L 4Gb, 16-bit, 533MHz. - * Reference used NT5CC256M16DP-DI from NANYA - * - * DDR type / Platform DDR3/3L - * freq 533MHz - * width 16 - * datasheet 0 = MT41J256M16-187 / DDR3-1066 bin G - * DDR density 4 - * timing mode optimized - * Scheduling/QoS options : type = 2 - * address mapping : RBC - * Tc > + 85C : N + * Save Date: 2020.02.20, save Time: 18:45:20 */ -#define DDR_MEM_NAME "DDR3-1066/888 bin G 1x4Gb 533MHz v1.45" -#define DDR_MEM_SPEED 533000 -#define DDR_MEM_SIZE 0x20000000 + +#define DDR_MEM_NAME "DDR3-DDR3L 16bits 533000Khz" +#define DDR_MEM_SPEED 533000 +#define DDR_MEM_SIZE 0x20000000 #define DDR_MSTR 0x00041401 #define DDR_MRCTRL0 0x00000010 @@ -50,15 +49,6 @@ #define DDR_DFIUPD1 0x00000000 #define DDR_DFIUPD2 0x00000000 #define DDR_DFIPHYMSTR 0x00000000 -#define DDR_ADDRMAP1 0x00070707 -#define DDR_ADDRMAP2 0x00000000 -#define DDR_ADDRMAP3 0x1F000000 -#define DDR_ADDRMAP4 0x00001F1F -#define DDR_ADDRMAP5 0x06060606 -#define DDR_ADDRMAP6 0x0F060606 -#define DDR_ADDRMAP9 0x00000000 -#define DDR_ADDRMAP10 0x00000000 -#define DDR_ADDRMAP11 0x00000000 #define DDR_ODTCFG 0x06000600 #define DDR_ODTMAP 0x00000001 #define DDR_SCHED 0x00000C01 @@ -83,6 +73,15 @@ #define DDR_PCFGQOS1_1 0x00800040 #define DDR_PCFGWQOS0_1 0x01100C03 #define DDR_PCFGWQOS1_1 0x01000200 +#define DDR_ADDRMAP1 0x00070707 +#define DDR_ADDRMAP2 0x00000000 +#define DDR_ADDRMAP3 0x1F000000 +#define DDR_ADDRMAP4 0x00001F1F +#define DDR_ADDRMAP5 0x06060606 +#define DDR_ADDRMAP6 0x0F060606 +#define DDR_ADDRMAP9 0x00000000 +#define DDR_ADDRMAP10 0x00000000 +#define DDR_ADDRMAP11 0x00000000 #define DDR_PGCR 0x01442E02 #define DDR_PTR0 0x0022AA5B #define DDR_PTR1 0x04841104 diff --git a/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi b/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi index 4b70b6055..fc226d254 100644 --- a/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi +++ b/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi @@ -1,24 +1,23 @@ // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause /* * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +/* + * File generated by STMicroelectronics STM32CubeMX DDR Tool for MPUs + * DDR type: DDR3 / DDR3L + * DDR width: 32bits + * DDR density: 8Gb + * System frequency: 533000Khz + * Relaxed Timing Mode: false + * Address mapping type: RBC * - * STM32MP157C ED1 BOARD configuration - * 2x DDR3L 4Gb each, 16-bit, 533MHz, Single Die Package in flyby topology. - * Reference used NT5CC256M16DP-DI from NANYA - * - * DDR type / Platform DDR3/3L - * freq 533MHz - * width 32 - * datasheet 0 = MT41J256M16-187 / DDR3-1066 bin G - * DDR density 8 - * timing mode optimized - * Scheduling/QoS options : type = 2 - * address mapping : RBC - * Tc > + 85C : N + * Save Date: 2020.02.20, save Time: 18:49:33 */ -#define DDR_MEM_NAME "DDR3-1066/888 bin G 2x4Gb 533MHz v1.45" -#define DDR_MEM_SPEED 533000 -#define DDR_MEM_SIZE 0x40000000 + +#define DDR_MEM_NAME "DDR3-DDR3L 32bits 533000Khz" +#define DDR_MEM_SPEED 533000 +#define DDR_MEM_SIZE 0x40000000 #define DDR_MSTR 0x00040401 #define DDR_MRCTRL0 0x00000010 @@ -50,15 +49,6 @@ #define DDR_DFIUPD1 0x00000000 #define DDR_DFIUPD2 0x00000000 #define DDR_DFIPHYMSTR 0x00000000 -#define DDR_ADDRMAP1 0x00080808 -#define DDR_ADDRMAP2 0x00000000 -#define DDR_ADDRMAP3 0x00000000 -#define DDR_ADDRMAP4 0x00001F1F -#define DDR_ADDRMAP5 0x07070707 -#define DDR_ADDRMAP6 0x0F070707 -#define DDR_ADDRMAP9 0x00000000 -#define DDR_ADDRMAP10 0x00000000 -#define DDR_ADDRMAP11 0x00000000 #define DDR_ODTCFG 0x06000600 #define DDR_ODTMAP 0x00000001 #define DDR_SCHED 0x00000C01 @@ -83,6 +73,15 @@ #define DDR_PCFGQOS1_1 0x00800040 #define DDR_PCFGWQOS0_1 0x01100C03 #define DDR_PCFGWQOS1_1 0x01000200 +#define DDR_ADDRMAP1 0x00080808 +#define DDR_ADDRMAP2 0x00000000 +#define DDR_ADDRMAP3 0x00000000 +#define DDR_ADDRMAP4 0x00001F1F +#define DDR_ADDRMAP5 0x07070707 +#define DDR_ADDRMAP6 0x0F070707 +#define DDR_ADDRMAP9 0x00000000 +#define DDR_ADDRMAP10 0x00000000 +#define DDR_ADDRMAP11 0x00000000 #define DDR_PGCR 0x01442E02 #define DDR_PTR0 0x0022AA5B #define DDR_PTR1 0x04841104 diff --git a/fdts/stm32mp15-pinctrl.dtsi b/fdts/stm32mp15-pinctrl.dtsi new file mode 100644 index 000000000..d3d1744ec --- /dev/null +++ b/fdts/stm32mp15-pinctrl.dtsi @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ +#include + +&pinctrl { + fmc_pins_a: fmc-0 { + pins1 { + pinmux = , /* FMC_NOE */ + , /* FMC_NWE */ + , /* FMC_A16_FMC_CLE */ + , /* FMC_A17_FMC_ALE */ + , /* FMC_D0 */ + , /* FMC_D1 */ + , /* FMC_D2 */ + , /* FMC_D3 */ + , /* FMC_D4 */ + , /* FMC_D5 */ + , /* FMC_D6 */ + , /* FMC_D7 */ + ; /* FMC_NE2_FMC_NCE */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + pins2 { + pinmux = ; /* FMC_NWAIT */ + bias-pull-up; + }; + }; + + qspi_clk_pins_a: qspi-clk-0 { + pins { + pinmux = ; /* QSPI_CLK */ + bias-disable; + drive-push-pull; + slew-rate = <3>; + }; + }; + + qspi_bk1_pins_a: qspi-bk1-0 { + pins1 { + pinmux = , /* QSPI_BK1_IO0 */ + , /* QSPI_BK1_IO1 */ + , /* QSPI_BK1_IO2 */ + ; /* QSPI_BK1_IO3 */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + pins2 { + pinmux = ; /* QSPI_BK1_NCS */ + bias-pull-up; + drive-push-pull; + slew-rate = <1>; + }; + }; + + qspi_bk2_pins_a: qspi-bk2-0 { + pins1 { + pinmux = , /* QSPI_BK2_IO0 */ + , /* QSPI_BK2_IO1 */ + , /* QSPI_BK2_IO2 */ + ; /* QSPI_BK2_IO3 */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + pins2 { + pinmux = ; /* QSPI_BK2_NCS */ + bias-pull-up; + drive-push-pull; + slew-rate = <1>; + }; + }; + + rtc_out2_rmp_pins_a: rtc-out2-rmp-pins-0 { + pins { + pinmux = ; /* RTC_OUT2_RMP */ + }; + }; + + sdmmc1_b4_pins_a: sdmmc1-b4-0 { + pins1 { + pinmux = , /* SDMMC1_D0 */ + , /* SDMMC1_D1 */ + , /* SDMMC1_D2 */ + , /* SDMMC1_D3 */ + ; /* SDMMC1_CMD */ + slew-rate = <1>; + drive-push-pull; + bias-disable; + }; + pins2 { + pinmux = ; /* SDMMC1_CK */ + slew-rate = <2>; + drive-push-pull; + bias-disable; + }; + }; + + sdmmc1_dir_pins_a: sdmmc1-dir-0 { + pins1 { + pinmux = , /* SDMMC1_D0DIR */ + , /* SDMMC1_D123DIR */ + ; /* SDMMC1_CDIR */ + slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + pins2{ + pinmux = ; /* SDMMC1_CKIN */ + bias-pull-up; + }; + }; + + sdmmc2_b4_pins_a: sdmmc2-b4-0 { + pins1 { + pinmux = , /* SDMMC2_D0 */ + , /* SDMMC2_D1 */ + , /* SDMMC2_D2 */ + , /* SDMMC2_D3 */ + ; /* SDMMC2_CMD */ + slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + pins2 { + pinmux = ; /* SDMMC2_CK */ + slew-rate = <2>; + drive-push-pull; + bias-pull-up; + }; + }; + + sdmmc2_b4_pins_b: sdmmc2-b4-1 { + pins1 { + pinmux = , /* SDMMC2_D0 */ + , /* SDMMC2_D1 */ + , /* SDMMC2_D2 */ + , /* SDMMC2_D3 */ + ; /* SDMMC2_CMD */ + slew-rate = <1>; + drive-push-pull; + bias-disable; + }; + pins2 { + pinmux = ; /* SDMMC2_CK */ + slew-rate = <2>; + drive-push-pull; + bias-disable; + }; + }; + + sdmmc2_d47_pins_a: sdmmc2-d47-0 { + pins { + pinmux = , /* SDMMC2_D4 */ + , /* SDMMC2_D5 */ + , /* SDMMC2_D6 */ + ; /* SDMMC2_D7 */ + slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + }; + + uart4_pins_a: uart4-0 { + pins1 { + pinmux = ; /* UART4_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART4_RX */ + bias-disable; + }; + }; + + uart4_pins_b: uart4-1 { + pins1 { + pinmux = ; /* UART4_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART4_RX */ + bias-disable; + }; + }; + + uart7_pins_a: uart7-0 { + pins1 { + pinmux = ; /* UART4_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* UART4_RX */ + , /* UART4_CTS */ + ; /* UART4_RTS */ + bias-disable; + }; + }; + + uart7_pins_b: uart7-1 { + pins1 { + pinmux = ; /* USART7_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* USART7_RX */ + bias-disable; + }; + }; + + usart2_pins_a: usart2-0 { + pins1 { + pinmux = , /* USART2_TX */ + ; /* USART2_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <3>; + }; + pins2 { + pinmux = , /* USART2_RX */ + ; /* USART2_CTS_NSS */ + bias-disable; + }; + }; + + usart3_pins_a: usart3-0 { + pins1 { + pinmux = , /* USART3_TX */ + ; /* USART3_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART3_RX */ + ; /* USART3_CTS_NSS */ + bias-disable; + }; + }; + + usart3_pins_b: usart3-1 { + pins1 { + pinmux = , /* USART3_TX */ + ; /* USART3_RTS */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART3_RX */ + ; /* USART3_CTS_NSS */ + bias-disable; + }; + }; + + usbotg_hs_pins_a: usbotg_hs-0 { + pins { + pinmux = ; /* OTG_ID */ + }; + }; + + usbotg_fs_dp_dm_pins_a: usbotg-fs-dp-dm-0 { + pins { + pinmux = , /* OTG_FS_DM */ + ; /* OTG_FS_DP */ + }; + }; +}; + +&pinctrl_z { + i2c4_pins_a: i2c4-0 { + pins { + pinmux = , /* I2C4_SCL */ + ; /* I2C4_SDA */ + bias-disable; + drive-open-drain; + slew-rate = <0>; + }; + }; +}; diff --git a/fdts/stm32mp151.dtsi b/fdts/stm32mp151.dtsi new file mode 100644 index 000000000..6e6dff4f7 --- /dev/null +++ b/fdts/stm32mp151.dtsi @@ -0,0 +1,683 @@ +// 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 + +/ { + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + 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>; + + nvmem-cell-names = "cfg0_otp", + "part_number_otp", + "monotonic_otp", + "nand_otp", + "uid_otp", + "package_otp", + "hw2_otp"; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + intc: interrupt-controller@a0021000 { + compatible = "arm,cortex-a7-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0xa0021000 0x1000>, + <0xa0022000 0x2000>; + }; + + clocks { + clk_hse: clk-hse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24000000>; + }; + + clk_hsi: clk-hsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <64000000>; + }; + + clk_lse: clk-lse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; + }; + + clk_lsi: clk-lsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32000>; + }; + + clk_csi: clk-csi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <4000000>; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&intc>; + ranges; + + timers12: timer@40006000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32-timers"; + reg = <0x40006000 0x400>; + clocks = <&rcc TIM12_K>; + clock-names = "int"; + status = "disabled"; + secure-status = "disabled"; + }; + + usart2: serial@4000e000 { + compatible = "st,stm32h7-uart"; + reg = <0x4000e000 0x400>; + interrupts = ; + clocks = <&rcc USART2_K>; + resets = <&rcc USART2_R>; + status = "disabled"; + }; + + usart3: serial@4000f000 { + compatible = "st,stm32h7-uart"; + reg = <0x4000f000 0x400>; + interrupts = ; + clocks = <&rcc USART3_K>; + resets = <&rcc USART3_R>; + status = "disabled"; + }; + + uart4: serial@40010000 { + compatible = "st,stm32h7-uart"; + reg = <0x40010000 0x400>; + interrupts-extended = <&exti 30 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc UART4_K>; + resets = <&rcc UART4_R>; + wakeup-source; + status = "disabled"; + }; + + uart5: serial@40011000 { + compatible = "st,stm32h7-uart"; + reg = <0x40011000 0x400>; + interrupts = ; + clocks = <&rcc UART5_K>; + resets = <&rcc UART5_R>; + status = "disabled"; + }; + + uart7: serial@40018000 { + compatible = "st,stm32h7-uart"; + reg = <0x40018000 0x400>; + interrupts = ; + clocks = <&rcc UART7_K>; + resets = <&rcc UART7_R>; + status = "disabled"; + }; + + uart8: serial@40019000 { + compatible = "st,stm32h7-uart"; + reg = <0x40019000 0x400>; + interrupts = ; + clocks = <&rcc UART8_K>; + resets = <&rcc UART8_R>; + status = "disabled"; + }; + + usart6: serial@44003000 { + compatible = "st,stm32h7-uart"; + reg = <0x44003000 0x400>; + interrupts = ; + clocks = <&rcc USART6_K>; + resets = <&rcc USART6_R>; + status = "disabled"; + }; + + timers15: timer@44006000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32-timers"; + reg = <0x44006000 0x400>; + clocks = <&rcc TIM15_K>; + clock-names = "int"; + status = "disabled"; + secure-status = "disabled"; + }; + + usbotg_hs: usb-otg@49000000 { + compatible = "st,stm32mp1-hsotg", "snps,dwc2"; + reg = <0x49000000 0x10000>; + clocks = <&rcc USBO_K>; + clock-names = "otg"; + resets = <&rcc USBO_R>; + reset-names = "dwc2"; + interrupts = ; + g-rx-fifo-size = <512>; + g-np-tx-fifo-size = <32>; + g-tx-fifo-size = <256 16 16 16 16 16 16 16>; + dr_mode = "otg"; + usb33d-supply = <&usb33>; + status = "disabled"; + }; + + 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"; + }; + + pwr_regulators: pwr@50001000 { + compatible = "st,stm32mp1,pwr-reg"; + reg = <0x50001000 0x10>; + st,tzcr = <&rcc 0x0 0x1>; + + reg11: reg11 { + regulator-name = "reg11"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + }; + + reg18: reg18 { + regulator-name = "reg18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + usb33: usb33 { + regulator-name = "usb33"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; + + pwr_mcu: pwr_mcu@50001014 { + compatible = "syscon"; + reg = <0x50001014 0x4>; + }; + + pwr_irq: pwr@50001020 { + compatible = "st,stm32mp1-pwr"; + reg = <0x50001020 0x100>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + }; + + exti: interrupt-controller@5000d000 { + compatible = "st,stm32mp1-exti", "syscon"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x5000d000 0x400>; + + /* exti_pwr is an extra interrupt controller used for + * EXTI 55 to 60. It's mapped on pwr interrupt + * controller. + */ + exti_pwr: exti-pwr { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&pwr_irq>; + st,irq-number = <6>; + }; + }; + + syscfg: syscon@50020000 { + compatible = "st,stm32mp157-syscfg", "syscon"; + reg = <0x50020000 0x400>; + clocks = <&rcc SYSCFG>; + }; + + hash1: hash@54002000 { + compatible = "st,stm32f756-hash"; + reg = <0x54002000 0x400>; + interrupts = ; + clocks = <&rcc HASH1>; + resets = <&rcc HASH1_R>; + status = "disabled"; + secure-status = "disabled"; + }; + + rng1: rng@54003000 { + compatible = "st,stm32-rng"; + reg = <0x54003000 0x400>; + clocks = <&rcc RNG1_K>; + resets = <&rcc RNG1_R>; + status = "disabled"; + secure-status = "disabled"; + }; + + fmc: nand-controller@58002000 { + compatible = "st,stm32mp15-fmc2"; + reg = <0x58002000 0x1000>, + <0x80000000 0x1000>, + <0x88010000 0x1000>, + <0x88020000 0x1000>, + <0x81000000 0x1000>, + <0x89010000 0x1000>, + <0x89020000 0x1000>; + interrupts = ; + clocks = <&rcc FMC_K>; + resets = <&rcc FMC_R>; + status = "disabled"; + }; + + qspi: spi@58003000 { + compatible = "st,stm32f469-qspi"; + reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; + reg-names = "qspi", "qspi_mm"; + interrupts = ; + clocks = <&rcc QSPI_K>; + resets = <&rcc QSPI_R>; + status = "disabled"; + }; + + sdmmc1: sdmmc@58005000 { + compatible = "st,stm32-sdmmc2", "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00253180>; + reg = <0x58005000 0x1000>, <0x58006000 0x1000>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&rcc SDMMC1_K>; + clock-names = "apb_pclk"; + resets = <&rcc SDMMC1_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + + sdmmc2: sdmmc@58007000 { + compatible = "st,stm32-sdmmc2", "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00253180>; + reg = <0x58007000 0x1000>, <0x58008000 0x1000>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&rcc SDMMC2_K>; + clock-names = "apb_pclk"; + resets = <&rcc SDMMC2_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + + iwdg2: watchdog@5a002000 { + compatible = "st,stm32mp1-iwdg"; + reg = <0x5a002000 0x400>; + secure-interrupts = ; + clocks = <&rcc IWDG2>, <&rcc CK_LSI>; + clock-names = "pclk", "lsi"; + status = "disabled"; + secure-status = "disabled"; + }; + + usbphyc: usbphyc@5a006000 { + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <0>; + compatible = "st,stm32mp1-usbphyc"; + reg = <0x5a006000 0x1000>; + clocks = <&rcc USBPHY_K>; + resets = <&rcc USBPHY_R>; + vdda1v1-supply = <®11>; + vdda1v8-supply = <®18>; + status = "disabled"; + + usbphyc_port0: usb-phy@0 { + #phy-cells = <0>; + reg = <0>; + }; + + usbphyc_port1: usb-phy@1 { + #phy-cells = <1>; + reg = <1>; + }; + }; + + usart1: serial@5c000000 { + compatible = "st,stm32h7-uart"; + reg = <0x5c000000 0x400>; + interrupts = ; + clocks = <&rcc USART1_K>; + resets = <&rcc USART1_R>; + status = "disabled"; + secure-status = "disabled"; + }; + + spi6: spi@5c001000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32h7-spi"; + reg = <0x5c001000 0x400>; + interrupts = ; + clocks = <&rcc SPI6_K>; + resets = <&rcc SPI6_R>; + status = "disabled"; + secure-status = "disabled"; + }; + + i2c4: i2c@5c002000 { + compatible = "st,stm32mp15-i2c"; + reg = <0x5c002000 0x400>; + interrupt-names = "event", "error"; + interrupts-extended = <&exti 24 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc I2C4_K>; + resets = <&rcc I2C4_R>; + #address-cells = <1>; + #size-cells = <0>; + st,syscfg-fmp = <&syscfg 0x4 0x8>; + wakeup-source; + status = "disabled"; + secure-status = "disabled"; + }; + + iwdg1: watchdog@5c003000 { + compatible = "st,stm32mp1-iwdg"; + reg = <0x5C003000 0x400>; + interrupts = ; + clocks = <&rcc IWDG1>, <&rcc CK_LSI>; + clock-names = "pclk", "lsi"; + status = "disabled"; + secure-status = "disabled"; + }; + + rtc: rtc@5c004000 { + compatible = "st,stm32mp1-rtc"; + reg = <0x5c004000 0x400>; + clocks = <&rcc RTCAPB>, <&rcc RTC>; + clock-names = "pclk", "rtc_ck"; + interrupts-extended = <&exti 19 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + secure-status = "disabled"; + }; + + bsec: nvmem@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>; + }; + mac_addr: mac_addr@e4 { + reg = <0xe4 0x8>; + st,non-secure-otp; + }; + }; + + etzpc: etzpc@5c007000 { + compatible = "st,stm32-etzpc"; + reg = <0x5C007000 0x400>; + clocks = <&rcc TZPC>; + status = "disabled"; + secure-status = "okay"; + }; + + stgen: stgen@5c008000 { + compatible = "st,stm32-stgen"; + reg = <0x5C008000 0x1000>; + }; + + i2c6: i2c@5c009000 { + compatible = "st,stm32mp15-i2c"; + reg = <0x5c009000 0x400>; + interrupt-names = "event", "error"; + interrupts-extended = <&exti 54 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc I2C6_K>; + resets = <&rcc I2C6_R>; + #address-cells = <1>; + #size-cells = <0>; + st,syscfg-fmp = <&syscfg 0x4 0x20>; + wakeup-source; + status = "disabled"; + secure-status = "disabled"; + }; + + tamp: tamp@5c00a000 { + compatible = "st,stm32-tamp", "simple-bus", "syscon", "simple-mfd"; + reg = <0x5c00a000 0x400>; + secure-interrupts = ; + clocks = <&rcc RTCAPB>; + }; + + /* + * Break node order to solve dependency probe issue between + * pinctrl and exti. + */ + pinctrl: pin-controller@50002000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,stm32mp157-pinctrl"; + ranges = <0 0x50002000 0xa400>; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; + pins-are-numbered; + + gpioa: gpio@50002000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x0 0x400>; + clocks = <&rcc GPIOA>; + st,bank-name = "GPIOA"; + status = "disabled"; + }; + + gpiob: gpio@50003000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x1000 0x400>; + clocks = <&rcc GPIOB>; + st,bank-name = "GPIOB"; + status = "disabled"; + }; + + gpioc: gpio@50004000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x2000 0x400>; + clocks = <&rcc GPIOC>; + st,bank-name = "GPIOC"; + status = "disabled"; + }; + + gpiod: gpio@50005000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x3000 0x400>; + clocks = <&rcc GPIOD>; + st,bank-name = "GPIOD"; + status = "disabled"; + }; + + gpioe: gpio@50006000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x4000 0x400>; + clocks = <&rcc GPIOE>; + st,bank-name = "GPIOE"; + status = "disabled"; + }; + + gpiof: gpio@50007000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x5000 0x400>; + clocks = <&rcc GPIOF>; + st,bank-name = "GPIOF"; + status = "disabled"; + }; + + gpiog: gpio@50008000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x6000 0x400>; + clocks = <&rcc GPIOG>; + st,bank-name = "GPIOG"; + status = "disabled"; + }; + + gpioh: gpio@50009000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x7000 0x400>; + clocks = <&rcc GPIOH>; + st,bank-name = "GPIOH"; + status = "disabled"; + }; + + gpioi: gpio@5000a000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x8000 0x400>; + clocks = <&rcc GPIOI>; + st,bank-name = "GPIOI"; + status = "disabled"; + }; + + gpioj: gpio@5000b000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x9000 0x400>; + clocks = <&rcc GPIOJ>; + st,bank-name = "GPIOJ"; + status = "disabled"; + }; + + gpiok: gpio@5000c000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0xa000 0x400>; + clocks = <&rcc GPIOK>; + st,bank-name = "GPIOK"; + status = "disabled"; + }; + }; + + pinctrl_z: pin-controller-z@54004000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,stm32mp157-z-pinctrl"; + ranges = <0 0x54004000 0x400>; + pins-are-numbered; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; + + gpioz: gpio@54004000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0 0x400>; + clocks = <&rcc GPIOZ>; + st,bank-name = "GPIOZ"; + st,bank-ioport = <11>; + status = "disabled"; + secure-status = "disabled"; + }; + }; + }; +}; diff --git a/fdts/stm32mp153.dtsi b/fdts/stm32mp153.dtsi new file mode 100644 index 000000000..617380a52 --- /dev/null +++ b/fdts/stm32mp153.dtsi @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include "stm32mp151.dtsi" + +/ { + cpus { + cpu1: cpu@1 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <1>; + clocks = <&rcc CK_MPU>; + clock-names = "cpu"; + operating-points-v2 = <&cpu0_opp_table>; + }; + }; +}; diff --git a/fdts/stm32mp157-pinctrl.dtsi b/fdts/stm32mp157-pinctrl.dtsi deleted file mode 100644 index 8e480b2c1..000000000 --- a/fdts/stm32mp157-pinctrl.dtsi +++ /dev/null @@ -1,348 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* - * Copyright (C) STMicroelectronics 2017 - All Rights Reserved - * Author: Ludovic Barre for STMicroelectronics. - */ -#include - -/ { - soc { - pinctrl: pin-controller@50002000 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,stm32mp157-pinctrl"; - ranges = <0 0x50002000 0xa400>; - pins-are-numbered; - - gpioa: gpio@50002000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x0 0x400>; - clocks = <&rcc GPIOA>; - st,bank-name = "GPIOA"; - status = "disabled"; - }; - - gpiob: gpio@50003000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x1000 0x400>; - clocks = <&rcc GPIOB>; - st,bank-name = "GPIOB"; - status = "disabled"; - }; - - gpioc: gpio@50004000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x2000 0x400>; - clocks = <&rcc GPIOC>; - st,bank-name = "GPIOC"; - status = "disabled"; - }; - - gpiod: gpio@50005000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x3000 0x400>; - clocks = <&rcc GPIOD>; - st,bank-name = "GPIOD"; - status = "disabled"; - }; - - gpioe: gpio@50006000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x4000 0x400>; - clocks = <&rcc GPIOE>; - st,bank-name = "GPIOE"; - status = "disabled"; - }; - - gpiof: gpio@50007000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x5000 0x400>; - clocks = <&rcc GPIOF>; - st,bank-name = "GPIOF"; - status = "disabled"; - }; - - gpiog: gpio@50008000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x6000 0x400>; - clocks = <&rcc GPIOG>; - st,bank-name = "GPIOG"; - status = "disabled"; - }; - - gpioh: gpio@50009000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x7000 0x400>; - clocks = <&rcc GPIOH>; - st,bank-name = "GPIOH"; - status = "disabled"; - }; - - gpioi: gpio@5000a000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x8000 0x400>; - clocks = <&rcc GPIOI>; - st,bank-name = "GPIOI"; - status = "disabled"; - }; - - gpioj: gpio@5000b000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x9000 0x400>; - clocks = <&rcc GPIOJ>; - st,bank-name = "GPIOJ"; - status = "disabled"; - }; - - gpiok: gpio@5000c000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0xa000 0x400>; - clocks = <&rcc GPIOK>; - st,bank-name = "GPIOK"; - status = "disabled"; - }; - - qspi_bk1_pins_a: qspi-bk1-0 { - pins1 { - pinmux = , /* QSPI_BK1_IO0 */ - , /* QSPI_BK1_IO1 */ - , /* QSPI_BK1_IO2 */ - ; /* QSPI_BK1_IO3 */ - bias-disable; - drive-push-pull; - slew-rate = <1>; - }; - pins2 { - pinmux = ; /* QSPI_BK1_NCS */ - bias-pull-up; - drive-push-pull; - slew-rate = <1>; - }; - }; - - qspi_bk2_pins_a: qspi-bk2-0 { - pins1 { - pinmux = , /* QSPI_BK2_IO0 */ - , /* QSPI_BK2_IO1 */ - , /* QSPI_BK2_IO2 */ - ; /* QSPI_BK2_IO3 */ - bias-disable; - drive-push-pull; - slew-rate = <1>; - }; - pins2 { - pinmux = ; /* QSPI_BK2_NCS */ - bias-pull-up; - drive-push-pull; - slew-rate = <1>; - }; - }; - - qspi_clk_pins_a: qspi-clk-0 { - pins { - pinmux = ; /* QSPI_CLK */ - bias-disable; - drive-push-pull; - slew-rate = <3>; - }; - }; - - sdmmc1_b4_pins_a: sdmmc1-b4-0 { - pins1 { - pinmux = , /* SDMMC1_D0 */ - , /* SDMMC1_D1 */ - , /* SDMMC1_D2 */ - , /* SDMMC1_D3 */ - ; /* SDMMC1_CMD */ - slew-rate = <1>; - drive-push-pull; - bias-disable; - }; - pins2 { - pinmux = ; /* SDMMC1_CK */ - slew-rate = <2>; - drive-push-pull; - bias-disable; - }; - }; - - sdmmc1_dir_pins_a: sdmmc1-dir-0 { - pins1 { - pinmux = , /* SDMMC1_D0DIR */ - , /* SDMMC1_D123DIR */ - ; /* SDMMC1_CDIR */ - slew-rate = <1>; - drive-push-pull; - bias-pull-up; - }; - pins2{ - pinmux = ; /* SDMMC1_CKIN */ - bias-pull-up; - }; - }; - - sdmmc2_b4_pins_a: sdmmc2-b4-0 { - pins1 { - pinmux = , /* SDMMC2_D0 */ - , /* SDMMC2_D1 */ - , /* SDMMC2_D2 */ - , /* SDMMC2_D3 */ - ; /* SDMMC2_CMD */ - slew-rate = <1>; - drive-push-pull; - bias-pull-up; - }; - pins2 { - pinmux = ; /* SDMMC2_CK */ - slew-rate = <2>; - drive-push-pull; - bias-pull-up; - }; - }; - - sdmmc2_d47_pins_a: sdmmc2-d47-0 { - pins { - pinmux = , /* SDMMC2_D4 */ - , /* SDMMC2_D5 */ - , /* SDMMC2_D6 */ - ; /* SDMMC2_D7 */ - slew-rate = <1>; - drive-push-pull; - bias-pull-up; - }; - }; - - uart4_pins_a: uart4-0 { - pins1 { - pinmux = ; /* UART4_TX */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = ; /* UART4_RX */ - bias-disable; - }; - }; - - uart4_pins_b: uart4-1 { - pins1 { - pinmux = ; /* UART4_TX */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = ; /* UART4_RX */ - bias-disable; - }; - }; - - uart7_pins_a: uart7-0 { - pins1 { - pinmux = ; /* USART7_TX */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = ; /* USART7_RX */ - bias-disable; - }; - }; - - usart3_pins_a: usart3-0 { - pins1 { - pinmux = , /* USART3_TX */ - ; /* USART3_RTS */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = , /* USART3_RX */ - ; /* USART3_CTS_NSS */ - bias-disable; - }; - }; - - usart3_pins_b: usart3-1 { - pins1 { - pinmux = , /* USART3_TX */ - ; /* USART3_RTS */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = , /* USART3_RX */ - ; /* USART3_CTS_NSS */ - bias-disable; - }; - }; - }; - - pinctrl_z: pin-controller-z@54004000 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,stm32mp157-z-pinctrl"; - ranges = <0 0x54004000 0x400>; - pins-are-numbered; - - gpioz: gpio@54004000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0 0x400>; - clocks = <&rcc GPIOZ>; - st,bank-name = "GPIOZ"; - st,bank-ioport = <11>; - status = "disabled"; - }; - - i2c4_pins_a: i2c4-0 { - pins { - pinmux = , /* I2C4_SCL */ - ; /* I2C4_SDA */ - bias-disable; - drive-open-drain; - slew-rate = <0>; - }; - }; - }; - }; -}; diff --git a/fdts/stm32mp157.dtsi b/fdts/stm32mp157.dtsi new file mode 100644 index 000000000..c83402907 --- /dev/null +++ b/fdts/stm32mp157.dtsi @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include "stm32mp153.dtsi" diff --git a/fdts/stm32mp157a-avenger96.dts b/fdts/stm32mp157a-avenger96.dts index 907940c78..d9b3d1d8f 100644 --- a/fdts/stm32mp157a-avenger96.dts +++ b/fdts/stm32mp157a-avenger96.dts @@ -9,21 +9,49 @@ /dts-v1/; -#include "stm32mp157c.dtsi" -#include "stm32mp157cac-pinctrl.dtsi" +#include "stm32mp157.dtsi" +#include "stm32mp15xa.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxac-pinctrl.dtsi" +#include +#include +#include +#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" / { model = "Arrow Electronics STM32MP157A Avenger96 board"; - compatible = "st,stm32mp157a-avenger96", "st,stm32mp157"; + compatible = "arrow,stm32mp157a-avenger96", "st,stm32mp157"; aliases { + mmc0 = &sdmmc1; serial0 = &uart4; + serial1 = &uart7; }; chosen { stdout-path = "serial0:115200n8"; }; + memory@c0000000 { + device_type = "memory"; + reg = <0xc0000000 0x40000000>; + }; +}; + +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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 { @@ -43,16 +71,17 @@ st,main-control-register = <0x04>; st,vin-control-register = <0xc0>; - st,usb-control-register = <0x20>; + st,usb-control-register = <0x30>; 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"; @@ -61,6 +90,16 @@ regulator-always-on; regulator-initial-mode = <0>; regulator-over-current-protection; + 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: buck2 { @@ -70,6 +109,17 @@ regulator-always-on; regulator-initial-mode = <0>; regulator-over-current-protection; + 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: buck3 { @@ -80,6 +130,18 @@ st,mask-reset; regulator-initial-mode = <0>; regulator-over-current-protection; + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; }; v3v3: buck4 { @@ -89,18 +151,36 @@ regulator-always-on; regulator-over-current-protection; regulator-initial-mode = <0>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; vdda: ldo1 { regulator-name = "vdda"; regulator-min-microvolt = <2900000>; regulator-max-microvolt = <2900000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; v2v8: ldo2 { regulator-name = "v2v8"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; vtt_ddr: ldo3 { @@ -109,12 +189,27 @@ regulator-max-microvolt = <750000>; regulator-always-on; regulator-over-current-protection; + lp-stop { + regulator-off-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; vdd_usb: ldo4 { regulator-name = "vdd_usb"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; vdd_sd: ldo5 { @@ -122,18 +217,52 @@ regulator-min-microvolt = <2900000>; regulator-max-microvolt = <2900000>; regulator-boot-on; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; v1v8: ldo6 { regulator-name = "v1v8"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; }; vref_ddr: vref_ddr { regulator-name = "vref_ddr"; regulator-always-on; regulator-over-current-protection; + lp-stop { + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + 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>; }; }; }; @@ -142,56 +271,21 @@ &iwdg2 { timeout-sec = <32>; status = "okay"; + secure-status = "okay"; }; -&rng1 { - status = "okay"; -}; - -&rtc { - status = "okay"; -}; - -&sdmmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>; - broken-cd; - st,sig-dir; - st,neg-edge; - st,use-ckin; - bus-width = <4>; - vmmc-supply = <&vdda>; - status = "okay"; -}; - -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&uart4_pins_b>; - status = "okay"; -}; - -/* ATF Specific */ -#include -#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" -#include "stm32mp157c-security.dtsi" - -/ { - aliases { - gpio0 = &gpioa; - gpio1 = &gpiob; - gpio2 = &gpioc; - gpio3 = &gpiod; - gpio4 = &gpioe; - gpio5 = &gpiof; - gpio6 = &gpiog; - gpio7 = &gpioh; - gpio8 = &gpioi; - gpio25 = &gpioz; - i2c3 = &i2c4; - }; +&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>; }; -/* CLOCK init */ &rcc { secure-status = "disabled"; st,clksrc = < @@ -260,24 +354,69 @@ /* VCO = 1300.0 MHz => P = 650 (CPU) */ pll1: st,pll@0 { - cfg = < 2 80 0 0 0 PQR(1,0,0) >; - frac = < 0x800 >; + 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 { - cfg = < 2 65 1 0 0 PQR(1,1,1) >; - frac = < 0x1400 >; + 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 { - cfg = < 1 33 1 16 36 PQR(1,1,1) >; - frac = < 0x1a04 >; + compatible = "st,stm32mp1-pll"; + reg = <2>; + cfg = <1 33 1 16 36 PQR(1,1,1)>; + frac = <0x1a04>; }; /* VCO = 480.0 MHz => P = 120, Q = 40, R = 96 */ pll4: st,pll@3 { - cfg = < 1 39 3 11 4 PQR(1,1,1) >; + compatible = "st,stm32mp1-pll"; + reg = <3>; + cfg = <1 39 3 11 4 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>; + st,sig-dir; + st,neg-edge; + st,use-ckin; + bus-width = <4>; + vmmc-supply = <&vdd_sd>; + status = "okay"; +}; + +&uart4 { + /* On Low speed expansion header */ + label = "LS-UART1"; + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins_b>; + status = "okay"; +}; + +&uart7 { + /* On Low speed expansion header */ + label = "LS-UART0"; + pinctrl-names = "default"; + pinctrl-0 = <&uart7_pins_a>; + status = "okay"; +}; diff --git a/fdts/stm32mp157a-dk1.dts b/fdts/stm32mp157a-dk1.dts index 4ea83f7cd..4d506bce5 100644 --- a/fdts/stm32mp157a-dk1.dts +++ b/fdts/stm32mp157a-dk1.dts @@ -1,13 +1,17 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2018-2019 - All Rights Reserved - * Author: Alexandre Torgue . + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. */ /dts-v1/; -#include "stm32mp157c.dtsi" -#include "stm32mp157cac-pinctrl.dtsi" +#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"; @@ -22,290 +26,20 @@ chosen { stdout-path = "serial0:115200n8"; }; - -}; - -&clk_hse { - st,digbypass; -}; - -&i2c4 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c4_pins_a>; - i2c-scl-rising-time-ns = <185>; - i2c-scl-falling-time-ns = <20>; - status = "okay"; - - pmic: stpmic@33 { - compatible = "st,stpmic1"; - reg = <0x33>; - interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>; - interrupt-controller; - #interrupt-cells = <2>; - status = "okay"; - - st,main-control-register = <0x04>; - st,vin-control-register = <0xc0>; - st,usb-control-register = <0x20>; - - regulators { - compatible = "st,stpmic1-regulators"; - - ldo1-supply = <&v3v3>; - ldo3-supply = <&vdd_ddr>; - ldo6-supply = <&v3v3>; - - vddcore: buck1 { - regulator-name = "vddcore"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1350000>; - regulator-always-on; - regulator-initial-mode = <0>; - regulator-over-current-protection; - }; - - vdd_ddr: buck2 { - regulator-name = "vdd_ddr"; - regulator-min-microvolt = <1350000>; - regulator-max-microvolt = <1350000>; - regulator-always-on; - regulator-initial-mode = <0>; - regulator-over-current-protection; - }; - - vdd: buck3 { - regulator-name = "vdd"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - st,mask-reset; - regulator-initial-mode = <0>; - regulator-over-current-protection; - }; - - v3v3: buck4 { - regulator-name = "v3v3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - regulator-over-current-protection; - regulator-initial-mode = <0>; - }; - - v1v8_audio: ldo1 { - regulator-name = "v1v8_audio"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - - v3v3_hdmi: ldo2 { - regulator-name = "v3v3_hdmi"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - vtt_ddr: ldo3 { - regulator-name = "vtt_ddr"; - regulator-min-microvolt = <500000>; - regulator-max-microvolt = <750000>; - regulator-always-on; - regulator-over-current-protection; - }; - - vdd_usb: ldo4 { - regulator-name = "vdd_usb"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; - - vdda: ldo5 { - regulator-name = "vdda"; - regulator-min-microvolt = <2900000>; - regulator-max-microvolt = <2900000>; - regulator-boot-on; - }; - - v1v2_hdmi: ldo6 { - regulator-name = "v1v2_hdmi"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-always-on; - }; - - vref_ddr: vref_ddr { - regulator-name = "vref_ddr"; - regulator-always-on; - regulator-over-current-protection; - }; - }; - }; -}; - -&iwdg2 { - timeout-sec = <32>; - status = "okay"; -}; - -&pwr { - pwr-regulators { - vdd-supply = <&vdd>; - }; -}; - -&rng1 { - status = "okay"; -}; - -&rtc { - status = "okay"; }; -&sdmmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc1_b4_pins_a>; - broken-cd; - st,neg-edge; - bus-width = <4>; - vmmc-supply = <&v3v3>; - status = "okay"; -}; - -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&uart4_pins_a>; - status = "okay"; -}; - -&uart7 { - pinctrl-names = "default"; - pinctrl-0 = <&uart7_pins_a>; - status = "disabled"; -}; - -&usart3 { - pinctrl-names = "default"; - pinctrl-0 = <&usart3_pins_b>; - status = "disabled"; -}; - -/* ATF Specific */ -#include -#include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi" -#include "stm32mp157c-security.dtsi" - -/ { - aliases { - gpio0 = &gpioa; - gpio1 = &gpiob; - gpio2 = &gpioc; - gpio3 = &gpiod; - gpio4 = &gpioe; - gpio5 = &gpiof; - gpio6 = &gpiog; - gpio7 = &gpioh; - gpio8 = &gpioi; - gpio25 = &gpioz; - i2c3 = &i2c4; - }; -}; - -/* CLOCK init */ -&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 +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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) >; - - 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 = 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) >; - }; -}; - -&bsec { - board_id: board_id@ec { - reg = <0xec 0x4>; - status = "okay"; - secure-status = "okay"; - }; }; diff --git a/fdts/stm32mp157a-ed1.dts b/fdts/stm32mp157a-ed1.dts new file mode 100644 index 000000000..4f84ec623 --- /dev/null +++ b/fdts/stm32mp157a-ed1.dts @@ -0,0 +1,46 @@ +// 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"; + }; + + aliases { + serial0 = &uart4; + }; +}; + +&cpu1 { + cpu-supply = <&vddcore>; +}; + +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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.dts b/fdts/stm32mp157a-ev1.dts new file mode 100644 index 000000000..c577a9052 --- /dev/null +++ b/fdts/stm32mp157a-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 "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 { + serial1 = &usart3; + }; +}; + diff --git a/fdts/stm32mp157c-dk2.dts b/fdts/stm32mp157c-dk2.dts index fdcf4c802..436a15970 100644 --- a/fdts/stm32mp157c-dk2.dts +++ b/fdts/stm32mp157c-dk2.dts @@ -1,16 +1,51 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2018 - All Rights Reserved - * Author: Alexandre Torgue . + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. */ /dts-v1/; -#include "stm32mp157a-dk1.dts" +#include "stm32mp157.dtsi" +#include "stm32mp15xc.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxac-pinctrl.dtsi" +#include "stm32mp15xx-dkx.dtsi" +#include / { model = "STMicroelectronics STM32MP157C-DK2 Discovery Board"; compatible = "st,stm32mp157c-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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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.dts b/fdts/stm32mp157c-ed1.dts index 779492552..5aadb1ff0 100644 --- a/fdts/stm32mp157c-ed1.dts +++ b/fdts/stm32mp157c-ed1.dts @@ -1,12 +1,16 @@ // 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.dtsi" -#include "stm32mp157caa-pinctrl.dtsi" +#include "stm32mp157.dtsi" +#include "stm32mp15xc.dtsi" +#include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxaa-pinctrl.dtsi" +#include "stm32mp15xx-edx.dtsi" +#include / { model = "STMicroelectronics STM32MP157C eval daughter"; @@ -21,297 +25,27 @@ }; }; -&clk_hse { - st,digbypass; +&cpu1 { + cpu-supply = <&vddcore>; }; -&i2c4 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c4_pins_a>; - i2c-scl-rising-time-ns = <185>; - i2c-scl-falling-time-ns = <20>; - status = "okay"; - - pmic: stpmic@33 { - compatible = "st,stpmic1"; - reg = <0x33>; - interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>; - interrupt-controller; - #interrupt-cells = <2>; - status = "okay"; - - st,main-control-register = <0x04>; - st,vin-control-register = <0xc0>; - st,usb-control-register = <0x20>; - - regulators { - compatible = "st,stpmic1-regulators"; - - ldo1-supply = <&v3v3>; - ldo2-supply = <&v3v3>; - ldo3-supply = <&vdd_ddr>; - ldo5-supply = <&v3v3>; - ldo6-supply = <&v3v3>; - - vddcore: buck1 { - regulator-name = "vddcore"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1350000>; - regulator-always-on; - regulator-initial-mode = <0>; - regulator-over-current-protection; - }; - - vdd_ddr: buck2 { - regulator-name = "vdd_ddr"; - regulator-min-microvolt = <1350000>; - regulator-max-microvolt = <1350000>; - regulator-always-on; - regulator-initial-mode = <0>; - regulator-over-current-protection; - }; - - vdd: buck3 { - regulator-name = "vdd"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - st,mask-reset; - regulator-initial-mode = <0>; - regulator-over-current-protection; - }; - - v3v3: buck4 { - regulator-name = "v3v3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - regulator-over-current-protection; - regulator-initial-mode = <0>; - }; - - vdda: ldo1 { - regulator-name = "vdda"; - regulator-min-microvolt = <2900000>; - regulator-max-microvolt = <2900000>; - }; - - v2v8: ldo2 { - regulator-name = "v2v8"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; - - vtt_ddr: ldo3 { - regulator-name = "vtt_ddr"; - regulator-min-microvolt = <500000>; - regulator-max-microvolt = <750000>; - regulator-always-on; - regulator-over-current-protection; - }; - - vdd_usb: ldo4 { - regulator-name = "vdd_usb"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; - - vdd_sd: ldo5 { - regulator-name = "vdd_sd"; - regulator-min-microvolt = <2900000>; - regulator-max-microvolt = <2900000>; - regulator-boot-on; - }; - - v1v8: ldo6 { - regulator-name = "v1v8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - vref_ddr: vref_ddr { - regulator-name = "vref_ddr"; - regulator-always-on; - regulator-over-current-protection; - }; - }; - }; -}; - -&iwdg2 { - timeout-sec = <32>; - status = "okay"; -}; - -&pwr { - pwr-regulators { - vdd-supply = <&vdd>; - }; -}; - -&rng1 { - status = "okay"; -}; - -&rtc { - status = "okay"; -}; - -&sdmmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>; - broken-cd; - st,sig-dir; - st,neg-edge; - st,use-ckin; - bus-width = <4>; - vmmc-supply = <&vdd_sd>; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - sd-uhs-ddr50; - sd-uhs-sdr104; - status = "okay"; -}; - -&sdmmc2 { - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>; - non-removable; - no-sd; - no-sdio; - st,neg-edge; - bus-width = <8>; - vmmc-supply = <&v3v3>; - vqmmc-supply = <&v3v3>; - mmc-ddr-3_3v; - status = "okay"; -}; - -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&uart4_pins_a>; +&cryp1 { status = "okay"; }; -/* ATF Specific */ -#include -#include "stm32mp15-ddr3-2x4Gb-1066-binG.dtsi" -#include "stm32mp157c-security.dtsi" - -/ { - aliases { - gpio0 = &gpioa; - gpio1 = &gpiob; - gpio2 = &gpioc; - gpio3 = &gpiod; - gpio4 = &gpioe; - gpio5 = &gpiof; - gpio6 = &gpiog; - gpio7 = &gpioh; - gpio8 = &gpioi; - gpio9 = &gpioj; - gpio10 = &gpiok; - gpio25 = &gpioz; - i2c3 = &i2c4; - }; -}; - -/* CLOCK init */ -&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 +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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) >; - - 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 = 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) >; - }; -}; - -&bsec { - board_id: board_id@ec { - reg = <0xec 0x4>; - status = "okay"; - secure-status = "okay"; - }; }; diff --git a/fdts/stm32mp157c-ev1.dts b/fdts/stm32mp157c-ev1.dts index cfde8ed90..dd7da4195 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 - 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"; @@ -19,49 +20,3 @@ serial1 = &usart3; }; }; - -&fmc { - status = "okay"; - #address-cells = <1>; - #size-cells = <0>; - - nand: nand@0 { - reg = <0>; - nand-on-flash-bbt; - #address-cells = <1>; - #size-cells = <1>; - }; -}; - -&qspi { - pinctrl-names = "default"; - pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a &qspi_bk2_pins_a>; - reg = <0x58003000 0x1000>, <0x70000000 0x4000000>; - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - - flash0: mx66l51235l@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-rx-bus-width = <4>; - spi-max-frequency = <108000000>; - #address-cells = <1>; - #size-cells = <1>; - }; - - flash1: mx66l51235l@1 { - compatible = "jedec,spi-nor"; - reg = <1>; - spi-rx-bus-width = <4>; - spi-max-frequency = <108000000>; - #address-cells = <1>; - #size-cells = <1>; - }; -}; - -&usart3 { - pinctrl-names = "default"; - pinctrl-0 = <&usart3_pins_a>; - status = "disabled"; -}; diff --git a/fdts/stm32mp157c-security.dtsi b/fdts/stm32mp157c-security.dtsi deleted file mode 100644 index 165ffa0cb..000000000 --- a/fdts/stm32mp157c-security.dtsi +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved - * - * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause - */ - -/ { - soc { - stgen: stgen@5c008000 { - compatible = "st,stm32-stgen"; - reg = <0x5C008000 0x1000>; - status = "okay"; - }; - }; -}; - -&bsec { - mac_addr: mac_addr@e4 { - reg = <0xe4 0x6>; - status = "okay"; - secure-status = "okay"; - }; - /* Spare field to align on 32-bit OTP granularity */ - spare_ns_ea: spare_ns_ea@ea { - reg = <0xea 0x2>; - status = "okay"; - secure-status = "okay"; - }; -}; - -&hash1 { - secure-status = "okay"; -}; - -&sdmmc1 { - compatible = "st,stm32-sdmmc2"; -}; - -&sdmmc2 { - compatible = "st,stm32-sdmmc2"; -}; diff --git a/fdts/stm32mp157c.dtsi b/fdts/stm32mp157c.dtsi deleted file mode 100644 index 0942a91c2..000000000 --- a/fdts/stm32mp157c.dtsi +++ /dev/null @@ -1,366 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* - * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved - * Author: Ludovic Barre for STMicroelectronics. - */ -#include -#include -#include - -/ { - #address-cells = <1>; - #size-cells = <1>; - - intc: interrupt-controller@a0021000 { - compatible = "arm,cortex-a7-gic"; - #interrupt-cells = <3>; - interrupt-controller; - reg = <0xa0021000 0x1000>, - <0xa0022000 0x2000>; - }; - - clocks { - clk_hse: clk-hse { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <24000000>; - }; - - clk_hsi: clk-hsi { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <64000000>; - }; - - clk_lse: clk-lse { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <32768>; - }; - - clk_lsi: clk-lsi { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <32000>; - }; - - clk_csi: clk-csi { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <4000000>; - }; - - clk_i2s_ckin: i2s_ckin { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <0>; - }; - - clk_dsi_phy: ck_dsi_phy { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <0>; - }; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - interrupt-parent = <&intc>; - ranges; - - timers12: timer@40006000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "st,stm32-timers"; - reg = <0x40006000 0x400>; - clocks = <&rcc TIM12_K>; - clock-names = "int"; - status = "disabled"; - }; - - usart2: serial@4000e000 { - compatible = "st,stm32h7-uart"; - reg = <0x4000e000 0x400>; - clocks = <&rcc USART2_K>; - resets = <&rcc USART2_R>; - status = "disabled"; - }; - - usart3: serial@4000f000 { - compatible = "st,stm32h7-uart"; - reg = <0x4000f000 0x400>; - clocks = <&rcc USART3_K>; - resets = <&rcc USART3_R>; - status = "disabled"; - }; - - uart4: serial@40010000 { - compatible = "st,stm32h7-uart"; - reg = <0x40010000 0x400>; - clocks = <&rcc UART4_K>; - resets = <&rcc UART4_R>; - status = "disabled"; - }; - - uart5: serial@40011000 { - compatible = "st,stm32h7-uart"; - reg = <0x40011000 0x400>; - clocks = <&rcc UART5_K>; - resets = <&rcc UART5_R>; - status = "disabled"; - }; - - - uart7: serial@40018000 { - compatible = "st,stm32h7-uart"; - reg = <0x40018000 0x400>; - clocks = <&rcc UART7_K>; - resets = <&rcc UART7_R>; - status = "disabled"; - }; - - uart8: serial@40019000 { - compatible = "st,stm32h7-uart"; - reg = <0x40019000 0x400>; - clocks = <&rcc UART8_K>; - resets = <&rcc UART8_R>; - status = "disabled"; - }; - - usart6: serial@44003000 { - compatible = "st,stm32h7-uart"; - reg = <0x44003000 0x400>; - clocks = <&rcc USART6_K>; - resets = <&rcc USART6_R>; - status = "disabled"; - }; - - timers15: timer@44006000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "st,stm32-timers"; - reg = <0x44006000 0x400>; - clocks = <&rcc TIM15_K>; - clock-names = "int"; - status = "disabled"; - }; - - sdmmc3: sdmmc@48004000 { - compatible = "arm,pl18x", "arm,primecell"; - arm,primecell-periphid = <0x00253180>; - reg = <0x48004000 0x400>, <0x48005000 0x400>; - clocks = <&rcc SDMMC3_K>; - clock-names = "apb_pclk"; - resets = <&rcc SDMMC3_R>; - cap-sd-highspeed; - cap-mmc-highspeed; - max-frequency = <120000000>; - status = "disabled"; - }; - - usbotg_hs: usb-otg@49000000 { - compatible = "st,stm32mp1-hsotg", "snps,dwc2"; - reg = <0x49000000 0x10000>; - clocks = <&rcc USBO_K>; - clock-names = "otg"; - resets = <&rcc USBO_R>; - reset-names = "dwc2"; - status = "disabled"; - }; - - rcc: rcc@50000000 { - compatible = "st,stm32mp1-rcc", "syscon"; - reg = <0x50000000 0x1000>; - #clock-cells = <1>; - #reset-cells = <1>; - interrupts = ; - }; - - pwr: pwr@50001000 { - compatible = "st,stm32mp1-pwr", "syscon", "simple-mfd"; - reg = <0x50001000 0x400>; - }; - - exti: interrupt-controller@5000d000 { - compatible = "st,stm32mp1-exti", "syscon"; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x5000d000 0x400>; - - /* exti_pwr is an extra interrupt controller used for - * EXTI 55 to 60. It's mapped on pwr interrupt - * controller. - */ - exti_pwr: exti-pwr { - interrupt-controller; - #interrupt-cells = <2>; - interrupt-parent = <&pwr>; - st,irq-number = <6>; - }; - }; - - syscfg: syscon@50020000 { - compatible = "st,stm32mp157-syscfg", "syscon"; - reg = <0x50020000 0x400>; - clocks = <&rcc SYSCFG>; - }; - - cryp1: cryp@54001000 { - compatible = "st,stm32mp1-cryp"; - reg = <0x54001000 0x400>; - interrupts = ; - clocks = <&rcc CRYP1>; - resets = <&rcc CRYP1_R>; - status = "disabled"; - }; - - hash1: hash@54002000 { - compatible = "st,stm32f756-hash"; - reg = <0x54002000 0x400>; - interrupts = ; - clocks = <&rcc HASH1>; - resets = <&rcc HASH1_R>; - status = "disabled"; - }; - - rng1: rng@54003000 { - compatible = "st,stm32-rng"; - reg = <0x54003000 0x400>; - clocks = <&rcc RNG1_K>; - resets = <&rcc RNG1_R>; - status = "disabled"; - }; - - fmc: nand-controller@58002000 { - compatible = "st,stm32mp15-fmc2"; - reg = <0x58002000 0x1000>, - <0x80000000 0x1000>, - <0x88010000 0x1000>, - <0x88020000 0x1000>, - <0x81000000 0x1000>, - <0x89010000 0x1000>, - <0x89020000 0x1000>; - clocks = <&rcc FMC_K>; - resets = <&rcc FMC_R>; - status = "disabled"; - }; - - qspi: qspi@58003000 { - compatible = "st,stm32f469-qspi"; - reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; - reg-names = "qspi", "qspi_mm"; - clocks = <&rcc QSPI_K>; - resets = <&rcc QSPI_R>; - status = "disabled"; - }; - - sdmmc1: sdmmc@58005000 { - compatible = "arm,pl18x", "arm,primecell"; - arm,primecell-periphid = <0x00253180>; - reg = <0x58005000 0x1000>, <0x58006000 0x1000>; - clocks = <&rcc SDMMC1_K>; - clock-names = "apb_pclk"; - resets = <&rcc SDMMC1_R>; - cap-sd-highspeed; - cap-mmc-highspeed; - max-frequency = <120000000>; - status = "disabled"; - }; - - sdmmc2: sdmmc@58007000 { - compatible = "arm,pl18x", "arm,primecell"; - arm,primecell-periphid = <0x00253180>; - reg = <0x58007000 0x1000>, <0x58008000 0x1000>; - clocks = <&rcc SDMMC2_K>; - clock-names = "apb_pclk"; - resets = <&rcc SDMMC2_R>; - cap-sd-highspeed; - cap-mmc-highspeed; - max-frequency = <120000000>; - status = "disabled"; - }; - - iwdg2: watchdog@5a002000 { - compatible = "st,stm32mp1-iwdg"; - reg = <0x5a002000 0x400>; - clocks = <&rcc IWDG2>, <&rcc CK_LSI>; - clock-names = "pclk", "lsi"; - status = "disabled"; - }; - - usart1: serial@5c000000 { - compatible = "st,stm32h7-uart"; - reg = <0x5c000000 0x400>; - interrupt-names = "event", "wakeup"; - interrupts-extended = <&intc GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>, - <&exti 26 1>; - clocks = <&rcc USART1_K>; - resets = <&rcc USART1_R>; - status = "disabled"; - }; - - spi6: spi@5c001000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "st,stm32h7-spi"; - reg = <0x5c001000 0x400>; - interrupts = ; - clocks = <&rcc SPI6_K>; - resets = <&rcc SPI6_R>; - status = "disabled"; - }; - - i2c4: i2c@5c002000 { - compatible = "st,stm32f7-i2c"; - reg = <0x5c002000 0x400>; - interrupt-names = "event", "error", "wakeup"; - interrupts-extended = <&intc GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>, - <&intc GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>, - <&exti 24 1>; - clocks = <&rcc I2C4_K>; - resets = <&rcc I2C4_R>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - rtc: rtc@5c004000 { - compatible = "st,stm32mp1-rtc"; - reg = <0x5c004000 0x400>; - clocks = <&rcc RTCAPB>, <&rcc RTC>; - clock-names = "pclk", "rtc_ck"; - interrupts-extended = <&intc GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, - <&exti 19 1>; - status = "disabled"; - }; - - bsec: nvmem@5c005000 { - compatible = "st,stm32mp15-bsec"; - reg = <0x5c005000 0x400>; - #address-cells = <1>; - #size-cells = <1>; - ts_cal1: calib@5c { - reg = <0x5c 0x2>; - }; - ts_cal2: calib@5e { - reg = <0x5e 0x2>; - }; - }; - - i2c6: i2c@5c009000 { - compatible = "st,stm32f7-i2c"; - reg = <0x5c009000 0x400>; - interrupt-names = "event", "error", "wakeup"; - interrupts-extended = <&intc GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>, - <&intc GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>, - <&exti 54 1>; - clocks = <&rcc I2C6_K>; - resets = <&rcc I2C6_R>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - }; -}; diff --git a/fdts/stm32mp157caa-pinctrl.dtsi b/fdts/stm32mp157caa-pinctrl.dtsi deleted file mode 100644 index 9b9cd086c..000000000 --- a/fdts/stm32mp157caa-pinctrl.dtsi +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* - * Copyright (C) STMicroelectronics 2018 - All Rights Reserved - * Author: Alexandre Torgue - */ - -#include "stm32mp157-pinctrl.dtsi" -/ { - soc { - pinctrl: pin-controller@50002000 { - st,package = ; - - gpioa: gpio@50002000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 0 16>; - }; - - gpiob: gpio@50003000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 16 16>; - }; - - gpioc: gpio@50004000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 32 16>; - }; - - gpiod: gpio@50005000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 48 16>; - }; - - gpioe: gpio@50006000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 64 16>; - }; - - gpiof: gpio@50007000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 80 16>; - }; - - gpiog: gpio@50008000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 96 16>; - }; - - gpioh: gpio@50009000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 112 16>; - }; - - gpioi: gpio@5000a000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 128 16>; - }; - - gpioj: gpio@5000b000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 144 16>; - }; - - gpiok: gpio@5000c000 { - status = "okay"; - ngpios = <8>; - gpio-ranges = <&pinctrl 0 160 8>; - }; - }; - - pinctrl_z: pin-controller-z@54004000 { - st,package = ; - - gpioz: gpio@54004000 { - status = "okay"; - ngpios = <8>; - gpio-ranges = <&pinctrl_z 0 400 8>; - }; - }; - }; -}; diff --git a/fdts/stm32mp157cac-pinctrl.dtsi b/fdts/stm32mp157cac-pinctrl.dtsi deleted file mode 100644 index 777f9919d..000000000 --- a/fdts/stm32mp157cac-pinctrl.dtsi +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* - * Copyright (C) STMicroelectronics 2018 - All Rights Reserved - * Author: Alexandre Torgue - */ - -#include "stm32mp157-pinctrl.dtsi" -/ { - soc { - pinctrl: pin-controller@50002000 { - st,package = ; - - gpioa: gpio@50002000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 0 16>; - }; - - gpiob: gpio@50003000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 16 16>; - }; - - gpioc: gpio@50004000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 32 16>; - }; - - gpiod: gpio@50005000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 48 16>; - }; - - gpioe: gpio@50006000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 64 16>; - }; - - gpiof: gpio@50007000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 80 16>; - }; - - gpiog: gpio@50008000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 96 16>; - }; - - gpioh: gpio@50009000 { - status = "okay"; - ngpios = <16>; - gpio-ranges = <&pinctrl 0 112 16>; - }; - - gpioi: gpio@5000a000 { - status = "okay"; - ngpios = <12>; - gpio-ranges = <&pinctrl 0 128 12>; - }; - }; - - pinctrl_z: pin-controller-z@54004000 { - st,package = ; - - gpioz: gpio@54004000 { - status = "okay"; - ngpios = <8>; - gpio-ranges = <&pinctrl_z 0 400 8>; - }; - }; - }; -}; diff --git a/fdts/stm32mp157d-dk1.dts b/fdts/stm32mp157d-dk1.dts new file mode 100644 index 000000000..d320f993e --- /dev/null +++ b/fdts/stm32mp157d-dk1.dts @@ -0,0 +1,49 @@ +// 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"; + }; +}; + +&cpu1 { + cpu-supply = <&vddcore>; +}; + +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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.dts b/fdts/stm32mp157d-ed1.dts new file mode 100644 index 000000000..76f0614d6 --- /dev/null +++ b/fdts/stm32mp157d-ed1.dts @@ -0,0 +1,46 @@ +// 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"; + }; + + aliases { + serial0 = &uart4; + }; +}; + +&cpu1 { + cpu-supply = <&vddcore>; +}; + +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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.dts b/fdts/stm32mp157d-ev1.dts new file mode 100644 index 000000000..47d962b57 --- /dev/null +++ b/fdts/stm32mp157d-ev1.dts @@ -0,0 +1,22 @@ +// 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 { + serial1 = &usart3; + }; +}; diff --git a/fdts/stm32mp157f-dk2.dts b/fdts/stm32mp157f-dk2.dts new file mode 100644 index 000000000..9c79bfb43 --- /dev/null +++ b/fdts/stm32mp157f-dk2.dts @@ -0,0 +1,55 @@ +// 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"; + }; +}; + +&cpu1 { + cpu-supply = <&vddcore>; +}; + +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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.dts b/fdts/stm32mp157f-ed1.dts new file mode 100644 index 000000000..a659cf84d --- /dev/null +++ b/fdts/stm32mp157f-ed1.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 "stm32mp15xxaa-pinctrl.dtsi" +#include "stm32mp15xx-edx.dtsi" +#include + +/ { + model = "STMicroelectronics STM32MP157F eval daughter"; + compatible = "st,stm32mp157f-ed1", "st,stm32mp157"; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + aliases { + serial0 = &uart4; + }; +}; + +&cpu1{ + cpu-supply = <&vddcore>; +}; + +&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_S_RW, DECPROT_LOCK) + DECPROT(STM32MP1_ETZPC_DDRPHYC_ID, DECPROT_S_RW, 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.dts b/fdts/stm32mp157f-ev1.dts new file mode 100644 index 000000000..c8598ce55 --- /dev/null +++ b/fdts/stm32mp157f-ev1.dts @@ -0,0 +1,22 @@ +// 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 { + serial1 = &usart3; + }; +}; diff --git a/fdts/stm32mp15xa.dtsi b/fdts/stm32mp15xa.dtsi new file mode 100644 index 000000000..5ed7e594f --- /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 new file mode 100644 index 000000000..68d822d8c --- /dev/null +++ b/fdts/stm32mp15xc.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 "stm32mp15xa.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/stm32mp15xd.dtsi b/fdts/stm32mp15xd.dtsi new file mode 100644 index 000000000..18b05ee38 --- /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 000000000..526a1627c --- /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 new file mode 100644 index 000000000..53790f29b --- /dev/null +++ b/fdts/stm32mp15xx-dkx.dtsi @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include +#include +#include "stm32mp15-ddr3-1x4Gb-1066-binG.dtsi" + +/ { + memory@c0000000 { + device_type = "memory"; + reg = <0xc0000000 0x20000000>; + }; + + 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 = <&vin>; + ldo3-supply = <&vdd_ddr>; + ldo4-supply = <&vin>; + ldo5-supply = <&vin>; + 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; + 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: buck2 { + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-initial-mode = <0>; + regulator-over-current-protection; + 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: 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; + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + }; + + v3v3: buck4 { + regulator-name = "v3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-over-current-protection; + regulator-initial-mode = <0>; + lp-stop { + regulator-suspend-microvolt = <3300000>; + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + v1v8_audio: ldo1 { + regulator-name = "v1v8_audio"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + v3v3_hdmi: ldo2 { + regulator-name = "v3v3_hdmi"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vtt_ddr: ldo3 { + regulator-name = "vtt_ddr"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <750000>; + regulator-always-on; + regulator-over-current-protection; + lp-stop { + regulator-off-in-suspend; + }; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vdd_usb: ldo4 { + regulator-name = "vdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vdda: ldo5 { + regulator-name = "vdda"; + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; + regulator-boot-on; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + v1v2_hdmi: ldo6 { + regulator-name = "v1v2_hdmi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vref_ddr: vref_ddr { + regulator-name = "vref_ddr"; + regulator-always-on; + regulator-over-current-protection; + lp-stop { + regulator-on-in-suspend; + }; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + 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>; + 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 { + 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>; + disable-wp; + st,neg-edge; + bus-width = <4>; + vmmc-supply = <&v3v3>; + 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"; +}; + +&uart7 { + pinctrl-names = "default"; + pinctrl-0 = <&uart7_pins_b>; + status = "disabled"; +}; + +&usart3 { + pinctrl-names = "default"; + pinctrl-0 = <&usart3_pins_b>; + uart-has-rtscts; + status = "disabled"; +}; + +&usbotg_hs { + phys = <&usbphyc_port1 0>; + phy-names = "usb2-phy"; + usb-role-switch; + status = "okay"; +}; + +&usbphyc { + status = "okay"; +}; + +&usbphyc_port0 { + phy-supply = <&vdd_usb>; +}; + +&usbphyc_port1 { + phy-supply = <&vdd_usb>; +}; diff --git a/fdts/stm32mp15xx-edx.dtsi b/fdts/stm32mp15xx-edx.dtsi new file mode 100644 index 000000000..dd921908b --- /dev/null +++ b/fdts/stm32mp15xx-edx.dtsi @@ -0,0 +1,479 @@ +// 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>; + }; + + 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>; +}; + +&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; + 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: buck2 { + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-initial-mode = <0>; + regulator-over-current-protection; + 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: 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; + 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; + }; + }; + + v3v3: buck4 { + regulator-name = "v3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-over-current-protection; + regulator-initial-mode = <0>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vdda: ldo1 { + regulator-name = "vdda"; + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + v2v8: ldo2 { + regulator-name = "v2v8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vtt_ddr: ldo3 { + regulator-name = "vtt_ddr"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <750000>; + regulator-always-on; + regulator-over-current-protection; + 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; + }; + }; + + vdd_usb: ldo4 { + regulator-name = "vdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + standby-ddr-sr { + regulator-on-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vdd_sd: ldo5 { + regulator-name = "vdd_sd"; + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; + regulator-boot-on; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + v1v8: ldo6 { + regulator-name = "v1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + standby-ddr-sr { + regulator-off-in-suspend; + }; + standby-ddr-off { + regulator-off-in-suspend; + }; + }; + + vref_ddr: vref_ddr { + regulator-name = "vref_ddr"; + regulator-always-on; + regulator-over-current-protection; + 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; + }; + }; + + 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>; + 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; + sd-uhs-sdr104; + status = "okay"; +}; + +&sdmmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>; + non-removable; + no-sd; + no-sdio; + st,neg-edge; + bus-width = <8>; + vmmc-supply = <&v3v3>; + vqmmc-supply = <&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>; +}; diff --git a/fdts/stm32mp15xx-evx.dtsi b/fdts/stm32mp15xx-evx.dtsi new file mode 100644 index 000000000..fee2bac86 --- /dev/null +++ b/fdts/stm32mp15xx-evx.dtsi @@ -0,0 +1,71 @@ +// 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"; + #address-cells = <1>; + #size-cells = <0>; + + 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_a>; + 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 new file mode 100644 index 000000000..341529b30 --- /dev/null +++ b/fdts/stm32mp15xxaa-pinctrl.dtsi @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue + */ + +&pinctrl { + st,package = ; + + gpioa: gpio@50002000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 0 16>; + }; + + gpiob: gpio@50003000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 16 16>; + }; + + gpioc: gpio@50004000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 32 16>; + }; + + gpiod: gpio@50005000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 48 16>; + }; + + gpioe: gpio@50006000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 64 16>; + }; + + gpiof: gpio@50007000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 80 16>; + }; + + gpiog: gpio@50008000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 96 16>; + }; + + gpioh: gpio@50009000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 112 16>; + }; + + gpioi: gpio@5000a000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 128 16>; + }; + + gpioj: gpio@5000b000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 144 16>; + }; + + gpiok: gpio@5000c000 { + status = "okay"; + ngpios = <8>; + gpio-ranges = <&pinctrl 0 160 8>; + }; +}; + +&pinctrl_z { + st,package = ; + + 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 new file mode 100644 index 000000000..d29af8986 --- /dev/null +++ b/fdts/stm32mp15xxab-pinctrl.dtsi @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue + */ + +&pinctrl { + st,package = ; + + gpioa: gpio@50002000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 0 16>; + }; + + gpiob: gpio@50003000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 16 16>; + }; + + gpioc: gpio@50004000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 32 16>; + }; + + gpiod: gpio@50005000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 48 16>; + }; + + gpioe: gpio@50006000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 64 16>; + }; + + gpiof: gpio@50007000 { + status = "okay"; + ngpios = <6>; + gpio-ranges = <&pinctrl 6 86 6>; + }; + + gpiog: gpio@50008000 { + status = "okay"; + ngpios = <10>; + gpio-ranges = <&pinctrl 6 102 10>; + }; + + gpioh: gpio@50009000 { + status = "okay"; + ngpios = <2>; + gpio-ranges = <&pinctrl 0 112 2>; + }; +}; diff --git a/fdts/stm32mp15xxac-pinctrl.dtsi b/fdts/stm32mp15xxac-pinctrl.dtsi new file mode 100644 index 000000000..02070bba5 --- /dev/null +++ b/fdts/stm32mp15xxac-pinctrl.dtsi @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue + */ + +&pinctrl { + st,package = ; + + gpioa: gpio@50002000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 0 16>; + }; + + gpiob: gpio@50003000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 16 16>; + }; + + gpioc: gpio@50004000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 32 16>; + }; + + gpiod: gpio@50005000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 48 16>; + }; + + gpioe: gpio@50006000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 64 16>; + }; + + gpiof: gpio@50007000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 80 16>; + }; + + gpiog: gpio@50008000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 96 16>; + }; + + gpioh: gpio@50009000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 112 16>; + }; + + gpioi: gpio@5000a000 { + status = "okay"; + ngpios = <12>; + gpio-ranges = <&pinctrl 0 128 12>; + }; +}; + +&pinctrl_z { + st,package = ; + + 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 new file mode 100644 index 000000000..023f5404c --- /dev/null +++ b/fdts/stm32mp15xxad-pinctrl.dtsi @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Alexandre Torgue + */ + +&pinctrl { + st,package = ; + + gpioa: gpio@50002000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 0 16>; + }; + + gpiob: gpio@50003000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 16 16>; + }; + + gpioc: gpio@50004000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 32 16>; + }; + + gpiod: gpio@50005000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 48 16>; + }; + + gpioe: gpio@50006000 { + status = "okay"; + ngpios = <16>; + gpio-ranges = <&pinctrl 0 64 16>; + }; + + gpiof: gpio@50007000 { + status = "okay"; + ngpios = <6>; + gpio-ranges = <&pinctrl 6 86 6>; + }; + + gpiog: gpio@50008000 { + status = "okay"; + ngpios = <10>; + gpio-ranges = <&pinctrl 6 102 10>; + }; + + gpioh: gpio@50009000 { + status = "okay"; + ngpios = <2>; + gpio-ranges = <&pinctrl 0 112 2>; + }; +}; diff --git a/include/arch/aarch32/arch.h b/include/arch/aarch32/arch.h index 20175481f..3aa7f9126 100644 --- a/include/arch/aarch32/arch.h +++ b/include/arch/aarch32/arch.h @@ -457,13 +457,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 @@ -522,6 +522,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 @@ -531,6 +534,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 @@ -581,6 +585,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 ******************************************************************************/ @@ -634,6 +644,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 cbac84b93..8749dc91d 100644 --- a/include/arch/aarch32/arch_helpers.h +++ b/include/arch/aarch32/arch_helpers.h @@ -246,6 +246,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) @@ -288,6 +291,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 7559de446..4fd746d5a 100644 --- a/include/arch/aarch32/el3_common_macros.S +++ b/include/arch/aarch32/el3_common_macros.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 */ @@ -329,6 +329,11 @@ bl inv_dcache_range #endif + /* + * zeromem uses r12 whereas it is used to save previous BL arg3, + * save it in r7 + */ + mov r7, r12 ldr r0, =__BSS_START__ ldr r1, =__BSS_SIZE__ bl zeromem @@ -339,6 +344,9 @@ bl zeromem #endif + /* Restore r12 */ + mov r12, r7 + #if defined(IMAGE_BL1) || (defined(IMAGE_BL2) && BL2_AT_EL3 && BL2_IN_XIP_MEM) /* ----------------------------------------------------- * Copy data from ROM to RAM. diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h index 3ff2912f1..307dad585 100644 --- a/include/arch/aarch64/arch.h +++ b/include/arch/aarch64/arch.h @@ -670,13 +670,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/confine_array_index.h b/include/common/confine_array_index.h new file mode 100644 index 000000000..23c8b4e67 --- /dev/null +++ b/include/common/confine_array_index.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2020 Linaro Limited */ +// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * Content of LICENSE file mentioned above: +Copyright 2019 The Fuchsia Authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef FBL_CONFINE_ARRAY_INDEX_H_ +#define FBL_CONFINE_ARRAY_INDEX_H_ + +#include + +// confine_array_index() bounds-checks and sanitizes an array index safely in the presence of +// speculative execution information leak bugs such as Spectre V1. confine_array_index() always +// returns a sanitized index, even in speculative-path execution. +// +// Callers need to combine confine_array_index with a conventional bounds check; the bounds +// check will return any necessary errors in the nonspeculative path, confine_array_index will +// confine indexes in the speculative path. +// +// Use: +// confine_array_index() returns |index|, if it is < size, or 0 if |index| is >= size. +// +// Example (may leak table1 contents): +// 1: int lookup3(size_t index) { +// 2: if (index >= table1_size) { +// 3: return -1; +// 4: } +// 5: size_t index2 = table1[index]; +// 6: return table2[index2]; +// 7: } +// +// Converted: +// +// 1: int lookup3(size_t index) { +// 2: if (index >= table1_size) { +// 3: return -1; +// 4: } +// 5: size_t safe_index = confine_array_index(index, table1_size); +// 6: size_t index2 = table1[safe_index]; +// 7: return table2[index2]; +// 8: } +#ifdef __aarch64__ +static inline size_t confine_array_index(size_t index, size_t size) { + size_t safe_index; + // Use a conditional select and a CSDB barrier to enforce validation of |index|. + // See "Cache Speculation Side-channels" whitepaper, section "Software Mitigation". + // "" The combination of both a conditional select/conditional move and the new barrier are + // sufficient to address this problem on ALL Arm implementations... "" + asm( + "cmp %1, %2\n" // %1 holds the unsanitized index + "csel %0, %1, xzr, lo\n" // Select index or zero based on carry (%1 within range) + "csdb\n" + : "=r"(safe_index) + : "r"(index), "r"(size) + : "cc"); + return safe_index; +} +#endif +#ifdef __arm__ +static inline size_t confine_array_index(size_t index, size_t size) +{ + size_t ret_val = index; + + /* + * For the ARMv7/AArch32 case we're basing the select and barrier + * code on __load_no_speculate1() in as we + * lack the csel instruction. + */ + +#ifdef __thumb2__ + asm volatile ( + ".syntax unified\n" + "cmp %0, %1\n" + "it cs\n" +#ifdef __clang__ +#pragma clang diagnostic push + /* Avoid 'deprecated instruction in IT block [-Werror,-Winline-asm]' */ +#pragma clang diagnostic ignored "-Winline-asm" +#endif + "movcs %0, #0\n" +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + ".inst.n 0xf3af\t@ CSDB\n" + ".inst.n 0x8014\t@ CSDB" + : "+r" (ret_val) : "r" (size) : "cc"); +#else + asm volatile ( + ".syntax unified\n" + "cmp %0, %1\n" /* %0 holds the unsanitized index */ + "movcs %0, #0\n" + ".inst 0xe320f014\t@ CSDB" + : "+r" (ret_val) : "r" (size) : "cc"); +#endif + + return ret_val; +} +#endif /* __arm__ */ + +#ifdef __x86_64__ +static inline size_t confine_array_index(size_t index, size_t size) { + size_t safe_index = 0; + // Use a conditional move to enforce validation of |index|. + // The conditional move has a data dependency on the result of a comparison and cannot + // execute until the comparison is resolved. + // See "Software Techniques for Managing Speculation on AMD Processors", Mitigation V1-2. + // See "Analyzing potential bounds check bypass vulnerabilities", Revision 002, + // Section 5.2 Bounds clipping + __asm__( + "cmp %1, %2\n" + "cmova %1, %0\n" // Select between $0 and |index| + : "+r"(safe_index) + : "r"(index), "r"(size) + : "cc"); + return safe_index; +} +#endif +#endif // FBL_CONFINE_ARRAY_INDEX_H_ diff --git a/include/common/speculation_barrier.h b/include/common/speculation_barrier.h new file mode 100644 index 000000000..ef74e3617 --- /dev/null +++ b/include/common/speculation_barrier.h @@ -0,0 +1,508 @@ +/* SPDX-License-Identifier: BSL-1.0 */ +/* Copyright (c) 2017 Arm Limited. All rights reserved. + + Boost Software License - Version 1.0 - August 17th, 2003 + + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to do + so, all subject to the following: + + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, must + be included in all copies of the Software, in whole or in part, and all + derivative works of the Software, unless such copies or derivative works are + solely in the form of machine-executable object code generated by a source + language processor. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR + ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. */ + + +#ifdef __HAVE_LOAD_NO_SPECULATE +#define load_no_speculate(__ptr, __low, __high) \ +(__extension__ ({ \ + __typeof__ ((__ptr)) __ptr_once = (__ptr); \ + __builtin_load_no_speculate (__ptr_once, __low, __high, \ + 0, __ptr_once); \ +})) + +#define load_no_speculate_fail(__ptr, __low, __high, __failval) \ +(__extension__ ({ \ + __typeof__ ((__ptr)) __ptr_once = (__ptr); \ + __builtin_load_no_speculate (__ptr_once, __low, __high, \ + __failval, __ptr_once); \ +})) + +#define load_no_speculate_cmp(__ptr, __low, __high, __failval, __cmpptr) \ + (__builtin_load_no_speculate (__ptr, __low, __high, __failval, __cmpptr)) + +#else + +#ifdef __GNUC__ +#define __UNUSED __attribute__((unused)) +#else +#define __UNUSED +#endif + +#ifdef __aarch64__ + +#define __load_no_speculate1(__ptr, __low, __high, __failval, \ + __cmpptr, __w, __sz) \ +(__extension__ ({ \ + __typeof__ (0 + (*(__ptr))) __nln_val; \ + /* This typecasting is required to ensure correct handling of upper \ + bits of failval, to ensure a clean return from the CSEL below. */ \ + __typeof__(*(__ptr)) __fv \ + = (__typeof__(*(__ptr)))(unsigned long long) (__failval); \ + /* If __high is explicitly NULL, we must not emit the \ + upper-bound comparison. We need to cast __high to an \ + unsigned long long before handing it to __builtin_constant_p to \ + ensure that clang/llvm correctly detects NULL as a constant if it \ + is defined as (void*) 0. */ \ + if (__builtin_constant_p ((unsigned long long)__high) \ + && __high == ((void *)0)) \ + { \ + __asm__ volatile ( \ + "cmp\t%[__c], %[__l]\n\t" \ + "bcc\t.ns%=\n\t" \ + "ldr" __sz "\t%" __w "[__v], %[__p]\n" \ + ".ns%=:\n\t" \ + "csel\t%" __w "[__v], %" __w "[__v], %" __w "[__f], cs\n\t" \ + "hint\t#0x14 // CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&r" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + /* The memory location from which we will load. */ \ + [__p] "m" (*(__ptr)), \ + /* The value to return if the condition check fails. */ \ + [__f] "rZ" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + else \ + { \ + __asm__ volatile ( \ + "cmp\t%[__c], %[__l]\n\t" \ + "ccmp\t%[__c], %[__h], 2, cs\n\t" \ + "bcs\t.ns%=\n\t" \ + "ldr" __sz "\t%" __w "[__v], %[__p]\n" \ + ".ns%=:\n\t" \ + "csel\t%" __w "[__v], %" __w "[__v], %" __w "[__f], cc\n\t" \ + "hint\t#0x14 // CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&r" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + [__h] "r" (__high), \ + /* The memory location from which we will load. */ \ + [__p] "m" (*(__ptr)), \ + /* The value to return if the condition check fails. */ \ + [__f] "rZ" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + (__typeof__ (*(__ptr))) __nln_val; \ +})) + +#define __load_no_speculate(__ptr, __low, __high, __failval, __cmpptr) \ +(__extension__ ({ \ + __typeof__ (0 + *(__ptr)) __nl_val; \ + \ + switch (sizeof(*(__ptr))) { \ + case 1: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, "w", "b"); \ + break; \ + case 2: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, "w", "h"); \ + break; \ + case 4: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, "w", ""); \ + break; \ + case 8: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, "x", ""); \ + break; \ + default: \ + { \ + char __static_assert_no_speculate_load_size_too_big \ + [sizeof (__nl_val) > 8 ? -1 : 1] __UNUSED; \ + break; \ + } \ + } \ + \ + (__typeof__ (*(__ptr))) __nl_val; \ +})) + +#define load_no_speculate(__ptr, __low, __high) \ +(__extension__ ({ \ + __typeof__ ((__ptr)) __ptr_once = (__ptr); \ + __load_no_speculate (__ptr_once, __low, __high, 0, __ptr_once); \ +})) + +#define load_no_speculate_fail(__ptr, __low, __high, __failval) \ +(__extension__ ({ \ + __typeof__ ((__ptr)) __ptr_once = (__ptr); \ + __load_no_speculate (__ptr_once, __low, __high, \ + __failval, __ptr_once); \ +})) + +#define load_no_speculate_cmp(__ptr, __low, __high, __failval, __cmpptr) \ + (__load_no_speculate (__ptr, __low, __high, __failval, __cmpptr)) + +/* AArch32 support for ARM and Thumb-2. Thumb-1 is not supported. */ +#elif defined (__ARM_32BIT_STATE) && (defined (__thumb2__) || !defined (__thumb__)) +#ifdef __thumb2__ +/* Thumb2 case. */ + +#define __load_no_speculate1(__ptr, __low, __high, __failval, \ + __cmpptr, __sz) \ +(__extension__ ({ \ + __typeof__ (0 + *(__ptr)) __nln_val; \ + __typeof__(*(__ptr)) __fv \ + = (__typeof__(*(__ptr)))(unsigned long) (__failval); \ + /* If __high is explicitly NULL, we must not emit the \ + upper-bound comparison. We need to cast __high to an \ + unsigned long before handing it to __builtin_constant_p to \ + ensure that clang/llvm correctly detects NULL as a constant if it \ + is defined as (void*) 0. */ \ + if (__builtin_constant_p ((unsigned long)__high) \ + && __high == ((void *)0)) \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "bcc\t.ns%=\n\t" \ + "ldr" __sz "\t%[__v], %[__p]\n" \ + ".ns%=:\n\t" \ + "it\tcc\n\t" \ + "movcc\t%[__v], %[__f]\n\t" \ + ".inst.n 0xf3af\t@ CSDB\n\t" \ + ".inst.n 0x8014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&l" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + /* The memory location from which we will load. */ \ + [__p] "m" (*(__ptr)), \ + /* The value to return if the condition check fails. */ \ + [__f] "r" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + else \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "it\tcs\n\t" \ + "cmpcs\t%[__h], %[__c]\n\t" \ + "bls\t.ns%=\n\t" \ + "ldr" __sz "\t%[__v], %[__p]\n" \ + ".ns%=:\n\t" \ + "it\tls\n\t" \ + "movls\t%[__v], %[__f]\n\t" \ + ".inst.n 0xf3af\t@ CSDB\n\t" \ + ".inst.n 0x8014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&l" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + [__h] "r" (__high), \ + /* The memory location from which we will load. */ \ + [__p] "m" (*(__ptr)), \ + /* The value to return if the condition check fails. */ \ + [__f] "r" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + (__typeof__ (*(__ptr))) __nln_val; \ +})) \ + \ +/* Double-word version. */ +#define __load_no_speculate2(__ptr, __low, __high, __failval, \ + __cmpptr) \ +(__extension__ ({ \ + __typeof__ (0 + *(__ptr)) __nln_val; \ + __typeof__(*(__ptr)) __fv \ + = (__typeof__(*(__ptr)))(unsigned long) (__failval); \ + /* If __high is explicitly NULL, we must not emit the \ + upper-bound comparison. We need to cast __high to an \ + unsigned long before handing it to __builtin_constant_p to \ + ensure that clang/llvm correctly detects NULL as a constant if it \ + is defined as (void*) 0. */ \ + if (__builtin_constant_p ((unsigned long)__high) \ + && __high == ((void *)0)) \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "bcc\t.ns%=\n\t" \ + "ldr\t%Q[__v], [%[__p]]\n\t" \ + "ldr\t%R[__v], [%[__p], #4]\n" \ + ".ns%=:\n\t" \ + "it\tcc\n\t" \ + "movcc\t%Q[__v], %Q[__f]\n\t" \ + "it\tcc\n\t" \ + "movcc\t%R[__v], %R[__f]\n\t" \ + ".inst.n 0xf3af\t@ CSDB\n\t" \ + ".inst.n 0x8014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&l" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + /* The memory location from which we will load. */ \ + [__p] "r" (__ptr), \ + /* The value to return if the condition check fails. */ \ + [__f] "r" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + else \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "it\tcs\n\t" \ + "cmpcs\t%[__h], %[__c]\n\t" \ + "bls\t.ns%=\n\t" \ + "ldr\t%Q[__v], [%[__p]]\n\t" \ + "ldr\t%R[__v], [%[__p], #4]\n" \ + ".ns%=:\n\t" \ + "it\tls\n\t" \ + "movls\t%Q[__v], %Q[__f]\n\t" \ + "it\tls\n\t" \ + "movls\t%R[__v], %R[__f]\n\t" \ + ".inst.n 0xf3af\t@ CSDB\n\t" \ + ".inst.n 0x8014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&l" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + [__h] "r" (__high), \ + /* The memory location from which we will load. */ \ + [__p] "r" (__ptr), \ + /* The value to return if the condition check fails. */ \ + [__f] "r" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + (__typeof__ (*(__ptr))) __nln_val; \ +})) + +#else +/* ARM case. */ + +#define __load_no_speculate1(__ptr, __low, __high, __failval, \ + __cmpptr, __sz) \ +(__extension__ ({ \ + __typeof__ (0 + *(__ptr)) __nln_val; \ + __typeof__(*(__ptr)) __fv \ + = (__typeof__(*(__ptr)))(unsigned long) (__failval); \ + /* If __high is explicitly NULL, we must not emit the \ + upper-bound comparison. We need to cast __high to an \ + unsigned long before handing it to __builtin_constant_p to \ + ensure that clang/llvm correctly detects NULL as a constant if it \ + is defined as (void*) 0. */ \ + if (__builtin_constant_p ((unsigned long)__high) \ + && __high == ((void *)0)) \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "ldr" __sz "cs\t%[__v], %[__p]\n\t" \ + "movcc\t%[__v], %[__f]\n\t" \ + ".inst 0xe320f014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&r" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + /* The memory location from which we will load. */ \ + [__p] "m" (*(__ptr)), \ + /* The value to return if the condition check fails. */ \ + [__f] "rKI" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + else \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "cmpcs\t%[__h], %[__c]\n\t" \ + "ldr" __sz "hi\t%[__v], %[__p]\n\t" \ + "movls\t%[__v], %[__f]\n\t" \ + ".inst 0xe320f014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&r" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + [__h] "r" (__high), \ + /* The memory location from which we will load. */ \ + [__p] "m" (*(__ptr)), \ + /* The value to return if the condition check fails. */ \ + [__f] "rKI" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + (__typeof__ (*(__ptr))) __nln_val; \ +})) + +/* Double-word version. */ +#define __load_no_speculate2(__ptr, __low, __high, __failval, \ + __cmpptr) \ +(__extension__ ({ \ + __typeof__ (0 + *(__ptr)) __nln_val; \ + __typeof__(*(__ptr)) __fv \ + = (__typeof__(*(__ptr)))(unsigned long) (__failval); \ + /* If __high is explicitly NULL, we must not emit the \ + upper-bound comparison. We need to cast __high to an \ + unsigned long before handing it to __builtin_constant_p to \ + ensure that clang/llvm correctly detects NULL as a constant if it \ + is defined as (void*) 0. */ \ + if (__builtin_constant_p ((unsigned long)__high) \ + && __high == ((void *)0)) \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "ldrcs\t%Q[__v], [%[__p]]\n\t" \ + "ldrcs\t%R[__v], [%[__p], #4]\n\t" \ + "movcc\t%Q[__v], %Q[__f]\n\t" \ + "movcc\t%R[__v], %R[__f]\n\t" \ + ".inst 0xe320f014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&r" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + /* The memory location from which we will load. */ \ + [__p] "r" (__ptr), \ + /* The value to return if the condition check fails. */ \ + [__f] "r" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + else \ + { \ + __asm__ volatile ( \ + ".syntax unified\n\t" \ + "cmp\t%[__c], %[__l]\n\t" \ + "cmpcs\t%[__h], %[__c]\n\t" \ + "ldrhi\t%Q[__v], [%[__p]]\n\t" \ + "ldrhi\t%R[__v], [%[__p], #4]\n\t" \ + "movls\t%Q[__v], %Q[__f]\n\t" \ + "movls\t%R[__v], %R[__f]\n\t" \ + ".inst 0xe320f014\t@ CSDB" \ + /* The value we have loaded, or failval if the condition check \ + fails. */ \ + : [__v] "=&r" (__nln_val) \ + /* The pointer we wish to use for comparisons, and the low and \ + high bounds to use in that comparison. Note that this need \ + not be the same as the pointer from which we will load. */ \ + : [__c] "r" (__cmpptr), [__l] "r" (__low), \ + [__h] "r" (__high), \ + /* The memory location from which we will load. */ \ + [__p] "r" (__ptr), \ + /* The value to return if the condition check fails. */ \ + [__f] "r" (__fv) \ + /* We always clobber the condition codes. */ \ + : "cc"); \ + } \ + (__typeof__ (*(__ptr))) __nln_val; \ +})) + +#endif // __thumb2__ + +/* Common to ARM and Thumb2. */ + +#define __load_no_speculate(__ptr, __low, __high, __failval, __cmpptr) \ +(__extension__ ({ \ + __typeof__ (0 + *(__ptr)) __nl_val; \ + \ + switch (sizeof(*(__ptr))) { \ + case 1: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, "b"); \ + break; \ + case 2: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, "h"); \ + break; \ + case 4: \ + __nl_val = __load_no_speculate1 (__ptr, __low, __high, \ + __failval, __cmpptr, ""); \ + break; \ + case 8: \ + __nl_val = __load_no_speculate2 (__ptr, __low, __high, \ + __failval, __cmpptr); \ + break; \ + default: \ + { \ + char __static_assert_no_speculate_load_size_too_big \ + [sizeof (__nl_val) > 8 ? -1 : 1] __UNUSED; \ + break; \ + } \ + } \ + \ + (__typeof__ (*(__ptr))) __nl_val; \ +})) + +#define load_no_speculate(__ptr, __low, __high) \ +(__extension__ ({ \ + __typeof__ ((__ptr)) __ptr_once = (__ptr); \ + __load_no_speculate (__ptr_once, __low, __high, 0, __ptr_once); \ +})) + +#define load_no_speculate_fail(__ptr, __low, __high, __failval) \ +(__extension__ ({ \ + __typeof__ ((__ptr)) __ptr_once = (__ptr); \ + __load_no_speculate (__ptr_once, __low, __high, \ + __failval, __ptr_once); \ +})) + +#define load_no_speculate_cmp(__ptr, __low, __high, __failval, __cmpptr) \ + (__load_no_speculate (__ptr, __low, __high, __failval, __cmpptr)) + +#else +#error "No fallback provided for load_no_speculate" +#endif + +#endif diff --git a/include/drivers/arm/tzc400.h b/include/drivers/arm/tzc400.h index 32aeb0350..a0d134d64 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-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -33,6 +33,7 @@ #define BUILD_CONFIG_NR_SHIFT 0 #define BUILD_CONFIG_NR_MASK U(0x1f) +#define FILTER_OFFSET 0x10 /* * Number of gate keepers is implementation defined. But we know the max for * this device is 4. Get implementation details from BUILD_CONFIG. @@ -103,39 +104,40 @@ ******************************************************************************/ 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_set_action(unsigned int action); void tzc400_enable_filters(void); void tzc400_disable_filters(void); +void tzc400_clear_all_interrupts(void); +int tzc400_is_pending_interrupt(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 +145,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/io/io_driver.h b/include/drivers/io/io_driver.h index 2b704f491..d8bb435aa 100644 --- a/include/drivers/io/io_driver.h +++ b/include/drivers/io/io_driver.h @@ -39,7 +39,7 @@ typedef struct io_dev_funcs { io_type_t (*type)(void); int (*open)(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); - int (*seek)(io_entity_t *entity, int mode, ssize_t offset); + int (*seek)(io_entity_t *entity, int mode, signed long long offset); int (*size)(io_entity_t *entity, size_t *length); int (*read)(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); diff --git a/include/drivers/io/io_mtd.h b/include/drivers/io/io_mtd.h new file mode 100644 index 000000000..1395ff601 --- /dev/null +++ b/include/drivers/io/io_mtd.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IO_MTD_H +#define IO_MTD_H + +#include +#include + +#include + +/* MTD devices ops */ +typedef struct io_mtd_ops { + /* + * Initialize MTD framework and retrieve device information. + * + * @size: [out] MTD device size in bytes. + * @erase_size: [out] MTD erase size in bytes. + * Return 0 on success, a negative error code otherwise. + */ + int (*init)(unsigned long long *size, unsigned int *erase_size); + + /* + * Execute a read memory operation. + * + * @offset: Offset in bytes to start read operation. + * @buffer: [out] Buffer to store read data. + * @length: Required length to be read in bytes. + * @out_length: [out] Length read in bytes. + * Return 0 on success, a negative error code otherwise. + */ + int (*read)(unsigned int offset, uintptr_t buffer, size_t length, + size_t *out_length); + + /* + * Execute a write memory operation. + * + * @offset: Offset in bytes to start write operation. + * @buffer: Buffer to be written in device. + * @length: Required length to be written in bytes. + * Return 0 on success, a negative error code otherwise. + */ + int (*write)(unsigned int offset, uintptr_t buffer, size_t length); +} io_mtd_ops_t; + +typedef struct io_mtd_dev_spec { + unsigned long long device_size; + unsigned int erase_size; + io_mtd_ops_t ops; +} io_mtd_dev_spec_t; + +struct io_dev_connector; + +int register_io_dev_mtd(const struct io_dev_connector **dev_con); + +#endif /* IO_MTD_H */ diff --git a/include/drivers/io/io_storage.h b/include/drivers/io/io_storage.h index 084c67c47..f04214b69 100644 --- a/include/drivers/io/io_storage.h +++ b/include/drivers/io/io_storage.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,8 +22,11 @@ typedef enum { IO_TYPE_DUMMY, IO_TYPE_FIRMWARE_IMAGE_PACKAGE, IO_TYPE_BLOCK, + IO_TYPE_MTD, IO_TYPE_MMC, IO_TYPE_STM32IMAGE, + IO_TYPE_UART, + IO_TYPE_USB, IO_TYPE_MAX } io_type_t; @@ -86,7 +89,7 @@ int io_dev_close(uintptr_t dev_handle); /* Synchronous operations */ int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle); -int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset); +int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset); int io_size(uintptr_t handle, size_t *length); diff --git a/include/drivers/mmc.h b/include/drivers/mmc.h index 7611f019a..2b4f5ab8b 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 new file mode 100644 index 000000000..1dbb008f9 --- /dev/null +++ b/include/drivers/nand.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_NAND_H +#define DRIVERS_NAND_H + +#include +#include + +#include + +#define PSEC_TO_MSEC(x) div_round_up((x), 1000000000ULL) + +struct ecc { + unsigned int mode; /* ECC mode NAND_ECC_MODE_{NONE|HW|ONDIE} */ + unsigned int size; /* Data byte per ECC step */ + unsigned int bytes; /* ECC bytes per step */ + unsigned int max_bit_corr; /* Max correctible bits per ECC steps */ +}; + +struct nand_device { + unsigned int block_size; + unsigned int page_size; + unsigned long long size; + unsigned int nb_planes; + unsigned int buswidth; + struct ecc ecc; + int (*mtd_block_is_bad)(unsigned int block); + int (*mtd_read_page)(struct nand_device *nand, unsigned int page, + uintptr_t buffer); +}; + +/* + * Read bytes from NAND device + * + * @offset: Byte offset to read from in device + * @buffer: [out] Bytes read from device + * @length: Number of bytes to read + * @length_read: [out] Number of bytes read from device + * Return: 0 on success, a negative errno on failure + */ +int nand_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read); + +/* + * Get NAND device instance + * + * Return: NAND device instance reference + */ +struct nand_device *get_nand_device(void); + +#endif /* DRIVERS_NAND_H */ diff --git a/include/drivers/raw_nand.h b/include/drivers/raw_nand.h new file mode 100644 index 000000000..9018f0242 --- /dev/null +++ b/include/drivers/raw_nand.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2019-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_RAW_NAND_H +#define DRIVERS_RAW_NAND_H + +#include +#include + +#include + +/* NAND ONFI default value mode 0 in picosecond */ +#define NAND_TADL_MIN 400000UL +#define NAND_TALH_MIN 20000UL +#define NAND_TALS_MIN 50000UL +#define NAND_TAR_MIN 25000UL +#define NAND_TCCS_MIN 500000UL +#define NAND_TCEA_MIN 100000UL +#define NAND_TCEH_MIN 20000UL +#define NAND_TCH_MIN 20000UL +#define NAND_TCHZ_MAX 100000UL +#define NAND_TCLH_MIN 20000UL +#define NAND_TCLR_MIN 20000UL +#define NAND_TCLS_MIN 50000UL +#define NAND_TCOH_MIN 0UL +#define NAND_TCS_MIN 70000UL +#define NAND_TDH_MIN 20000UL +#define NAND_TDS_MIN 40000UL +#define NAND_TFEAT_MAX 1000000UL +#define NAND_TIR_MIN 10000UL +#define NAND_TITC_MIN 1000000UL +#define NAND_TR_MAX 200000000UL +#define NAND_TRC_MIN 100000UL +#define NAND_TREA_MAX 40000UL +#define NAND_TREH_MIN 30000UL +#define NAND_TRHOH_MIN 0UL +#define NAND_TRHW_MIN 200000UL +#define NAND_TRHZ_MAX 200000UL +#define NAND_TRLOH_MIN 0UL +#define NAND_TRP_MIN 50000UL +#define NAND_TRR_MIN 40000UL +#define NAND_TRST_MAX 250000000000ULL +#define NAND_TWB_MAX 200000UL +#define NAND_TWC_MIN 100000UL +#define NAND_TWH_MIN 30000UL +#define NAND_TWHR_MIN 120000UL +#define NAND_TWP_MIN 50000UL +#define NAND_TWW_MIN 100000UL + +/* NAND request types */ +#define NAND_REQ_CMD 0x0000U +#define NAND_REQ_ADDR 0x1000U +#define NAND_REQ_DATAIN 0x2000U +#define NAND_REQ_DATAOUT 0x3000U +#define NAND_REQ_WAIT 0x4000U +#define NAND_REQ_MASK GENMASK(14, 12) +#define NAND_REQ_BUS_WIDTH_8 BIT(15) + +#define PARAM_PAGE_SIZE 256 + +/* NAND ONFI commands */ +#define NAND_CMD_READ_1ST 0x00U +#define NAND_CMD_CHANGE_1ST 0x05U +#define NAND_CMD_READID_SIG_ADDR 0x20U +#define NAND_CMD_READ_2ND 0x30U +#define NAND_CMD_STATUS 0x70U +#define NAND_CMD_READID 0x90U +#define NAND_CMD_CHANGE_2ND 0xE0U +#define NAND_CMD_READ_PARAM_PAGE 0xECU +#define NAND_CMD_RESET 0xFFU + +#define ONFI_REV_21 BIT(3) +#define ONFI_FEAT_BUS_WIDTH_16 BIT(0) +#define ONFI_FEAT_EXTENDED_PARAM BIT(7) + +/* NAND ECC type */ +#define NAND_ECC_NONE U(0) +#define NAND_ECC_HW U(1) +#define NAND_ECC_ONDIE U(2) + +/* NAND bus width */ +#define NAND_BUS_WIDTH_8 U(0) +#define NAND_BUS_WIDTH_16 U(1) + +struct nand_req { + struct nand_device *nand; + uint16_t type; + uint8_t *addr; + unsigned int length; + unsigned int delay_ms; + unsigned int inst_delay; +}; + +struct nand_param_page { + /* Rev information and feature block */ + uint32_t page_sig; + uint16_t rev; + uint16_t features; + uint16_t opt_cmd; + uint8_t jtg; + uint8_t train_cmd; + uint16_t ext_param_length; + uint8_t nb_param_pages; + uint8_t reserved1[17]; + /* Manufacturer information */ + uint8_t manufacturer[12]; + uint8_t model[20]; + uint8_t manufacturer_id; + uint16_t data_code; + uint8_t reserved2[13]; + /* Memory organization */ + uint32_t bytes_per_page; + uint16_t spare_per_page; + uint32_t bytes_per_partial; + uint16_t spare_per_partial; + uint32_t num_pages_per_blk; + uint32_t num_blk_in_lun; + uint8_t num_lun; + uint8_t num_addr_cycles; + uint8_t bit_per_cell; + uint16_t max_bb_per_lun; + uint16_t blk_endur; + uint8_t valid_blk_begin; + uint16_t blk_enbur_valid; + uint8_t nb_prog_page; + uint8_t partial_prog_attr; + uint8_t nb_ecc_bits; + uint8_t plane_addr; + uint8_t mplanes_ops; + uint8_t ez_nand; + uint8_t reserved3[12]; + /* Electrical parameters */ + uint8_t io_pin_cap_max; + uint16_t sdr_timing_mode; + uint16_t sdr_prog_cache_timing; + uint16_t tprog; + uint16_t tbers; + uint16_t tr; + uint16_t tccs; + uint8_t nvddr_timing_mode; + uint8_t nvddr2_timing_mode; + uint8_t nvddr_features; + uint16_t clk_input_cap_typ; + uint16_t io_pin_cap_typ; + uint16_t input_pin_cap_typ; + uint8_t input_pin_cap_max; + uint8_t drv_strength_support; + uint16_t tr_max; + uint16_t tadl; + uint16_t tr_typ; + uint8_t reserved4[6]; + /* Vendor block */ + uint16_t vendor_revision; + uint8_t vendor[88]; + uint16_t crc16; +} __packed; + +struct nand_ctrl_ops { + int (*exec)(struct nand_req *req); + void (*setup)(struct nand_device *nand); +}; + +struct rawnand_device { + struct nand_device *nand_dev; + const struct nand_ctrl_ops *ops; +}; + +int nand_raw_init(unsigned long long *size, unsigned int *erase_size); +int nand_wait_ready(unsigned long delay); +int nand_read_page_cmd(unsigned int page, unsigned int offset, + uintptr_t buffer, unsigned int len); +int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer, + unsigned int len); +void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops); + +/* + * Platform can implement this to override default raw NAND instance + * configuration. + * + * @device: target raw NAND instance. + * Return 0 on success, negative value otherwise. + */ +int plat_get_raw_nand_data(struct rawnand_device *device); + +#endif /* DRIVERS_RAW_NAND_H */ diff --git a/include/drivers/spi_mem.h b/include/drivers/spi_mem.h new file mode 100644 index 000000000..d1953acf4 --- /dev/null +++ b/include/drivers/spi_mem.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_SPI_MEM_H +#define DRIVERS_SPI_MEM_H + +#include +#include +#include + +#define SPI_MEM_BUSWIDTH_1_LINE 1U +#define SPI_MEM_BUSWIDTH_2_LINE 2U +#define SPI_MEM_BUSWIDTH_4_LINE 4U + +/* + * enum spi_mem_data_dir - Describes the direction of a SPI memory data + * transfer from the controller perspective. + * @SPI_MEM_DATA_IN: data coming from the SPI memory. + * @SPI_MEM_DATA_OUT: data sent to the SPI memory. + */ +enum spi_mem_data_dir { + SPI_MEM_DATA_IN, + SPI_MEM_DATA_OUT, +}; + +/* + * struct spi_mem_op - Describes a SPI memory operation. + * + * @cmd.buswidth: Number of IO lines used to transmit the command. + * @cmd.opcode: Operation opcode. + * @addr.nbytes: Number of address bytes to send. Can be zero if the operation + * does not need to send an address. + * @addr.buswidth: Number of IO lines used to transmit the address. + * @addr.val: Address value. This value is always sent MSB first on the bus. + * Note that only @addr.nbytes are taken into account in this + * address value, so users should make sure the value fits in the + * assigned number of bytes. + * @dummy.nbytes: Number of dummy bytes to send after an opcode or address. Can + * be zero if the operation does not require dummy bytes. + * @dummy.buswidth: Number of IO lines used to transmit the dummy bytes. + * @data.buswidth: Number of IO lines used to send/receive the data. + * @data.dir: Direction of the transfer. + * @data.nbytes: Number of data bytes to transfer. + * @data.buf: Input or output data buffer depending on data::dir. + */ +struct spi_mem_op { + struct { + uint8_t buswidth; + uint8_t opcode; + } cmd; + + struct { + uint8_t nbytes; + uint8_t buswidth; + uint64_t val; + } addr; + + struct { + uint8_t nbytes; + uint8_t buswidth; + } dummy; + + struct { + uint8_t buswidth; + enum spi_mem_data_dir dir; + unsigned int nbytes; + void *buf; + } data; +}; + +/* SPI mode flags */ +#define SPI_CPHA BIT(0) /* clock phase */ +#define SPI_CPOL BIT(1) /* clock polarity */ +#define SPI_CS_HIGH BIT(2) /* CS active high */ +#define SPI_LSB_FIRST BIT(3) /* per-word bits-on-wire */ +#define SPI_3WIRE BIT(4) /* SI/SO signals shared */ +#define SPI_PREAMBLE BIT(5) /* Skip preamble bytes */ +#define SPI_TX_DUAL BIT(6) /* transmit with 2 wires */ +#define SPI_TX_QUAD BIT(7) /* transmit with 4 wires */ +#define SPI_RX_DUAL BIT(8) /* receive with 2 wires */ +#define SPI_RX_QUAD BIT(9) /* receive with 4 wires */ + +struct spi_bus_ops { + /* + * Claim the bus and prepare it for communication. + * + * @cs: The chip select. + * Returns: 0 if the bus was claimed successfully, or a negative value + * if it wasn't. + */ + int (*claim_bus)(unsigned int cs); + + /* + * Release the SPI bus. + */ + void (*release_bus)(void); + + /* + * Set transfer speed. + * + * @hz: The transfer speed in Hertz. + * Returns: 0 on success, a negative error code otherwise. + */ + int (*set_speed)(unsigned int hz); + + /* + * Set the SPI mode/flags. + * + * @mode: Requested SPI mode (SPI_... flags). + * Returns: 0 on success, a negative error code otherwise. + */ + int (*set_mode)(unsigned int mode); + + /* + * Execute a SPI memory operation. + * + * @op: The memory operation to execute. + * Returns: 0 on success, a negative error code otherwise. + */ + int (*exec_op)(const struct spi_mem_op *op); +}; + +int spi_mem_exec_op(const struct spi_mem_op *op); +int spi_mem_init_slave(void *fdt, int bus_node, + const struct spi_bus_ops *ops); + +#endif /* DRIVERS_SPI_MEM_H */ diff --git a/include/drivers/spi_nand.h b/include/drivers/spi_nand.h new file mode 100644 index 000000000..40e206375 --- /dev/null +++ b/include/drivers/spi_nand.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_SPI_NAND_H +#define DRIVERS_SPI_NAND_H + +#include +#include + +#define SPI_NAND_OP_GET_FEATURE 0x0FU +#define SPI_NAND_OP_SET_FEATURE 0x1FU +#define SPI_NAND_OP_READ_ID 0x9FU +#define SPI_NAND_OP_LOAD_PAGE 0x13U +#define SPI_NAND_OP_RESET 0xFFU +#define SPI_NAND_OP_READ_FROM_CACHE 0x03U +#define SPI_NAND_OP_READ_FROM_CACHE_2X 0x3BU +#define SPI_NAND_OP_READ_FROM_CACHE_4X 0x6BU + +/* Configuration register */ +#define SPI_NAND_REG_CFG 0xB0U +#define SPI_NAND_CFG_ECC_EN BIT(4) +#define SPI_NAND_CFG_QE BIT(0) + +/* Status register */ +#define SPI_NAND_REG_STATUS 0xC0U +#define SPI_NAND_STATUS_BUSY BIT(0) +#define SPI_NAND_STATUS_ECC_UNCOR BIT(5) + +struct spinand_device { + struct nand_device *nand_dev; + struct spi_mem_op spi_read_cache_op; + uint8_t cfg_cache; /* Cached value of SPI NAND device register CFG */ +}; + +int spi_nand_init(unsigned long long *size, unsigned int *erase_size); + +/* + * Platform can implement this to override default SPI-NAND instance + * configuration. + * + * @device: target SPI-NAND instance. + * Return 0 on success, negative value otherwise. + */ +int plat_get_spi_nand_data(struct spinand_device *device); + +#endif /* DRIVERS_SPI_NAND_H */ diff --git a/include/drivers/spi_nor.h b/include/drivers/spi_nor.h new file mode 100644 index 000000000..72cfe5b34 --- /dev/null +++ b/include/drivers/spi_nor.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_SPI_NOR_H +#define DRIVERS_SPI_NOR_H + +#include + +/* OPCODE */ +#define SPI_NOR_OP_WREN 0x06U /* Write enable */ +#define SPI_NOR_OP_WRSR 0x01U /* Write status register 1 byte */ +#define SPI_NOR_OP_READ_ID 0x9FU /* Read JEDEC ID */ +#define SPI_NOR_OP_READ_CR 0x35U /* Read configuration register */ +#define SPI_NOR_OP_READ_SR 0x05U /* Read status register */ +#define SPI_NOR_OP_READ_FSR 0x70U /* Read flag status register */ +#define SPINOR_OP_RDEAR 0xC8U /* Read Extended Address Register */ +#define SPINOR_OP_WREAR 0xC5U /* Write Extended Address Register */ + +/* Used for Spansion flashes only. */ +#define SPINOR_OP_BRWR 0x17U /* Bank register write */ +#define SPINOR_OP_BRRD 0x16U /* Bank register read */ + +#define SPI_NOR_OP_READ 0x03U /* Read data bytes (low frequency) */ +#define SPI_NOR_OP_READ_FAST 0x0BU /* Read data bytes (high frequency) */ +#define SPI_NOR_OP_READ_1_1_2 0x3BU /* Read data bytes (Dual Output SPI) */ +#define SPI_NOR_OP_READ_1_2_2 0xBBU /* Read data bytes (Dual I/O SPI) */ +#define SPI_NOR_OP_READ_1_1_4 0x6BU /* Read data bytes (Quad Output SPI) */ +#define SPI_NOR_OP_READ_1_4_4 0xEBU /* Read data bytes (Quad I/O SPI) */ + +/* Flags for NOR specific configuration */ +#define SPI_NOR_USE_FSR BIT(0) +#define SPI_NOR_USE_BANK BIT(1) + +struct nor_device { + struct spi_mem_op read_op; + uint32_t size; + uint32_t flags; + uint8_t selected_bank; + uint8_t bank_write_cmd; + uint8_t bank_read_cmd; +}; + +int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read); +int spi_nor_init(unsigned long long *device_size, unsigned int *erase_size); + +/* + * Platform can implement this to override default NOR instance configuration. + * + * @device: target NOR instance. + * Return 0 on success, negative value otherwise. + */ +int plat_get_nor_data(struct nor_device *device); + +#endif /* DRIVERS_SPI_NOR_H */ diff --git a/include/drivers/st/bsec.h b/include/drivers/st/bsec.h index d833e7ab2..6601e56a9 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-2019, 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 +#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_LOCK register + * OTP MODE */ -#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) - -/* 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,6 +96,9 @@ 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); @@ -190,14 +116,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 000000000..9da952603 --- /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/etzpc.h b/include/drivers/st/etzpc.h new file mode 100644 index 000000000..c0ed06f6e --- /dev/null +++ b/include/drivers/st/etzpc.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __ETZPC_H__ +#define __ETZPC_H__ + +/* Define security level for each peripheral (DECPROT) */ +enum etzpc_decprot_attributes { + TZPC_DECPROT_S_RW = 0, + TZPC_DECPROT_NS_R_S_W = 1, + TZPC_DECPROT_MCU_ISOLATION = 2, + TZPC_DECPROT_NS_RW = 3, + TZPC_DECPROT_MAX = 4, +}; + +void etzpc_configure_decprot(uint32_t decprot_id, + enum etzpc_decprot_attributes decprot_attr); +enum etzpc_decprot_attributes etzpc_get_decprot(uint32_t decprot_id); +void etzpc_lock_decprot(uint32_t decprot_id); +void etzpc_configure_tzma(uint32_t tzma_id, uint16_t tzma_value); +uint16_t etzpc_get_tzma(uint32_t tzma_id); +void etzpc_lock_tzma(uint32_t tzma_id); +bool etzpc_get_lock_tzma(uint32_t tzma_id); +uint8_t etzpc_get_num_per_sec(void); +uint8_t etzpc_get_revision(void); +uintptr_t etzpc_get_base_address(void); +int etzpc_init(void); + +#endif /* __ETZPC_H__ */ diff --git a/include/drivers/st/io_programmer.h b/include/drivers/st/io_programmer.h new file mode 100644 index 000000000..c6c2de10f --- /dev/null +++ b/include/drivers/st/io_programmer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __IO_PROGRAMMER_H__ +#define __IO_PROGRAMMER_H__ + +/* Phase definition */ +#define PHASE_FLASHLAYOUT 0 +#define PHASE_FSBL1 1 +#define PHASE_FSBL2 2 +#define PHASE_SSBL 3 + +/* Command definition */ +#define GET_CMD_COMMAND 0x00 +#define GET_VER_COMMAND 0x01 +#define GET_ID_COMMAND 0x02 +#define PHASE_COMMAND 0x03 +#define START_COMMAND 0x21 +#define DOWNLOAD_COMMAND 0x31 + +/* Answer defines */ +#define INIT_BYTE 0x7F +#define ACK_BYTE 0x79 +#define NACK_BYTE 0x1F +#define ABORT 0x5F + +#define PROGRAMMER_TIMEOUT 0xFFFFFFFE + +#define DEVICE_ID_BYTE1 0x05 +#define DEVICE_ID_BYTE2 0x00 + +/* phase structure */ +struct phase_struct { + uint32_t keep_header; + uint32_t current_packet; + size_t max_size; + uint8_t phase_id; +}; + +/* current phase struct variable */ +static struct phase_struct current_phase = { + .phase_id = PHASE_FSBL1, + .max_size = 0, + .keep_header = 0, + .current_packet = 0, +}; + +#endif /* __IO_PROGRAMMER_H__ */ diff --git a/include/drivers/st/io_programmer_st_usb.h b/include/drivers/st/io_programmer_st_usb.h new file mode 100644 index 000000000..79effad87 --- /dev/null +++ b/include/drivers/st/io_programmer_st_usb.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __IO_USB_H__ +#define __IO_USB_H__ + +int register_io_dev_usb(const io_dev_connector_t **dev_con); + +#endif /* __IO_USB_H__ */ diff --git a/include/drivers/st/io_stm32image.h b/include/drivers/st/io_stm32image.h index 68060558b..f9fa3630c 100644 --- a/include/drivers/st/io_stm32image.h +++ b/include/drivers/st/io_stm32image.h @@ -23,7 +23,7 @@ struct stm32image_part_info { struct stm32image_device_info { struct stm32image_part_info part_info[STM32_PART_NUM]; - uint32_t device_size; + unsigned long long device_size; uint32_t lba_size; }; diff --git a/include/drivers/st/io_uart.h b/include/drivers/st/io_uart.h new file mode 100644 index 000000000..86f9483a9 --- /dev/null +++ b/include/drivers/st/io_uart.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __IO_UART_H__ +#define __IO_UART_H__ + +int register_io_dev_uart(const io_dev_connector_t **dev_con); + +#endif /* __IO_UART_H__ */ diff --git a/include/drivers/st/scmi-msg.h b/include/drivers/st/scmi-msg.h new file mode 100644 index 000000000..6fa597185 --- /dev/null +++ b/include/drivers/st/scmi-msg.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ + +#ifndef SCMI_MSG_H +#define SCMI_MSG_H + +#include +#include +#include + +/* Minimum size expected for SMT based shared memory message buffers */ +#define SMT_BUF_SLOT_SIZE 128U + +/* A channel abstract a communication path between agent and server */ +struct scmi_msg_channel; + +/* + * struct scmi_msg_channel - Shared memory buffer for a agent-to-server channel + * + * @shm_addr: Address of the shared memory for the SCMI channel + * @shm_size: Byte size of the shared memory for the SCMI channel + * @busy: True when channel is busy, flase when channel is free + * @agent_name: Agent name or NULL, SCMI protocol exposes 16 bytes max. + */ +struct scmi_msg_channel { + uintptr_t shm_addr; + size_t shm_size; + bool busy; + const char *agent_name; +}; + +/* + * Initialize SMT memory buffer, called by platform at init for each + * agent channel using the SMT header format. + * + * @chan: Pointer to the channel shared memory to be initialized + */ +void scmi_smt_init_agent_channel(struct scmi_msg_channel *chan); + +/* + * Process SMT formatted message in a fastcall SMC execution context. + * Called by platform on SMC entry. When returning, output message is + * available in shared memory for agent to read the response. + * + * @agent_id: SCMI agent ID the SMT belongs to + */ +void scmi_smt_fastcall_smc_entry(unsigned int agent_id); + +/* + * Process SMT formatted message in a secure interrupt execution context. + * Called by platform interrupt handler. When returning, output message is + * available in shared memory for agent to read the response. + * + * @agent_id: SCMI agent ID the SMT belongs to + */ +void scmi_smt_interrupt_entry(unsigned int agent_id); + +/* Platform callback functions */ + +/* + * Return the SCMI channel related to an agent + * @agent_id: SCMI agent ID + * Return a pointer to channel on success, NULL otherwise + */ +struct scmi_msg_channel *plat_scmi_get_channel(unsigned int agent_id); + +/* + * Return how many SCMI protocols supported by the platform + * According to the SCMI specification, this function does not target + * a specific agent ID and shall return all platform known capabilities. + */ +size_t plat_scmi_protocol_count(void); + +/* + * Get the count and list of SCMI protocols (but base) supported for an agent + * + * @agent_id: SCMI agent ID + * Return a pointer to a null terminated array supported protocol IDs. + */ +const uint8_t *plat_scmi_protocol_list(unsigned int agent_id); + +/* Get the name of the SCMI vendor for the platform */ +const char *plat_scmi_vendor_name(void); + +/* Get the name of the SCMI sub-vendor for the platform */ +const char *plat_scmi_sub_vendor_name(void); + +/* Handlers for SCMI Clock protocol services */ + +/* + * Return number of clock controllers for an agent + * @agent_id: SCMI agent ID + * Return number of clock controllers + */ +size_t plat_scmi_clock_count(unsigned int agent_id); + +/* + * Get clock controller string ID (aka name) + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * Return pointer to name or NULL + */ +const char *plat_scmi_clock_get_name(unsigned int agent_id, + unsigned int scmi_id); + +/* + * Get clock possible rate as an array of frequencies in Hertz. + * + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * @rates: If NULL, function returns, else output rates array + * @nb_elts: Array size of @rates. + * Return an SCMI compliant error code + */ +int32_t plat_scmi_clock_rates_array(unsigned int agent_id, unsigned int scmi_id, + unsigned long *rates, size_t *nb_elts); + +/* + * Get clock possible rate as range with regular steps in Hertz + * + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * @min_max_step: 3 cell array for min, max and step rate data + * Return an SCMI compliant error code + */ +int32_t plat_scmi_clock_rates_by_step(unsigned int agent_id, + unsigned int scmi_id, + unsigned long *min_max_step); + +/* + * Get clock rate in Hertz + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * Return clock rate or 0 if not supported + */ +unsigned long plat_scmi_clock_get_rate(unsigned int agent_id, + unsigned int scmi_id); + +/* + * Set clock rate in Hertz + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * @rate: Target clock frequency in Hertz + * Return a compliant SCMI error code + */ +int32_t plat_scmi_clock_set_rate(unsigned int agent_id, unsigned int scmi_id, + unsigned long rate); + +/* + * Get clock state (enabled or disabled) + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * Return 1 if clock is enabled, 0 if disables, or a negative SCMI error code + */ +int32_t plat_scmi_clock_get_state(unsigned int agent_id, unsigned int scmi_id); + +/* + * Get clock state (enabled or disabled) + * @agent_id: SCMI agent ID + * @scmi_id: SCMI clock ID + * @enable_not_disable: Enable clock if true, disable clock otherwise + * Return a compliant SCMI error code + */ +int32_t plat_scmi_clock_set_state(unsigned int agent_id, unsigned int scmi_id, + bool enable_not_disable); + +/* Handlers for SCMI Reset Domain protocol services */ + +/* + * Return number of reset domains for the agent + * @agent_id: SCMI agent ID + * Return number of reset domains + */ +size_t plat_scmi_rd_count(unsigned int agent_id); + +/* + * Get reset domain string ID (aka name) + * @agent_id: SCMI agent ID + * @scmi_id: SCMI reset domain ID + * Return pointer to name or NULL + */ +const char *plat_scmi_rd_get_name(unsigned int agent_id, unsigned int scmi_id); + +/* + * Perform a reset cycle on a target reset domain + * @agent_id: SCMI agent ID + * @scmi_id: SCMI reset domain ID + * @state: Target reset state (see SCMI specification, 0 means context loss) + * Return a compliant SCMI error code + */ +int32_t plat_scmi_rd_autonomous(unsigned int agent_id, unsigned int scmi_id, + unsigned int state); + +/* + * Assert or deassert target reset domain + * @agent_id: SCMI agent ID + * @scmi_id: SCMI reset domain ID + * @assert_not_deassert: Assert domain if true, otherwise deassert domain + * Return a compliant SCMI error code + */ +int32_t plat_scmi_rd_set_state(unsigned int agent_id, unsigned int scmi_id, + bool assert_not_deassert); + +#endif /* SCMI_MSG_H */ diff --git a/include/drivers/st/scmi.h b/include/drivers/st/scmi.h new file mode 100644 index 000000000..ac9c798f0 --- /dev/null +++ b/include/drivers/st/scmi.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + */ +#ifndef SCMI_MSG_SCMI_H +#define SCMI_MSG_SCMI_H + +#define SCMI_PROTOCOL_ID_BASE 0x10 +#define SCMI_PROTOCOL_ID_POWER_DOMAIN 0x11 +#define SCMI_PROTOCOL_ID_SYS_POWER 0x12 +#define SCMI_PROTOCOL_ID_PERF 0x13 +#define SCMI_PROTOCOL_ID_CLOCK 0x14 +#define SCMI_PROTOCOL_ID_SENSOR 0x15 +#define SCMI_PROTOCOL_ID_RESET_DOMAIN 0x16 + +/* SCMI error codes reported to agent through server-to-agent messages */ +#define SCMI_SUCCESS 0 +#define SCMI_NOT_SUPPORTED (-1) +#define SCMI_INVALID_PARAMETERS (-2) +#define SCMI_DENIED (-3) +#define SCMI_NOT_FOUND (-4) +#define SCMI_OUT_OF_RANGE (-5) +#define SCMI_BUSY (-6) +#define SCMI_COMMS_ERROR (-7) +#define SCMI_GENERIC_ERROR (-8) +#define SCMI_HARDWARE_ERROR (-9) +#define SCMI_PROTOCOL_ERROR (-10) + +#endif /* SCMI_MSG_SCMI_H */ diff --git a/include/drivers/st/stm32_fmc2_nand.h b/include/drivers/st/stm32_fmc2_nand.h new file mode 100644 index 000000000..81d5b9de1 --- /dev/null +++ b/include/drivers/st/stm32_fmc2_nand.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef STM32_FMC2_NAND_H +#define STM32_FMC2_NAND_H + +int stm32_fmc2_init(void); + +#endif /* STM32_FMC2_NAND_H */ diff --git a/include/drivers/st/stm32_i2c.h b/include/drivers/st/stm32_i2c.h index 170d4cf81..e1a3782b2 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 bad25244a..c0c009779 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_qspi.h b/include/drivers/st/stm32_qspi.h new file mode 100644 index 000000000..f47fca445 --- /dev/null +++ b/include/drivers/st/stm32_qspi.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef STM32_QSPI_H +#define STM32_QSPI_H + +int stm32_qspi_init(void); + +#endif /* STM32_QSPI_H */ diff --git a/include/drivers/st/stm32_rng.h b/include/drivers/st/stm32_rng.h new file mode 100644 index 000000000..a64411865 --- /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 000000000..128dd2d14 --- /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 4853208c2..5b4bd0e16 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 000000000..a4a6a964f --- /dev/null +++ b/include/drivers/st/stm32_tamp.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014-2019, 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_DEDICTED 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 000000000..0e2eb91fe --- /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/stm32mp1_calib.h b/include/drivers/st/stm32mp1_calib.h new file mode 100644 index 000000000..ef69cb456 --- /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 1ebd39ff7..ded8185f2 100644 --- a/include/drivers/st/stm32mp1_clk.h +++ b/include/drivers/st/stm32mp1_clk.h @@ -22,41 +22,49 @@ enum stm32mp_osc_id { extern const char *stm32mp_osc_node_label[NB_OSC]; -int stm32mp1_clk_probe(void); -int stm32mp1_clk_init(void); +#define PLL1_SETTINGS_VALID_ID U(0x504C4C31) /* "PLL1" */ -bool stm32mp1_rcc_is_secure(void); -bool stm32mp1_rcc_is_mckprot(void); +int stm32mp1_clk_probe(void); +int stm32mp1_clk_init(uint32_t pll1_freq_mhz); -void __stm32mp1_clk_enable(unsigned long id, bool caller_is_secure); -void __stm32mp1_clk_disable(unsigned long id, bool caller_is_secure); +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); -static inline void stm32mp1_clk_enable_non_secure(unsigned long id) -{ - __stm32mp1_clk_enable(id, false); -} +int stm32mp1_clk_get_maxfreq_opp(uint32_t *freq_mhz, uint32_t *voltage_mv); -static inline void stm32mp1_clk_enable_secure(unsigned long id) -{ - __stm32mp1_clk_enable(id, true); -} +bool stm32mp1_rcc_is_secure(void); +bool stm32mp1_rcc_is_mckprot(void); -static inline void stm32mp1_clk_disable_non_secure(unsigned long id) -{ - __stm32mp1_clk_disable(id, false); -} +void stm32mp1_clk_force_enable(unsigned long id); +void stm32mp1_clk_force_disable(unsigned long id); -static inline void stm32mp1_clk_disable_secure(unsigned long id) -{ - __stm32mp1_clk_disable(id, true); -} +unsigned long stm32mp_clk_timer_get_rate(unsigned long id); -unsigned int stm32mp1_clk_get_refcount(unsigned long id); +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); -void stm32mp1_stgen_increment(unsigned long long offset_in_ms); +unsigned long stm32mp1_clk_rcc2id(unsigned int offset, unsigned int bit); + +int stm32mp1_round_opp_khz(uint32_t *freq_khz); +int stm32mp1_set_opp_khz(uint32_t freq_khz); + +void stm32mp1_clock_suspend(void); +void stm32mp1_clock_resume(void); + +void stm32mp1_clock_stopmode_save(void); +int stm32mp1_clock_stopmode_resume(void); + +void restore_clock_pm_context(void); +void save_clock_pm_context(void); + +void stm32mp1_register_clock_parents_secure(unsigned long id); + +void stm32mp1_update_earlyboot_clocks_state(void); + +void stm32mp1_dump_clocks_state(void); #endif /* STM32MP1_CLK_H */ diff --git a/include/drivers/st/stm32mp1_ddr.h b/include/drivers/st/stm32mp1_ddr.h index 4ab37d6b4..c8f8d12ad 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; @@ -166,9 +163,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 38f24152a..210beee8a 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,23 @@ #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); +int ddr_standby_sr_entry(uint32_t *zq0cr0_zdata); +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 342239a52..af232a308 100644 --- a/include/drivers/st/stm32mp1_ddr_regs.h +++ b/include/drivers/st/stm32mp1_ddr_regs.h @@ -380,6 +380,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 +400,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 e17df44fb..9b662f2d1 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_rcc.h b/include/drivers/st/stm32mp1_rcc.h index 4b4aac87d..91c6f087b 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 */ @@ -390,7 +390,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 @@ -449,6 +450,9 @@ #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 @@ -462,8 +466,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) @@ -473,6 +484,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) @@ -558,4 +575,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/stm32mp1xx_hal.h b/include/drivers/st/stm32mp1xx_hal.h new file mode 100644 index 000000000..547f7f9ae --- /dev/null +++ b/include/drivers/st/stm32mp1xx_hal.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32_HAL_H +#define __STM32_HAL_H + +#include +#include + +#include +#include +#include + +typedef enum { + HAL_OK = 0x00, + HAL_ERROR = 0x01, + HAL_BUSY = 0x02, + HAL_TIMEOUT = 0x03 +} HAL_StatusTypeDef; + +typedef enum { + HAL_UNLOCKED = 0x00, + HAL_LOCKED = 0x01 +} HAL_LockTypeDef; + +typedef enum { + RESET = 0, + SET = !RESET +} FlagStatus, ITStatus; + +typedef uint32_t Std_ReturnType; + +#define STD_OK ((uint32_t)0x77) +#define STD_NOT_OK ((uint32_t)0x66) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) +#define READ_REG(REG) ((REG)) +#define MODIFY_REG(REG, CLEARMASK, SETMASK) \ + WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != RESET) +#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == RESET) + +#define SET_BIT(_reg, _val) mmio_setbits_32((uintptr_t)&(_reg), _val) +#define CLEAR_BIT(_reg, _val) mmio_clrbits_32((uintptr_t)&(_reg), _val) + +#define __IO volatile /*!< Defines 'read / write' permissions */ +#define __I volatile const /*!< Defines 'read only' permissions */ + +#define HAL_MAX_DELAY 0xFFFFFFFF + +#define assert_param(expr) ((void)0) + +#define HAL_GetTick() (uint32_t)read_cntpct_el0() + +static inline void HAL_Delay(uint32_t x) +{ + udelay(x); +} + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct { + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + uint16_t RESERVED2; /*!< Reserved, 0x12 */ + __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ + __IO uint16_t RQR; /*!< USART Request register, Address offset: 0x18 */ + uint16_t RESERVED3; /*!< Reserved, 0x1A */ + __IO uint32_t ISReg; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint16_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + uint16_t RESERVED4; /*!< Reserved, 0x26 */ + __IO uint16_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ + uint16_t RESERVED5; /*!< Reserved, 0x2A */ + __IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */ +} USART_TypeDef; + +typedef struct { + __IO uint32_t POWER; /*!< SDMMC power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDMMC clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDMMC argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDMMC command register, Address offset: 0x0C */ + __I uint32_t RESPCMD; /*!< SDMMC command response register, Address offset: 0x10 */ + __I uint32_t RESP1; /*!< SDMMC response 1 register, Address offset: 0x14 */ + __I uint32_t RESP2; /*!< SDMMC response 2 register, Address offset: 0x18 */ + __I uint32_t RESP3; /*!< SDMMC response 3 register, Address offset: 0x1C */ + __I uint32_t RESP4; /*!< SDMMC response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDMMC data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDMMC data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDMMC data control register, Address offset: 0x2C */ + __I uint32_t DCOUNT; /*!< SDMMC data counter register, Address offset: 0x30 */ + __I uint32_t STA; /*!< SDMMC status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDMMC interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDMMC mask register, Address offset: 0x3C */ + __IO uint32_t ACKTIME; /*!< SDMMC Acknowledgment timer register, Address offset: 0x40 */ + uint32_t RESERVED0[3]; /*!< Reserved, 0x44 - 0x4C - 0x4C */ + __IO uint32_t IDMACTRL; /*!< SDMMC DMA control register, Address offset: 0x50 */ + __IO uint32_t IDMABSIZE; /*!< SDMMC DMA buffer size register, Address offset: 0x54 */ + __IO uint32_t IDMABASE0; /*!< SDMMC DMA buffer 0 base address register, Address offset: 0x58 */ + __IO uint32_t IDMABASE1; /*!< SDMMC DMA buffer 1 base address register, Address offset: 0x5C */ + uint32_t RESERVED1[8]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDMMC data FIFO register, Address offset: 0x80 */ +} SDMMC_TypeDef; + +typedef struct +{ + __IO uint32_t BCR1; /*!< Address offset: 0x00 */ + __IO uint32_t BTR1; /*!< Address offset: 0x04 */ + __IO uint32_t BCR2; /*!< Address offset: 0x08 */ + __IO uint32_t BTR2; /*!< Address offset: 0x0C */ + __IO uint32_t BCR3; /*!< Address offset: 0x10 */ + __IO uint32_t BTR3; /*!< Address offset: 0x14 */ + __IO uint32_t BCR4; /*!< Address offset: 0x18 */ + __IO uint32_t BTR4; /*!< Address offset: 0x1C */ + uint32_t RESERVED0[24]; + __IO uint32_t PCReg; /*!< Address offset: 0x80 */ + __IO uint32_t SR; /*!< Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< Address offset: 0x88 */ + __IO uint32_t PATT; /*!< Address offset: 0x8C */ + __IO uint32_t HPR; /*! Address offset: 0x90 */ + __IO uint32_t HECCR; /*!< Address offset: 0x94 */ + uint32_t RESERVED2[27]; + __IO uint32_t BWTR1; /*!< Address offset: 0x104 */ + uint32_t RESERVED3; + __IO uint32_t BWTR2; /*!< Address offset: 0x10C */ + uint32_t RESERVED4; + __IO uint32_t BWTR3; /*!< Address offset: 0x114 */ + uint32_t RESERVED5; + __IO uint32_t BWTR4; /*!< Address offset: 0x11c */ + uint32_t RESERVED6[8]; + __IO uint32_t SDCR1; /*!< Address offset: 0x140 */ + __IO uint32_t SDCR2; /*!< Address offset: 0x144 */ + __IO uint32_t SDTR1; /*!< Address offset: 0x148 */ + __IO uint32_t SDTR2; /*!< Address offset: 0x14c */ + __IO uint32_t SDCMR; /*!< Address offset: 0x150 */ + __IO uint32_t SDRTR; /*!< Address offset: 0x154 */ + __IO uint32_t SDSR; /*!< Address offset: 0x158 */ + uint32_t RESERVED7[9]; + __IO uint32_t IER; /*!< Address offset: 0x180 */ + __IO uint32_t ISReg; /*!< Address offset: 0x184 */ + __IO uint32_t ICR; /*!< Address offset: 0x188 */ + uint32_t RESERVED8[29]; + __IO uint32_t CSQCR; /*!< Address offset: 0x200 */ + __IO uint32_t CSQCFGR1; /*!< Address offset: 0x204 */ + __IO uint32_t CSQCFGR2; /*!< Address offset: 0x208 */ + __IO uint32_t CSQCFGR3; /*!< Address offset: 0x20c */ + __IO uint32_t CSQAR1; /*!< Address offset: 0x210 */ + __IO uint32_t CSQAR2; /*!< Address offset: 0x214 */ + uint32_t RESERVED9[2]; + __IO uint32_t CSQIER; /*!< Address offset: 0x220 */ + __IO uint32_t CSQISR; /*!< Address offset: 0x224 */ + __IO uint32_t CSQICR; /*!< Address offset: 0x228 */ + uint32_t RESERVED10; + __IO uint32_t CSQEMSR; /*!< Address offset: 0x230 */ + uint32_t RESERVED11[7]; + __IO uint32_t BCHIER; /*!< Address offset: 0x250 */ + __IO uint32_t BCHISR; /*!< Address offset: 0x254 */ + __IO uint32_t BCHICR; /*!< Address offset: 0x258 */ + __IO uint32_t BCHSR; /*!< Address offset: 0x25c */ + __IO uint32_t BCHPBR1; /*!< Address offset: 0x260 */ + __IO uint32_t BCHPBR2; /*!< Address offset: 0x264 */ + __IO uint32_t BCHPBR3; /*!< Address offset: 0x268 */ + __IO uint32_t BCHPBR4; /*!< Address offset: 0x26c */ + uint32_t RESERVED12[3]; + __IO uint32_t BCHDSR0; /*!< Address offset: 0x27c */ + __IO uint32_t BCHDSR1; /*!< Address offset: 0x280 */ + __IO uint32_t BCHDSR2; /*!< Address offset: 0x284 */ + __IO uint32_t BCHDSR3; /*!< Address offset: 0x288 */ + __IO uint32_t BCHDSR4; /*!< Address offset: 0x28c */ + uint32_t RESERVED13[347]; + __IO uint32_t HWCFGR2; /*!< Address offset: 0x3ec */ + __IO uint32_t HWCFGR1; /*!< Address offset: 0x3f0 */ + __IO uint32_t VER; /*!< Address offset: 0x3f4 */ + __IO uint32_t ID; /*!< Address offset: 0x3f8 */ + __IO uint32_t SID; /*!< Address offset: 0x3fc */ +} FMC_TypeDef; + +#define __HAL_LOCK(__HANDLE__) \ + do{ \ + if((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + }while (0) + + #define __HAL_UNLOCK(__HANDLE__) \ + do{ \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + }while (0) + +#endif /*__STM32_HAL_H*/ diff --git a/include/drivers/st/stm32mp1xx_hal_uart.h b/include/drivers/st/stm32mp1xx_hal_uart.h new file mode 100644 index 000000000..465aa9273 --- /dev/null +++ b/include/drivers/st/stm32mp1xx_hal_uart.h @@ -0,0 +1,1586 @@ +/** + ****************************************************************************** + * @file stm32mp1xx_hal_uart.h + * @author MCD Application Team + * @version V0.3.0 + * @date 14-January-2015 + * @brief Header file of UART HAL module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * SPDX-License-Identifier: BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32MP1xx_HAL_UART_H +#define __STM32MP1xx_HAL_UART_H + +#ifdef __cplusplus + extern "C" { +#endif + +#define HAL_UART_MODULE_ENABLED + +/* Includes ------------------------------------------------------------------*/ +#include +#include + +/** + * @brief UART Init Structure definition + */ +typedef struct +{ + uint32_t BaudRate; /*!< This member configures the UART communication baud rate. + The baud rate register is computed using the following formula: + - If oversampling is 16 or in LIN mode, + Baud Rate Register = ((PCLKx) / ((huart->Init.BaudRate))) + - If oversampling is 8, + Baud Rate Register[15:4] = ((2 * PCLKx) / ((huart->Init.BaudRate)))[15:4] + Baud Rate Register[3] = 0 + Baud Rate Register[2:0] = (((2 * PCLKx) / ((huart->Init.BaudRate)))[3:0]) >> 1 */ + + uint32_t WordLength; /*!< Specifies the number of data bits transmitted or received in a frame. + This parameter can be a value of @ref UARTEx_Word_Length. */ + + uint32_t StopBits; /*!< Specifies the number of stop bits transmitted. + This parameter can be a value of @ref UART_Stop_Bits. */ + + uint32_t Parity; /*!< Specifies the parity mode. + This parameter can be a value of @ref UART_Parity + @note When parity is enabled, the computed parity is inserted + at the MSB position of the transmitted data (9th bit when + the word length is set to 9 data bits; 8th bit when the + word length is set to 8 data bits). */ + + uint32_t Mode; /*!< Specifies whether the Receive or Transmit mode is enabled or disabled. + This parameter can be a value of @ref UART_Mode. */ + + uint32_t HwFlowCtl; /*!< Specifies whether the hardware flow control mode is enabled + or disabled. + This parameter can be a value of @ref UART_Hardware_Flow_Control. */ + + uint32_t OverSampling; /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to f_PCLK/8). + This parameter can be a value of @ref UART_Over_Sampling. */ + + uint32_t OneBitSampling; /*!< Specifies whether a single sample or three samples' majority vote is selected. + Selecting the single sample method increases the receiver tolerance to clock + deviations. This parameter can be a value of @ref UART_OneBit_Sampling. */ + + uint32_t Prescaler; /*!< Specifies the prescaler value used to divide the UART clock source. + This parameter can be a value of @ref UART_Prescaler. */ + + uint32_t FIFOMode; /*!< Specifies if the FIFO mode will be used. This parameter can be a value + of @ref UART_FIFO_mode. */ + + uint32_t TXFIFOThreshold; /*!< Specifies the TXFIFO threshold level. + This parameter can be a value of @ref UART_TXFIFO_threshold_level. */ + + uint32_t RXFIFOThreshold; /*!< Specifies the RXFIFO threshold level. + This parameter can be a value of @ref UART_RXFIFO_threshold_level. */ + +} UART_InitTypeDef; + +/** + * @brief UART Advanced Features initalization structure definition + */ +typedef struct +{ + uint32_t AdvFeatureInit; /*!< Specifies which advanced UART features is initialized. Several + Advanced Features may be initialized at the same time . + This parameter can be a value of @ref UART_Advanced_Features_Initialization_Type. */ + + uint32_t TxPinLevelInvert; /*!< Specifies whether the TX pin active level is inverted. + This parameter can be a value of @ref UART_Tx_Inv. */ + + uint32_t RxPinLevelInvert; /*!< Specifies whether the RX pin active level is inverted. + This parameter can be a value of @ref UART_Rx_Inv. */ + + uint32_t DataInvert; /*!< Specifies whether data are inverted (positive/direct logic + vs negative/inverted logic). + This parameter can be a value of @ref UART_Data_Inv. */ + + uint32_t Swap; /*!< Specifies whether TX and RX pins are swapped. + This parameter can be a value of @ref UART_Rx_Tx_Swap. */ + + uint32_t OverrunDisable; /*!< Specifies whether the reception overrun detection is disabled. + This parameter can be a value of @ref UART_Overrun_Disable. */ + + uint32_t DMADisableonRxError; /*!< Specifies whether the DMA is disabled in case of reception error. + This parameter can be a value of @ref UART_DMA_Disable_on_Rx_Error. */ + + uint32_t AutoBaudRateEnable; /*!< Specifies whether auto Baud rate detection is enabled. + This parameter can be a value of @ref UART_AutoBaudRate_Enable */ + + uint32_t AutoBaudRateMode; /*!< If auto Baud rate detection is enabled, specifies how the rate + detection is carried out. + This parameter can be a value of @ref UART_AutoBaud_Rate_Mode. */ + + uint32_t MSBFirst; /*!< Specifies whether MSB is sent first on UART line. + This parameter can be a value of @ref UART_MSB_First. */ +} UART_AdvFeatureInitTypeDef; + + + +/** + * @brief HAL UART State structures definition + * @note HAL UART State value is a combination of 2 different substates: gState and RxState. + * - gState contains UART state information related to global Handle management + * and also information related to Tx operations. + * gState value coding follow below described bitmap : + * b7-b6 Error information + * 00 : No Error + * 01 : (Not Used) + * 10 : Timeout + * 11 : Error + * b5 IP initilisation status + * 0 : Reset (IP not initialized) + * 1 : Init done (IP not initialized. HAL UART Init function already called) + * b4-b3 (not used) + * xx : Should be set to 00 + * b2 Intrinsic process state + * 0 : Ready + * 1 : Busy (IP busy with some configuration or internal operations) + * b1 (not used) + * x : Should be set to 0 + * b0 Tx state + * 0 : Ready (no Tx operation ongoing) + * 1 : Busy (Tx operation ongoing) + * - RxState contains information related to Rx operations. + * RxState value coding follow below described bitmap : + * b7-b6 (not used) + * xx : Should be set to 00 + * b5 IP initilisation status + * 0 : Reset (IP not initialized) + * 1 : Init done (IP not initialized) + * b4-b2 (not used) + * xxx : Should be set to 000 + * b1 Rx state + * 0 : Ready (no Rx operation ongoing) + * 1 : Busy (Rx operation ongoing) + * b0 (not used) + * x : Should be set to 0. + */ +typedef enum { + HAL_UART_STATE_RESET = 0x00U, /*!< Peripheral is not initialized + Value is allowed for gState and RxState */ + HAL_UART_STATE_READY = 0x20U, /*!< Peripheral Initialized and ready for use + Value is allowed for gState and RxState */ + HAL_UART_STATE_BUSY = 0x24U, /*!< an internal process is ongoing + Value is allowed for gState only */ + HAL_UART_STATE_BUSY_TX = 0x21U, /*!< Data Transmission process is ongoing + Value is allowed for gState only */ + HAL_UART_STATE_BUSY_RX = 0x22U, /*!< Data Reception process is ongoing + Value is allowed for RxState only */ + HAL_UART_STATE_BUSY_TX_RX = 0x23U, /*!< Data Transmission and Reception process is ongoing + Not to be used for neither gState nor RxState. + Value is result of combination (Or) between gState and RxState values */ + HAL_UART_STATE_TIMEOUT = 0xA0U, /*!< Timeout state + Value is allowed for gState only */ + HAL_UART_STATE_ERROR = 0xE0U /*!< Error + Value is allowed for gState only */ +} HAL_UART_StateTypeDef; + +/** + * @brief HAL UART Error Code structure definition + */ +typedef enum +{ + HAL_UART_ERROR_NONE = 0x00U, /*!< No error */ + HAL_UART_ERROR_PE = 0x01U, /*!< Parity error */ + HAL_UART_ERROR_NE = 0x02U, /*!< Noise error */ + HAL_UART_ERROR_FE = 0x04U, /*!< frame error */ + HAL_UART_ERROR_ORE = 0x08U, /*!< Overrun error */ + HAL_UART_ERROR_DMA = 0x10U /*!< DMA transfer error */ +} HAL_UART_ErrorTypeDef; + +/** + * @brief UART clock sources definition + */ +typedef enum { + UART_CLOCKSOURCE_PCLK1 = 0x00U, /*!< PCLK1 clock source */ + UART_CLOCKSOURCE_PCLK2 = 0x01U, /*!< PCLK2 clock source */ + UART_CLOCKSOURCE_PCLK5 = 0x02U, /*!< PCLK5 clock source (only used by UART1) */ + UART_CLOCKSOURCE_PLL3Q = 0x04U, /*!< PLL3Q clock source (only used by UART1) */ + UART_CLOCKSOURCE_PLL4Q = 0x08U, /*!< PLL4Q clock source */ + UART_CLOCKSOURCE_HSI = 0x10U, /*!< HSI clock source */ + UART_CLOCKSOURCE_CSI = 0x20U, /*!< CSI clock source */ + UART_CLOCKSOURCE_HSE = 0x40U, /*!< HSE clock source */ + UART_CLOCKSOURCE_UNDEFINED = 0x80U /*!< Undefined clock source */ +} UART_ClockSourceTypeDef; + +/** + * @brief UART handle Structure definition + */ +typedef struct { + USART_TypeDef *Instance; /*!< UART registers base address */ + + UART_InitTypeDef Init; /*!< UART communication parameters */ + + UART_AdvFeatureInitTypeDef AdvancedInit; /*!< UART Advanced Features initialization parameters */ + + uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */ + + uint16_t TxXferSize; /*!< UART Tx Transfer size */ + + __IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */ + + uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */ + + uint16_t RxXferSize; /*!< UART Rx Transfer size */ + + __IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */ + + uint16_t Mask; /*!< UART Rx RDR register mask */ + +#if defined(HAL_DMA_MODULE_ENABLED) + DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */ + + DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */ +#endif +#if defined(HAL_MDMA_MODULE_ENABLED) + MDMA_HandleTypeDef *hmdmatx; /*!< UART Tx MDMA Handle parameters */ + + MDMA_HandleTypeDef *hmdmarx; /*!< UART Rx MDMA Handle parameters */ +#endif /* HAL_MDMA_MODULE_ENABLED */ + + HAL_LockTypeDef Lock; /*!< Locking object */ + + __IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management + and also related to Tx operations. + This parameter can be a value of @ref HAL_UART_StateTypeDef */ + + __IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations. + This parameter can be a value of @ref HAL_UART_StateTypeDef */ + + __IO uint32_t ErrorCode; /*!< UART Error code */ + +} UART_HandleTypeDef; + +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup UART_Exported_Constants UART Exported Constants + * @{ + */ + +/** @defgroup UART_Stop_Bits UART Number of Stop Bits + * @{ + */ +#define UART_STOPBITS_0_5 USART_CR2_STOP_0 /*!< UART frame with 0.5 stop bit */ +#define UART_STOPBITS_1 ((uint32_t)0x00000000U) /*!< UART frame with 1 stop bit */ +#define UART_STOPBITS_1_5 (USART_CR2_STOP_0 | USART_CR2_STOP_1) /*!< UART frame with 1.5 stop bits */ +#define UART_STOPBITS_2 USART_CR2_STOP_1 /*!< UART frame with 2 stop bits */ +/** + * @} + */ + +/** @defgroup UART_Parity UART Parity + * @{ + */ +#define UART_PARITY_NONE ((uint32_t)0x00000000U) /*!< No parity */ +#define UART_PARITY_EVEN ((uint32_t)USART_CR1_PCE) /*!< Even parity */ +#define UART_PARITY_ODD ((uint32_t)(USART_CR1_PCE | USART_CR1_PS)) /*!< Odd parity */ +/** + * @} + */ + +/** @defgroup UART_Hardware_Flow_Control UART Hardware Flow Control + * @{ + */ +#define UART_HWCONTROL_NONE ((uint32_t)0x00000000U) /*!< No hardware control */ +#define UART_HWCONTROL_RTS ((uint32_t)USART_CR3_RTSE) /*!< Request To Send */ +#define UART_HWCONTROL_CTS ((uint32_t)USART_CR3_CTSE) /*!< Clear To Send */ +#define UART_HWCONTROL_RTS_CTS ((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE)) /*!< Request and Clear To Send */ +/** + * @} + */ + +/** @defgroup UART_Mode UART Transfer Mode + * @{ + */ +#define UART_MODE_RX ((uint32_t)USART_CR1_RE) /*!< RX mode */ +#define UART_MODE_TX ((uint32_t)USART_CR1_TE) /*!< TX mode */ +#define UART_MODE_TX_RX ((uint32_t)(USART_CR1_TE | USART_CR1_RE)) /*!< RX and TX mode */ +/** + * @} + */ + +/** @defgroup UART_State UART State + * @{ + */ +#define UART_STATE_DISABLE ((uint32_t)0x00000000U) /*!< UART disabled */ +#define UART_STATE_ENABLE ((uint32_t)USART_CR1_UE) /*!< UART enabled */ +/** + * @} + */ + +/** @defgroup UART_Over_Sampling UART Over Sampling + * @{ + */ +#define UART_OVERSAMPLING_16 ((uint32_t)0x00000000U) /*!< Oversampling by 16 */ +#define UART_OVERSAMPLING_8 ((uint32_t)USART_CR1_OVER8) /*!< Oversampling by 8 */ +/** + * @} + */ + +/** @defgroup UART_OneBit_Sampling UART One Bit Sampling Method + * @{ + */ +#define UART_ONE_BIT_SAMPLE_DISABLE ((uint32_t)0x00000000U) /*!< One-bit sampling disable */ +#define UART_ONE_BIT_SAMPLE_ENABLE ((uint32_t)USART_CR3_ONEBIT) /*!< One-bit sampling enable */ +/** + * @} + */ + +/** @defgroup UART_Prescaler UART Prescaler + * @{ + */ +#define UART_PRESCALER_DIV1 ((uint32_t)0x00000000U) /*!< UART clock /1 */ +#define UART_PRESCALER_DIV2 ((uint32_t)0x00000001U) /*!< UART clock /2 */ +#define UART_PRESCALER_DIV4 ((uint32_t)0x00000002U) /*!< UART clock /4 */ +#define UART_PRESCALER_DIV6 ((uint32_t)0x00000003U) /*!< UART clock /6 */ +#define UART_PRESCALER_DIV8 ((uint32_t)0x00000004U) /*!< UART clock /8 */ +#define UART_PRESCALER_DIV10 ((uint32_t)0x00000005U) /*!< UART clock /10 */ +#define UART_PRESCALER_DIV12 ((uint32_t)0x00000006U) /*!< UART clock /12 */ +#define UART_PRESCALER_DIV16 ((uint32_t)0x00000007U) /*!< UART clock /16 */ +#define UART_PRESCALER_DIV32 ((uint32_t)0x00000008U) /*!< UART clock /32 */ +#define UART_PRESCALER_DIV64 ((uint32_t)0x00000009U) /*!< UART clock /64 */ +#define UART_PRESCALER_DIV128 ((uint32_t)0x0000000AU) /*!< UART clock /128 */ +#define UART_PRESCALER_DIV256 ((uint32_t)0x0000000BU) /*!< UART clock /256 */ + +/** + * @} + */ + +/** @defgroup UART_FIFO_mode UART FIFO mode + * @brief UART FIFO mode + * @{ + */ +#define UART_FIFOMODE_DISABLE ((uint32_t)0x00000000U) /*!< FIFO mode disable */ +#define UART_FIFOMODE_ENABLE ((uint32_t)USART_CR1_FIFOEN) /*!< FIFO mode enable */ +/** + * @} + */ + +/** @defgroup UART_TXFIFO_threshold_level UART TXFIFO threshold level + * @brief UART TXFIFO level + * @{ + */ +#define UART_TXFIFO_THRESHOLD_1EIGHTHFULL ((uint32_t)0x00000000U) /*!< TXFIFO reaches 1/8 of its depth */ +#define UART_TXFIFO_THRESHOLD_1QUARTERFULL ((uint32_t)USART_CR3_TXFTCFG_0) /*!< TXFIFO reaches 1/4 of its depth */ +#define UART_TXFIFO_THRESHOLD_HALFFULL ((uint32_t)USART_CR3_TXFTCFG_1) /*!< TXFIFO reaches 1/2 of its depth */ +#define UART_TXFIFO_THRESHOLD_3QUARTERSFULL ((uint32_t)(USART_CR3_TXFTCFG_0 | USART_CR3_TXFTCFG_1)) /*!< TXFIFO reaches 3/4 of its depth */ +#define UART_TXFIFO_THRESHOLD_7EIGHTHFULL ((uint32_t)USART_CR3_TXFTCFG_2) /*!< TXFIFO reaches 7/8 of its depth */ +#define UART_TXFIFO_THRESHOLD_EMPTY ((uint32_t)(USART_CR3_TXFTCFG_2 | USART_CR3_TXFTCFG_0)) /*!< TXFIFO becomes empty */ +/** + * @} + */ + +/** @defgroup UART_RXFIFO_threshold_level UART RXFIFO threshold level + * @brief UART RXFIFO level + * @{ + */ +#define UART_RXFIFO_THRESHOLD_1EIGHTHFULL ((uint32_t)0x00000000U) /*!< RXFIFO reaches 1/8 of its depth */ +#define UART_RXFIFO_THRESHOLD_1QUARTERFULL ((uint32_t)USART_CR3_RXFTCFG_0) /*!< RXFIFO reaches 1/4 of its depth */ +#define UART_RXFIFO_THRESHOLD_HALFFULL ((uint32_t)USART_CR3_RXFTCFG_1) /*!< RXFIFO reaches 1/2 of its depth */ +#define UART_RXFIFO_THRESHOLD_3QUARTERSFULL ((uint32_t)(USART_CR3_RXFTCFG_0 | USART_CR3_RXFTCFG_1)) /*!< RXFIFO reaches 3/4 of its depth */ +#define UART_RXFIFO_THRESHOLD_7EIGHTHFULL ((uint32_t)USART_CR3_RXFTCFG_2) /*!< RXFIFO reaches 7/8 of its depth */ +#define UART_RXFIFO_THRESHOLD_FULL ((uint32_t)(USART_CR3_RXFTCFG_2 | USART_CR3_RXFTCFG_0)) /*!< RXFIFO becomes full */ +/** + * @} + */ + +/** @defgroup UART_AutoBaud_Rate_Mode UART Advanced Feature AutoBaud Rate Mode + * @{ + */ +#define UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT ((uint32_t)0x00000000U) /*!< Auto Baud rate detection on start bit */ +#define UART_ADVFEATURE_AUTOBAUDRATE_ONFALLINGEDGE ((uint32_t)USART_CR2_ABRMODE_0) /*!< Auto Baud rate detection on falling edge */ +#define UART_ADVFEATURE_AUTOBAUDRATE_ON0X7FFRAME ((uint32_t)USART_CR2_ABRMODE_1) /*!< Auto Baud rate detection on 0x7F frame detection */ +#define UART_ADVFEATURE_AUTOBAUDRATE_ON0X55FRAME ((uint32_t)USART_CR2_ABRMODE) /*!< Auto Baud rate detection on 0x55 frame detection */ +/** + * @} + */ + +/** @defgroup UART_Receiver_TimeOut UART Receiver TimeOut + * @{ + */ +#define UART_RECEIVER_TIMEOUT_DISABLE ((uint32_t)0x00000000U) /*!< UART receiver timeout disable */ +#define UART_RECEIVER_TIMEOUT_ENABLE ((uint32_t)USART_CR2_RTOEN) /*!< UART receiver timeout enable */ +/** + * @} + */ + +/** @defgroup UART_LIN UART Local Interconnection Network mode + * @{ + */ +#define UART_LIN_DISABLE ((uint32_t)0x00000000U) /*!< Local Interconnect Network disable */ +#define UART_LIN_ENABLE ((uint32_t)USART_CR2_LINEN) /*!< Local Interconnect Network enable */ +/** + * @} + */ + +/** @defgroup UART_LIN_Break_Detection UART LIN Break Detection + * @{ + */ +#define UART_LINBREAKDETECTLENGTH_10B ((uint32_t)0x00000000U) /*!< LIN 10-bit break detection length */ +#define UART_LINBREAKDETECTLENGTH_11B ((uint32_t)USART_CR2_LBDL) /*!< LIN 11-bit break detection length */ +/** + * @} + */ + +/** @defgroup UART_DMA_Tx UART DMA Tx + * @{ + */ +#define UART_DMA_TX_DISABLE ((uint32_t)0x00000000U) /*!< UART DMA TX disabled */ +#define UART_DMA_TX_ENABLE ((uint32_t)USART_CR3_DMAT) /*!< UART DMA TX enabled */ +/** + * @} + */ + +/** @defgroup UART_DMA_Rx UART DMA Rx + * @{ + */ +#define UART_DMA_RX_DISABLE ((uint32_t)0x00000000U) /*!< UART DMA RX disabled */ +#define UART_DMA_RX_ENABLE ((uint32_t)USART_CR3_DMAR) /*!< UART DMA RX enabled */ +/** + * @} + */ + +/** @defgroup UART_Half_Duplex_Selection UART Half Duplex Selection + * @{ + */ +#define UART_HALF_DUPLEX_DISABLE ((uint32_t)0x00000000U) /*!< UART half-duplex disabled */ +#define UART_HALF_DUPLEX_ENABLE ((uint32_t)USART_CR3_HDSEL) /*!< UART half-duplex enabled */ +/** + * @} + */ + +/** @defgroup UART_WakeUp_Methods UART WakeUp Methods + * @{ + */ +#define UART_WAKEUPMETHOD_IDLELINE ((uint32_t)0x00000000U) /*!< UART wake-up on idle line */ +#define UART_WAKEUPMETHOD_ADDRESSMARK ((uint32_t)USART_CR1_WAKE) /*!< UART wake-up on address mark */ +/** + * @} + */ + +/** @defgroup UART_Request_Parameters UART Request Parameters + * @{ + */ +#define UART_AUTOBAUD_REQUEST ((uint32_t)USART_RQR_ABRRQ) /*!< Auto-Baud Rate Request */ +#define UART_SENDBREAK_REQUEST ((uint32_t)USART_RQR_SBKRQ) /*!< Send Break Request */ +#define UART_MUTE_MODE_REQUEST ((uint32_t)USART_RQR_MMRQ) /*!< Mute Mode Request */ +#define UART_RXDATA_FLUSH_REQUEST ((uint32_t)USART_RQR_RXFRQ) /*!< Receive Data flush Request */ +#define UART_TXDATA_FLUSH_REQUEST ((uint32_t)USART_RQR_TXFRQ) /*!< Transmit data flush Request */ +/** + * @} + */ + +/** @defgroup UART_Advanced_Features_Initialization_Type UART Advanced Feature Initialization Type + * @{ + */ +#define UART_ADVFEATURE_NO_INIT ((uint32_t)0x00000000U) /*!< No advanced feature initialization */ +#define UART_ADVFEATURE_TXINVERT_INIT ((uint32_t)0x00000001U) /*!< TX pin active level inversion */ +#define UART_ADVFEATURE_RXINVERT_INIT ((uint32_t)0x00000002U) /*!< RX pin active level inversion */ +#define UART_ADVFEATURE_DATAINVERT_INIT ((uint32_t)0x00000004U) /*!< Binary data inversion */ +#define UART_ADVFEATURE_SWAP_INIT ((uint32_t)0x00000008U) /*!< TX/RX pins swap */ +#define UART_ADVFEATURE_RXOVERRUNDISABLE_INIT ((uint32_t)0x00000010U) /*!< RX overrun disable */ +#define UART_ADVFEATURE_DMADISABLEONERROR_INIT ((uint32_t)0x00000020U) /*!< DMA disable on Reception Error */ +#define UART_ADVFEATURE_AUTOBAUDRATE_INIT ((uint32_t)0x00000040U) /*!< Auto Baud rate detection initialization */ +#define UART_ADVFEATURE_MSBFIRST_INIT ((uint32_t)0x00000080U) /*!< Most significant bit sent/received first */ +/** + * @} + */ + +/** @defgroup UART_Tx_Inv UART Advanced Feature TX Pin Active Level Inversion + * @{ + */ +#define UART_ADVFEATURE_TXINV_DISABLE ((uint32_t)0x00000000U) /*!< TX pin active level inversion disable */ +#define UART_ADVFEATURE_TXINV_ENABLE ((uint32_t)USART_CR2_TXINV) /*!< TX pin active level inversion enable */ +/** + * @} + */ + +/** @defgroup UART_Rx_Inv UART Advanced Feature RX Pin Active Level Inversion + * @{ + */ +#define UART_ADVFEATURE_RXINV_DISABLE ((uint32_t)0x00000000U) /*!< RX pin active level inversion disable */ +#define UART_ADVFEATURE_RXINV_ENABLE ((uint32_t)USART_CR2_RXINV) /*!< RX pin active level inversion enable */ +/** + * @} + */ + +/** @defgroup UART_Data_Inv UART Advanced Feature Binary Data Inversion + * @{ + */ +#define UART_ADVFEATURE_DATAINV_DISABLE ((uint32_t)0x00000000U) /*!< Binary data inversion disable */ +#define UART_ADVFEATURE_DATAINV_ENABLE ((uint32_t)USART_CR2_DATAINV) /*!< Binary data inversion enable */ +/** + * @} + */ + +/** @defgroup UART_Rx_Tx_Swap UART Advanced Feature RX TX Pins Swap + * @{ + */ +#define UART_ADVFEATURE_SWAP_DISABLE ((uint32_t)0x00000000U) /*!< TX/RX pins swap disable */ +#define UART_ADVFEATURE_SWAP_ENABLE ((uint32_t)USART_CR2_SWAP) /*!< TX/RX pins swap enable */ +/** + * @} + */ + +/** @defgroup UART_Overrun_Disable UART Advanced Feature Overrun Disable + * @{ + */ +#define UART_ADVFEATURE_OVERRUN_ENABLE ((uint32_t)0x00000000U) /*!< RX overrun enable */ +#define UART_ADVFEATURE_OVERRUN_DISABLE ((uint32_t)USART_CR3_OVRDIS) /*!< RX overrun disable */ +/** + * @} + */ + +/** @defgroup UART_AutoBaudRate_Enable UART Advanced Feature Auto BaudRate Enable + * @{ + */ +#define UART_ADVFEATURE_AUTOBAUDRATE_DISABLE ((uint32_t)0x00000000U) /*!< RX Auto Baud rate detection enable */ +#define UART_ADVFEATURE_AUTOBAUDRATE_ENABLE ((uint32_t)USART_CR2_ABREN) /*!< RX Auto Baud rate detection disable */ +/** + * @} + */ + +/** @defgroup UART_DMA_Disable_on_Rx_Error UART Advanced Feature DMA Disable On Rx Error + * @{ + */ +#define UART_ADVFEATURE_DMA_ENABLEONRXERROR ((uint32_t)0x00000000U) /*!< DMA enable on Reception Error */ +#define UART_ADVFEATURE_DMA_DISABLEONRXERROR ((uint32_t)USART_CR3_DDRE) /*!< DMA disable on Reception Error */ +/** + * @} + */ + +/** @defgroup UART_MSB_First UART Advanced Feature MSB First + * @{ + */ +#define UART_ADVFEATURE_MSBFIRST_DISABLE ((uint32_t)0x00000000U) /*!< Most significant bit sent/received first disable */ +#define UART_ADVFEATURE_MSBFIRST_ENABLE ((uint32_t)USART_CR2_MSBFIRST) /*!< Most significant bit sent/received first enable */ +/** + * @} + */ + +/** @defgroup UART_Stop_Mode_Enable UART Advanced Feature Stop Mode Enable + * @{ + */ +#define UART_ADVFEATURE_STOPMODE_DISABLE ((uint32_t)0x00000000U) /*!< UART stop mode disable */ +#define UART_ADVFEATURE_STOPMODE_ENABLE ((uint32_t)USART_CR1_UESM) /*!< UART stop mode enable */ +/** + * @} + */ + +/** @defgroup UART_Mute_Mode UART Advanced Feature Mute Mode Enable + * @{ + */ +#define UART_ADVFEATURE_MUTEMODE_DISABLE ((uint32_t)0x00000000U) /*!< UART mute mode disable */ +#define UART_ADVFEATURE_MUTEMODE_ENABLE ((uint32_t)USART_CR1_MME) /*!< UART mute mode enable */ +/** + * @} + */ + +/** @defgroup UART_CR2_ADDRESS_LSB_POS UART Address-matching LSB Position In CR2 Register + * @{ + */ +#define UART_CR2_ADDRESS_LSB_POS ((uint32_t)24U) /*!< UART address-matching LSB position in CR2 register */ +/** + * @} + */ + +/** @defgroup UART_WakeUp_from_Stop_Selection UART WakeUp From Stop Selection + * @{ + */ +#define UART_WAKEUP_ON_ADDRESS ((uint32_t)0x00000000U) /*!< UART wake-up on address */ +#define UART_WAKEUP_ON_STARTBIT ((uint32_t)USART_CR3_WUS_1) /*!< UART wake-up on start bit */ +#define UART_WAKEUP_ON_READDATA_NONEMPTY ((uint32_t)USART_CR3_WUS) /*!< UART wake-up on receive data register not empty */ +#define UART_WAKEUP_ON_RXFIFO_THRESHOLD ((uint32_t)USART_CR3_RXFTIE) /*!< UART wake-up when the RXFIFO reaches threshold */ +#define UART_WAKEUP_ON_RXFIFO_FULL ((uint32_t)USART_CR1_RXFFIE) /*!< UART wake-up when the RXFIFO is full */ +#define UART_WAKEUP_ON_TXFIFO_THRESHOLD ((uint32_t)USART_CR3_TXFTIE) /*!< UART wake-up when the TXFIFO reaches threshold */ +#define UART_WAKEUP_ON_TXFIFO_EMPTY ((uint32_t)USART_CR1_TXFEIE) /*!< UART wake-up when the TXFIFO is empty */ +/** + * @} + */ + +/** @defgroup UART_DriverEnable_Polarity UART DriverEnable Polarity + * @{ + */ +#define UART_DE_POLARITY_HIGH ((uint32_t)0x00000000U) /*!< Driver enable signal is active high */ +#define UART_DE_POLARITY_LOW ((uint32_t)USART_CR3_DEP) /*!< Driver enable signal is active low */ +/** + * @} + */ + +/** @defgroup UART_CR1_DEAT_ADDRESS_LSB_POS UART Driver Enable Assertion Time LSB Position In CR1 Register + * @{ + */ +#define UART_CR1_DEAT_ADDRESS_LSB_POS ((uint32_t)21U) /*!< UART Driver Enable assertion time LSB position in CR1 register */ +/** + * @} + */ + +/** @defgroup UART_CR1_DEDT_ADDRESS_LSB_POS UART Driver Enable DeAssertion Time LSB Position In CR1 Register + * @{ + */ +#define UART_CR1_DEDT_ADDRESS_LSB_POS ((uint32_t)16U) /*!< UART Driver Enable de-assertion time LSB position in CR1 register */ +/** + * @} + */ + +/** @defgroup UART_Interruption_Mask UART Interruptions Flag Mask + * @{ + */ +#define UART_IT_MASK ((uint32_t)0x001FU) /*!< UART interruptions flags mask */ +/** + * @} + */ + +/** @defgroup UART_TimeOut_Value UART polling-based communications time-out value + * @{ + */ +#define HAL_UART_TIMEOUT_VALUE 0x1FFFFFFU /*!< UART polling-based communications time-out value */ +/** + * @} + */ + +/** @defgroup UART_Flags UART Status Flags + * Elements values convention: 0xXXXX + * - 0xXXXX : Flag mask in the ISR register + * @{ + */ +#define UART_FLAG_TXFT USART_ISR_TXFT /*!< UART TXFIFO threshold flag */ +#define UART_FLAG_RXFT USART_ISR_RXFT /*!< UART RXFIFO threshold flag */ +#define UART_FLAG_RXFF USART_ISR_RXFF /*!< UART RXFIFO Full flag */ +#define UART_FLAG_TXFE USART_ISR_TXFE /*!< UART TXFIFO Empty flag */ +#define UART_FLAG_REACK USART_ISR_REACK /*!< UART receive enable acknowledge flag */ +#define UART_FLAG_TEACK USART_ISR_TEACK /*!< UART transmit enable acknowledge flag */ +#define UART_FLAG_WUF USART_ISR_WUF /*!< UART wake-up from stop mode flag */ +#define UART_FLAG_RWU USART_ISR_RWU /*!< UART receiver wake-up from mute mode flag */ +#define UART_FLAG_SBKF USART_ISR_SBKF /*!< UART send break flag */ +#define UART_FLAG_CMF USART_ISR_CMF /*!< UART character match flag */ +#define UART_FLAG_BUSY USART_ISR_BUSY /*!< UART busy flag */ +#define UART_FLAG_ABRF USART_ISR_ABRF /*!< UART auto Baud rate flag */ +#define UART_FLAG_ABRE USART_ISR_ABRE /*!< UART uto Baud rate error */ +#define UART_FLAG_RTOF USART_ISR_RTOF /*!< UART receiver timeout flag */ +#define UART_FLAG_CTS USART_ISR_CTS /*!< UART clear to send flag */ +#define UART_FLAG_CTSIF USART_ISR_CTSIF /*!< UART clear to send interrupt flag */ +#define UART_FLAG_LBDF USART_ISR_LBDF /*!< UART LIN break detection flag */ +#define UART_FLAG_TXE USART_ISR_TXE /*!< UART transmit data register empty */ +#define UART_FLAG_TXFNF USART_ISR_TXE /*!< UART TXFIFO not full */ +#define UART_FLAG_TC USART_ISR_TC /*!< UART transmission complete */ +#define UART_FLAG_RXNE USART_ISR_RXNE /*!< UART read data register not empty */ +#define UART_FLAG_RXFNE USART_ISR_RXNE /*!< UART RXFIFO not empty */ +#define UART_FLAG_IDLE USART_ISR_IDLE /*!< UART idle flag */ +#define UART_FLAG_ORE USART_ISR_ORE /*!< UART overrun error */ +#define UART_FLAG_NE USART_ISR_NE /*!< UART noise error */ +#define UART_FLAG_FE USART_ISR_FE /*!< UART frame error */ +#define UART_FLAG_PE USART_ISR_PE /*!< UART parity error */ +/** + * @} + */ + +/** @defgroup UART_Interrupt_definition UART Interrupts Definition + * Elements values convention: 000ZZZZZ0XXYYYYYb + * - YYYYY : Interrupt source position in the XX register (5bits) + * - XX : Interrupt source register (2bits) + * - 01: CR1 register + * - 10: CR2 register + * - 11: CR3 register + * - ZZZZZ : Flag position in the ISR register(5bits) + * @{ + */ +#define UART_IT_PE ((uint32_t)0x0028U) /*!< UART parity error interruption */ +#define UART_IT_TXE ((uint32_t)0x0727U) /*!< UART transmit data register empty interruption */ +#define UART_IT_TC ((uint32_t)0x0626U) /*!< UART transmission complete interruption */ +#define UART_IT_RXNE ((uint32_t)0x0525U) /*!< UART read data register not empty interruption */ +#define UART_IT_LBD ((uint32_t)0x0846U) /*!< UART LIN break detection interruption */ +#define UART_IT_CTS ((uint32_t)0x096AU) /*!< UART CTS interruption */ +#define UART_IT_CM ((uint32_t)0x112EU) /*!< UART character match interruption */ +#define UART_IT_WUF ((uint32_t)0x1476U) /*!< UART wake-up from stop mode interruption */ +#define UART_IT_RXFF ((uint16_t)0x183FU) +#define UART_IT_TXFE ((uint16_t)0x173EU) +#define UART_IT_RXFT ((uint16_t)0x1A7CU) +#define UART_IT_TXFT ((uint16_t)0x1B77U) + + +/** Elements values convention: 000000000XXYYYYYb + * - YYYYY : Interrupt source position in the XX register (5bits) + * - XX : Interrupt source register (2bits) + * - 01: CR1 register + * - 10: CR2 register + * - 11: CR3 register + */ +#define UART_IT_ERR ((uint32_t)0x0060U) /*!< UART error interruption */ + +/** Elements values convention: 0000ZZZZ00000000b + * - ZZZZ : Flag position in the ISR register(4bits) + */ +#define UART_IT_ORE ((uint32_t)0x0300U) /*!< UART overrun error interruption */ +#define UART_IT_NE ((uint32_t)0x0200U) /*!< UART noise error interruption */ +#define UART_IT_FE ((uint32_t)0x0100U) /*!< UART frame error interruption */ +/** + * @} + */ + +/** @defgroup UART_IT_CLEAR_Flags UART Interruption Clear Flags + * @{ + */ +#define UART_CLEAR_PEF USART_ICR_PECF /*!< Parity Error Clear Flag */ +#define UART_CLEAR_FEF USART_ICR_FECF /*!< Framing Error Clear Flag */ +#define UART_CLEAR_NEF USART_ICR_NCF /*!< Noise detected Clear Flag */ +#define UART_CLEAR_OREF USART_ICR_ORECF /*!< OverRun Error Clear Flag */ +#define UART_CLEAR_IDLEF USART_ICR_IDLECF /*!< IDLE line detected Clear Flag */ +#define UART_CLEAR_TXFECF USART_ICR_TXFECF /*!< TXFIFO empty clear flag */ +#define UART_CLEAR_TCF USART_ICR_TCCF /*!< Transmission Complete Clear Flag */ +#define UART_CLEAR_LBDF USART_ICR_LBDCF /*!< LIN Break Detection Clear Flag */ +#define UART_CLEAR_CTSF USART_ICR_CTSCF /*!< CTS Interrupt Clear Flag */ +#define UART_CLEAR_RTOF USART_ICR_RTOCF /*!< Receiver Time Out Clear Flag */ +#define UART_CLEAR_CMF USART_ICR_CMCF /*!< Character Match Clear Flag */ +#define UART_CLEAR_WUF USART_ICR_WUCF /*!< Wake Up from stop mode Clear Flag */ +/** + * @} + */ + + +/** + * @} + */ + +/* Exported macros -----------------------------------------------------------*/ +/** @defgroup UART_Exported_Macros UART Exported Macros + * @{ + */ + +/** @brief Reset UART handle states. + * @param __HANDLE__: UART handle. + * @retval None + */ +#define __HAL_UART_RESET_HANDLE_STATE(__HANDLE__) \ + do { \ + (__HANDLE__)->gState = HAL_UART_STATE_RESET; \ + (__HANDLE__)->RxState = HAL_UART_STATE_RESET; \ + } while (0) + +/** @brief Flush the UART Data registers. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_FLUSH_DRREGISTER(__HANDLE__) \ + do { \ + SET_BIT((__HANDLE__)->Instance->RQR, \ + UART_RXDATA_FLUSH_REQUEST); \ + SET_BIT((__HANDLE__)->Instance->RQR, \ + UART_TXDATA_FLUSH_REQUEST); \ + } while (0) + +/** @brief Clear the specified UART pending flag. + * @param __HANDLE__: specifies the UART Handle. + * @param __FLAG__: specifies the flag to check. + * This parameter can be any combination of the following values: + * @arg UART_FLAG_WUF: Wake up from stop mode flag + * @arg UART_FLAG_CMF: Character match flag + * @arg UART_FLAG_RTOF: Receiver timeout flag + * @arg UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5) + * @arg UART_FLAG_LBD: LIN Break detection flag + * @arg UART_FLAG_TC: Transmission Complete flag + * @arg UART_FLAG_TXFE: TXFIFO Empty flag + * @arg UART_FLAG_IDLE: Idle Line detection flag + * @arg UART_FLAG_ORE: OverRun Error flag + * @arg UART_FLAG_NE: Noise Error flag + * @arg UART_FLAG_FE: Framing Error flag + * @arg UART_FLAG_PE: Parity Error flag + * @retval The new state of __FLAG__ (TRUE or FALSE). + */ +#define __HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__) \ + ((__HANDLE__)->Instance->ICR = (__FLAG__)) + +/** @brief Clear the UART PE pending flag. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_CLEAR_PEFLAG(__HANDLE__) \ + __HAL_UART_CLEAR_FLAG((__HANDLE__), UART_CLEAR_PEF) + +/** @brief Clear the UART FE pending flag. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_CLEAR_FEFLAG(__HANDLE__) \ + __HAL_UART_CLEAR_FLAG((__HANDLE__), UART_CLEAR_FEF) + +/** @brief Clear the UART NE pending flag. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_CLEAR_NEFLAG(__HANDLE__) \ + __HAL_UART_CLEAR_FLAG((__HANDLE__), UART_CLEAR_NEF) + +/** @brief Clear the UART ORE pending flag. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_CLEAR_OREFLAG(__HANDLE__) \ + __HAL_UART_CLEAR_FLAG((__HANDLE__), UART_CLEAR_OREF) + +/** @brief Clear the UART IDLE pending flag. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) \ + __HAL_UART_CLEAR_FLAG((__HANDLE__), UART_CLEAR_IDLEF) + +/** @brief Clear the UART TX FIFO empty clear flag. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_CLEAR_TXFECF(__HANDLE__) \ + __HAL_UART_CLEAR_FLAG((__HANDLE__), UART_CLEAR_TXFECF) + +/** @brief Check whether the specified UART flag is set or not. + * @param __HANDLE__: specifies the UART Handle. + * @param __FLAG__: specifies the flag to check. + * This parameter can be one of the following values: + * @arg UART_FLAG_TXFT: TXFIFO threshold flag + * @arg UART_FLAG_RXFT: RXFIFO threshold flag + * @arg UART_FLAG_RXFF: RXFIFO Full flag + * @arg UART_FLAG_TXFE: TXFIFO Empty flag + * @arg UART_FLAG_REACK: Receive enable acknowledge flag + * @arg UART_FLAG_TEACK: Transmit enable acknowledge flag + * @arg UART_FLAG_WUF: Wake up from stop mode flag + * @arg UART_FLAG_RWU: Receiver wake up flag (if the UART in mute mode) + * @arg UART_FLAG_SBKF: Send Break flag + * @arg UART_FLAG_CMF: Character match flag + * @arg UART_FLAG_BUSY: Busy flag + * @arg UART_FLAG_ABRF: Auto Baud rate detection flag + * @arg UART_FLAG_ABRE: Auto Baud rate detection error flag + * @arg UART_FLAG_RTOF: Receiver timeout flag + * @arg UART_FLAG_CTS: CTS Change flag + * @arg UART_FLAG_LBD: LIN Break detection flag + * @arg UART_FLAG_TXE: Transmit data register empty flag + * @arg UART_FLAG_TC: Transmission Complete flag + * @arg UART_FLAG_RXNE: Receive data register not empty flag + * @arg UART_FLAG_IDLE: Idle Line detection flag + * @arg UART_FLAG_ORE: OverRun Error flag + * @arg.UART_FLAG_NE: Noise Error flag + * @arg UART_FLAG_FE: Framing Error flag + * @arg UART_FLAG_PE: Parity Error flag + * @retval The new state of __FLAG__ (TRUE or FALSE). + */ +#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) \ + (((__HANDLE__)->Instance->ISReg & (__FLAG__)) == (__FLAG__)) + +/** @brief Enable the specified UART interrupt. + * @param __HANDLE__: specifies the UART Handle. + * @param __INTERRUPT__: specifies the UART interrupt source to enable. + * This parameter can be one of the following values: + * @arg UART_IT_RXFF : RXFIFO Full interrupt + * @arg UART_IT_TXFE : TXFIFO Empty interrupt + * @arg UART_IT_RXFT : RXFIFO threshold interrupt + * @arg UART_IT_TXFT : TXFIFO threshold interrupt + * @arg UART_IT_WUF: Wakeup from stop mode interrupt + * @arg UART_IT_CM: Character match interrupt + * @arg UART_IT_CTS: CTS change interrupt + * @arg UART_IT_LBD: LIN Break detection interrupt + * @arg UART_IT_TXE: Transmit Data Register empty interrupt + * @arg UART_IT_TC: Transmission complete interrupt + * @arg UART_IT_RXNE: Receive Data register not empty interrupt + * @arg UART_IT_IDLE: Idle line detection interrupt + * @arg UART_IT_PE: Parity Error interrupt + * @arg UART_IT_ERR: Error interrupt (Frame error, noise error, overrun error) + * @retval None + */ +#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) \ + (((((uint8_t)((__INTERRUPT__) & 0xFF)) >> 5U) == 1) ? \ + ((__HANDLE__)->Instance->CR1 |= \ + (1U << ((__INTERRUPT__) & UART_IT_MASK))) : \ + ((((uint8_t)((__INTERRUPT__) & 0xFF)) >> 5U) == 2) ? \ + ((__HANDLE__)->Instance->CR2 |= \ + (1U << ((__INTERRUPT__) & UART_IT_MASK))) : \ + ((__HANDLE__)->Instance->CR3 |= \ + (1U << ((__INTERRUPT__) & UART_IT_MASK)))) + + +/** @brief Disable the specified UART interrupt. + * @param __HANDLE__: specifies the UART Handle. + * @param __INTERRUPT__: specifies the UART interrupt source to disable. + * This parameter can be one of the following values: + * @arg UART_IT_RXFF : RXFIFO Full interrupt + * @arg UART_IT_TXFE : TXFIFO Empty interrupt + * @arg UART_IT_RXFT : RXFIFO threshold interrupt + * @arg UART_IT_TXFT : TXFIFO threshold interrupt + * @arg UART_IT_WUF: Wakeup from stop mode interrupt + * @arg UART_IT_CM: Character match interrupt + * @arg UART_IT_CTS: CTS change interrupt + * @arg UART_IT_LBD: LIN Break detection interrupt + * @arg UART_IT_TXE: Transmit Data Register empty interrupt + * @arg UART_IT_TC: Transmission complete interrupt + * @arg UART_IT_RXNE: Receive Data register not empty interrupt + * @arg UART_IT_IDLE: Idle line detection interrupt + * @arg UART_IT_PE: Parity Error interrupt + * @arg UART_IT_ERR: Error interrupt (Frame error, noise error, overrun error) + * @retval None + */ +#define __HAL_UART_DISABLE_IT(__HANDLE__, __INTERRUPT__) \ + (((((uint8_t)((__INTERRUPT__) & 0xFF)) >> 5U) == 1) ? \ + ((__HANDLE__)->Instance->CR1 &= \ + ~(1U << ((__INTERRUPT__) & UART_IT_MASK))) : \ + ((((uint8_t)((__INTERRUPT__) & 0xFF)) >> 5U) == 2) ? \ + ((__HANDLE__)->Instance->CR2 &= \ + ~(1U << ((__INTERRUPT__) & UART_IT_MASK))) : \ + ((__HANDLE__)->Instance->CR3 &= \ + ~(1U << ((__INTERRUPT__) & UART_IT_MASK)))) + +/** @brief Check whether the specified UART interrupt has occurred or not. + * @param __HANDLE__: specifies the UART Handle. + * @param __IT__: specifies the UART interrupt to check. + * This parameter can be one of the following values: + * @arg UART_IT_RXFF : RXFIFO Full interrupt + * @arg UART_IT_TXFE : TXFIFO Empty interrupt + * @arg UART_IT_RXFT : RXFIFO threshold interrupt + * @arg UART_IT_TXFT : TXFIFO threshold interrupt + * @arg UART_IT_WUF: Wakeup from stop mode interrupt + * @arg UART_IT_CM: Character match interrupt + * @arg UART_IT_CTS: CTS change interrupt + * @arg UART_IT_LBD: LIN Break detection interrupt + * @arg UART_IT_TXE: Transmit Data Register empty interrupt + * @arg UART_IT_TC: Transmission complete interrupt + * @arg UART_IT_RXNE: Receive Data register not empty interrupt + * @arg UART_IT_IDLE: Idle line detection interrupt + * @arg UART_IT_ORE: OverRun Error interrupt + * @arg UART_IT_NE: Noise Error interrupt + * @arg UART_IT_FE: Framing Error interrupt + * @arg UART_IT_PE: Parity Error interrupt + * @retval The new state of __IT__ (TRUE or FALSE). + */ +#define __HAL_UART_GET_IT(__HANDLE__, __IT__) \ + ((__HANDLE__)->Instance->ISReg & ((uint32_t)1 << ((__IT__) >> 0x08))) + +/** @brief Check whether the specified UART interrupt source is enabled or not. + * @param __HANDLE__: specifies the UART Handle. + * @param __IT__: specifies the UART interrupt source to check. + * This parameter can be one of the following values: + * @arg UART_IT_RXFF : RXFIFO Full interrupt + * @arg UART_IT_TXFE : TXFIFO Empty interrupt + * @arg UART_IT_RXFT : RXFIFO threshold interrupt + * @arg UART_IT_TXFT : TXFIFO threshold interrupt + * @arg UART_IT_CTS: CTS change interrupt (not available for UART4 and UART5) + * @arg UART_IT_LBD: LIN Break detection interrupt + * @arg UART_IT_TXE: Transmit Data Register empty interrupt + * @arg UART_IT_TC: Transmission complete interrupt + * @arg UART_IT_RXNE: Receive Data register not empty interrupt + * @arg UART_IT_IDLE: Idle line detection interrupt + * @arg UART_IT_ORE: OverRun Error interrupt + * @arg UART_IT_NE: Noise Error interrupt + * @arg UART_IT_FE: Framing Error interrupt + * @arg UART_IT_PE: Parity Error interrupt + * @retval The new state of __IT__ (TRUE or FALSE). + */ +#define __HAL_UART_GET_IT_SOURCE(__HANDLE__, __IT__) \ + ((((((uint8_t)(__IT__)) >> 5U) == 1) ? \ + (__HANDLE__)->Instance->CR1 : \ + (((((uint8_t)(__IT__)) >> 5U) == 2) ? \ + (__HANDLE__)->Instance->CR2 : \ + (__HANDLE__)->Instance->CR3)) & \ + ((uint32_t)1 << (((uint16_t)(__IT__)) & UART_IT_MASK))) + +/** @brief Clear the specified UART ISR flag, in setting the proper ICR register flag. + * @param __HANDLE__: specifies the UART Handle. + * @param __IT_CLEAR__: specifies the interrupt clear register flag that needs to be set + * to clear the corresponding interrupt + * This parameter can be one of the following values: + * @arg UART_CLEAR_PEF: Parity Error Clear Flag + * @arg UART_CLEAR_FEF: Framing Error Clear Flag + * @arg UART_CLEAR_NEF: Noise detected Clear Flag + * @arg UART_CLEAR_OREF: OverRun Error Clear Flag + * @arg UART_CLEAR_IDLEF: IDLE line detected Clear Flag + * @arg UART_CLEAR_TCF: Transmission Complete Clear Flag + * @arg UART_CLEAR_LBDF: LIN Break Detection Clear Flag + * @arg UART_CLEAR_CTSF: CTS Interrupt Clear Flag + * @arg UART_CLEAR_RTOF: Receiver Time Out Clear Flag + * @arg UART_CLEAR_CMF: Character Match Clear Flag + * @arg UART_CLEAR_WUF: Wake Up from stop mode Clear Flag + * @arg UART_CLEAR_TXFECF: TXFIFO empty Clear Flag + * @retval None + */ +#define __HAL_UART_CLEAR_IT(__HANDLE__, __IT_CLEAR__) \ + ((__HANDLE__)->Instance->ICR = (uint32_t)(__IT_CLEAR__)) + +/** @brief Set a specific UART request flag. + * @param __HANDLE__: specifies the UART Handle. + * @param __REQ__: specifies the request flag to set + * This parameter can be one of the following values: + * @arg UART_AUTOBAUD_REQUEST: Auto-Baud Rate Request + * @arg UART_SENDBREAK_REQUEST: Send Break Request + * @arg UART_MUTE_MODE_REQUEST: Mute Mode Request + * @arg UART_RXDATA_FLUSH_REQUEST: Receive Data flush Request + * @arg UART_TXDATA_FLUSH_REQUEST: Transmit data flush Request + * @retval None + */ +#define __HAL_UART_SEND_REQ(__HANDLE__, __REQ__) \ + ((__HANDLE__)->Instance->RQR |= (uint32_t)(__REQ__)) + +/** @brief Enable the UART one bit sample method. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_ONE_BIT_SAMPLE_ENABLE(__HANDLE__) \ + ((__HANDLE__)->Instance->CR3 |= USART_CR3_ONEBIT) + +/** @brief Disable the UART one bit sample method. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_ONE_BIT_SAMPLE_DISABLE(__HANDLE__) \ + ((__HANDLE__)->Instance->CR3 &= (uint32_t)~((uint32_t)USART_CR3_ONEBIT)) + +/** @brief Enable UART. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_ENABLE(__HANDLE__) \ + ((__HANDLE__)->Instance->CR1 |= USART_CR1_UE) + +/** @brief Disable UART. + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_DISABLE(__HANDLE__) \ + ((__HANDLE__)->Instance->CR1 &= ~USART_CR1_UE) + +/** @brief Enable TX UART + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_ENABLE_TX(__HANDLE__) \ + ((__HANDLE__)->Instance->CR1 |= USART_CR1_TE) + +/** @brief Disable TX UART + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_DISABLE_TX(__HANDLE__) \ + ((__HANDLE__)->Instance->CR1 &= ~USART_CR1_TE) + +/** @brief Enable CTS flow control + * This macro allows to enable CTS hardware flow control for a given UART instance, + * without need to call HAL_UART_Init() function. + * As involving direct access to UART registers, usage of this macro should be fully endorsed by user. + * @note As macro is expected to be used for modifying CTS Hw flow control feature activation, without need + * for USART instance Deinit/Init, following conditions for macro call should be fulfilled : + * - UART instance should have already been initialised (through call of HAL_UART_Init() ) + * - macro could only be called when corresponding UART instance is disabled (i.e. __HAL_UART_DISABLE(__HANDLE__)) + * and should be followed by an Enable macro (i.e. __HAL_UART_ENABLE(__HANDLE__)). + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_HWCONTROL_CTS_ENABLE(__HANDLE__) \ + do { \ + SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_CTSE); \ + (__HANDLE__)->Init.HwFlowCtl |= USART_CR3_CTSE; \ + } while (0) + +/** @brief Disable CTS flow control. + * @note This macro allows to disable CTS hardware flow control for a given UART instance, + * without need to call HAL_UART_Init() function. + * As involving direct access to UART registers, usage of this macro should be fully endorsed by user. + * @note As macro is expected to be used for modifying CTS Hw flow control feature activation, without need + * for USART instance Deinit/Init, following conditions for macro call should be fulfilled : + * - UART instance should have already been initialised (through call of HAL_UART_Init() ) + * - macro could only be called when corresponding UART instance is disabled (i.e. __HAL_UART_DISABLE(__HANDLE__)) + * and should be followed by an Enable macro (i.e. __HAL_UART_ENABLE(__HANDLE__)). + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_HWCONTROL_CTS_DISABLE(__HANDLE__) \ + do { \ + CLEAR_BIT((__HANDLE__)->Instance->CR3, USART_CR3_CTSE); \ + (__HANDLE__)->Init.HwFlowCtl &= ~(USART_CR3_CTSE); \ + } while (0) + +/** @brief Enable RTS flow control. + * @note This macro allows to enable RTS hardware flow control for a given UART instance, + * without need to call HAL_UART_Init() function. + * As involving direct access to UART registers, usage of this macro should be fully endorsed by user. + * @note As macro is expected to be used for modifying RTS Hw flow control feature activation, without need + * for USART instance Deinit/Init, following conditions for macro call should be fulfilled : + * - UART instance should have already been initialised (through call of HAL_UART_Init() ) + * - macro could only be called when corresponding UART instance is disabled (i.e. __HAL_UART_DISABLE(__HANDLE__)) + * and should be followed by an Enable macro (i.e. __HAL_UART_ENABLE(__HANDLE__)). + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_HWCONTROL_RTS_ENABLE(__HANDLE__) \ + do { \ + SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_RTSE); \ + (__HANDLE__)->Init.HwFlowCtl |= USART_CR3_RTSE; \ + } while (0) + +/** @brief Disable RTS flow control. + * @note This macro allows to disable RTS hardware flow control for a given UART instance, + * without need to call HAL_UART_Init() function. + * As involving direct access to UART registers, usage of this macro should be fully endorsed by user. + * @note As macro is expected to be used for modifying RTS Hw flow control feature activation, without need + * for USART instance Deinit/Init, following conditions for macro call should be fulfilled : + * - UART instance should have already been initialised (through call of HAL_UART_Init() ) + * - macro could only be called when corresponding UART instance is disabled (i.e. __HAL_UART_DISABLE(__HANDLE__)) + * and should be followed by an Enable macro (i.e. __HAL_UART_ENABLE(__HANDLE__)). + * @param __HANDLE__: specifies the UART Handle. + * @retval None + */ +#define __HAL_UART_HWCONTROL_RTS_DISABLE(__HANDLE__) \ + do { \ + CLEAR_BIT((__HANDLE__)->Instance->CR3, USART_CR3_RTSE);\ + (__HANDLE__)->Init.HwFlowCtl &= ~(USART_CR3_RTSE); \ + } while (0) + +/** + * @} + */ + +/* Private variables -----------------------------------------------------*/ +/** @defgroup UART_Private_Variables UART Private Variables + * @{ + */ +static const uint16_t presc_table[12] = { + 1, 2, 4, 6, 8, 10, 12, 16, 32, 64, 128, 256 +}; + +/** @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 inline 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 inline 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; + +} + +/* Private macros --------------------------------------------------------*/ +/** @defgroup UART_Private_Macros UART Private Macros + * @{ + */ +/** @brief Check UART Baud rate. + * @param __BAUDRATE__: Baudrate specified by the user. + * The maximum Baud Rate is derived from the maximum clock on MP1 (i.e. 100 MHz) + * divided by the smallest oversampling used on the USART (i.e. 8) + * @retval SET (__BAUDRATE__ is valid) or RESET (__BAUDRATE__ is invalid) + */ +#define IS_UART_BAUDRATE(__BAUDRATE__) ((__BAUDRATE__) < 12500001U) + +/** @brief Check UART assertion time. + * @param __TIME__: 5-bit value assertion time. + * @retval Test result (TRUE or FALSE). + */ +#define IS_UART_ASSERTIONTIME(__TIME__) ((__TIME__) <= 0x1FU) + +/** @brief Check UART deassertion time. + * @param __TIME__: 5-bit value deassertion time. + * @retval Test result (TRUE or FALSE). + */ +#define IS_UART_DEASSERTIONTIME(__TIME__) ((__TIME__) <= 0x1FU) + +/** + * @brief Ensure that UART frame number of stop bits is valid. + * @param __STOPBITS__: UART frame number of stop bits. + * @retval SET (__STOPBITS__ is valid) or RESET (__STOPBITS__ is invalid) + */ +#define IS_UART_STOPBITS(__STOPBITS__) \ + (((__STOPBITS__) == UART_STOPBITS_0_5) || \ + ((__STOPBITS__) == UART_STOPBITS_1) || \ + ((__STOPBITS__) == UART_STOPBITS_1_5) || \ + ((__STOPBITS__) == UART_STOPBITS_2)) + +/** + * @brief Ensure that UART frame parity is valid. + * @param __PARITY__: UART frame parity. + * @retval SET (__PARITY__ is valid) or RESET (__PARITY__ is invalid) + */ +#define IS_UART_PARITY(__PARITY__) \ + (((__PARITY__) == UART_PARITY_NONE) || \ + ((__PARITY__) == UART_PARITY_EVEN) || \ + ((__PARITY__) == UART_PARITY_ODD)) + +/** + * @brief Ensure that UART hardware flow control is valid. + * @param __CONTROL__: UART hardware flow control. + * @retval SET (__CONTROL__ is valid) or RESET (__CONTROL__ is invalid) + */ +#define IS_UART_HARDWARE_FLOW_CONTROL(__CONTROL__) \ + (((__CONTROL__) == UART_HWCONTROL_NONE) || \ + ((__CONTROL__) == UART_HWCONTROL_RTS) || \ + ((__CONTROL__) == UART_HWCONTROL_CTS) || \ + ((__CONTROL__) == UART_HWCONTROL_RTS_CTS)) + +/** + * @brief Ensure that UART communication mode is valid. + * @param __MODE__: UART communication mode. + * @retval SET (__MODE__ is valid) or RESET (__MODE__ is invalid) + */ +#define IS_UART_MODE(__MODE__) \ + ((((__MODE__) & (~((uint32_t)(UART_MODE_TX_RX)))) == \ + (uint32_t)0x00) && \ + ((__MODE__) != (uint32_t)0x00)) + +/** + * @brief Ensure that UART state is valid. + * @param __STATE__: UART state. + * @retval SET (__STATE__ is valid) or RESET (__STATE__ is invalid) + */ +#define IS_UART_STATE(__STATE__) (((__STATE__) == UART_STATE_DISABLE) || \ + ((__STATE__) == UART_STATE_ENABLE)) + +/** + * @brief Ensure that UART oversampling is valid. + * @param __SAMPLING__: UART oversampling. + * @retval SET (__SAMPLING__ is valid) or RESET (__SAMPLING__ is invalid) + */ +#define IS_UART_OVERSAMPLING(__SAMPLING__) \ + (((__SAMPLING__) == UART_OVERSAMPLING_16) || \ + ((__SAMPLING__) == UART_OVERSAMPLING_8)) + +/** + * @brief Ensure that UART frame sampling is valid. + * @param __ONEBIT__: UART frame sampling. + * @retval SET (__ONEBIT__ is valid) or RESET (__ONEBIT__ is invalid) + */ +#define IS_UART_ONE_BIT_SAMPLE(__ONEBIT__) \ + (((__ONEBIT__) == UART_ONE_BIT_SAMPLE_DISABLE) || \ + ((__ONEBIT__) == UART_ONE_BIT_SAMPLE_ENABLE)) + +/** + * @brief Ensure that UART auto Baud rate detection mode is valid. + * @param __MODE__: UART auto Baud rate detection mode. + * @retval SET (__MODE__ is valid) or RESET (__MODE__ is invalid) + */ +#define IS_UART_ADVFEATURE_AUTOBAUDRATEMODE(__MODE__) \ + (((__MODE__) == UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT) || \ + ((__MODE__) == UART_ADVFEATURE_AUTOBAUDRATE_ONFALLINGEDGE) || \ + ((__MODE__) == UART_ADVFEATURE_AUTOBAUDRATE_ON0X7FFRAME) || \ + ((__MODE__) == UART_ADVFEATURE_AUTOBAUDRATE_ON0X55FRAME)) + +/** + * @brief Ensure that UART receiver timeout setting is valid. + * @param __TIMEOUT__: UART receiver timeout setting. + * @retval SET (__TIMEOUT__ is valid) or RESET (__TIMEOUT__ is invalid) + */ +#define IS_UART_RECEIVER_TIMEOUT(__TIMEOUT__) \ + (((__TIMEOUT__) == UART_RECEIVER_TIMEOUT_DISABLE) || \ + ((__TIMEOUT__) == UART_RECEIVER_TIMEOUT_ENABLE)) + +/** + * @brief Ensure that UART LIN state is valid. + * @param __LIN__: UART LIN state. + * @retval SET (__LIN__ is valid) or RESET (__LIN__ is invalid) + */ +#define IS_UART_LIN(__LIN__) \ + (((__LIN__) == UART_LIN_DISABLE) || \ + ((__LIN__) == UART_LIN_ENABLE)) + +/** + * @brief Ensure that UART LIN break detection length is valid. + * @param __LENGTH__: UART LIN break detection length. + * @retval SET (__LENGTH__ is valid) or RESET (__LENGTH__ is invalid) + */ +#define IS_UART_LIN_BREAK_DETECT_LENGTH(__LENGTH__) \ + (((__LENGTH__) == UART_LINBREAKDETECTLENGTH_10B) || \ + ((__LENGTH__) == UART_LINBREAKDETECTLENGTH_11B)) + +/** + * @brief Ensure that UART DMA TX state is valid. + * @param __DMATX__: UART DMA TX state. + * @retval SET (__DMATX__ is valid) or RESET (__DMATX__ is invalid) + */ +#define IS_UART_DMA_TX(__DMATX__) \ + (((__DMATX__) == UART_DMA_TX_DISABLE) || \ + ((__DMATX__) == UART_DMA_TX_ENABLE)) + +/** + * @brief Ensure that UART DMA RX state is valid. + * @param __DMARX__: UART DMA RX state. + * @retval SET (__DMARX__ is valid) or RESET (__DMARX__ is invalid) + */ +#define IS_UART_DMA_RX(__DMARX__) \ + (((__DMARX__) == UART_DMA_RX_DISABLE) || \ + ((__DMARX__) == UART_DMA_RX_ENABLE)) + +/** + * @brief Ensure that UART half-duplex state is valid. + * @param __HDSEL__: UART half-duplex state. + * @retval SET (__HDSEL__ is valid) or RESET (__HDSEL__ is invalid) + */ +#define IS_UART_HALF_DUPLEX(__HDSEL__) \ + (((__HDSEL__) == UART_HALF_DUPLEX_DISABLE) || \ + ((__HDSEL__) == UART_HALF_DUPLEX_ENABLE)) + +/** + * @brief Ensure that UART wake-up method is valid. + * @param __WAKEUP__: UART wake-up method . + * @retval SET (__WAKEUP__ is valid) or RESET (__WAKEUP__ is invalid) + */ +#define IS_UART_WAKEUPMETHOD(__WAKEUP__) \ + (((__WAKEUP__) == UART_WAKEUPMETHOD_IDLELINE) || \ + ((__WAKEUP__) == UART_WAKEUPMETHOD_ADDRESSMARK)) + +/** + * @brief Ensure that UART request parameter is valid. + * @param __PARAM__: UART request parameter. + * @retval SET (__PARAM__ is valid) or RESET (__PARAM__ is invalid) + */ +#define IS_UART_REQUEST_PARAMETER(__PARAM__) \ + (((__PARAM__) == UART_AUTOBAUD_REQUEST) || \ + ((__PARAM__) == UART_SENDBREAK_REQUEST) || \ + ((__PARAM__) == UART_MUTE_MODE_REQUEST) || \ + ((__PARAM__) == UART_RXDATA_FLUSH_REQUEST) || \ + ((__PARAM__) == UART_TXDATA_FLUSH_REQUEST)) + +/** + * @brief Ensure that UART advanced features initialization is valid. + * @param __INIT__: UART advanced features initialization. + * @retval SET (__INIT__ is valid) or RESET (__INIT__ is invalid) + */ +#define IS_UART_ADVFEATURE_INIT(__INIT__) \ + ((__INIT__) <= (UART_ADVFEATURE_NO_INIT | \ + UART_ADVFEATURE_TXINVERT_INIT | \ + UART_ADVFEATURE_RXINVERT_INIT | \ + UART_ADVFEATURE_DATAINVERT_INIT | \ + UART_ADVFEATURE_SWAP_INIT | \ + UART_ADVFEATURE_RXOVERRUNDISABLE_INIT | \ + UART_ADVFEATURE_DMADISABLEONERROR_INIT | \ + UART_ADVFEATURE_AUTOBAUDRATE_INIT | \ + UART_ADVFEATURE_MSBFIRST_INIT)) + +/** + * @brief Ensure that UART frame TX inversion setting is valid. + * @param __TXINV__: UART frame TX inversion setting. + * @retval SET (__TXINV__ is valid) or RESET (__TXINV__ is invalid) + */ +#define IS_UART_ADVFEATURE_TXINV(__TXINV__) \ + (((__TXINV__) == UART_ADVFEATURE_TXINV_DISABLE) || \ + ((__TXINV__) == UART_ADVFEATURE_TXINV_ENABLE)) + +/** + * @brief Ensure that UART frame RX inversion setting is valid. + * @param __RXINV__: UART frame RX inversion setting. + * @retval SET (__RXINV__ is valid) or RESET (__RXINV__ is invalid) + */ +#define IS_UART_ADVFEATURE_RXINV(__RXINV__) \ + (((__RXINV__) == UART_ADVFEATURE_RXINV_DISABLE) || \ + ((__RXINV__) == UART_ADVFEATURE_RXINV_ENABLE)) + +/** + * @brief Ensure that UART frame data inversion setting is valid. + * @param __DATAINV__: UART frame data inversion setting. + * @retval SET (__DATAINV__ is valid) or RESET (__DATAINV__ is invalid) + */ +#define IS_UART_ADVFEATURE_DATAINV(__DATAINV__) \ + (((__DATAINV__) == UART_ADVFEATURE_DATAINV_DISABLE) || \ + ((__DATAINV__) == UART_ADVFEATURE_DATAINV_ENABLE)) + +/** + * @brief Ensure that UART frame RX/TX pins swap setting is valid. + * @param __SWAP__: UART frame RX/TX pins swap setting. + * @retval SET (__SWAP__ is valid) or RESET (__SWAP__ is invalid) + */ +#define IS_UART_ADVFEATURE_SWAP(__SWAP__) \ + (((__SWAP__) == UART_ADVFEATURE_SWAP_DISABLE) || \ + ((__SWAP__) == UART_ADVFEATURE_SWAP_ENABLE)) + +/** + * @brief Ensure that UART frame overrun setting is valid. + * @param __OVERRUN__: UART frame overrun setting. + * @retval SET (__OVERRUN__ is valid) or RESET (__OVERRUN__ is invalid) + */ +#define IS_UART_OVERRUN(__OVERRUN__) \ + (((__OVERRUN__) == UART_ADVFEATURE_OVERRUN_ENABLE) || \ + ((__OVERRUN__) == UART_ADVFEATURE_OVERRUN_DISABLE)) + +/** + * @brief Ensure that UART auto Baud rate state is valid. + * @param __AUTOBAUDRATE__: UART auto Baud rate state. + * @retval SET (__AUTOBAUDRATE__ is valid) or RESET (__AUTOBAUDRATE__ is invalid) + */ +#define IS_UART_ADVFEATURE_AUTOBAUDRATE(__AUTOBAUDRATE__) \ + (((__AUTOBAUDRATE__) == UART_ADVFEATURE_AUTOBAUDRATE_DISABLE) || \ + ((__AUTOBAUDRATE__) == UART_ADVFEATURE_AUTOBAUDRATE_ENABLE)) + +/** + * @brief Ensure that UART DMA enabling or disabling on error setting is valid. + * @param __DMA__: UART DMA enabling or disabling on error setting. + * @retval SET (__DMA__ is valid) or RESET (__DMA__ is invalid) + */ +#define IS_UART_ADVFEATURE_DMAONRXERROR(__DMA__) \ + (((__DMA__) == UART_ADVFEATURE_DMA_ENABLEONRXERROR) || \ + ((__DMA__) == UART_ADVFEATURE_DMA_DISABLEONRXERROR)) + +/** + * @brief Ensure that UART frame MSB first setting is valid. + * @param __MSBFIRST__: UART frame MSB first setting. + * @retval SET (__MSBFIRST__ is valid) or RESET (__MSBFIRST__ is invalid) + */ +#define IS_UART_ADVFEATURE_MSBFIRST(__MSBFIRST__) \ + (((__MSBFIRST__) == UART_ADVFEATURE_MSBFIRST_DISABLE) || \ + ((__MSBFIRST__) == UART_ADVFEATURE_MSBFIRST_ENABLE)) + +/** + * @brief Ensure that UART stop mode state is valid. + * @param __STOPMODE__: UART stop mode state. + * @retval SET (__STOPMODE__ is valid) or RESET (__STOPMODE__ is invalid) + */ +#define IS_UART_ADVFEATURE_STOPMODE(__STOPMODE__) \ + (((__STOPMODE__) == UART_ADVFEATURE_STOPMODE_DISABLE) || \ + ((__STOPMODE__) == UART_ADVFEATURE_STOPMODE_ENABLE)) + +/** + * @brief Ensure that UART mute mode state is valid. + * @param __MUTE__: UART mute mode state. + * @retval SET (__MUTE__ is valid) or RESET (__MUTE__ is invalid) + */ +#define IS_UART_MUTE_MODE(__MUTE__) \ + (((__MUTE__) == UART_ADVFEATURE_MUTEMODE_DISABLE) || \ + ((__MUTE__) == UART_ADVFEATURE_MUTEMODE_ENABLE)) + +/** + * @brief Ensure that UART wake-up selection is valid. + * @param __WAKE__: UART wake-up selection. + * @retval SET (__WAKE__ is valid) or RESET (__WAKE__ is invalid) + */ +#define IS_UART_WAKEUP_SELECTION(__WAKE__) \ + (((__WAKE__) == UART_WAKEUP_ON_ADDRESS) || \ + ((__WAKE__) == UART_WAKEUP_ON_STARTBIT) || \ + ((__WAKE__) == UART_WAKEUP_ON_READDATA_NONEMPTY) || \ + ((__WAKE__) == UART_WAKEUP_ON_RXFIFO_THRESHOLD) || \ + ((__WAKE__) == UART_WAKEUP_ON_RXFIFO_FULL) || \ + ((__WAKE__) == UART_WAKEUP_ON_TXFIFO_THRESHOLD) || \ + ((__WAKE__) == UART_WAKEUP_ON_TXFIFO_EMPTY)) + +/** + * @brief Ensure that UART driver enable polarity is valid. + * @param __POLARITY__: UART driver enable polarity. + * @retval SET (__POLARITY__ is valid) or RESET (__POLARITY__ is invalid) + */ +#define IS_UART_DE_POLARITY(__POLARITY__) \ + (((__POLARITY__) == UART_DE_POLARITY_HIGH) || \ + ((__POLARITY__) == UART_DE_POLARITY_LOW)) + +/** + * @brief Ensure that UART Prescaler is valid. + * @param __PRESCALER__: UART Prescaler value. + * @retval SET (__PRESCALER__ is valid) or RESET (__PRESCALER__ is invalid) + */ +#define IS_UART_PRESCALER(__PRESCALER__) \ + (((__PRESCALER__) == UART_PRESCALER_DIV1) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV2) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV4) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV6) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV8) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV10) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV12) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV16) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV32) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV64) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV128) || \ + ((__PRESCALER__) == UART_PRESCALER_DIV256)) + +/** + * @brief Ensure that UART FIFO mode is valid. + * @param __STATE__: UART FIFO mode. + * @retval SET (__STATE__ is valid) or RESET (__STATE__ is invalid) + */ +#define IS_UART_FIFO_MODE_STATE(__STATE__) \ + (((__STATE__) == UART_FIFOMODE_DISABLE) || \ + ((__STATE__) == UART_FIFOMODE_ENABLE)) + +/** + * @brief Ensure that UART TXFIFO threshold level is valid. + * @param __THRESHOLD__: UART TXFIFO threshold level. + * @retval SET (__THRESHOLD__ is valid) or RESET (__THRESHOLD__ is invalid) + */ +#define IS_UART_TXFIFO_THRESHOLD(__THRESHOLD__) \ + ((((__THRESHOLD__) == UART_TXFIFO_THRESHOLD_1EIGHTHFULL) || \ + ((__THRESHOLD__) == UART_TXFIFO_THRESHOLD_1QUARTERFULL) || \ + ((__THRESHOLD__) == UART_TXFIFO_THRESHOLD_HALFFULL) || \ + ((__THRESHOLD__) == UART_TXFIFO_THRESHOLD_3QUARTERSFULL) || \ + ((__THRESHOLD__) == UART_TXFIFO_THRESHOLD_7EIGHTHFULL)) || \ + ((__THRESHOLD__) == UART_TXFIFO_THRESHOLD_EMPTY)) + +/** + * @brief Ensure that UART RXFIFO threshold level is valid. + * @param __THRESHOLD__: UART RXFIFO threshold level. + * @retval SET (__THRESHOLD__ is valid) or RESET (__THRESHOLD__ is invalid) + */ +#define IS_UART_RXFIFO_THRESHOLD(__THRESHOLD__) \ + ((((__THRESHOLD__) == UART_RXFIFO_THRESHOLD_1EIGHTHFULL) || \ + ((__THRESHOLD__) == UART_RXFIFO_THRESHOLD_1QUARTERFULL) || \ + ((__THRESHOLD__) == UART_RXFIFO_THRESHOLD_HALFFULL) || \ + ((__THRESHOLD__) == UART_RXFIFO_THRESHOLD_3QUARTERSFULL) || \ + ((__THRESHOLD__) == UART_RXFIFO_THRESHOLD_7EIGHTHFULL)) || \ + ((__THRESHOLD__) == UART_RXFIFO_THRESHOLD_FULL)) + +/* Include UART HAL Extension module */ +#include "stm32mp1xx_hal_uart_ex.h" + +/* Initialization and de-initialization functions ****************************/ +HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart); + +/* IO operation functions *****************************************************/ +HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, + uint16_t Size, uint32_t Timeout); +HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, + uint16_t Size, uint32_t Timeout); + +/* Peripheral State and Errors functions **************************************************/ +HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart); +uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart); + +/* Private functions -----------------------------------------------------------*/ +/** @addtogroup UART_Private_Functions UART Private Functions + * @{ + */ + +HAL_StatusTypeDef UART_SetConfig(UART_HandleTypeDef *huart); +HAL_StatusTypeDef UART_CheckIdleState(UART_HandleTypeDef *huart); +HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, + uint32_t Flag, FlagStatus Status, + uint32_t Tickstart, + uint32_t Timeout); +void UART_AdvFeatureConfig(UART_HandleTypeDef *huart); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32MP1xx_HAL_UART_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/include/drivers/st/stm32mp1xx_hal_uart_ex.h b/include/drivers/st/stm32mp1xx_hal_uart_ex.h new file mode 100644 index 000000000..28ec13c3b --- /dev/null +++ b/include/drivers/st/stm32mp1xx_hal_uart_ex.h @@ -0,0 +1,88 @@ +/** + ****************************************************************************** + * @file stm32mp1xx_hal_uart_ex.h + * @author MCD Application Team + * @version $VERSION$ + * @date $DATE$ + * @brief Header file of UART HAL Extended module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015-2017 STMicroelectronics

+ * + * SPDX-License-Identifier: BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32MP1xx_HAL_UART_EX_H +#define __STM32MP1xx_HAL_UART_EX_H + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup UARTEx_Exported_Constants UARTEx Exported Constants + * @{ + */ + +/** @defgroup UARTEx_Word_Length UART Word Length + * @{ + */ +#define UART_WORDLENGTH_7B ((uint32_t)USART_CR1_M1) /*!< 7-bit long UART frame */ +#define UART_WORDLENGTH_8B ((uint32_t)0x00000000U) /*!< 8-bit long UART frame */ +#define UART_WORDLENGTH_9B ((uint32_t)USART_CR1_M0) /*!< 9-bit long UART frame */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup UARTEx_Private_Macros UARTEx Private Macros + * @{ + */ + +/** @brief Report the UART mask to apply to retrieve the received data + * according to the word length and to the parity bits activation. + * @note If PCE = 1, the parity bit is not included in the data extracted + * by the reception API(). + * This masking operation is not carried out in the case of + * DMA transfers. + * @param __HANDLE__: specifies the UART Handle. + * @retval None, the mask to apply to UART RDR register is stored + * in (__HANDLE__)->Mask field. + */ +#define UART_MASK_COMPUTATION(__HANDLE__) \ + do { \ + if ((__HANDLE__)->Init.WordLength == UART_WORDLENGTH_9B) \ + { \ + if ((__HANDLE__)->Init.Parity == UART_PARITY_NONE) \ + { \ + (__HANDLE__)->Mask = 0x01FF ; \ + } \ + else \ + { \ + (__HANDLE__)->Mask = 0x00FF ; \ + } \ + } \ + else if ((__HANDLE__)->Init.WordLength == UART_WORDLENGTH_8B) \ + { \ + if ((__HANDLE__)->Init.Parity == UART_PARITY_NONE) \ + { \ + (__HANDLE__)->Mask = 0x00FF ; \ + } \ + else \ + { \ + (__HANDLE__)->Mask = 0x007F ; \ + } \ + } \ + else if ((__HANDLE__)->Init.WordLength == UART_WORDLENGTH_7B) \ + { \ + if ((__HANDLE__)->Init.Parity == UART_PARITY_NONE) \ + { \ + (__HANDLE__)->Mask = 0x007F ; \ + } \ + else \ + { \ + (__HANDLE__)->Mask = 0x003F ; \ + } \ + } \ +} while (0) + +#endif /* __STM32MP1xx_HAL_UART_EX_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/include/drivers/st/stm32mp_clkfunc.h b/include/drivers/st/stm32mp_clkfunc.h index 076916730..a0aaa002f 100644 --- a/include/drivers/st/stm32mp_clkfunc.h +++ b/include/drivers/st/stm32mp_clkfunc.h @@ -19,15 +19,25 @@ uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, const char *prop_name, uint32_t dflt_value); -int fdt_get_rcc_node(void *fdt); -uint32_t fdt_rcc_read_addr(void); +int fdt_get_rcc_node(void); int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t *array, uint32_t count); +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); +int fdt_rcc_enable_it(const char *name); -uintptr_t fdt_get_stgen_base(void); 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 000000000..6804192ba --- /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 984cd6014..898a28b44 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 000000000..7a66b97ba --- /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 2da5adf44..7114dddf0 100644 --- a/include/drivers/st/stm32mp_reset.h +++ b/include/drivers/st/stm32mp_reset.h @@ -9,7 +9,42 @@ #include -void stm32mp_reset_assert(uint32_t reset_id); -void stm32mp_reset_deassert(uint32_t reset_id); +/* + * Assert target reset, if @to_us non null, wait until reset is asserted + * + * @reset_id: Reset controller ID + * @to_us: Timeout in microsecond, or 0 if not waiting + * Return 0 on success and -ETIMEDOUT if waiting and timeout expired + */ +int stm32mp_reset_assert_to(uint32_t reset_id, unsigned int to_us); + +/* + * Enable reset control for target resource + * + * @reset_id: Reset controller ID + */ +static inline void stm32mp_reset_set(uint32_t reset_id) +{ + (void)stm32mp_reset_assert_to(reset_id, 0); +} + +/* + * Deassert target reset, if @to_us non null, wait until reset is deasserted + * + * @reset_id: Reset controller ID + * @to_us: Timeout in microsecond, or 0 if not waiting + * Return 0 on success and -ETIMEDOUT if waiting and timeout expired + */ +int stm32mp_reset_deassert_to(uint32_t reset_id, unsigned int to_us); + +/* + * Release reset control for target resource + * + * @reset_id: Reset controller ID + */ +static inline void stm32mp_reset_release(uint32_t reset_id) +{ + (void)stm32mp_reset_deassert_to(reset_id, 0); +} #endif /* STM32MP_RESET_H */ diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h index f7e293b18..5c8933d84 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 000000000..caff54669 --- /dev/null +++ b/include/drivers/st/usb_dwc2.h @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __USB_DWC2_H +#define __USB_DWC2_H + +#include + +/* define value use in register */ + +#define USB_OTG_MODE_DEVICE 0 +#define USB_OTG_MODE_HOST 1 +#define USB_OTG_MODE_DRD 2 + +#define DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ (0 << 1) +#define DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ (1 << 1) +#define DSTS_ENUMSPD_LS_PHY_6MHZ (2 << 1) +#define DSTS_ENUMSPD_FS_PHY_48MHZ (3 << 1) + +#define EP_TYPE_CTRL 0 +#define EP_TYPE_ISOC 1 +#define EP_TYPE_BULK 2 +#define EP_TYPE_INTR 3 + +#define STS_GOUT_NAK 1 +#define STS_DATA_UPDT 2 +#define STS_XFER_COMP 3 +#define STS_SETUP_COMP 4 +#define STS_SETUP_UPDT 6 + +#define USBD_HS_TRDT_VALUE 9 + +#define USB_OTG_DEVICE_BASE ((uint32_t)0x800) +#define USB_OTG_IN_ENDPOINT_BASE ((uint32_t)0x900) +#define USB_OTG_OUT_ENDPOINT_BASE ((uint32_t)0xB00) +#define USB_OTG_FIFO_BASE ((uint32_t)0x1000) +#define USB_OTG_FIFO_SIZE ((uint32_t)0x1000) +#define USB_MAX_ENDPOINT_NB 0x10 + +/* Bit definition for register */ +/* USB_OTG_GRSTCTL register */ +/* Core soft reset */ +#define USB_OTG_GRSTCTL_CSRST ((uint32_t)0x00000001) +/* HCLK soft reset */ +#define USB_OTG_GRSTCTL_HSRST ((uint32_t)0x00000002) +/* Host frame counter reset */ +#define USB_OTG_GRSTCTL_FCRST ((uint32_t)0x00000004) +/* RxFIFOflush */ +#define USB_OTG_GRSTCTL_RXFFLSH ((uint32_t)0x00000010) +/* TxFIFOflush */ +#define USB_OTG_GRSTCTL_TXFFLSH ((uint32_t)0x00000020) + +/* USB_OTG_DIEPCTLregister */ +/* Maximum packet size */ +#define USB_OTG_DIEPCTL_MPSIZ ((uint32_t)0x000007FF) +/* USB active endpoint */ +#define USB_OTG_DIEPCTL_USBAEP ((uint32_t)0x00008000) +/* Even/odd frame */ +#define USB_OTG_DIEPCTL_EONUM_DPID ((uint32_t)0x00010000) +/* NAK status */ +#define USB_OTG_DIEPCTL_NAKSTS ((uint32_t)0x00020000) +/* Endpoint type */ +#define USB_OTG_DIEPCTL_EPTYP ((uint32_t)0x000C0000) +/* Bit0 */ +#define USB_OTG_DIEPCTL_EPTYP_0 ((uint32_t)0x00040000) +/* Bit1 */ +#define USB_OTG_DIEPCTL_EPTYP_1 ((uint32_t)0x00080000) +/* STALL handshake */ +#define USB_OTG_DIEPCTL_STALL ((uint32_t)0x00200000) +/* TxFIFO number */ +#define USB_OTG_DIEPCTL_TXFNUM ((uint32_t)0x03C00000) +/* Bit0 */ +#define USB_OTG_DIEPCTL_TXFNUM_0 ((uint32_t)0x00400000) +/* Bit1 */ +#define USB_OTG_DIEPCTL_TXFNUM_1 ((uint32_t)0x00800000) +/* Bit2 */ +#define USB_OTG_DIEPCTL_TXFNUM_2 ((uint32_t)0x01000000) +/* Bit3 */ +#define USB_OTG_DIEPCTL_TXFNUM_3 ((uint32_t)0x02000000) +/* Clear NAK */ +#define USB_OTG_DIEPCTL_CNAK ((uint32_t)0x04000000) +/* Set NAK */ +#define USB_OTG_DIEPCTL_SNAK ((uint32_t)0x08000000) +/* Set DATA0 PID */ +#define USB_OTG_DIEPCTL_SD0PID_SEVNFRM ((uint32_t)0x10000000) +/* Set odd frame */ +#define USB_OTG_DIEPCTL_SODDFRM ((uint32_t)0x20000000) +/* Endpoint disable */ +#define USB_OTG_DIEPCTL_EPDIS ((uint32_t)0x40000000) +/* Endpoint enable */ +#define USB_OTG_DIEPCTL_EPENA ((uint32_t)0x80000000) + +/* USB_OTG_DOEPCTL register */ +/* Maximum packet size */ +#define USB_OTG_DOEPCTL_MPSIZ ((uint32_t)0x000007FF) +/* USB active endpoint */ +#define USB_OTG_DOEPCTL_USBAEP ((uint32_t)0x00008000) +/* NAK status */ +#define USB_OTG_DOEPCTL_NAKSTS ((uint32_t)0x00020000) +/* Set DATA0 PID */ +#define USB_OTG_DOEPCTL_SD0PID_SEVNFRM ((uint32_t)0x10000000) +/* Set odd frame */ +#define USB_OTG_DOEPCTL_SODDFRM ((uint32_t)0x20000000) +/* Endpoint type */ +#define USB_OTG_DOEPCTL_EPTYP ((uint32_t)0x000C0000) +/* Bit0 */ +#define USB_OTG_DOEPCTL_EPTYP_0 ((uint32_t)0x00040000) +/* Bit1 */ +#define USB_OTG_DOEPCTL_EPTYP_1 ((uint32_t)0x00080000) +/* Snoop mode */ +#define USB_OTG_DOEPCTL_SNPM ((uint32_t)0x00100000) +/* STALL handshake */ +#define USB_OTG_DOEPCTL_STALL ((uint32_t)0x00200000) +/* Clear NAK */ +#define USB_OTG_DOEPCTL_CNAK ((uint32_t)0x04000000) +/* Set NAK */ +#define USB_OTG_DOEPCTL_SNAK ((uint32_t)0x08000000) +/* Endpoint disable */ +#define USB_OTG_DOEPCTL_EPDIS ((uint32_t)0x40000000) +/* Endpoint enable */ +#define USB_OTG_DOEPCTL_EPENA ((uint32_t)0x80000000) + +/* USB_OTG_DSTSregister */ +/* Suspend status */ +#define USB_OTG_DSTS_SUSPSTS ((uint32_t)0x00000001) +/* Enumerated speed */ +#define USB_OTG_DSTS_ENUMSPD ((uint32_t)0x00000006) +/* Bit0 */ +#define USB_OTG_DSTS_ENUMSPD_0 ((uint32_t)0x00000002) +/* Bit1 */ +#define USB_OTG_DSTS_ENUMSPD_1 ((uint32_t)0x00000004) +/* Erratic error */ +#define USB_OTG_DSTS_EERR ((uint32_t)0x00000008) +/* Frame number of the received SOF */ +#define USB_OTG_DSTS_FNSOF ((uint32_t)0x003FFF00) + +/* USB_OTG_DCTLregister */ +/* Remote wakeup signaling */ +#define USB_OTG_DCTL_RWUSIG ((uint32_t)0x00000001) +/* Soft disconnect */ +#define USB_OTG_DCTL_SDIS ((uint32_t)0x00000002) +/* Global IN NAK status */ +#define USB_OTG_DCTL_GINSTS ((uint32_t)0x00000004) +/* Global OUT NAK status */ +#define USB_OTG_DCTL_GONSTS ((uint32_t)0x00000008) +/* Test control */ +#define USB_OTG_DCTL_TCTL ((uint32_t)0x00000070) +/* Bit0 */ +#define USB_OTG_DCTL_TCTL_0 ((uint32_t)0x00000010) +/* Bit1 */ +#define USB_OTG_DCTL_TCTL_1 ((uint32_t)0x00000020) +/* Bit2 */ +#define USB_OTG_DCTL_TCTL_2 ((uint32_t)0x00000040) +/* Set global IN NAK */ +#define USB_OTG_DCTL_SGINAK ((uint32_t)0x00000080) +/* Clear global IN NAK */ +#define USB_OTG_DCTL_CGINAK ((uint32_t)0x00000100) +/* Set global OUT NAK */ +#define USB_OTG_DCTL_SGONAK ((uint32_t)0x00000200) +/* Clear global OUT NAK */ +#define USB_OTG_DCTL_CGONAK ((uint32_t)0x00000400) +/* Power-on programming done */ +#define USB_OTG_DCTL_POPRGDNE ((uint32_t)0x00000800) + +/* USB_OTG_GAHBCFG register */ +/* Global interrupt mask */ +#define USB_OTG_GAHBCFG_GINT ((uint32_t)0x00000001) + +/* USB_OTG_DOEPTSIZr egister */ +/* Transfer size */ +#define USB_OTG_DOEPTSIZ_XFRSIZ ((uint32_t)0x0007FFFF) +/* Packet count */ +#define USB_OTG_DOEPTSIZ_PKTCNT ((uint32_t)0x1FF80000) +/* SETUP packet count */ +#define USB_OTG_DOEPTSIZ_STUPCNT ((uint32_t)0x60000000) +/* Bit0 */ +#define USB_OTG_DOEPTSIZ_STUPCNT_0 ((uint32_t)0x20000000) +/* Bit1 */ +#define USB_OTG_DOEPTSIZ_STUPCNT_1 ((uint32_t)0x40000000) + +/* USB_OTG_DIEPTSIZ register */ +/* Transfer size */ +#define USB_OTG_DIEPTSIZ_XFRSIZ ((uint32_t)0x0007FFFF) +/* Packet count */ +#define USB_OTG_DIEPTSIZ_PKTCNT ((uint32_t)0x1FF80000) +/* Packet count */ +#define USB_OTG_DIEPTSIZ_MULCNT ((uint32_t)0x60000000) + +/* USB_OTG_DCFG register */ +/* Device address */ +#define USB_OTG_DCFG_DAD ((uint32_t)0x000007F0) + +/* USB_OTG_DTXFSTS register */ +/* IN endpoint Tx FIFO space available */ +#define USB_OTG_DTXFSTS_INEPTFSAV ((uint32_t)0x0000FFFF) + +/* USB_OTG_GINTSTS register */ +/* Current mode of operation */ +#define USB_OTG_GINTSTS_CMOD ((uint32_t)0x00000001) +/* Modem is match interrupt */ +#define USB_OTG_GINTSTS_MMIS ((uint32_t)0x00000002) +/* OTG interrupt */ +#define USB_OTG_GINTSTS_OTGINT ((uint32_t)0x00000004) +/* Start offrame */ +#define USB_OTG_GINTSTS_SOF ((uint32_t)0x00000008) +/* Rx FIFO nonempty */ +#define USB_OTG_GINTSTS_RXFLVL ((uint32_t)0x00000010) +/* Non periodic Tx FIFO empty */ +#define USB_OTG_GINTSTS_NPTXFE ((uint32_t)0x00000020) +/* Global IN non periodic NAK effective */ +#define USB_OTG_GINTSTS_GINAKEFF ((uint32_t)0x00000040) +/* Global OUT NAK effective */ +#define USB_OTG_GINTSTS_BOUTNAKEFF ((uint32_t)0x00000080) +/* Early suspend */ +#define USB_OTG_GINTSTS_ESUSP ((uint32_t)0x00000400) +/* USB suspend */ +#define USB_OTG_GINTSTS_USBSUSP ((uint32_t)0x00000800) +/* USB reset */ +#define USB_OTG_GINTSTS_USBRST ((uint32_t)0x00001000) +/* Enumeration done */ +#define USB_OTG_GINTSTS_ENUMDNE ((uint32_t)0x00002000) +/* Isochronous OUT packet dropped interrupt */ +#define USB_OTG_GINTSTS_ISOODRP ((uint32_t)0x00004000) +/* End of periodic frame interrupt */ +#define USB_OTG_GINTSTS_EOPF ((uint32_t)0x00008000) +/* IN endpoint interrupt */ +#define USB_OTG_GINTSTS_IEPINT ((uint32_t)0x00040000) +/* OUT endpoint interrupt */ +#define USB_OTG_GINTSTS_OEPINT ((uint32_t)0x00080000) +/* Incomplete isochronous IN transfer */ +#define USB_OTG_GINTSTS_IISOIXFR ((uint32_t)0x00100000) +/* Incomplete periodic transfer */ +#define USB_OTG_GINTSTS_PXFR_INCOMPISOOUT ((uint32_t)0x00200000) +/* Data fetch suspended */ +#define USB_OTG_GINTSTS_DATAFSUSP ((uint32_t)0x00400000) +/* Reset detected interrupt */ +#define USB_OTG_GINTSTS_RSTDET ((uint32_t)0x00800000) +/* Host port interrupt */ +#define USB_OTG_GINTSTS_HPRTINT ((uint32_t)0x01000000) +/* Host channels interrupt */ +#define USB_OTG_GINTSTS_HCINT ((uint32_t)0x02000000) +/* Periodic Tx FIFO empty */ +#define USB_OTG_GINTSTS_PTXFE ((uint32_t)0x04000000) +/* LPM interrupt */ +#define USB_OTG_GINTSTS_LPMINT ((uint32_t)0x08000000) +/* Connector ID status change */ +#define USB_OTG_GINTSTS_CIDSCHG ((uint32_t)0x10000000) +/* Disconnect detected interrupt */ +#define USB_OTG_GINTSTS_DISCINT ((uint32_t)0x20000000) +/* Session request/new session detected interrupt */ +#define USB_OTG_GINTSTS_SRQINT ((uint32_t)0x40000000) +/* Resume/remote wakeup detected interrupt */ +#define USB_OTG_GINTSTS_WKUINT ((uint32_t)0x80000000) + +/* USB_OTG_DOEPINT register */ +/* Transfer completed interrupt */ +#define USB_OTG_DOEPINT_XFRC ((uint32_t)0x00000001) +/* Endpoint disabled interrupt */ +#define USB_OTG_DOEPINT_EPDISD ((uint32_t)0x00000002) +/* SETUP phase done */ +#define USB_OTG_DOEPINT_STUP ((uint32_t)0x00000008) +/* OUT token received when endpoint disabled */ +#define USB_OTG_DOEPINT_OTEPDIS ((uint32_t)0x00000010) +/* Back-to-back SETUP packets received */ +#define USB_OTG_DOEPINT_B2BSTUP ((uint32_t)0x00000040) +/* NYET interrupt */ +#define USB_OTG_DOEPINT_NYET ((uint32_t)0x00004000) + +/* USB_OTG_DIEPINTregister */ +/* Transfer completed interrupt */ +#define USB_OTG_DIEPINT_XFRC ((uint32_t)0x00000001) +/* Endpoint disabled interrupt */ +#define USB_OTG_DIEPINT_EPDISD ((uint32_t)0x00000002) +/* Timeout condition */ +#define USB_OTG_DIEPINT_TOC ((uint32_t)0x00000008) +/* IN token received when Tx FIFO is empty */ +#define USB_OTG_DIEPINT_ITTXFE ((uint32_t)0x00000010) +/* IN endpoint NAK effective */ +#define USB_OTG_DIEPINT_INEPNE ((uint32_t)0x00000040) +/* Transmit Fifo empty */ +#define USB_OTG_DIEPINT_TXFE ((uint32_t)0x00000080) +/* Transmit Fifo Underrun */ +#define USB_OTG_DIEPINT_TXFIFOUDRN ((uint32_t)0x00000100) +/* Buffer not available interrupt */ +#define USB_OTG_DIEPINT_BNA ((uint32_t)0x00000200) +/* Packet dropped status */ +#define USB_OTG_DIEPINT_PKTDRPSTS ((uint32_t)0x00000800) +/* Babble error interrupt */ +#define USB_OTG_DIEPINT_BERR ((uint32_t)0x00001000) +/* NAK interrupt */ +#define USB_OTG_DIEPINT_NAK ((uint32_t)0x00002000) + +/* USB_OTG_GLPMCFG register */ +/* BESL value received with last ACKed LPM Token */ +#define USB_OTG_GLPMCFG_BESL ((uint32_t)0x0000003C) + +/* USB_OTG_GOTGINT register */ +/* Session end detected */ +#define USB_OTG_GOTGINT_SEDET ((uint32_t)0x00000004) + +/* USB_OTG_GRXSTSP register */ +/* IN EP interrupt mask bits */ +#define USB_OTG_GRXSTSP_EPNUM ((uint32_t)0x0000000F) +/* OUT EP interrupt mask bits */ +#define USB_OTG_GRXSTSP_BCNT ((uint32_t)0x00007FF0) +/* OUT EP interrupt mask bits */ +#define USB_OTG_GRXSTSP_DPID ((uint32_t)0x00018000) +/* OUT EP interrupt mask bits */ +#define USB_OTG_GRXSTSP_PKTSTS ((uint32_t)0x001E0000) + +/* USB_OTG_GUSBCFG register */ +/* USB turn around time */ +#define USB_OTG_GUSBCFG_TRDT ((uint32_t)0x00003C00) + +/* USB_OTG_DOEPMSK register */ +/* Transfer completed interrupt mask */ +#define USB_OTG_DOEPMSK_XFRCM ((uint32_t)0x00000001) +/* Endpoint disabled interrupt mask */ +#define USB_OTG_DOEPMSK_EPDM ((uint32_t)0x00000002) +/* SETUP phase done mask */ +#define USB_OTG_DOEPMSK_STUPM ((uint32_t)0x00000008) +/* OUT token received when endpoint disabled mask */ +#define USB_OTG_DOEPMSK_OTEPDM ((uint32_t)0x00000010) +/* Back-to-back SETUP packets received mask */ +#define USB_OTG_DOEPMSK_B2BSTUP ((uint32_t)0x00000040) +/* OUT packet error mask */ +#define USB_OTG_DOEPMSK_OPEM ((uint32_t)0x00000100) +/* BNA interrupt mask */ +#define USB_OTG_DOEPMSK_BOIM ((uint32_t)0x00000200) + +/* USB_OTG_DIEPMSK register */ +/* Transfer completed interrupt mask */ +#define USB_OTG_DIEPMSK_XFRCM ((uint32_t)0x00000001) +/* Endpoint disabled interrupt mask */ +#define USB_OTG_DIEPMSK_EPDM ((uint32_t)0x00000002) +/* Timeout condition mask(non isochronous endpoints) */ +#define USB_OTG_DIEPMSK_TOM ((uint32_t)0x00000008) +/* IN token received when Tx FIFO empty mask */ +#define USB_OTG_DIEPMSK_ITTXFEMSK ((uint32_t)0x00000010) +/* IN token received with EP mismatch mask */ +#define USB_OTG_DIEPMSK_INEPNMM ((uint32_t)0x00000020) +/* IN endpoint NAK effective mask */ +#define USB_OTG_DIEPMSK_INEPNEM ((uint32_t)0x00000040) +/* FIFO under run mask */ +#define USB_OTG_DIEPMSK_TXFURM ((uint32_t)0x00000100) +/* BNA interrupt mask */ +#define USB_OTG_DIEPMSK_BIM ((uint32_t)0x00000200) + +typedef struct { + uint32_t dcfg;/* dev Configuration Register */ + uint32_t dctl;/* dev Control Register */ + uint32_t dsts;/* dev Status Register(RO) */ + uint32_t reserved1;/* reserved */ + uint32_t diepmsk;/* dev IN Endpoint Mask */ + uint32_t doepmsk;/* dev OUT Endpoint Mask */ + uint32_t daint;/* dev All Endpoints Itr Reg */ + uint32_t daintmsk;/* dev All Endpoints Itr Mask */ + uint32_t reserved2;/* reserved */ + uint32_t reserved3;/* reserved */ + uint32_t dvbusdis;/* dev VBUS discharge Register */ + uint32_t dvbuspulse;/* dev VBUS Pulse Register */ + uint32_t dthrctl;/* dev threshold */ + uint32_t diepempmsk;/* dev empty msk */ + uint32_t deachint;/* dedicated EP interrupt */ + uint32_t deachmsk;/* dedicated EP msk */ + uint32_t reserved4;/* dedicated EP mask */ + uint32_t dinep1msk;/* dedicated EP mask */ + uint32_t reserved5[15];/* reserved */ + uint32_t doutep1msk;/* dedicated EP msk */ +} usb_dwc2_device_t; + +typedef struct { + uint32_t epctl;/* dev IN Endpoint Control Reg */ + uint32_t reserved1;/* reserved */ + uint32_t epint;/* dev IN Endpoint Itr Reg */ + uint32_t reserved2;/* reserved*/ + uint32_t eptsiz;/* IN Endpoint Txfer Size */ + uint32_t epdma;/* IN Endpoint DMA Address Reg */ + uint32_t txfsts;/* IN Endpoint Tx FIFO Status Reg */ + uint32_t reserved3;/* reserved */ +} usb_dwc2_endpoint_t; + +typedef struct { + uint32_t gotgctl;/* USB_OTG Control and Status Register */ + uint32_t gotgint;/* USB_OTG Interrupt Register */ + uint32_t gahbcfg;/* Core AHB Configuration Register */ + uint32_t gusbcfg;/* Core USB Configuration Register */ + uint32_t grstctl;/* Core Reset Register */ + uint32_t gintsts;/* Core Interrupt Register */ + uint32_t gintmsk;/* Core Interrupt Mask Register */ + uint32_t grxstsr;/* Receive StsQ Read Register */ + uint32_t grxstsp;/* Receive StsQ Read & POP Register */ + uint32_t grxfsiz;/* Receive FIFO SizeRegister */ + uint32_t dieptxfo;/* EP0/Non Periodic Tx FIFO Size Reg */ + uint32_t hnptxsts;/* Non Periodic Tx FIFO / Queue Sts reg */ + uint32_t reserved1[2];/* reserved */ + uint32_t gccfg;/* General Purpose IO Register */ + uint32_t cid;/* User ID Register */ + uint32_t reserved2[3];/* reserved */ + uint32_t ghwcfg3;/* User HW config */ + uint32_t reserved3;/* reserved */ + uint32_t glpmcfg;/* LPM Register */ + uint32_t gpwrdn;/* Power Down Register */ + uint32_t gdfifocfg;/* DFIFO Software Config Register */ + uint32_t gadpctl;/* ADPTimer, Control and Status Register */ + uint32_t reserved4[39];/* reserved */ + uint32_t hptxfsiz;/* Host Periodic Tx FIFO Size Reg */ + uint32_t dieptxf[0x0F];/* dev Periodic Transmit FIFO */ +} usb_dwc2_global_t; + +typedef struct { + usb_dwc2_global_t *usb_global; + usb_dwc2_device_t *usb_device; + usb_dwc2_endpoint_t *usb_in_endpoint[USB_MAX_ENDPOINT_NB]; + usb_dwc2_endpoint_t *usb_out_endpoint[USB_MAX_ENDPOINT_NB]; + uint32_t *usb_fifo[USB_MAX_ENDPOINT_NB]; +} usb_dwc2_t; + +usb_status_t usb_dwc2_disable_int(void *handle); +usb_status_t usb_dwc2_ep0_out_start(void *handle); +usb_status_t usb_dwc2_ep_start_xfer(void *handle, usb_otg_ep_t *ep); +usb_status_t usb_dwc2_ep0_start_xfer(void *handle, usb_otg_ep_t *ep); +usb_status_t usb_dwc2_write_packet(void *handle, uint8_t *src, + uint8_t ch_ep_num, uint16_t len); +void *usb_dwc2_read_packet(void *handle, uint8_t *dest, uint16_t len); +usb_status_t usb_dwc2_ep_set_stall(void *handle, usb_otg_ep_t *ep); +usb_status_t usb_dwc2_stop_device(void *handle); +usb_status_t usb_dwc2_set_address(void *handle, uint8_t address); +usb_status_t usb_dwc2_dev_disconnect(void *handle); +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); +usb_action_t usb_dwc2_it_handler(void *handle, uint32_t *param); +void usb_dwc2_init_driver(usb_handle_t *usb_core_handle, + uint32_t *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 18bdb57f3..824dd45b2 100644 --- a/include/dt-bindings/clock/stm32mp1-clks.h +++ b/include/dt-bindings/clock/stm32mp1-clks.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause */ /* * Copyright (C) STMicroelectronics 2018 - All Rights Reserved * Author: Gabriel Fernandez for STMicroelectronics. @@ -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 @@ -248,4 +254,31 @@ #define STM32MP1_LAST_CLK 232 +/* SCMI clock identifiers */ +#define CK_SCMI0_HSE 0 +#define CK_SCMI0_HSI 1 +#define CK_SCMI0_CSI 2 +#define CK_SCMI0_LSE 3 +#define CK_SCMI0_LSI 4 +#define CK_SCMI0_PLL2_Q 5 +#define CK_SCMI0_PLL2_R 6 +#define CK_SCMI0_MPU 7 +#define CK_SCMI0_AXI 8 +#define CK_SCMI0_BSEC 9 +#define CK_SCMI0_CRYP1 10 +#define CK_SCMI0_GPIOZ 11 +#define CK_SCMI0_HASH1 12 +#define CK_SCMI0_I2C4 13 +#define CK_SCMI0_I2C6 14 +#define CK_SCMI0_IWDG1 15 +#define CK_SCMI0_RNG1 16 +#define CK_SCMI0_RTC 17 +#define CK_SCMI0_RTCAPB 18 +#define CK_SCMI0_SPI6 19 +#define CK_SCMI0_USART1 20 + +#define CK_SCMI1_PLL3_Q 0 +#define CK_SCMI1_PLL3_R 1 +#define CK_SCMI1_MCU 2 + #endif /* _DT_BINDINGS_STM32MP1_CLKS_H_ */ diff --git a/include/dt-bindings/pinctrl/stm32-pinfunc.h b/include/dt-bindings/pinctrl/stm32-pinfunc.h index 7f6e4b94d..1bc2c40fc 100644 --- a/include/dt-bindings/pinctrl/stm32-pinfunc.h +++ b/include/dt-bindings/pinctrl/stm32-pinfunc.h @@ -26,6 +26,7 @@ #define AF14 0xf #define AF15 0x10 #define ANALOG 0x11 +#define RSVD 0x12 /* define Pins number*/ #define PIN_NO(port, line) (((port) - 'A') * 0x10 + (line)) @@ -33,9 +34,9 @@ #define STM32_PINMUX(port, line, mode) (((PIN_NO(port, line)) << 8) | (mode)) /* package information */ -#define STM32MP157CAA 0x1 -#define STM32MP157CAB 0x2 -#define STM32MP157CAC 0x4 -#define STM32MP157CAD 0x8 +#define STM32MP_PKG_AA 0x1 +#define STM32MP_PKG_AB 0x2 +#define STM32MP_PKG_AC 0x4 +#define STM32MP_PKG_AD 0x8 #endif /* _DT_BINDINGS_STM32_PINFUNC_H */ diff --git a/include/dt-bindings/power/stm32mp1-power.h b/include/dt-bindings/power/stm32mp1-power.h new file mode 100644 index 000000000..d588dd71f --- /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 f0c3aaef6..bc71924fa 100644 --- a/include/dt-bindings/reset/stm32mp1-resets.h +++ b/include/dt-bindings/reset/stm32mp1-resets.h @@ -105,4 +105,17 @@ #define GPIOJ_R 19785 #define GPIOK_R 19786 +/* SCMI reset domain identifiers */ +#define RST_SCMI0_SPI6 0 +#define RST_SCMI0_I2C4 1 +#define RST_SCMI0_I2C6 2 +#define RST_SCMI0_USART1 3 +#define RST_SCMI0_STGEN 4 +#define RST_SCMI0_GPIOZ 5 +#define RST_SCMI0_CRYP1 6 +#define RST_SCMI0_HASH1 7 +#define RST_SCMI0_RNG1 8 +#define RST_SCMI0_MDMA 9 +#define RST_SCMI0_MCU 10 + #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 new file mode 100644 index 000000000..6678b8e66 --- /dev/null +++ b/include/dt-bindings/soc/st,stm32-etzpc.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#ifndef _DT_BINDINGS_STM32_ETZPC_H +#define _DT_BINDINGS_STM32_ETZPC_H + +/* define DECPROT modes */ +#define DECPROT_S_RW 0x0 +#define DECPROT_NS_R_S_W 0x1 +#define DECPROT_MCU_ISOLATION 0x2 +#define DECPROT_NS_RW 0x3 + +/* define DECPROT lock */ +#define DECPROT_UNLOCK 0x0 +#define DECPROT_LOCK 0x1 + +/* define ETZPC ID */ +#define STM32MP1_ETZPC_STGENC_ID 0 +#define STM32MP1_ETZPC_BKPSRAM_ID 1 +#define STM32MP1_ETZPC_IWDG1_ID 2 +#define STM32MP1_ETZPC_USART1_ID 3 +#define STM32MP1_ETZPC_SPI6_ID 4 +#define STM32MP1_ETZPC_I2C4_ID 5 +#define STM32MP1_ETZPC_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/lib/optee_utils.h b/include/lib/optee_utils.h index 6067caff4..ba44f998e 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/usb/usb_core.h b/include/lib/usb/usb_core.h new file mode 100644 index 000000000..e4dd43862 --- /dev/null +++ b/include/lib/usb/usb_core.h @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2015-2019, 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 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 0x03 + +#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_HS_MAX_PACKET_SIZE 512 +#define USB_FS_MAX_PACKET_SIZE 64 +#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 USB_OTG_HS_MAX_PACKET_SIZE 512 +#define USB_OTG_FS_MAX_PACKET_SIZE 64 +#define USB_OTG_MAX_EP0_SIZE 64 + +#define USB_OTG_OUT_EPNUM_MASK 0x0000FFFF +#define USB_OTG_OUT_COUNT_MASK 0xFFFF0000 + +#define SWAPBYTE(addr) (((uint16_t)(*((uint8_t *)(addr)))) + \ + (((uint16_t)(*(((uint8_t *)(addr)) + 1))) << 8)) + +#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); + uint32_t reserved; +} 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_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_hs_config_desc)(uint16_t *length); + uint8_t *(*get_fs_config_desc)(uint16_t *length); + uint8_t *(*get_other_speed_config_desc)(uint16_t *length); + uint8_t *(*get_device_qualifier_desc)(uint16_t *length); + uint8_t *(*get_dfu_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 { + uint32_t dev_endpoints; /* Device Endpoints number. + * This parameter depends on the used USB core. + * This This parameter must be a number between + * This Min_Data = 1 and Max_Data = 15 + */ + uint32_t host_channels; /* Host Channels number. + * This parameter Depends on the used USB core. + * This parameter must be a number between + * Min_Data = 1 and Max_Data = 15 + */ + uint32_t speed; /* USB Core speed. */ + uint32_t can_be_deleted; /* Enable or disable of the + * USB embedded DMA. + */ + uint32_t ep0_mps; /* Set the Endpoint 0 Max Packet size. */ + uint32_t phy_itface; /* Select the used PHY interface. */ + uint32_t sof_enable; /* Enable or disable the output of + * the SOF signal. + */ + uint32_t low_power_enable; /* Enable or disable the low power mode. */ + uint32_t lpm_enable; /* Enable or disable Link Power Management.*/ + uint32_t vbus_sensing_enable; /* Enable or disable the VBUS + * Sensing feature. + */ + uint32_t use_dedicated_ep1; /* Enable or disable the use of the + * dedicated EP1 interrupt. + */ + uint32_t use_external_vbus; /* Enable or disable the use of + * the external VBUS. + */ +} usb_otg_cfg_t; + +typedef struct { + uint8_t num;/* Endpoint number + * This parameter must be a number between Min_Data = 1 + * and Max_Data = 15 + */ + uint8_t is_in; /* Endpoint direction + * This parameter must be a number between + * Min_Data = 0 and Max_Data = 1 + */ + uint8_t is_stall; /* Endpoint stall condition + * This parameter must be a number between + * Min_Data = 0 and Max_Data = 1 + */ + uint8_t type; /* Endpoint type */ + uint8_t data_pid_start; /* Initial data PID + * This parameter must be a number between + * Min_Data = 0 and Max_Data = 1 + */ + uint8_t even_odd_frame; /* IFrame parity + * This parameter must be a number between + * Min_Data = 0 and Max_Data = 1 + */ + uint16_t tx_fifo_num; /* Transmission FIFO number + * This parameter must be a number between + * Min_Data = 1 and Max_Data = 15 + */ + 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 dma_addr; /* 32 bits aligned transfer buffer address */ + uint32_t xfer_len; /* Current transfer length */ + uint32_t xfer_count; /* Partial transfer length in case of multi + * packet transfer + */ +} usb_otg_ep_t; + +typedef enum { + HAL_PCD_STATE_RESET = 0x00, + HAL_PCD_STATE_READY = 0x01, + HAL_PCD_STATE_ERROR = 0x02, + HAL_PCD_STATE_BUSY = 0x03, + HAL_PCD_STATE_TIMEOUT = 0x04 +} pcd_state_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 (*disable_int)(void *handle); + usb_status_t (*ep0_out_start)(void *handle); + usb_status_t (*ep_start_xfer)(void *handle, usb_otg_ep_t *ep); + usb_status_t (*ep0_start_xfer)(void *handle, usb_otg_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, usb_otg_ep_t *ep); + usb_status_t (*stop_device)(void *handle); + usb_status_t (*set_address)(void *handle, uint8_t address); + usb_status_t (*dev_disconnect)(void *handle); + 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; + +typedef struct { + void *instance; /* Register base address */ + usb_otg_cfg_t init; /* PCD required parameters */ + usb_otg_ep_t in_ep[15]; /* IN endpoint parameters */ + usb_otg_ep_t out_ep[15]; /* OUT endpoint parameters */ + pcd_state_t state; /* PCD communication state */ + uint32_t setup[12]; /* Setup packet buffer */ + pcd_lpm_state_t lpm_state; /* LPM State */ + uint32_t besl; + uint32_t lpm_active; /* Enable or disable the Link Power Management. + * This parameter can be set to ENABLE or DISABLE + */ + void *p_data; /* Pointer to upper stack Handler*/ + uint32_t RESERVED[4]; /* For future use */ +} pcd_handle_t; + +/* USB Device handle structure */ +typedef struct usb_handle { + uint8_t id; + uint32_t dev_config; + uint32_t dev_default_config; + uint32_t dev_config_status; + uint32_t dev_speed; + 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; + uint8_t dev_connection_status; + uint8_t dev_test_mode; + 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; + uint32_t RESERVED[3]; +} 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); +void usb_core_ctl_error(usb_handle_t *pdev); +usb_status_t usb_core_stop(usb_handle_t *pdev); +usb_status_t register_usb_driver(usb_handle_t *pdev, 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 000000000..8a3a5a505 --- /dev/null +++ b/include/lib/usb/usb_st_dfu.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-2019, 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 + +/* 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_GET_PHASE 0x5 + +/* 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 + +/* DFU Manifestation State */ +#define DFU_MANIFEST_COMPLETE 0x00 +#define DFU_MANIFEST_IN_PROGRESS 0x01 + +/* Special Commands with Download Request */ +#define DFU_CMD_GETCOMMANDS 0x00 +#define DFU_CMD_SETADDRESSPOINTER 0x21 +#define DFU_CMD_ERASE 0x41 + +#define DFU_MEDIA_STATE_READY 0x00 +#define DFU_MEDIA_STATE_WRITTEN 0x01 +#define DFU_MEDIA_STATE_ERROR 0x02 + +/* Bit Detach capable = bit 3 in bmAttributes field */ +#define DFU_DETACH_MASK (uint8_t)(1 << 4) +#define DFU_STATUS_DEPTH (6) + +/* Undefined download address */ +#define UNDEFINE_DOWN_ADDR 0xFFFFFFFF + +typedef enum { + DFU_DETACH = 0, + DFU_DNLOAD, + DFU_UPLOAD, + DFU_GETSTATUS, + DFU_CLRSTATUS, + DFU_GETSTATE, + DFU_ABORT +} dfu_request_t; + +typedef void (*p_function)(void); + +typedef struct { + uint8_t buffer[10]; + uint8_t dev_state; + uint8_t dev_status[DFU_STATUS_DEPTH]; + uint8_t manif_state; + uint32_t wblock_num; + uint32_t wlength; + uintptr_t data_ptr; + uint32_t alt_setting; +} usb_dfu_handle_t; + +typedef struct { + uint16_t (*write_done)(uint32_t *written_in, uint32_t len); + uint8_t* (*read)(uint8_t *src, uint8_t *dest, uint32_t len); + uint16_t (*get_status)(void); +} usb_dfu_media_t; + +void usb_dfu_register_callback(usb_handle_t *pdev); +void usb_dfu_set_phase_id(uint32_t phase_id); +void usb_dfu_set_download_addr(uintptr_t addr); +uint32_t usb_dfu_download_is_completed(void); +uint32_t usb_dfu_get_current_req(void); +uint32_t usb_dfu_detach_req(void); +void usb_dfu_request_detach(void); + +#endif /* USB_ST_DFU_H */ diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index 35ae33a68..8efa90751 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -63,6 +63,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); \ @@ -77,6 +87,15 @@ _x > _y ? _x : _y; \ }) +#define CLAMP(x, min, max) __extension__ ({ \ + __typeof__(x) _x = (x); \ + __typeof__(min) _min = (min); \ + __typeof__(max) _max = (max); \ + (void)(&_x == &_min); \ + (void)(&_x == &_max); \ + (_x > _max ? _max : (_x < _min ? _min : _x)); \ +}) + /* * The round_up() macro rounds up a value to the given boundary in a * type-agnostic yet type-safe manner. The boundary must be a power of two. diff --git a/include/lib/xlat_tables/xlat_tables_v2_helpers.h b/include/lib/xlat_tables/xlat_tables_v2_helpers.h index b17b71a87..69f142aab 100644 --- a/include/lib/xlat_tables/xlat_tables_v2_helpers.h +++ b/include/lib/xlat_tables/xlat_tables_v2_helpers.h @@ -122,6 +122,37 @@ struct xlat_ctx { /* do nothing */ #endif /* PLAT_XLAT_TABLES_DYNAMIC */ +#ifdef PLAT_XLAT_BASE +#define tf_xlat_tables (void *)PLAT_XLAT_BASE + +#define XLAT_TABLES(_ctx_name, _xlat_tables_count, _section_name) \ + CASSERT(!(PLAT_XLAT_BASE & (XLAT_TABLE_SIZE - 1)), \ + invalid_plat_xlat_base); \ + CASSERT(PLAT_XLAT_SIZE >= (sizeof(uint64_t) * \ + XLAT_TABLE_ENTRIES * _xlat_tables_count), \ + invalid_plat_xlat_size); + +#else +#define XLAT_TABLES(_ctx_name, _xlat_tables_count, _section_name) \ + static uint64_t _ctx_name##_xlat_tables[_xlat_tables_count][XLAT_TABLE_ENTRIES] \ + __aligned(XLAT_TABLE_SIZE) __section(_section_name); +#endif + +#ifdef PLAT_BASE_XLAT_BASE +#define tf_base_xlat_table (void *)PLAT_BASE_XLAT_BASE + +#define BASE_XLAT_TABLE(_ctx_name, _virt_addr_space_size) \ + CASSERT(!(PLAT_BASE_XLAT_BASE & \ + ((GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size) * \ + sizeof(uint64_t)) - 1)), invalid_plat_base_xlat_cfg); +#else +#define BASE_XLAT_TABLE(_ctx_name, _virt_addr_space_size) \ + static uint64_t _ctx_name##_base_xlat_table \ + [GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)] \ + __aligned(GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size) * \ + sizeof(uint64_t)); +#endif + #define REGISTER_XLAT_CONTEXT_FULL_SPEC(_ctx_name, _mmap_count, \ _xlat_tables_count, _virt_addr_space_size, \ _phy_addr_space_size, _xlat_regime, _section_name)\ @@ -130,14 +161,8 @@ struct xlat_ctx { \ static mmap_region_t _ctx_name##_mmap[_mmap_count + 1]; \ \ - static uint64_t _ctx_name##_xlat_tables[_xlat_tables_count] \ - [XLAT_TABLE_ENTRIES] \ - __aligned(XLAT_TABLE_SIZE) __section(_section_name); \ - \ - static uint64_t _ctx_name##_base_xlat_table \ - [GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)] \ - __aligned(GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)\ - * sizeof(uint64_t)); \ + XLAT_TABLES(_ctx_name, _xlat_tables_count, _section_name) \ + BASE_XLAT_TABLE(_ctx_name, _virt_addr_space_size) \ \ XLAT_ALLOC_DYNMAP_STRUCT(_ctx_name, _xlat_tables_count) \ \ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index eeae62141..0922c5342 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -96,6 +96,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); int plat_crash_console_flush(void); @@ -103,7 +108,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 diff --git a/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S b/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S new file mode 100644 index 000000000..038ae5d72 --- /dev/null +++ b/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S @@ -0,0 +1,46 @@ +//===-- aeabi_ldivmod.S - EABI ldivmod implementation ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { int64_t quot, int64_t rem} +// __aeabi_ldivmod(int64_t numerator, int64_t denominator) { +// int64_t rem, quot; +// quot = __divmoddi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + +#if defined(__MINGW32__) +#define __aeabi_ldivmod __rt_sdiv64 +#endif + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod) + push {r6, lr} + sub sp, sp, #16 + add r6, sp, #8 + str r6, [sp] +#if defined(__MINGW32__) + movs r6, r0 + movs r0, r2 + movs r2, r6 + movs r6, r1 + movs r1, r3 + movs r3, r6 +#endif + bl SYMBOL_NAME(__divmoddi4) + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r6, pc} +END_COMPILERRT_FUNCTION(__aeabi_ldivmod) + +NO_EXEC_STACK_DIRECTIVE + diff --git a/lib/compiler-rt/builtins/divdi3.c b/lib/compiler-rt/builtins/divdi3.c new file mode 100644 index 000000000..b8eebcb20 --- /dev/null +++ b/lib/compiler-rt/builtins/divdi3.c @@ -0,0 +1,29 @@ +/* ===-- divdi3.c - Implement __divdi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a / b */ + +COMPILER_RT_ABI di_int +__divdi3(di_int a, di_int b) +{ + const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1; + di_int s_a = a >> bits_in_dword_m1; /* s_a = a < 0 ? -1 : 0 */ + di_int s_b = b >> bits_in_dword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /*sign of quotient */ + return (__udivmoddi4(a, b, (du_int*)0) ^ s_a) - s_a; /* negate if s_a == -1 */ +} diff --git a/lib/compiler-rt/builtins/divmoddi4.c b/lib/compiler-rt/builtins/divmoddi4.c new file mode 100644 index 000000000..0d4df67a6 --- /dev/null +++ b/lib/compiler-rt/builtins/divmoddi4.c @@ -0,0 +1,25 @@ +/*===-- divmoddi4.c - Implement __divmoddi4 --------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divmoddi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a / b, *rem = a % b */ + +COMPILER_RT_ABI di_int +__divmoddi4(di_int a, di_int b, di_int* rem) +{ + di_int d = __divdi3(a,b); + *rem = a - (d*b); + return d; +} diff --git a/lib/compiler-rt/builtins/popcountdi2.c b/lib/compiler-rt/builtins/popcountdi2.c new file mode 100644 index 000000000..5e8a62f07 --- /dev/null +++ b/lib/compiler-rt/builtins/popcountdi2.c @@ -0,0 +1,36 @@ +/* ===-- popcountdi2.c - Implement __popcountdi2 ----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: count of 1 bits */ + +COMPILER_RT_ABI si_int +__popcountdi2(di_int a) +{ + du_int x2 = (du_int)a; + x2 = x2 - ((x2 >> 1) & 0x5555555555555555uLL); + /* Every 2 bits holds the sum of every pair of bits (32) */ + x2 = ((x2 >> 2) & 0x3333333333333333uLL) + (x2 & 0x3333333333333333uLL); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) */ + x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0FuLL; + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) */ + su_int x = (su_int)(x2 + (x2 >> 32)); + /* The lower 32 bits hold four 16 bit sums (5 significant bits). */ + /* Upper 32 bits are garbage */ + x = x + (x >> 16); + /* The lower 16 bits hold two 32 bit sums (6 significant bits). */ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0x0000007F; /* (7 significant bits) */ +} diff --git a/lib/compiler-rt/builtins/popcountsi2.c b/lib/compiler-rt/builtins/popcountsi2.c new file mode 100644 index 000000000..44544ff49 --- /dev/null +++ b/lib/compiler-rt/builtins/popcountsi2.c @@ -0,0 +1,33 @@ +/* ===-- popcountsi2.c - Implement __popcountsi2 ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: count of 1 bits */ + +COMPILER_RT_ABI si_int +__popcountsi2(si_int a) +{ + su_int x = (su_int)a; + x = x - ((x >> 1) & 0x55555555); + /* Every 2 bits holds the sum of every pair of bits */ + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) */ + x = (x + (x >> 4)) & 0x0F0F0F0F; + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) */ + x = (x + (x >> 16)); + /* The lower 16 bits hold two 8 bit sums (5 significant bits).*/ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0x0000003F; /* (6 significant bits) */ +} diff --git a/lib/compiler-rt/compiler-rt.mk b/lib/compiler-rt/compiler-rt.mk index 49e497eb8..40c669f98 100644 --- a/lib/compiler-rt/compiler-rt.mk +++ b/lib/compiler-rt/compiler-rt.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -28,9 +28,15 @@ # POSSIBILITY OF SUCH DAMAGE. # +COMPILER_RT_SRCS := lib/compiler-rt/builtins/popcountdi2.c \ + lib/compiler-rt/builtins/popcountsi2.c + ifeq (${ARCH},aarch32) -COMPILER_RT_SRCS := lib/compiler-rt/builtins/arm/aeabi_uldivmod.S \ - lib/compiler-rt/builtins/udivmoddi4.c \ +COMPILER_RT_SRCS += lib/compiler-rt/builtins/arm/aeabi_ldivmod.S \ + lib/compiler-rt/builtins/arm/aeabi_uldivmod.S \ lib/compiler-rt/builtins/ctzdi2.c \ - lib/compiler-rt/builtins/lshrdi3.c + lib/compiler-rt/builtins/divdi3.c \ + lib/compiler-rt/builtins/divmoddi4.c \ + lib/compiler-rt/builtins/lshrdi3.c \ + lib/compiler-rt/builtins/udivmoddi4.c endif diff --git a/lib/optee/optee_utils.c b/lib/optee/optee_utils.c index 2a407939b..d5a336b55 100644 --- a/lib/optee/optee_utils.c +++ b/lib/optee/optee_utils.c @@ -132,6 +132,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; + int 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 = 0; 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. diff --git a/lib/usb/usb_core.c b/lib/usb/usb_core.c new file mode 100644 index 000000000..fd0f20424 --- /dev/null +++ b/lib/usb/usb_core.c @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include + +/* + * @brief Set a STALL condition over an endpoint + * @param hpcd: PCD 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) +{ + usb_otg_ep_t *ep; + pcd_handle_t *hpcd = (pcd_handle_t *)pdev->data; + + if ((0x80 & ep_addr) == 0x80) + ep = &hpcd->in_ep[ep_addr & 0x7F]; + else + ep = &hpcd->out_ep[ep_addr]; + + ep->is_stall = 1; + ep->num = ep_addr & 0x7F; + ep->is_in = ((ep_addr & 0x80) == 0x80); + + pdev->driver->ep_set_stall(hpcd->instance, ep); + if ((ep_addr & 0x7F) == 0) + 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; + + switch (req->value >> 8) { + case USB_DESC_TYPE_DEVICE: + pbuf = pdev->desc->get_device_desc(&len); + break; + + case USB_DESC_TYPE_CONFIGURATION: + pbuf = (uint8_t *)pdev->desc->get_hs_config_desc(&len); + pbuf[1] = USB_DESC_TYPE_CONFIGURATION; + break; + + case USB_DESC_TYPE_STRING: + switch ((uint8_t)(req->value)) { + 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; + + default: + pbuf = pdev->desc->get_usr_desc(req->value, &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_other_speed_config_desc(&len); + pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; + break; + + default: + ERROR("Unknown request %i\n", req->value >> 8); + usb_core_ctl_error(pdev); + return; + } + + if ((len != 0) && (req->length != 0)) { + len = MIN(len, req->length); + + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep_in[0].total_length = len; + pdev->ep_in[0].rem_length = len; + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, 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 = (uint8_t)(req->value); + + if (cfgidx > USBD_MAX_NUM_CONFIGURATION) { + usb_core_ctl_error(pdev); + } else { + switch (pdev->dev_state) { + case USBD_STATE_ADDRESSED: + if (cfgidx) { + 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) != 0) { + usb_core_ctl_error(pdev); + return; + } + } + break; + + case USBD_STATE_CONFIGURED: + if (cfgidx == 0) { + 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) { + 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) != 0) { + usb_core_ctl_error(pdev); + return; + } + } + break; + + default: + usb_core_ctl_error(pdev); + return; + } + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_STATUS_IN; + + /* Send status */ + usb_core_transmit(pdev, 0, NULL, 0); + } +} + +/* + * 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)) { + pdev->dev_config_status = USB_CONFIG_SELF_POWERED; + + if (pdev->dev_remote_wakeup) + pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; + + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep_in[0].total_length = 2; + pdev->ep_in[0].rem_length = 2; + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, + (uint8_t *)&pdev->dev_config_status, 2); + return; + } + + usb_core_ctl_error(pdev); +} + +/* + * 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) +{ + if ((req->index == 0) && (req->length == 0)) { + uint8_t dev_addr = (uint8_t)(req->value) & 0x7F; + + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + usb_core_ctl_error(pdev); + } else { + pdev->dev_address = dev_addr; + pdev->driver->set_address(((pcd_handle_t *) + (pdev->data))->instance, + dev_addr); + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_STATUS_IN; + + /* Send status */ + usb_core_transmit(pdev, 0, NULL, 0); + + if (dev_addr != 0) + pdev->dev_state = USBD_STATE_ADDRESSED; + else + pdev->dev_state = USBD_STATE_DEFAULT; + } + } else { + usb_core_ctl_error(pdev); + } +} + +/* + * 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) +{ + INFO("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) +{ + switch (pdev->dev_state) { + case USBD_STATE_CONFIGURED: + if (LOBYTE(req->index) <= USBD_MAX_NUM_INTERFACES) { + pdev->class->setup(pdev, req); + + if (req->length == 0) { + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_STATUS_IN; + + usb_core_transmit(pdev, 0, NULL, 0); + } + } else { + usb_core_ctl_error(pdev); + } + break; + + default: + usb_core_ctl_error(pdev); + break; + } + 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 = *(uint8_t *)(pdata); + req->b_request = *(uint8_t *)(pdata + 1); + req->value = SWAPBYTE(pdata + 2); + req->index = SWAPBYTE(pdata + 4); + req->length = SWAPBYTE(pdata + 6); +} + +/* + * 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 & 0x1F) { + 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 & 0x1F); + usb_core_set_stall(pdev, pdev->request.bm_request & 0x80); + 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 == 0) { + 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, 0, 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); + + pdev->ep0_state = USBD_EP0_STATUS_IN; + usb_core_transmit(pdev, 0x00, NULL, 0); + } + } + } else if (pdev->class->data_out && + (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 == 0) { + 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, 0, pdata, + pep->rem_length); + + /* Prepare endpoint for premature + * end of transfer + */ + usb_core_receive(pdev, 0, NULL, 0); + } else { + /* last packet is MPS multiple, + * so send ZLP packet + */ + if ((pep->total_length % pep->maxpacket == 0) && + (pep->total_length >= pep->maxpacket) && + (pep->total_length < pdev->ep0_data_len)) { + usb_core_transmit(pdev, 0, NULL, 0); + + pdev->ep0_data_len = 0; + + /* Prepare endpoint for premature + * end of transfer + */ + usb_core_receive(pdev, 0, NULL, 0); + } else { + if (pdev->class->ep0_tx_sent && + (pdev->dev_state == + USBD_STATE_CONFIGURED)) + pdev->class->ep0_tx_sent(pdev); + + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_STATUS_OUT; + + /* Start the transfer */ + usb_core_receive(pdev, 0, NULL, 0); + } + } + } + if (pdev->dev_test_mode == 1) { + ERROR("Not supported"); + pdev->dev_test_mode = 0; + return USBD_FAIL; + } + } else if (pdev->class->data_in && + (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) + pdev->class->sof(pdev); + } + + return USBD_OK; +} + +/* + * usb_core_DevDisconnected + * 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 = 0; + uint32_t len = 0; + usb_otg_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: + pdev->data->init.speed = USB_OTG_SPEED_HIGH; + pdev->data->init.ep0_mps = USB_OTG_HS_MAX_PACKET_SIZE; + break; + case USB_READ_DATA_PACKET: + ep = &pdev->data->out_ep[param & USB_OTG_OUT_EPNUM_MASK]; + len = (param & USB_OTG_OUT_COUNT_MASK) >> 0x10; + 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 & USB_OTG_OUT_EPNUM_MASK]; + len = (param & USB_OTG_OUT_COUNT_MASK) >> 0x10; + pdev->driver->read_packet(pdev->data->instance, + (uint8_t *)pdev->data->setup, 8); + ep->xfer_count += len; + 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; + pdev->data->besl = param; + } 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 hpcd: PCD handle + * @param ep_addr: endpoint address + * @param pBuf: pointer to the reception buffer + * @param len: amount of data to be received + * @retval HAL status + */ +usb_status_t usb_core_receive(usb_handle_t *pdev, uint8_t ep_addr, + uint8_t *buf, uint32_t len) +{ + usb_otg_ep_t *ep; + pcd_handle_t *hpcd = (pcd_handle_t *)pdev->data; + + ep = &hpcd->out_ep[ep_addr & 0x7F]; + + /*setup and start the Xfer */ + ep->xfer_buff = buf; + ep->xfer_len = len; + ep->xfer_count = 0; + ep->is_in = 0; + ep->num = ep_addr & 0x7F; + + if ((ep_addr & 0x7F) == 0) + 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 hpcd: PCD handle + * @param ep_addr: endpoint address + * @param pBuf: pointer to the transmission buffer + * @param len: amount of data to be sent + * @retval HAL status + */ +usb_status_t usb_core_transmit(usb_handle_t *pdev, uint8_t ep_addr, + uint8_t *buf, uint32_t len) +{ + usb_otg_ep_t *ep; + pcd_handle_t *hpcd = (pcd_handle_t *)pdev->data; + + ep = &hpcd->in_ep[ep_addr & 0x7F]; + + /*setup and start the Xfer */ + ep->xfer_buff = buf; + ep->xfer_len = len; + ep->xfer_count = 0; + ep->is_in = 1; + ep->num = ep_addr & 0x7F; + + if ((ep_addr & 0x7F) == 0) + pdev->driver->ep0_start_xfer(hpcd->instance, ep); + else + pdev->driver->ep_start_xfer(hpcd->instance, ep); + + return USBD_OK; +} + +/* + * @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, 0x80); + usb_core_set_stall(pdev, 0); +} + +/* + * 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->disable_int(pdev->data->instance); + pdev->driver->stop_device(pdev->data->instance); + pdev->driver->dev_disconnect(pdev->data->instance); + return USBD_OK; +} + +/* + * usb_core_stop + * Stop the USB Device Core. + * pdev: Device Handle + * return : USBD Status + */ +usb_status_t register_usb_driver(usb_handle_t *pdev, const usb_driver_t *driver, + void *driver_handle) +{ + /* Free Class Resources */ + pdev->driver = driver; + pdev->data->instance = driver_handle; + return USBD_OK; +} + +/* + * usb_core_stop + * Stop the USB Device Core. + * pdev: Device Handle + * return : USBD Status + */ +usb_status_t register_platform(usb_handle_t *pdev, + const usb_desc_t *plat_call_back) +{ + /* Free 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 000000000..8876b7499 --- /dev/null +++ b/lib/usb/usb_st_dfu.c @@ -0,0 +1,865 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include + +static uintptr_t usbd_dfu_download_address; +static uint32_t usbd_dfu_phase_id; +static uint32_t usbd_dfu_operation_complete; +static uint32_t usbd_dfu_current_req; +static uint32_t usbd_detach_req; + +/* + * @brief USBD_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 USBD_DFU_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 USBD_DFU_DataIn + * 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 DFU_Leave + * Handles the sub-protocol DFU leave DFU mode request (leaves DFU mode + * and resets device to jump to user loaded code). + * @param pdev: device instance + * @retval None + */ +static void usb_dfu_leave(usb_handle_t *pdev) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + hdfu->manif_state = DFU_MANIFEST_COMPLETE; + + if (DFU_BM_ATTRIBUTE & 0x04) { + hdfu->dev_state = DFU_STATE_MANIFEST_SYNC; + + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + } else { + hdfu->dev_state = DFU_STATE_MANIFEST_WAIT_RESET; + + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + + /* Disconnect the USB device */ + usb_core_stop(pdev); + } +} + +/* + * @brief USBD_DFU_EP0_RxReady + * 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 USBD_DFU_EP0_TxReady + * handle EP0 TRx Ready event + * @param pdev: device instance + * @retval status + */ +static uint8_t usb_dfu_ep0_tx_ready(usb_handle_t *pdev) +{ + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + uint16_t len, dfu_version = 0; + uint8_t *serial = pdev->desc->get_dfu_desc(&len); + + dfu_version = serial[len - 1] << 8 | serial[len - 2]; + + if (hdfu->dev_state == DFU_STATE_DNLOAD_BUSY) { + if (dfu_version == 0x011a) { + /* Decode the Special Command*/ + if (hdfu->wblock_num == 0) { + if (hdfu->buffer[0] == + DFU_CMD_SETADDRESSPOINTER && + hdfu->wlength == 5) { + hdfu->data_ptr = hdfu->buffer[1]; + hdfu->data_ptr += + hdfu->buffer[2] << 8; + hdfu->data_ptr += + hdfu->buffer[3] << 16; + hdfu->data_ptr += + hdfu->buffer[4] << 24; + } else if (hdfu->buffer[0] == + DFU_CMD_ERASE && + hdfu->wlength == 5) { + hdfu->data_ptr = hdfu->buffer[1]; + hdfu->data_ptr += + hdfu->buffer[2] << 8; + hdfu->data_ptr += + hdfu->buffer[3] << 16; + hdfu->data_ptr += + hdfu->buffer[4] << 24; + } else { + /* Reset the global length and block number */ + hdfu->wlength = 0; + hdfu->wblock_num = 0; + /* Call the error management function + * (command will be nacked) + */ + usb_core_ctl_error(pdev); + } + } + } + if ((hdfu->wblock_num > 1 && dfu_version == 0x011a) || + dfu_version != 0x011a) { + /* Perform the write operation */ + if (((usb_dfu_media_t *) + pdev->user_data)->write_done((uint32_t *) + hdfu->data_ptr, + hdfu->wlength) + != USBD_OK) + return USBD_FAIL; + } + + /* Reset the global length and block number */ + hdfu->wlength = 0; + hdfu->wblock_num = 0; + + /* Update the state machine */ + hdfu->dev_state = DFU_STATE_DNLOAD_SYNC; + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + return USBD_OK; + } else if (hdfu->dev_state == DFU_STATE_MANIFEST) { + /* Manifestation in progress*/ + /* Start leaving DFU mode */ + usb_dfu_leave(pdev); + } + + return USBD_OK; +} + +/* + * @brief USBD_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 USBD_DFU_IsoINIncomplete + * 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 USBD_DFU_IsoOutIncomplete + * 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 USBD_DFU_DataOut + * 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 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 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[0] = DFU_ERROR_NONE; + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/ + hdfu->dev_status[4] = hdfu->dev_state; + hdfu->dev_status[5] = 0; /*iString*/ + hdfu->wblock_num = 0; + } + hdfu->wlength = 0; + + usbd_detach_req = 0; +} + +/* + * @brief 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; + + /* Data setup request */ + if (req->length > 0) { + if ((hdfu->dev_state == DFU_STATE_IDLE) || + (hdfu->dev_state == DFU_STATE_DNLOAD_IDLE)) { + /* Update the global length and block number */ + hdfu->wblock_num = req->value; + hdfu->wlength = req->length; + + /* Update the data address */ + hdfu->data_ptr = usbd_dfu_download_address; + + /* Update the state machine */ + hdfu->dev_state = DFU_STATE_DNLOAD_SYNC; + hdfu->dev_status[4] = hdfu->dev_state; + + /* Prepare the reception of the buffer over EP0 */ + /* Set EP0 State */ + pdev->ep0_state = USBD_EP0_DATA_OUT; + pdev->ep_out[0].total_length = hdfu->wlength; + pdev->ep_out[0].rem_length = hdfu->wlength; + + /* Start the transfer */ + usb_core_receive(pdev, + 0, + (uint8_t *)usbd_dfu_download_address, + hdfu->wlength); + + usbd_dfu_download_address += hdfu->wlength; + } else { + /* Unsupported state */ + /* Call the error management function + * (command will be nacked) + */ + usb_core_ctl_error(pdev); + } + } else { + /* End of DNLOAD operation*/ + if (hdfu->dev_state == DFU_STATE_DNLOAD_IDLE || + hdfu->dev_state == DFU_STATE_IDLE) { + hdfu->manif_state = DFU_MANIFEST_IN_PROGRESS; + hdfu->dev_state = DFU_STATE_MANIFEST_SYNC; + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + } else { + /* Call the error management function + * (command will be nacked) + */ + usb_core_ctl_error(pdev); + } + } +} + +/* + * @brief 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; + + /* Data setup request */ + if (req->length > 0) { + if ((hdfu->dev_state == DFU_STATE_IDLE) || + (hdfu->dev_state == DFU_STATE_UPLOAD_IDLE)) { + /* Update the global length and block number */ + hdfu->wblock_num = req->value; + hdfu->wlength = req->length; + + /* DFU GetPhase Command */ + if (hdfu->wblock_num == 0) { + /* Update the state machine */ + hdfu->dev_state = (hdfu->wlength > 3) ? + DFU_STATE_IDLE : + DFU_STATE_UPLOAD_IDLE; + + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + + INFO("UPLOAD :\n"); + INFO("\t\tPhase ID : %i\n", usbd_dfu_phase_id); + INFO("\t\taddress 0x%lx\n", + usbd_dfu_download_address); + + hdfu->buffer[0] = usbd_dfu_phase_id; + hdfu->buffer[1] = (uint8_t) + (usbd_dfu_download_address); + hdfu->buffer[2] = (uint8_t) + (usbd_dfu_download_address >> + 8); + hdfu->buffer[3] = (uint8_t) + (usbd_dfu_download_address >> + 16); + hdfu->buffer[4] = (uint8_t) + (usbd_dfu_download_address >> + 24); + + hdfu->buffer[5] = 0x00; + hdfu->buffer[6] = 0x00; + hdfu->buffer[7] = 0x00; + hdfu->buffer[8] = 0x00; + + if ((usbd_dfu_download_address == + UNDEFINE_DOWN_ADDR) && + (usbd_detach_req)) { + INFO("Send detach request\n"); + hdfu->buffer[9] = 0x01; + pdev->ep_in[0].total_length = 10; + pdev->ep_in[0].rem_length = 10; + } else { + pdev->ep_in[0].total_length = 9; + pdev->ep_in[0].rem_length = 9; + } + + /* Send the status data over EP0 */ + pdev->ep0_state = USBD_EP0_DATA_IN; + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, + (uint8_t *)&hdfu->buffer[0], + pdev->ep_in[0].total_length); + } else { + /* unsupported hdfu->wblock_num */ + ERROR("UPLOAD : Unsupported block : %i\n", + hdfu->wblock_num); + + hdfu->dev_state = DFU_ERROR_STALLEDPKT; + + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + + /* Call the error management function + * (command will be nacked + */ + usb_core_ctl_error(pdev); + } + } else { + /* Unsupported state */ + ERROR("UPLOAD : Unsupported State\n"); + + hdfu->wlength = 0; + hdfu->wblock_num = 0; + /* Call the error management function + * (command will be nacked + */ + usb_core_ctl_error(pdev); + } + } else { + /* No Data setup request */ + INFO("USB : DFU : Nothing to do\n"); + hdfu->dev_state = DFU_STATE_IDLE; + + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + } +} + +/* + * @brief DFU_GetStatus + * 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; + uint16_t status; + uint8_t dfu_bm_attribute = DFU_BM_ATTRIBUTE; + + switch (hdfu->dev_state) { + case DFU_STATE_DNLOAD_SYNC: + status = ((usb_dfu_media_t *)pdev->user_data)->get_status(); + + switch (status) { + case DFU_MEDIA_STATE_WRITTEN: + /* SRAM block writing is finished, checks if checksum + * error has been detected + */ + hdfu->dev_state = DFU_STATE_DNLOAD_IDLE; + break; + + case DFU_MEDIA_STATE_ERROR: + hdfu->dev_state = DFU_STATE_ERROR; + break; + + case DFU_MEDIA_STATE_READY: + default: + /* SRAM is ready to be written */ + hdfu->dev_state = DFU_STATE_DNLOAD_BUSY; + break; + } + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + break; + + case DFU_STATE_MANIFEST_SYNC: + if (hdfu->manif_state == DFU_MANIFEST_IN_PROGRESS) { + hdfu->dev_state = DFU_STATE_MANIFEST; + + hdfu->dev_status[1] = 1;/*bwPollTimeout = 1ms*/ + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + } else if ((hdfu->manif_state == DFU_MANIFEST_COMPLETE) && + (dfu_bm_attribute & 0x04)) { + INFO("USB : DFU : end of download partition : %i\n", + hdfu->alt_setting); + hdfu->dev_state = DFU_STATE_IDLE; + usbd_dfu_operation_complete = 1; + + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; + hdfu->dev_status[4] = hdfu->dev_state; + } + break; + + default: + break; + } + + /* Send the status data over EP0 */ + pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep_in[0].total_length = 6; + pdev->ep_in[0].rem_length = 6; + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, (uint8_t *)&hdfu->dev_status[0], 6); +} + +/* + * @brief DFU_ClearStatus + * 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[0] = DFU_ERROR_NONE;/*bStatus*/ + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/ + hdfu->dev_status[4] = hdfu->dev_state;/*bState*/ + hdfu->dev_status[5] = 0;/*iString*/ + } else { + /*State Error*/ + hdfu->dev_state = DFU_STATE_ERROR; + hdfu->dev_status[0] = DFU_ERROR_UNKNOWN;/*bStatus*/ + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/ + hdfu->dev_status[4] = hdfu->dev_state;/*bState*/ + hdfu->dev_status[5] = 0;/*iString*/ + } +} + +/* + * @brief DFU_GetState + * 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 */ + /* Send the status data over EP0 */ + pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep_in[0].total_length = 1; + pdev->ep_in[0].rem_length = 1; + + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, &hdfu->dev_state, 1); +} + +/* + * @brief 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[0] = DFU_ERROR_NONE; + hdfu->dev_status[1] = 0; + hdfu->dev_status[2] = 0; + hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/ + hdfu->dev_status[4] = hdfu->dev_state; + hdfu->dev_status[5] = 0; /*iString*/ + hdfu->wblock_num = 0; + hdfu->wlength = 0; + } +} + +/* + * @brief USBD_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 = 0; + uint8_t ret = USBD_OK; + usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data; + + VERBOSE("alt_setting %i, bmRequest : 0x%x, brequest : 0x%x\n", + hdfu->alt_setting, req->bm_request & USB_REQ_TYPE_MASK, + req->b_request); + switch (req->bm_request & USB_REQ_TYPE_MASK) { + case USB_REQ_TYPE_CLASS: + usbd_dfu_current_req = req->b_request; + if (hdfu->alt_setting == usbd_dfu_phase_id) { + 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("phase ID :%i\n", usbd_dfu_phase_id); + usb_core_ctl_error(pdev); + ret = USBD_FAIL; + break; + } + } else if (hdfu->alt_setting == DFU_GET_PHASE) { + switch (req->b_request) { + case DFU_UPLOAD: + usb_dfu_upload(pdev, req); + break; + + case DFU_GETSTATUS: + INFO("GETSTATUS :\n"); + usb_dfu_get_status(pdev); + + switch (hdfu->dev_state) { + case APP_STATE_IDLE: + INFO("\t\tAPP_STATE_IDLE\n"); + break; + case APP_STATE_DETACH: + INFO("\t\tAPP_STATE_DETACH\n"); + break; + case DFU_STATE_IDLE: + INFO("\t\tDFU_STATE_IDLE\n"); + break; + case DFU_STATE_DNLOAD_SYNC: + INFO("\t\tDFU_STATE_DNLOAD_SYNC\n"); + break; + case DFU_STATE_DNLOAD_BUSY: + INFO("\t\tDFU_STATE_DNLOAD_BUSY\n"); + break; + case DFU_STATE_DNLOAD_IDLE: + INFO("\t\tDFU_STATE_DNLOAD_IDLE\n"); + break; + case DFU_STATE_MANIFEST_SYNC: + INFO("\t\tDFU_STATE_MANIFEST_SYNC\n"); + break; + case DFU_STATE_MANIFEST: + INFO("\t\tDFU_STATE_MANIFEST\n"); + break; + case DFU_STATE_MANIFEST_WAIT_RESET: + INFO("\t\tDFU_STATE_MANIFEST_WAIT_RESET\n"); + break; + case DFU_STATE_UPLOAD_IDLE: + INFO("\t\tDFU_STATE_UPLOAD_IDLE\n"); + break; + case DFU_STATE_ERROR: + ERROR("\t\tDFU_STATE_ERROR\n"); + break; + default: + break; + } + break; + + case DFU_CLRSTATUS: + INFO("Receive DFU clear status\n"); + usb_dfu_clear_status(pdev); + break; + + case DFU_GETSTATE: + INFO("GETSTATE :\n"); + usb_dfu_get_state(pdev); + + switch (hdfu->dev_state) { + case APP_STATE_IDLE: + INFO("\t\tAPP_STATE_IDLE\n"); + break; + case APP_STATE_DETACH: + INFO("\t\tAPP_STATE_DETACH\n"); + break; + case DFU_STATE_IDLE: + INFO("\t\tDFU_STATE_IDLE\n"); + break; + case DFU_STATE_DNLOAD_SYNC: + INFO("\t\tDFU_STATE_DNLOAD_SYNC\n"); + break; + case DFU_STATE_DNLOAD_BUSY: + INFO("\t\tDFU_STATE_DNLOAD_BUSY\n"); + break; + case DFU_STATE_DNLOAD_IDLE: + INFO("\t\tDFU_STATE_DNLOAD_IDLE\n"); + break; + case DFU_STATE_MANIFEST_SYNC: + INFO("\t\tDFU_STATE_MANIFEST_SYNC\n"); + break; + case DFU_STATE_MANIFEST: + INFO("\t\tDFU_STATE_MANIFEST\n"); + break; + case DFU_STATE_MANIFEST_WAIT_RESET: + INFO("\t\tDFU_STATE_MANIFEST_WAIT_RESET\n"); + break; + case DFU_STATE_UPLOAD_IDLE: + INFO("\t\tDFU_STATE_UPLOAD_IDLE\n"); + break; + case DFU_STATE_ERROR: + ERROR("\t\tDFU_STATE_ERROR\n"); + break; + default: + break; + } + break; + + case DFU_ABORT: + INFO("Receive DFU abort\n"); + usb_dfu_abort(pdev); + break; + + case DFU_DETACH: + usb_dfu_detach(pdev, req); + break; + + default: + ERROR("phase ID :%i\n", DFU_GET_PHASE); + usb_core_ctl_error(pdev); + ret = USBD_FAIL; + break; + } + } else { + ERROR("Unknown alternate : %i\n", hdfu->alt_setting); + ret = USBD_FAIL; + } + break; + case USB_REQ_TYPE_STANDARD: + switch (req->b_request) { + case USB_REQ_GET_DESCRIPTOR: + if ((req->value >> 8) == DFU_DESCRIPTOR_TYPE) { + pbuf = pdev->desc->get_dfu_desc(&len); + len = MIN(len, req->length); + } + + pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep_in[0].total_length = len; + pdev->ep_in[0].rem_length = len; + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, pbuf, len); + + break; + + case USB_REQ_GET_INTERFACE: + pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep_in[0].total_length = 1; + pdev->ep_in[0].rem_length = 1; + /* Start the transfer */ + usb_core_transmit(pdev, 0x00, + (uint8_t *)&hdfu->alt_setting, 1); + break; + + case USB_REQ_SET_INTERFACE: + hdfu->alt_setting = (uint8_t)(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 = { + usb_dfu_init, + usb_dfu_de_init, + usb_dfu_setup, + usb_dfu_ep0_tx_ready, + usb_dfu_ep0_rx_ready, + usb_dfu_data_in, + usb_dfu_data_out, + usb_dfu_sof, + usb_dfu_iso_in_incomplete, + usb_dfu_iso_out_incomplete, + 0 +}; + +void usb_dfu_register_callback(usb_handle_t *pdev) +{ + pdev->class = (usb_class_t *)&USBD_DFU_initvalue; +} + +void usb_dfu_set_phase_id(uint32_t phase_id) +{ + usbd_dfu_phase_id = phase_id; + usbd_dfu_operation_complete = 0; +} + +void usb_dfu_set_download_addr(uintptr_t addr) +{ + usbd_dfu_download_address = addr; +} + +uint32_t usb_dfu_download_is_completed(void) +{ + return usbd_dfu_operation_complete; +} + +uint32_t usb_dfu_get_current_req(void) +{ + return usbd_dfu_current_req; +} + +uint32_t usb_dfu_detach_req(void) +{ + return usbd_detach_req; +} + +void usb_dfu_request_detach(void) +{ + usbd_detach_req = 1; +} diff --git a/lib/xlat_tables/aarch32/xlat_tables.c b/lib/xlat_tables/aarch32/xlat_tables.c index 4b01b9b7a..f045d3e16 100644 --- a/lib/xlat_tables/aarch32/xlat_tables.c +++ b/lib/xlat_tables/aarch32/xlat_tables.c @@ -26,8 +26,17 @@ #define NUM_BASE_LEVEL_ENTRIES \ GET_NUM_BASE_LEVEL_ENTRIES(PLAT_VIRT_ADDR_SPACE_SIZE) +#ifdef PLAT_BASE_XLAT_BASE +CASSERT(!(PLAT_BASE_XLAT_BASE & + (NUM_BASE_LEVEL_ENTRIES * sizeof(uint64_t) - 1)), + invalid_plat_base_xlat_base); +CASSERT(PLAT_BASE_XLAT_SIZE == sizeof(uint64_t) * NUM_BASE_LEVEL_ENTRIES, + invalid_plat_base_xlat_size); +static uint64_t *base_xlation_table = (uint64_t *)PLAT_BASE_XLAT_BASE; +#else static uint64_t base_xlation_table[NUM_BASE_LEVEL_ENTRIES] __aligned(NUM_BASE_LEVEL_ENTRIES * sizeof(uint64_t)); +#endif #if ENABLE_ASSERTIONS static unsigned long long get_max_supported_pa(void) diff --git a/lib/xlat_tables/aarch64/xlat_tables.c b/lib/xlat_tables/aarch64/xlat_tables.c index c86412c9b..e62454d55 100644 --- a/lib/xlat_tables/aarch64/xlat_tables.c +++ b/lib/xlat_tables/aarch64/xlat_tables.c @@ -25,6 +25,10 @@ #define NUM_BASE_LEVEL_ENTRIES \ GET_NUM_BASE_LEVEL_ENTRIES(PLAT_VIRT_ADDR_SPACE_SIZE) +#ifdef PLAT_BASE_XLAT_BASE +#error "PLAT_BASE_XLAT_BASE is not allowed on AArch64 capable architectures" +#endif + static uint64_t base_xlation_table[NUM_BASE_LEVEL_ENTRIES] __aligned(NUM_BASE_LEVEL_ENTRIES * sizeof(uint64_t)); diff --git a/lib/xlat_tables/xlat_tables_common.c b/lib/xlat_tables/xlat_tables_common.c index 23fe3f0b4..ba0ffbe19 100644 --- a/lib/xlat_tables/xlat_tables_common.c +++ b/lib/xlat_tables/xlat_tables_common.c @@ -38,8 +38,17 @@ #define UNSET_DESC ~0ULL #define MT_UNKNOWN ~0U +#ifdef PLAT_XLAT_BASE +#define XLAT_TABLE_REQUIRED_ENTRIES (XLAT_TABLE_ENTRIES * MAX_XLAT_TABLES) +CASSERT(PLAT_XLAT_SIZE == sizeof(uint64_t) * XLAT_TABLE_REQUIRED_ENTRIES, + invalid_plat_xlat_size); +CASSERT(!(PLAT_XLAT_BASE & (XLAT_TABLE_SIZE - 1)), invalid_plat_xlat_base); +#define xlat_tables ((uint64_t (*)[XLAT_TABLE_ENTRIES]) \ + (unsigned long *)PLAT_XLAT_BASE) +#else static uint64_t xlat_tables[MAX_XLAT_TABLES][XLAT_TABLE_ENTRIES] __aligned(XLAT_TABLE_SIZE) __section("xlat_table"); +#endif static unsigned int next_xlat; static unsigned long long xlat_max_pa; @@ -411,6 +420,15 @@ void init_xlation_table(uintptr_t base_va, uint64_t *table, ap1_mask = 0ULL; } +#ifdef PLAT_BASE_XLAT_BASE + inv_dcache_range(PLAT_BASE_XLAT_BASE, PLAT_BASE_XLAT_SIZE); + zeromem((void *)PLAT_BASE_XLAT_BASE, PLAT_BASE_XLAT_SIZE); +#endif +#ifdef PLAT_XLAT_BASE + inv_dcache_range(PLAT_XLAT_BASE, PLAT_XLAT_SIZE); + zeromem((void *)PLAT_XLAT_BASE, PLAT_XLAT_SIZE); +#endif + init_xlation_table_inner(mmap, base_va, table, level); *max_va = xlat_max_va; *max_pa = xlat_max_pa; diff --git a/lib/xlat_tables_v2/xlat_tables_core.c b/lib/xlat_tables_v2/xlat_tables_core.c index 4f62f469f..2982b25e6 100644 --- a/lib/xlat_tables_v2/xlat_tables_core.c +++ b/lib/xlat_tables_v2/xlat_tables_core.c @@ -1191,7 +1191,12 @@ void __init init_xlat_tables_ctx(xlat_ctx_t *ctx) xlat_mmap_print(mm); /* All tables must be zeroed before mapping any region. */ - +#ifdef PLAT_BASE_XLAT_BASE + inv_dcache_range(PLAT_BASE_XLAT_BASE, PLAT_BASE_XLAT_SIZE); +#endif +#ifdef PLAT_XLAT_BASE + inv_dcache_range(PLAT_XLAT_BASE, PLAT_XLAT_SIZE); +#endif for (unsigned int i = 0U; i < ctx->base_table_entries; i++) ctx->base_table[i] = INVALID_DESC; diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index b7fb173b1..df8ab79e4 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -106,6 +106,9 @@ ENABLE_BTI := 0 # Use BRANCH_PROTECTION to enable PAUTH. ENABLE_PAUTH := 0 +# 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 diff --git a/plat/common/aarch32/platform_helpers.S b/plat/common/aarch32/platform_helpers.S index 5b9cb5914..d2c20b4a7 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 b46656c7a..bf7fba807 100644 --- a/plat/common/plat_bl_common.c +++ b/plat/common/plat_bl_common.c @@ -48,7 +48,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 38b2a0bd7..f94a5c2c2 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 */ @@ -14,23 +14,42 @@ #include #include #include +#include #include #include #include +#include +#include +#include #include #include +#include +#include #include #include #include #include +#if STM32MP_UART_PROGRAMMER +#include +#endif +#if STM32MP_USB_PROGRAMMER +#include +#include +#include +#include +#include +#endif + /* IO devices */ static const io_dev_connector_t *dummy_dev_con; static uintptr_t dummy_dev_handle; static uintptr_t dummy_dev_spec; 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 */ @@ -51,8 +70,63 @@ static const io_block_dev_spec_t mmc_block_dev_spec = { .block_size = MMC_BLOCK_SIZE, }; -static uintptr_t storage_dev_handle; 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 +static const io_dev_connector_t *uart_dev_con; + +static UART_HandleTypeDef uart_programmer = { + .Init.BaudRate = STM32MP_UART_BAUDRATE, + .Init.StopBits = UART_STOPBITS_1, + .Init.HwFlowCtl = UART_HWCONTROL_NONE, + .Init.Mode = UART_MODE_TX_RX, + .Init.OverSampling = UART_OVERSAMPLING_16, + .Init.FIFOMode = UART_FIFOMODE_ENABLE, + .AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_AUTOBAUDRATE_INIT, + .AdvancedInit.AutoBaudRateEnable = UART_ADVFEATURE_AUTOBAUDRATE_DISABLE, +}; +#endif /* STM32MP_UART_PROGRAMMER */ + +#if STM32MP_USB_PROGRAMMER +static usb_handle_t usb_core_handle; +static usb_dfu_handle_t usb_dfu_handle; +static pcd_handle_t pcd_handle; +static const io_dev_connector_t *usb_dev_con; +#endif /* STM32MP_USB_PROGRAMMER */ #ifdef AARCH32_SP_OPTEE static const struct stm32image_part_info optee_header_partition_spec = { @@ -96,7 +170,7 @@ enum { IMG_IDX_NUM }; -static struct stm32image_device_info stm32image_dev_info_spec = { +static struct stm32image_device_info stm32image_dev_info_spec __unused = { .lba_size = MMC_BLOCK_SIZE, .part_info[IMG_IDX_BL33] = { .name = BL33_IMAGE_NAME, @@ -123,7 +197,7 @@ static io_block_spec_t stm32image_block_spec = { .length = 0, }; -static const io_dev_connector_t *stm32image_dev_con; +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); @@ -169,11 +243,13 @@ static const struct plat_io_policy policies[] = { .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, @@ -205,8 +281,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"); + 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: + 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; } @@ -216,6 +308,7 @@ static void print_boot_device(boot_api_context_t *boot_context) } } +#if STM32MP_SDMMC || STM32MP_EMMC static void boot_mmc(enum mmc_device_type mmc_dev_type, uint16_t boot_interface_instance) { @@ -251,6 +344,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); @@ -305,6 +402,243 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, &image_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_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 */ + +#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_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); +} +#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_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 + + 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_NAND */ + +#if STM32MP_UART_PROGRAMMER +static void flash_uart(uint16_t boot_interface_instance) +{ + int io_result __unused; + uintptr_t uart_addr; + + /* Register the IO devices on this platform */ + io_result = register_io_dev_uart(&uart_dev_con); + assert(io_result == 0); + + uart_programmer.Init.WordLength = UART_WORDLENGTH_9B; + uart_programmer.Init.Parity = UART_PARITY_EVEN; + uart_addr = get_uart_address(boot_interface_instance); + + if (uart_addr != 0U) { + uart_programmer.Instance = (USART_TypeDef *)uart_addr; + } else { + WARN("UART instance not found, using default\n"); + uart_programmer.Instance = (USART_TypeDef *)USART2_BASE; + } + + /* Open connections to devices */ + io_result = io_dev_open(uart_dev_con, (uintptr_t)&uart_programmer, + &image_dev_handle); + assert(io_result == 0); +} +#endif + +#if STM32MP_USB_PROGRAMMER +static void flash_usb(struct usb_ctx *usb_context) +{ + int io_result __unused; + + pcd_handle.in_ep[0].maxpacket = 0x40; + pcd_handle.out_ep[0].maxpacket = 0x40; + + pcd_handle.state = HAL_PCD_STATE_READY; + + usb_core_handle.data = &pcd_handle; + + usb_dwc2_init_driver(&usb_core_handle, + (uint32_t *)USB_OTG_BASE); + + usb_dfu_register_callback(&usb_core_handle); + + stm32mp_usb_init_desc(&usb_core_handle); + + usb_core_handle.ep_in[0].maxpacket = 0x40; + usb_core_handle.ep_out[0].maxpacket = 0x40; + + usb_core_handle.ep0_state = + usb_context->pusbd_device_ctx->ep0_state; + usb_core_handle.dev_state = USBD_STATE_CONFIGURED; + + usb_core_handle.class_data = &usb_dfu_handle; + + usb_dfu_handle.dev_state = DFU_STATE_IDLE; + usb_dfu_handle.dev_status[1] = 0; + usb_dfu_handle.dev_status[2] = 0; + usb_dfu_handle.dev_status[3] = 0; + usb_dfu_handle.dev_status[4] = usb_dfu_handle.dev_state; + usb_dfu_handle.dev_status[5] = 0; + + /* Register the IO devices on this platform */ + io_result = register_io_dev_usb(&usb_dev_con); + assert(io_result == 0); + + /* Open connections to devices */ + io_result = io_dev_open(usb_dev_con, + (uintptr_t)&usb_core_handle, + &image_dev_handle); + + assert(io_result == 0); +} +#endif void stm32mp_io_setup(void) { @@ -328,18 +662,53 @@ void stm32mp_io_setup(void) assert(io_result == 0); 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); 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); break; +#endif +#if STM32MP_SPI_NOR + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: + dmbsy(); + boot_spi_nor(boot_context); + break; +#endif +#if STM32MP_RAW_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + dmbsy(); + boot_fmc2_nand(boot_context); + break; +#endif +#if STM32MP_SPI_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: + dmbsy(); + boot_spi_nand(boot_context); + break; +#endif +#if STM32MP_UART_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART: + dmbsy(); + flash_uart(boot_context->boot_interface_instance); + break; +#endif +#if STM32MP_USB_PROGRAMMER + case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB: + dmbsy(); + flash_usb((struct usb_ctx *)boot_context->usb_context); + break; +#endif default: ERROR("Boot interface %d not supported\n", boot_context->boot_interface_selected); + panic(); break; } } @@ -365,3 +734,58 @@ 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 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/stm32mp_auth.h b/plat/st/common/include/stm32mp_auth.h deleted file mode 100644 index 3075d18ac..000000000 --- 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 4f8567979..ab419f15f 100644 --- a/plat/st/common/include/stm32mp_common.h +++ b/plat/st/common/include/stm32mp_common.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 */ @@ -7,16 +7,22 @@ #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); 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); /* Return the base address of the DDR controller */ uintptr_t stm32mp_ddrctrl_base(void); @@ -30,9 +36,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 +63,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 +/* 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 @@ -67,14 +91,17 @@ void stm32mp_print_cpuinfo(void); /* Print board information */ void stm32mp_print_boardinfo(void); +/* Check HW CPU OPP support */ +bool stm32mp_supports_cpu_opp(uint32_t opp_id); + /* * 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); +unsigned long stm32mp_clk_get_rate(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); /* Initialise the IO layer and register platform IO devices */ void stm32mp_io_setup(void); @@ -87,4 +114,10 @@ void stm32mp_io_setup(void); */ int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer); +#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 + #endif /* STM32MP_COMMON_H */ diff --git a/plat/st/common/include/stm32mp_dt.h b/plat/st/common/include/stm32mp_dt.h index 74b01b3aa..873bed551 100644 --- a/plat/st/common/include/stm32mp_dt.h +++ b/plat/st/common/include/stm32mp_dt.h @@ -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 */ @@ -9,6 +9,8 @@ #include +#include + #define DT_DISABLED U(0) #define DT_NON_SECURE U(1) #define DT_SECURE U(2) @@ -28,20 +30,28 @@ int dt_open_and_check(void); 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); uint32_t fdt_read_uint32_default(int node, const char *prop_name, uint32_t dflt_value); int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, uint32_t count); +int fdt_get_reg_props_by_name(int node, const char *name, uintptr_t *base, + size_t *size); 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_get_node_by_compatible(const char *compatible); +int dt_match_instance_by_compatible(const char *compatible, uintptr_t address); uint32_t dt_get_ddr_size(void); -uintptr_t dt_get_ddrctrl_base(void); -uintptr_t dt_get_ddrphyc_base(void); -uintptr_t dt_get_pwr_base(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); -uintptr_t dt_get_syscfg_base(void); +const char *dt_get_cpu_regulator_name(void); const char *dt_get_board_model(void); +int fdt_get_gpio_bank_pinctrl_node(unsigned int bank); +int fdt_get_gpioz_nbpins_from_dt(void); #endif /* STM32MP_DT_H */ diff --git a/plat/st/common/include/stm32mp_shres_helpers.h b/plat/st/common/include/stm32mp_shres_helpers.h index 8b786cc04..8b048284c 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 000000000..ec3e3525c --- /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/stm32mp_auth.c b/plat/st/common/stm32mp_auth.c deleted file mode 100644 index 0ef6d5454..000000000 --- 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 afa87f487..e838995cc 100644 --- a/plat/st/common/stm32mp_common.c +++ b/plat/st/common/stm32mp_common.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,8 +12,15 @@ #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; @@ -24,6 +31,12 @@ 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; void stm32mp_save_boot_ctx_address(uintptr_t address) @@ -38,54 +51,22 @@ uintptr_t stm32mp_get_boot_ctx_address(void) uintptr_t stm32mp_ddrctrl_base(void) { - static uintptr_t ddrctrl_base; - - if (ddrctrl_base == 0) { - ddrctrl_base = dt_get_ddrctrl_base(); - - assert(ddrctrl_base == DDRCTRL_BASE); - } - - return ddrctrl_base; + return DDRCTRL_BASE; } uintptr_t stm32mp_ddrphyc_base(void) { - static uintptr_t ddrphyc_base; - - if (ddrphyc_base == 0) { - ddrphyc_base = dt_get_ddrphyc_base(); - - assert(ddrphyc_base == DDRPHYC_BASE); - } - - return ddrphyc_base; + return DDRPHYC_BASE; } uintptr_t stm32mp_pwr_base(void) { - static uintptr_t pwr_base; - - if (pwr_base == 0) { - pwr_base = dt_get_pwr_base(); - - assert(pwr_base == PWR_BASE); - } - - return pwr_base; + return PWR_BASE; } uintptr_t stm32mp_rcc_base(void) { - static uintptr_t rcc_base; - - if (rcc_base == 0) { - rcc_base = fdt_rcc_read_addr(); - - assert(rcc_base == RCC_BASE); - } - - return rcc_base; + return RCC_BASE; } bool stm32mp_lock_available(void) @@ -96,6 +77,20 @@ bool stm32mp_lock_available(void) return (read_sctlr() & c_m_bits) == c_m_bits; } +void stm32mp_pwr_regs_lock(void) +{ + if (stm32mp_lock_available()) { + spin_lock(&lock); + } +} + +void stm32mp_pwr_regs_unlock(void) +{ + if (stm32mp_lock_available()) { + spin_unlock(&lock); + } +} + uintptr_t stm32_get_gpio_bank_base(unsigned int bank) { if (bank == GPIO_BANK_Z) { @@ -120,34 +115,89 @@ uint32_t stm32_get_gpio_bank_offset(unsigned int bank) int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer) { - uint32_t i; - uint32_t img_checksum = 0U; - /* * 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; } + +/* 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; +} + +#if TRUSTED_BOARD_BOOT +/* 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 */ diff --git a/plat/st/common/stm32mp_cot.c b/plat/st/common/stm32mp_cot.c new file mode 100644 index 000000000..5f673fde7 --- /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 000000000..6e8c6355d --- /dev/null +++ b/plat/st/common/stm32mp_crypto_lib.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +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); +}; + +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.check_key = boot_context->bootrom_ecdsa_check_key; + auth_ops.verify_signature = + boot_context->bootrom_ecdsa_verify_signature; + + if (stm32_hash_register() != 0) { + panic(); + } +} + +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, MT_CODE | MT_SECURE); + if (result != 0) { + return result; + } + + if (!stm32mp_is_closed_device()) { + /* + * Check public key here in case of non-secure device + * It is done in the generic framework in case of close + * device. + */ + if (auth_ops.check_key(pk_ptr, NULL) != BOOT_API_RETURN_OK) { + ERROR("ROTPK verification failed\n"); + result = -EINVAL; + goto out; + } else { + NOTICE("ROTPK verification forced and checked OK\n"); + } + } + + /* 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); + 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; +} + +REGISTER_CRYPTO_LIB("stm32_crypto_lib", + crypto_lib_init, + crypto_verify_signature, + crypto_verify_hash); diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c index 17da4904a..f6de0b62a 100644 --- a/plat/st/common/stm32mp_dt.c +++ b/plat/st/common/stm32mp_dt.c @@ -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 */ @@ -13,8 +13,6 @@ #include #include -#include -#include #include @@ -92,6 +90,79 @@ uint8_t fdt_get_status(int node) return status; } +#if ENABLE_ASSERTIONS +/******************************************************************************* + * This function returns the address cells from the node parent. + * Returns: + * - #address-cells value if success. + * - invalid value if error. + * - a default value if undefined #address-cells property as per libfdt + * implementation. + ******************************************************************************/ +static int fdt_get_node_parent_address_cells(int node) +{ + int parent; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) { + return -FDT_ERR_NOTFOUND; + } + + return fdt_address_cells(fdt, parent); +} + +/******************************************************************************* + * This function returns the size cells from the node parent. + * Returns: + * - #size-cells value if success. + * - invalid value if error. + * - a default value if undefined #size-cells property as per libfdt + * implementation. + ******************************************************************************/ +static int fdt_get_node_parent_size_cells(int node) +{ + int parent; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) { + return -FDT_ERR_NOTFOUND; + } + + return fdt_size_cells(fdt, parent); +} +#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 reads a value of a node property (generic use of fdt * library). @@ -145,6 +216,46 @@ int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, return 0; } +/******************************************************************************* + * This function fills reg node info (base & size) with an index found by + * checking the reg-names node. + * Returns 0 on success and a negative FDT error code on failure. + ******************************************************************************/ +int fdt_get_reg_props_by_name(int node, const char *name, uintptr_t *base, + size_t *size) +{ + const fdt32_t *cuint; + int index, len; + + assert((fdt_get_node_parent_address_cells(node) == 1) && + (fdt_get_node_parent_size_cells(node) == 1)); + + index = fdt_stringlist_search(fdt, node, "reg-names", name); + if (index < 0) { + return index; + } + + cuint = fdt_getprop(fdt, node, "reg", &len); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + if ((index * (int)sizeof(uint32_t)) > len) { + return -FDT_ERR_BADVALUE; + } + + cuint += index << 1; + if (base != NULL) { + *base = fdt32_to_cpu(*cuint); + } + cuint++; + if (size != NULL) { + *size = fdt32_to_cpu(*cuint); + } + + return 0; +} + /******************************************************************************* * This function gets the stdout path node. * It reads the value indicated inside the device tree. @@ -215,6 +326,8 @@ void dt_fill_device_info(struct dt_node_info *info, int node) { const fdt32_t *cuint; + assert(fdt_get_node_parent_address_cells(node) == 1); + cuint = fdt_getprop(fdt, node, "reg", NULL); if (cuint != NULL) { info->base = fdt32_to_cpu(*cuint); @@ -277,6 +390,53 @@ 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. + * It is only valid for single instance peripherals (DDR, RCC, PWR, STGEN, + * SYSCFG...). + * Returns value on success, and error value on failure. + ******************************************************************************/ +int dt_get_node_by_compatible(const char *compatible) +{ + int node = fdt_node_offset_by_compatible(fdt, -1, compatible); + + if (node < 0) { + INFO("Cannot find %s node in DT\n", compatible); + } + + 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. @@ -285,9 +445,8 @@ uint32_t dt_get_ddr_size(void) { int node; - node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + node = dt_get_node_by_compatible(DT_DDR_COMPAT); if (node < 0) { - INFO("%s: Cannot read DDR node in DT\n", __func__); return 0; } @@ -295,70 +454,168 @@ uint32_t dt_get_ddr_size(void) } /******************************************************************************* - * This function gets DDRCTRL base address information from the DT. - * Returns value on success, and 0 on failure. + * This function gets OPP table node from the DT. + * Returns node offset on success and a negative FDT error code on failure. ******************************************************************************/ -uintptr_t dt_get_ddrctrl_base(void) +static int dt_get_opp_table_node(void) { - int node; - uint32_t array[4]; + return dt_get_node_by_compatible(DT_OPP_COMPAT); +} - 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; +/******************************************************************************* + * 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; + } } - if (fdt_read_uint32_array(node, "reg", array, 4) < 0) { - return 0; + 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; } - return array[0]; + *freq_khz = (uint32_t)read_freq_64; + + *voltage_mv = read_voltage_32; + + return 0; } /******************************************************************************* - * This function gets DDRPHYC base address information from the DT. - * Returns value on success, and 0 on failure. + * 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. ******************************************************************************/ -uintptr_t dt_get_ddrphyc_base(void) +int dt_get_max_opp_freqvolt(uint32_t *freq_khz, uint32_t *voltage_mv) { int node; - uint32_t array[4]; + int subnode; + uint32_t freq = 0U; + uint32_t voltage = 0U; + + assert(freq_khz != NULL); + assert(voltage_mv != NULL); - node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + node = dt_get_opp_table_node(); if (node < 0) { - INFO("%s: Cannot read DDR node in DT\n", __func__); - return 0; + return node; } - if (fdt_read_uint32_array(node, "reg", array, 4) < 0) { - return 0; + 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; } - return array[2]; + *freq_khz = freq; + *voltage_mv = voltage; + + return 0; } /******************************************************************************* - * This function gets PWR base address information from the DT. - * Returns value on success, and 0 on failure. + * 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. ******************************************************************************/ -uintptr_t dt_get_pwr_base(void) +int dt_get_all_opp_freqvolt(uint32_t *count, uint32_t *freq_khz_array, + uint32_t *voltage_mv_array) { int node; - const fdt32_t *cuint; + int subnode; + int idx = 0; + + assert(count != NULL); + assert(freq_khz_array != NULL); + assert(voltage_mv_array != NULL); - node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); + node = dt_get_opp_table_node(); if (node < 0) { - INFO("%s: Cannot read PWR node in DT\n", __func__); - return 0; + return node; } - cuint = fdt_getprop(fdt, node, "reg", NULL); - if (cuint == NULL) { - return 0; + 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++; } - return fdt32_to_cpu(*cuint); + if (idx == 0U) { + return -FDT_ERR_NOTFOUND; + } + + *count = idx; + + return 0; } /******************************************************************************* @@ -367,22 +624,15 @@ uintptr_t dt_get_pwr_base(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); + node = dt_get_node_by_compatible(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 (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; } @@ -401,26 +651,30 @@ uint32_t dt_get_pwr_vdd_voltage(void) } /******************************************************************************* - * This function gets SYSCFG base address information from the DT. - * Returns value on success, and 0 on failure. + * This function retrieves CPU regulator name from DT. + * Returns string taken from supply node, NULL otherwise. ******************************************************************************/ -uintptr_t dt_get_syscfg_base(void) +const char *dt_get_cpu_regulator_name(void) { int node; const fdt32_t *cuint; - node = fdt_node_offset_by_compatible(fdt, -1, DT_SYSCFG_COMPAT); + node = fdt_path_offset(fdt, "/cpus/cpu@0"); if (node < 0) { - INFO("%s: Cannot read SYSCFG node in DT\n", __func__); - return 0; + return NULL; } - cuint = fdt_getprop(fdt, node, "reg", NULL); + cuint = fdt_getprop(fdt, node, "cpu-supply", NULL); if (cuint == NULL) { - return 0; + return NULL; } - return fdt32_to_cpu(*cuint); + 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); } /******************************************************************************* @@ -437,3 +691,67 @@ const char *dt_get_board_model(void) return (const char *)fdt_getprop(fdt, node, "model", NULL); } + +/******************************************************************************* + * This function gets GPIO bank PINCTRL node information from the DT. + * Returns node value. + ******************************************************************************/ +int fdt_get_gpio_bank_pinctrl_node(unsigned int bank) +{ + switch (bank) { + case GPIO_BANK_A ... GPIO_BANK_K: + return fdt_path_offset(fdt, "/soc/pin-controller"); + case GPIO_BANK_Z: + return fdt_path_offset(fdt, "/soc/pin-controller-z"); + default: + panic(); + } +} + +/******************************************************************************* + * This function gets GPIOZ pin number information from the DT. + * It also checks node consistency. + ******************************************************************************/ +int fdt_get_gpioz_nbpins_from_dt(void) +{ + int pinctrl_node; + int pinctrl_subnode; + + pinctrl_node = fdt_get_gpio_bank_pinctrl_node(GPIO_BANK_Z); + if (pinctrl_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) { + uint32_t bank_offset; + const fdt32_t *cuint; + + if (fdt_getprop(fdt, pinctrl_subnode, + "gpio-controller", NULL) == NULL) { + continue; + } + + cuint = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL); + if (cuint == NULL) { + continue; + } + + bank_offset = stm32_get_gpio_bank_offset(GPIO_BANK_Z); + if (fdt32_to_cpu(*cuint) != bank_offset) { + continue; + } + + if (fdt_get_status(pinctrl_subnode) == DT_DISABLED) { + return 0; + } + + cuint = fdt_getprop(fdt, pinctrl_subnode, "ngpios", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + return (int)fdt32_to_cpu(*cuint); + } + + return 0; +} diff --git a/plat/st/common/stm32mp_img_parser_lib.c b/plat/st/common/stm32mp_img_parser_lib.c new file mode 100644 index 000000000..f4c8bd642 --- /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 000000000..12633e47d --- /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 000000000..f47584206 --- /dev/null +++ b/plat/st/common/stm32mp_trusted_boot.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include + +static uint32_t root_pk_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES / + sizeof(uint32_t)]; + +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; + + 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; + } + + for (i = 0U; i < ARRAY_SIZE(root_pk_hash); i++) { + if (stm32_get_otp_value_from_idx(otp_idx + i, &otp_val) != 0) { + return -EINVAL; + } + + root_pk_hash[i] = bswap32(otp_val); + } + + *key_ptr = &root_pk_hash; + *key_len = BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES; + *flags = ROTPK_IS_HASH; + + if (!stm32mp_is_closed_device()) { + *flags |= ROTPK_NOT_DEPLOYED; + } + + return 0; +} + +int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr) +{ + /* + * This monotonic counter is the counter used by ROM code + * to identify BL2. + */ + if ((cookie == NULL) && + (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; +} diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index d9e29b4e8..3f5eb5674 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_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 */ @@ -18,21 +18,47 @@ #include #include #include +#include #include #include #include #include #include +#if STM32MP_UART_PROGRAMMER +#include +#endif +#include #include #include #include #include +#include #include #include +#define PWRLP_TEMPO_5_HSI 5 + +#define TIMEOUT_US_1MS U(1000) + +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 struct console_stm32 console; -static struct stm32mp_auth_ops stm32mp1_auth_ops; +static enum boot_device_e boot_device = BOOT_DEVICE_BOARD; +static bool wakeup_standby; static void print_reset_reason(void) { @@ -119,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, @@ -131,9 +162,14 @@ void bl2_platform_setup(void) { int ret; - if (dt_pmic_status() > 0) { - initialize_pmic(); - } + /* + * Map DDR non cacheable during its initialisation to avoid + * speculative loads before accesses are fully setup. + */ + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + STM32MP_DDR_MAX_SIZE, + MT_NON_CACHEABLE | MT_RW | MT_NS); + assert(ret == 0); ret = stm32mp1_ddr_probe(); if (ret < 0) { @@ -141,13 +177,151 @@ void bl2_platform_setup(void) panic(); } + ret = mmap_remove_dynamic_region(STM32MP_DDR_BASE, + STM32MP_DDR_MAX_SIZE); + assert(ret == 0); + #ifdef AARCH32_SP_OPTEE INFO("BL2 runs OP-TEE setup\n"); + + /* Map non secure DDR for BL33 load, now with cacheable attribute */ + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + dt_get_ddr_size() - STM32MP_DDR_S_SIZE - + STM32MP_DDR_SHMEM_SIZE, + MT_MEMORY | MT_RW | MT_NS); + assert(ret == 0); + + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE + dt_get_ddr_size() - + STM32MP_DDR_S_SIZE - + STM32MP_DDR_SHMEM_SIZE, + STM32MP_DDR_BASE + dt_get_ddr_size() - + STM32MP_DDR_S_SIZE - + STM32MP_DDR_SHMEM_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"); + + /* Map non secure DDR for BL33 load, now with cacheable attribute */ + ret = mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE, + dt_get_ddr_size(), + MT_MEMORY | MT_RW | MT_NS); + assert(ret == 0); #endif + + if ((dt_pmic_status() > 0) && (!wakeup_standby)) { + configure_pmic(); + } +} + +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 (wakeup_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 (wakeup_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) +{ + if (stm32mp_reset_assert_to(reset, TIMEOUT_US_1MS)) { + panic(); + } + + udelay(2); + + if (stm32mp_reset_deassert_to(reset, TIMEOUT_US_1MS)) { + panic(); + } + + mdelay(1); } void bl2_el3_plat_arch_setup(void) @@ -160,20 +334,16 @@ void bl2_el3_plat_arch_setup(void) uint32_t clk_rate; uintptr_t pwr_base; uintptr_t rcc_base; + 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); mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, MT_CODE | MT_SECURE); #ifdef AARCH32_SP_OPTEE - /* OP-TEE image needs post load processing: keep RAM read/write */ - mmap_add_region(STM32MP_DDR_BASE + dt_get_ddr_size() - - STM32MP_DDR_S_SIZE - STM32MP_DDR_SHMEM_SIZE, - STM32MP_DDR_BASE + dt_get_ddr_size() - - STM32MP_DDR_S_SIZE - STM32MP_DDR_SHMEM_SIZE, - STM32MP_DDR_S_SIZE, - MT_MEMORY | MT_RW | MT_SECURE); - mmap_add_region(STM32MP_OPTEE_BASE, STM32MP_OPTEE_BASE, STM32MP_OPTEE_SIZE, MT_MEMORY | MT_RW | MT_SECURE); @@ -181,19 +351,12 @@ void bl2_el3_plat_arch_setup(void) /* Prevent corruption of preloaded BL32 */ mmap_add_region(BL32_BASE, BL32_BASE, BL32_LIMIT - BL32_BASE, - MT_MEMORY | MT_RO | MT_SECURE); - + MT_RO_DATA | MT_SECURE); #endif - /* Map non secure DDR for BL33 load and DDR training area restore */ - mmap_add_region(STM32MP_DDR_BASE, - STM32MP_DDR_BASE, - STM32MP_DDR_MAX_SIZE, - MT_MEMORY | MT_RW | MT_NS); - /* Prevent corruption of preloaded Device Tree */ mmap_add_region(DTB_BASE, DTB_BASE, DTB_LIMIT - DTB_BASE, - MT_MEMORY | MT_RO | MT_SECURE); + MT_RO_DATA | MT_SECURE); configure_mmu(); @@ -204,6 +367,11 @@ void bl2_el3_plat_arch_setup(void) pwr_base = stm32mp_pwr_base(); rcc_base = stm32mp_rcc_base(); + /* Clear Stop Request bits to correctly manage low-power exit */ + mmio_write_32(rcc_base + RCC_MP_SREQCLRR, + (uint32_t)(RCC_MP_SREQCLRR_STPREQ_P0 | + RCC_MP_SREQCLRR_STPREQ_P1)); + /* * Disable the backup domain write protection. * The protection is enable at each reset by hardware @@ -215,6 +383,12 @@ void bl2_el3_plat_arch_setup(void) ; } + /* + * 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); + if (bsec_probe() != 0) { panic(); } @@ -231,25 +405,72 @@ void bl2_el3_plat_arch_setup(void) mmio_clrbits_32(rcc_base + RCC_BDCR, RCC_BDCR_VSWRST); } + /* 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); + + /* Disable retention and backup RAM content after standby */ + mmio_clrbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | PWR_CR2_RREN); + /* 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); + + if ((boot_context->boot_action != + BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY) && + (boot_context->boot_action != + BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY)) { + mmio_write_32(bkpr_core1_addr, 0); + mmio_write_32(bkpr_core1_magic, 0); + } + + wakeup_standby = (mmio_read_32(bkpr_core1_addr) != 0U); + generic_delay_timer_init(); +#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 STM32MP_UART_PROGRAMMER + /* Disable programmer UART before changing clock tree */ + if (boot_context->boot_interface_selected == + BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART) { + uintptr_t uart_prog_addr = + get_uart_address(boot_context->boot_interface_instance); + + ((USART_TypeDef *)uart_prog_addr)->CR1 &= ~USART_CR1_UE; + } +#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) || +#if STM32MP_UART_PROGRAMMER + ((boot_context->boot_interface_selected == + BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART) && + (get_uart_address(boot_context->boot_interface_instance) == + dt_uart_info.base)) || +#endif (dt_uart_info.clock < 0) || (dt_uart_info.reset < 0)) { goto skip_console_init; @@ -259,12 +480,17 @@ void bl2_el3_plat_arch_setup(void) goto skip_console_init; } + if (dt_uart_info.status == DT_DISABLED) { + panic(); + } else if (dt_uart_info.status == DT_SECURE) { + stm32mp_register_secure_periph_iomem(dt_uart_info.base); + } else { + stm32mp_register_non_secure_periph_iomem(dt_uart_info.base); + } + stm32mp_clk_enable((unsigned long)dt_uart_info.clock); - stm32mp_reset_assert((uint32_t)dt_uart_info.reset); - udelay(2); - stm32mp_reset_deassert((uint32_t)dt_uart_info.reset); - mdelay(1); + reset_uart((uint32_t)dt_uart_info.reset); clk_rate = stm32mp_clk_get_rate((unsigned long)dt_uart_info.clock); @@ -285,22 +511,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, @@ -309,20 +553,47 @@ 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(); + + if (dt_pmic_status() > 0) { + initialize_pmic(); + print_pmic_info_and_debug(); + } + stm32mp_io_setup(); } #if defined(AARCH32_SP_OPTEE) +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`. @@ -331,30 +602,34 @@ 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); +#if defined(AARCH32_SP_OPTEE) bl_mem_params_node_t *bl32_mem_params; bl_mem_params_node_t *pager_mem_params; bl_mem_params_node_t *paged_mem_params; +#endif assert(bl_mem_params != NULL); +#if TRUSTED_BOARD_BOOT + /* Clean header to avoid loaded header reused */ + stm32mp_delete_loaded_header(); +#endif + switch (image_id) { case BL32_IMAGE_ID: +#if defined(AARCH32_SP_OPTEE) 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 + - (dt_get_ddr_size() - STM32MP_DDR_S_SIZE - - STM32MP_DDR_SHMEM_SIZE); - paged_mem_params->image_info.image_max_size = - STM32MP_DDR_S_SIZE; + + set_mem_params_info(&bl_mem_params->ep_info, + &pager_mem_params->image_info, + &paged_mem_params->image_info); err = parse_optee_header(&bl_mem_params->ep_info, &pager_mem_params->image_info, @@ -371,12 +646,18 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) 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 */ +#endif break; case BL33_IMAGE_ID: +#ifdef AARCH32_SP_OPTEE 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; +#endif + + flush_dcache_range(bl_mem_params->image_info.image_base, + bl_mem_params->image_info.image_max_size); break; default: @@ -386,4 +667,3 @@ int bl2_plat_handle_post_image_load(unsigned int image_id) return err; } -#endif diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h index 2284970fa..872e2044c 100644 --- a/plat/st/stm32mp1/include/boot_api.h +++ b/plat/st/stm32mp1/include/boot_api.h @@ -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' */ @@ -33,6 +117,21 @@ /* Boot occurred on EMMC */ #define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC 0x2U +/* Boot occurred on FMC */ +#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 + +/* 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 + /** * @brief Possible value of boot context field 'EmmcXferStatus' */ @@ -56,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 */ @@ -86,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 @@ -100,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' */ @@ -124,7 +318,58 @@ /* Closed = OTP_CFG0[6] */ #define BOOT_API_OTP_MODE_CLOSED_BIT_POS 6 -#define BOOT_API_RETURN_OK 0x66U +#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 + +/* + * Exported types + */ + +/* SSP Configuration structure */ +typedef struct { + /* SSP Command*/ + uint32_t ssp_cmd; + uint8_t reserved[20]; +} 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 @@ -142,9 +387,29 @@ typedef struct { */ uint16_t boot_interface_selected; uint16_t boot_interface_instance; - uint32_t reserved1[13]; + uint32_t reserved1[12]; + uint32_t usb_context; 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; /* @@ -194,6 +459,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 263e6d6e1..694c3c4bb 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -51,6 +51,7 @@ #define MAX_IO_DEVICES U(4) #define MAX_IO_HANDLES U(4) #define MAX_IO_BLOCK_DEVICES U(1) +#define MAX_IO_MTD_DEVICES U(1) /******************************************************************************* * BL2 specific defines. @@ -82,6 +83,10 @@ */ #define PLAT_STM32MP_NS_IMAGE_OFFSET BL33_BASE +/* need by flash programmer */ +#define FLASHLAYOUT_BASE STM32MP_DDR_BASE +#define FLASHLAYOUT_LIMIT STM32MP_BL33_BASE + /******************************************************************************* * DTB specific defines. ******************************************************************************/ @@ -112,6 +117,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) @@ -121,7 +128,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/stm32mp1_boot_device.h b/plat/st/stm32mp1/include/stm32mp1_boot_device.h new file mode 100644 index 000000000..a74598395 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_boot_device.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_BOOT_DEVICE_H +#define STM32MP1_BOOT_DEVICE_H + +#include +#include +#include + +int plat_get_raw_nand_data(struct rawnand_device *device); +int plat_get_spi_nand_data(struct spinand_device *device); +int plat_get_nor_data(struct nor_device *device); + +#endif /* STM32MP1_BOOT_DEVICE_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_context.h b/plat/st/stm32mp1/include/stm32mp1_context.h index 698415af2..21214d35a 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-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,8 +7,26 @@ #ifndef STM32MP1_CONTEXT_H #define STM32MP1_CONTEXT_H +#include #include +#define DDR_CRC_GRANULE 32 + +void stm32_clean_context(void); +int stm32_save_context(uint32_t zq0cr0_zdata); +int stm32_restore_context(void); +unsigned long long stm32_get_stgen_from_context(void); +int stm32_restore_backup_reg(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); int stm32_save_boot_interface(uint32_t interface, uint32_t instance); +int stm32_get_boot_interface(uint32_t *interface, uint32_t *instance); +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_low_power.h b/plat/st/stm32mp1/include/stm32mp1_low_power.h new file mode 100644 index 000000000..82b3d36c1 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_low_power.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_LOW_POWER_H +#define STM32MP1_LOW_POWER_H + +#include +#include + +void stm32_rcc_wakeup_update(bool state); +void stm32_apply_pmic_suspend_config(uint32_t mode); +void stm32_exit_cstop(void); +void stm32_pwr_down_wfi(void); +void stm32_enter_low_power(uint32_t mode, uint32_t nsec_addr); + +#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 000000000..7bd07956a --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_power_config.h @@ -0,0 +1,28 @@ +/* + * 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); + +#endif /* STM32MP1_POWER_CONFIG_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h index e38fca012..e9fadb063 100644 --- a/plat/st/stm32mp1/include/stm32mp1_private.h +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -9,16 +9,31 @@ #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); + +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_disable_io_compensation(void); +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); + #endif /* STM32MP1_PRIVATE_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_shared_resources.h b/plat/st/stm32mp1/include/stm32mp1_shared_resources.h new file mode 100644 index 000000000..89c333358 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_shared_resources.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_SHARED_RESOURCES_H +#define STM32MP1_SHARED_RESOURCES_H + +#include + +#include +#include + +#define STM32MP1_SHRES_GPIOZ(i) (STM32MP1_SHRES_GPIOZ_0 + (i)) + +enum stm32mp_shres { + STM32MP1_SHRES_CRYP1, + STM32MP1_SHRES_GPIOZ_0, + STM32MP1_SHRES_GPIOZ_1, + STM32MP1_SHRES_GPIOZ_2, + STM32MP1_SHRES_GPIOZ_3, + STM32MP1_SHRES_GPIOZ_4, + STM32MP1_SHRES_GPIOZ_5, + STM32MP1_SHRES_GPIOZ_6, + STM32MP1_SHRES_GPIOZ_7, + STM32MP1_SHRES_HASH1, + STM32MP1_SHRES_I2C4, + STM32MP1_SHRES_I2C6, + STM32MP1_SHRES_IWDG1, + STM32MP1_SHRES_MCU, + STM32MP1_SHRES_MDMA, + STM32MP1_SHRES_RNG1, + STM32MP1_SHRES_RTC, + STM32MP1_SHRES_SPI6, + STM32MP1_SHRES_USART1, + STM32MP1_SHRES_PLL3, + + STM32MP1_SHRES_COUNT +}; + +#ifdef IMAGE_BL32 +/* Register a peripheral as secure or non-secure based on identifier */ +void stm32mp_register_secure_periph(unsigned int id); +void stm32mp_register_non_secure_periph(unsigned int id); + +/* Register a peripheral as secure or non-secure based on IO base address */ +void stm32mp_register_secure_periph_iomem(uintptr_t base); +void stm32mp_register_non_secure_periph_iomem(uintptr_t base); + +/* Register a GPIO as secure or non-secure based on its bank and pin numbers */ +void stm32mp_register_secure_gpio(unsigned int bank, unsigned int pin); +void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin); + +/* + * Register a (non-)secure peripheral based on the ETZPC DECPROT configuration + */ +void stm32mp1_register_etzpc_decprot(unsigned int id, + enum etzpc_decprot_attributes attr); + +/* Consolidate peripheral states and locknew periph registering */ +void stm32mp_lock_periph_registering(void); + +/* Get peripheral state in shared resource driver */ +bool stm32mp1_periph_is_non_secure(unsigned long id); +bool stm32mp1_periph_is_secure(unsigned long id); + +bool stm32mp_gpio_bank_is_non_secure(unsigned int bank); +bool stm32mp_gpio_bank_is_shared(unsigned int bank); + +bool stm32mp_nsec_can_access_clock(unsigned long clock_id); +bool stm32mp_nsec_can_access_reset(unsigned int reset_id); +#else /* IMAGE_BL32 */ +static inline void stm32mp_register_secure_periph(unsigned int id) +{ +} + +static inline void stm32mp_register_non_secure_periph(unsigned int id) +{ +} + +static inline void stm32mp_register_secure_periph_iomem(uintptr_t base) +{ +} + +static inline void stm32mp_register_non_secure_periph_iomem(uintptr_t base) +{ +} + +static inline void stm32mp_register_secure_gpio(unsigned int bank, + unsigned int pin) +{ +} + +static inline void stm32mp_register_non_secure_gpio(unsigned int bank, + unsigned int pin) +{ +} + +static inline void stm32mp1_register_etzpc_decprot(unsigned int id, + enum etzpc_decprot_attributes attr) +{ +} + +static inline void stm32mp_lock_periph_registering(void) +{ + /* Platform is not expected to lock a non existing database */ + panic(); +} + +static inline bool stm32mp1_periph_is_non_secure(unsigned long id) +{ + return false; +} + +static inline bool stm32mp1_periph_is_secure(unsigned long id) +{ + return true; +} + +static inline bool stm32mp_gpio_bank_is_non_secure(unsigned int bank) +{ + return false; +} + +static inline bool stm32mp_gpio_bank_is_shared(unsigned int bank) +{ + return false; +} +#endif /* IMAGE_BL32 */ + +#endif /* STM32MP1_SHARED_RESOURCES_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_smc.h b/plat/st/stm32mp1/include/stm32mp1_smc.h index b87275839..19ae1d0a3 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 */ @@ -7,45 +7,157 @@ #ifndef STM32MP1_SMC_H #define STM32MP1_SMC_H +/* SMC service generic return codes */ +#define STM32_SMC_OK 0x00000000U +#define STM32_SMC_NOT_SUPPORTED 0xFFFFFFFFU +#define STM32_SMC_FAILED 0xFFFFFFFEU +#define STM32_SMC_INVALID_PARAMS 0xFFFFFFFDU + /* - * SMC function IDs for STM32 Service queries + * SMC function IDs for STM32 Service queries. * STM32 SMC services use the space between 0x82000000 and 0x8200FFFF * like this is defined in SMC calling Convention by ARM - * for SiP (silicon Partner) + * for SiP (silicon Partner). * https://developer.arm.com/docs/den0028/latest */ /* Secure Service access from Non-secure */ /* - * STM32_SMC_BSEC call API + * 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 + +/* + * SMC functions STM32_SMC_BSEC. * - * Argument a0: (input) SMCC ID - * (output) status return code - * Argument a1: (input) Service ID (STM32_SMC_BSEC_xxx) - * Argument a2: (input) OTP index - * (output) OTP read value, if applicable - * Argument a3: (input) OTP value if applicable + * Argument a0: (input) SMCC ID. + * (output) Status return code. + * Argument a1: (input) Service ID (STM32_SMC_READ_xxx/_PROG_xxx/_WRITE_xxx). + * (output) OTP read value, if applicable. + * Argument a2: (input) OTP index. + * Argument a3: (input) OTP value if applicable. */ #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_SIP_SVC_FUNC_SCMI_AGENT0/1 + * + * Process SCMI message pending in SCMI shared memory buffer + * related to SCMI agent IDs 0 and 1. No input or output arguments + * passed through CPU general purpose registers, messages are transfer + * through a dedicated area in SYSRAM, mapped as device memory. + */ +#define STM32_SMC_SCMI_MESSAGE_AGENT0 0x82002000 +#define STM32_SMC_SCMI_MESSAGE_AGENT1 0x82002001 + /* SMC function IDs for SiP Service queries */ + +/* + * SIP function STM32_SIP_SVC_CALL_COUNT. + * + * Argument a0: (input) SMCC ID. + * (output) Dummy value 0. + */ #define STM32_SIP_SVC_CALL_COUNT 0x8200ff00 + +/* + * SIP function STM32_SIP_SVC_UID. + * + * Argument a0: (input) SMCC ID. + * (output) Lowest 32bit of the stm32mp1 SIP service UUID. + * Argument a1: (output) Next 32bit of the stm32mp1 SIP service UUID. + * Argument a2: (output) Next 32bit of the stm32mp1 SIP service UUID. + * Argument a3: (output) Last 32bit of the stm32mp1 SIP service UUID. + */ #define STM32_SIP_SVC_UID 0x8200ff01 -/* 0x8200ff02 is reserved */ -#define STM32_SIP_SVC_VERSION 0x8200ff03 -/* STM32 SiP Service Calls version numbers */ -#define STM32_SIP_SVC_VERSION_MAJOR 0x0 -#define STM32_SIP_SVC_VERSION_MINOR 0x1 +/* 0x8200ff02 is reserved */ -/* Number of STM32 SiP Calls implemented */ -#define STM32_COMMON_SIP_NUM_CALLS 4 +/* + * SIP function STM32_SIP_SVC_VERSION. + * + * Argument a0: (input) SMCC ID. + * (output) STM32 SIP service major. + * Argument a1: (output) STM32 SIP service minor. + */ +#define STM32_SIP_SVC_VERSION 0x8200ff03 + +/* 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 */ +/* Service ID for STM32_SMC_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 + +/* Service ID for STM32_SMC_RCC_OPP */ +#define STM32_SMC_RCC_OPP_SET 0x0 +#define STM32_SMC_RCC_OPP_ROUND 0x1 + +/* STM32 SiP Service Calls version numbers */ +#define STM32_SIP_SVC_VERSION_MAJOR 0x0 +#define STM32_SIP_SVC_VERSION_MINOR 0x1 + +/* Number of STM32 SiP Calls implemented */ +#define STM32_COMMON_SIP_NUM_CALLS 13 #endif /* STM32MP1_SMC_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_usb_desc.h b/plat/st/stm32mp1/include/stm32mp1_usb_desc.h new file mode 100644 index 000000000..cb514b20a --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_usb_desc.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_USB_DESC_H +#define STM32MP1_USB_DESC_H + +#include + +/* 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_INTERFACE_STR + (n) + 1 /* iInterface: + * Index of + * string + * descriptor + */ +/* DFU1.1 Standard only supported */ +#define USB_DFU_VERSION 0x0110 +#define USBD_DESC_MAX_ITF_NUM 0x6 +#define USB_DFU_CONFIG_DESC_SIZ 72 +#define USB_DFU_DESC_SIZ 9 +/* 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_PRODUCT_HS_STRING "DFU in HS Mode @Device ID /0x500, @Revision ID /0x0000" +#define USBD_PRODUCT_FS_STRING "DFU in FS Mode @Device ID /0x500, @Revision ID /0x0000" +#define USBD_CONFIGURATION_HS_STRING "DFU Config" +#define USBD_INTERFACE_HS_STRING "DFU Interface" +#define USBD_CONFIGURATION_FS_STRING "DFU Config" +#define USBD_INTERFACE_FS_STRING "DFU Interface" + +void stm32mp_usb_init_desc(usb_handle_t *pdev); + +#endif /* STM32MP1_USB_DESC_H */ diff --git a/plat/st/stm32mp1/include/usb_ctx.h b/plat/st/stm32mp1/include/usb_ctx.h new file mode 100644 index 000000000..95ffa4da4 --- /dev/null +++ b/plat/st/stm32mp1/include/usb_ctx.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef USB_CTX_H +#define USB_CTX_H + +#include + +struct usb_ctx { + usb_handle_t *pusbd_device_ctx; + pcd_handle_t *phpcd_ctx; +}; + +#endif /* USB_CTX_H */ diff --git a/plat/st/stm32mp1/plat_image_load.c b/plat/st/stm32mp1/plat_image_load.c index a52db6cac..0a7437ba4 100644 --- a/plat/st/stm32mp1/plat_image_load.c +++ b/plat/st/stm32mp1/plat_image_load.c @@ -1,10 +1,14 @@ /* - * 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 */ +#include + +#include #include +#include #include /******************************************************************************* @@ -16,11 +20,73 @@ void plat_flush_next_bl_params(void) flush_bl_params_desc(); } +#ifdef AARCH32_SP_OPTEE +static bool addr_inside_backupsram(uintptr_t addr) +{ + return (addr >= STM32MP_BACKUP_RAM_BASE) && + (addr < (STM32MP_BACKUP_RAM_BASE + STM32MP_BACKUP_RAM_SIZE)); +} +#endif + /******************************************************************************* * This function returns the list of loadable images. ******************************************************************************/ bl_load_info_t *plat_get_bl_image_load_info(void) { + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp_get_boot_ctx_address(); +#ifdef AARCH32_SP_OPTEE + bl_mem_params_node_t *bl32 = get_bl_mem_params_node(BL32_IMAGE_ID); +#endif + bl_mem_params_node_t *bl33 = get_bl_mem_params_node(BL33_IMAGE_ID); + uint32_t rstsr = mmio_read_32(stm32mp_rcc_base() + RCC_MP_RSTSCLRR); + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uintptr_t pwr_base = stm32mp_pwr_base(); + + /* + * 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 (((boot_context->boot_action == + BOOT_API_CTX_BOOT_ACTION_WAKEUP_CSTANDBY) || + (boot_context->boot_action == + BOOT_API_CTX_BOOT_ACTION_WAKEUP_STANDBY)) && + ((mmio_read_32(pwr_base + PWR_CR3) & PWR_CR3_DDRSREN) != 0U) && + ((rstsr & RCC_MP_RSTSCLRR_PADRSTF) == 0U)) { + stm32mp_clk_enable(RTCAPB); + + if (mmio_read_32(bkpr_core1_addr) != 0U) { + bl33->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING; + +#ifdef AARCH32_SP_OPTEE + bl32->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING; + bl32->ep_info.pc = stm32_pm_get_optee_ep(); + + if (addr_inside_backupsram(bl32->ep_info.pc)) { + stm32mp_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 + } + + stm32mp_clk_disable(RTCAPB); + } + + /* Max size is non-secure DDR end address minus image_base */ + bl33->image_info.image_max_size = STM32MP_DDR_BASE + + dt_get_ddr_size() - + STM32MP_DDR_S_SIZE - + STM32MP_DDR_SHMEM_SIZE - + bl33->image_info.image_base; + return get_bl_load_info_from_mem_params_desc(); } diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 90b3e3c1e..840a38d14 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -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 # @@ -9,11 +9,31 @@ ARM_WITH_NEON := yes BL2_AT_EL3 := 1 USE_COHERENT_MEM := 0 +# Add specific ST version +ST_VERSION := r1.0 +VERSION_STRING := v${VERSION_MAJOR}.${VERSION_MINOR}-${ST_VERSION}(${BUILD_TYPE}):${BUILD_STRING} + +TRUSTED_BOARD_BOOT := 1 + +# Please don't increment this value without good understanding of +# the monotonic counter STM32_TF_VERSION ?= 0 +$(eval $(call add_define_val,STM32_TF_VERSION,${STM32_TF_VERSION})) + +# Enable dynamic memory mapping +PLAT_XLAT_TABLES_DYNAMIC := 1 +$(eval $(call assert_boolean,PLAT_XLAT_TABLES_DYNAMIC)) +$(eval $(call add_define,PLAT_XLAT_TABLES_DYNAMIC)) + +# STM32 image header version v1.0 +STM32_HEADER_VERSION_MAJOR:= 1 +STM32_HEADER_VERSION_MINOR:= 0 # 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 $(eval $(call add_define,STM32_TF_A_COPIES)) @@ -24,6 +44,39 @@ PLAT_PARTITION_MAX_ENTRIES := $(shell echo $$(($(STM32_TF_A_COPIES) + 1))) endif $(eval $(call add_define,PLAT_PARTITION_MAX_ENTRIES)) +# Boot devices +STM32MP_EMMC ?= 0 +STM32MP_SDMMC ?= 0 +STM32MP_RAW_NAND ?= 0 +STM32MP_SPI_NAND ?= 0 +STM32MP_SPI_NOR ?= 0 + +# Serial boot devices +STM32MP_UART_PROGRAMMER ?= 0 +STM32MP_USB_PROGRAMMER ?= 0 + +ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND} \ + ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR} ${STM32MP_UART_PROGRAMMER} \ + ${STM32MP_USB_PROGRAMMER}),) +$(error "No boot device driver is enabled") +endif + +$(eval $(call assert_boolean,STM32MP_EMMC)) +$(eval $(call assert_boolean,STM32MP_SDMMC)) +$(eval $(call assert_boolean,STM32MP_RAW_NAND)) +$(eval $(call assert_boolean,STM32MP_SPI_NAND)) +$(eval $(call assert_boolean,STM32MP_SPI_NOR)) +$(eval $(call add_define,STM32MP_EMMC)) +$(eval $(call add_define,STM32MP_SDMMC)) +$(eval $(call add_define,STM32MP_RAW_NAND)) +$(eval $(call add_define,STM32MP_SPI_NAND)) +$(eval $(call add_define,STM32MP_SPI_NOR)) + +$(eval $(call assert_boolean,STM32MP_UART_PROGRAMMER)) +$(eval $(call assert_boolean,STM32MP_USB_PROGRAMMER)) +$(eval $(call add_define,STM32MP_UART_PROGRAMMER)) +$(eval $(call add_define,STM32MP_USB_PROGRAMMER)) + PLAT_INCLUDES := -Iplat/st/common/include/ PLAT_INCLUDES += -Iplat/st/stm32mp1/include/ @@ -51,7 +104,7 @@ PLAT_BL_COMMON_SOURCES += lib/cpus/aarch32/cortex_a7.S PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.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 \ @@ -60,8 +113,11 @@ 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 \ @@ -70,18 +126,64 @@ PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ 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 + +BL2_SOURCES += $(AUTH_SOURCES) \ + plat/st/common/stm32mp_cot.c \ + plat/st/common/stm32mp_crypto_lib.c \ + plat/st/common/stm32mp_img_parser_lib.c \ + 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 \ drivers/partition/partition.c \ drivers/st/io/io_mmc.c \ drivers/st/mmc/stm32_sdmmc2.c +endif + +ifeq (${STM32MP_RAW_NAND},1) +$(eval $(call add_define_val,NAND_ONFI_DETECT,1)) +BL2_SOURCES += drivers/mtd/nand/raw_nand.c \ + drivers/st/fmc/stm32_fmc2_nand.c +endif + +ifeq (${STM32MP_SPI_NAND},1) +BL2_SOURCES += drivers/mtd/nand/spi_nand.c +endif + +ifeq (${STM32MP_SPI_NOR},1) +BL2_SOURCES += drivers/mtd/nor/spi_nor.c +endif + +ifneq ($(filter 1,${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) +BL2_SOURCES += drivers/mtd/spi-mem/spi_mem.c \ + drivers/st/spi/stm32_qspi.c +endif + +ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND}),) +BL2_SOURCES += drivers/mtd/nand/core.c +endif + +ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) +BL2_SOURCES += plat/st/stm32mp1/stm32mp1_boot_device.c +endif + +ifeq (${STM32MP_UART_PROGRAMMER},1) +BL2_SOURCES += drivers/st/uart/io_programmer_uart.c \ + drivers/st/uart/stm32mp1xx_hal_uart.c +endif BL2_SOURCES += drivers/st/ddr/stm32mp1_ddr.c \ drivers/st/ddr/stm32mp1_ram.c @@ -90,10 +192,22 @@ BL2_SOURCES += common/desc_image_load.c \ plat/st/stm32mp1/plat_bl2_mem_params_desc.c \ plat/st/stm32mp1/plat_image_load.c +ifeq (${STM32MP_USB_PROGRAMMER},1) +BL2_SOURCES += drivers/st/io/io_programmer_st_usb.c \ + drivers/st/usb_dwc2/usb_dwc2.c \ + lib/usb/usb_core.c \ + lib/usb/usb_st_dfu.c \ + plat/st/stm32mp1/stm32mp1_usb_desc.c +endif + ifeq ($(AARCH32_SP),optee) BL2_SOURCES += lib/optee/optee_utils.c endif + +# Do not use neon in TF-A code, it leads to issues in low-power functions +TF_CFLAGS += -mfloat-abi=soft + # Macros and rules to build TF binary STM32_TF_ELF_LDFLAGS := --hash-style=gnu --as-needed STM32_DT_BASENAME := $(DTB_FILE_NAME:.dtb=) @@ -105,16 +219,15 @@ STM32_TF_ELF := $(STM32_TF_STM32:.stm32=.elf) STM32_TF_DTBFILE := ${BUILD_PLAT}/fdts/${DTB_FILE_NAME} STM32_TF_OBJS := ${BUILD_PLAT}/stm32mp1.o -BL2_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 - # Variables for use with stm32image STM32IMAGEPATH ?= tools/stm32image STM32IMAGE ?= ${STM32IMAGEPATH}/stm32image${BIN_EXT} +STM32IMAGE_SRC := ${STM32IMAGEPATH}/stm32image.c -.PHONY: ${STM32_TF_STM32} +.PHONY: check_dtc_version stm32image clean_stm32image .SUFFIXES: -all: check_dtc_version ${STM32_TF_STM32} stm32image +all: check_dtc_version stm32image ${STM32_TF_STM32} ifeq ($(AARCH32_SP),sp_min) # BL32 is built only if using SP_MIN @@ -124,7 +237,9 @@ endif distclean realclean clean: clean_stm32image -stm32image: +stm32image: ${STM32IMAGE} + +${STM32IMAGE}: ${STM32IMAGE_SRC} ${Q}${MAKE} CPPFLAGS="" --no-print-directory -C ${STM32IMAGEPATH} clean_stm32image: @@ -161,10 +276,14 @@ ${STM32_TF_BINARY}: ${STM32_TF_ELF} @echo "Built $@ successfully" @echo -${STM32_TF_STM32}: stm32image ${STM32_TF_BINARY} +${STM32_TF_STM32}: ${STM32IMAGE} ${STM32_TF_BINARY} @echo - @echo "Generated $@" + @echo "Generate $@" $(eval LOADADDR = $(shell cat ${STM32_TF_MAPFILE} | grep RAM | awk '{print $$2}')) $(eval ENTRY = $(shell cat ${STM32_TF_MAPFILE} | grep "__BL2_IMAGE_START" | awk '{print $$1}')) - ${STM32IMAGE} -s ${STM32_TF_BINARY} -d $@ -l $(LOADADDR) -e ${ENTRY} -v ${STM32_TF_VERSION} + @${STM32IMAGE} -s ${STM32_TF_BINARY} -d $@ \ + -l $(LOADADDR) -e ${ENTRY} \ + -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 2a60e4339..e75571f98 100644 --- a/plat/st/stm32mp1/services/bsec_svc.c +++ b/plat/st/stm32mp1/services/bsec_svc.c @@ -1,23 +1,451 @@ /* - * 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" +#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 (SSP_OTP_REQ | SSP_OTP_SUCCESS) + +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; +} + +#if STM32MP_USB_PROGRAMMER || STM32MP_UART_PROGRAMMER +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; + } + } + + ret = bsec_write_debug_conf(exchange->debug_conf); + if (ret != BSEC_OK) { + return ret; + } + + 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; +} +#endif /* STM32MP_USB_PROGRAMMER || STM32MP_UART_PROGRAMMER */ + 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; + } + +#if STM32MP_USB_PROGRAMMER || STM32MP_UART_PROGRAMMER + otp_exch = NULL; + map_begin = 0U; + + if ((x1 == STM32_SMC_READ_ALL) || (x1 == STM32_SMC_WRITE_ALL)) { + 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; + } +#endif switch (x1) { case STM32_SMC_READ_SHADOW: @@ -25,6 +453,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 +490,27 @@ 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; +#if STM32MP_USB_PROGRAMMER || STM32MP_UART_PROGRAMMER + 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; +#endif + case STM32_SMC_WRLOCK_OTP: + result = bsec_permanent_lock_otp(x2); + break; + default: + return STM32_SMC_INVALID_PARAMS; + } + +#if STM32MP_USB_PROGRAMMER || STM32MP_UART_PROGRAMMER + if ((x1 == STM32_SMC_READ_ALL) || (x1 == STM32_SMC_WRITE_ALL)) { + ret = mmap_remove_dynamic_region(map_begin, map_size); + assert(ret == 0); } +#endif - 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 000000000..567a3c70f --- /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 000000000..eb98e9225 --- /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 000000000..1213d7ef6 --- /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 000000000..6dacdf80d --- /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 000000000..640816fca --- /dev/null +++ b/plat/st/stm32mp1/services/rcc_svc.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017-2019, 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 void 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; + case RCC_MP_GCR: + allowed_mask = RCC_MP_GCR_BOOT_MCU; + break; + default: + panic(); + } + + if (allowed_mask != 0U) { + access_allowed_mask(request, offset, value, allowed_mask); + } +} + +uint32_t rcc_scv_handler(uint32_t x1, uint32_t x2, uint32_t x3) +{ + uint32_t request = x1; + uint32_t offset = x2; + uint32_t value = x3; + + /* + * 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; + } + + raw_allowed_access_request(request, offset, value); + + return STM32_SMC_OK; +} + +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 000000000..23c75824f --- /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 72af9ff33..b2a84c15e 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 */ @@ -9,12 +9,16 @@ #include #include +#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, @@ -65,9 +69,39 @@ 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_SCMI_MESSAGE_AGENT0: + scmi_smt_fastcall_smc_entry(0U); + ret1 = STM32_SMC_OK; + break; + case STM32_SMC_SCMI_MESSAGE_AGENT1: + scmi_smt_fastcall_smc_entry(1U); + ret1 = STM32_SMC_OK; + break; + 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 4188cc58a..2ab8e8d4b 100644 --- a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk +++ b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk @@ -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 # @@ -7,19 +7,39 @@ SP_MIN_WITH_SECURE_FIQ := 1 BL32_SOURCES += plat/common/aarch32/platform_mp_stack.S \ + 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/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 # Generic GIC v2 BL32_SOURCES += drivers/arm/gic/common/gic_common.c \ drivers/arm/gic/v2/gicv2_helpers.c \ drivers/arm/gic/v2/gicv2_main.c \ 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 # 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 + +# SCMI server +BL32_SOURCES += drivers/st/scmi-msg/base.c \ + drivers/st/scmi-msg/clock.c \ + drivers/st/scmi-msg/entry.c \ + drivers/st/scmi-msg/reset_domain.c \ + drivers/st/scmi-msg/smt.c \ + plat/st/stm32mp1/stm32mp1_scmi.c diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c index e10dfbfc0..f57630604 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 */ @@ -17,10 +17,19 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include #include #include #include @@ -28,6 +37,7 @@ #include #include +#include /****************************************************************************** * Placeholder variables for copying the arguments that have been passed to @@ -36,23 +46,168 @@ static entry_point_info_t bl33_image_ep_info; static struct console_stm32 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 tzc_it_handler(void) +{ + ERROR("No IT handler in ARM tzc400 driver\n"); +} + +static void stm32_sgi1_it_handler(void) +{ + uint32_t id; + + stm32mp_mask_timer(); + + 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_plat_reset(plat_my_core_pos()); +} + +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(); + } +} /******************************************************************************* * Interrupt handler for FIQ (secure IRQ) ******************************************************************************/ void sp_min_plat_fiq_handler(uint32_t id) { + uint32_t value = 0; + 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 STM32MP1_IRQ_IWDG1: + case STM32MP1_IRQ_IWDG2: + stm32_iwdg_it_handler(id); + break; case STM32MP1_IRQ_AXIERRIRQ: ERROR("STM32MP1_IRQ_AXIERRIRQ generated\n"); + tzc400_init(STM32MP1_TZC_BASE); + __asm__("mrc p15, 1, %0, c9, c0, 3" : "=r" (value)); + if (value) { + /* we have a pending IT clear it */ + value = 0; + __asm__("mcr p15, 1, %0, c9, c0, 3" :: "r" (value)); + } else { + ERROR("IRQ_AXIERRIRQ handle call w/o any flag set!!\n"); + } + + /* Check if FIQ has been generated due to TZC400 abort*/ + if (tzc400_is_pending_interrupt()) { + tzc_it_handler(); + } else { + ERROR("IRQ_AXIERRIRQ cause can't be detected"); + } + 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; } } @@ -66,22 +221,99 @@ 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; + 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); 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 what was saved + */ if (next_image_info->pc == 0U) { - return NULL; + void *cpu_context; + uint32_t magic_nb, saved_pc; + + stm32mp_clk_enable(RTCAPB); + + magic_nb = mmio_read_32(bkpr_core1_magic); + saved_pc = mmio_read_32(bkpr_core1_addr); + + stm32mp_clk_disable(RTCAPB); + + 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 + */ + if (magic_nb == BOOT_API_A7_CORE0_MAGIC_NUMBER) { + /* BL33 return address should be in DDR */ + if ((saved_pc < STM32MP_DDR_BASE) || + (saved_pc > (STM32MP_DDR_BASE + + (dt_get_ddr_size() - 1U)))) { + panic(); + } + + 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; } +CASSERT((STM32MP_SEC_SYSRAM_BASE >= STM32MP_SYSRAM_BASE) && + ((STM32MP_SEC_SYSRAM_BASE + STM32MP_SEC_SYSRAM_SIZE) <= + (STM32MP_SYSRAM_BASE + STM32MP_SYSRAM_SIZE)), + assert_secure_sysram_fits_into_sysram); + +#ifdef STM32MP_NS_SYSRAM_BASE +CASSERT((STM32MP_NS_SYSRAM_BASE >= STM32MP_SEC_SYSRAM_BASE) && + ((STM32MP_NS_SYSRAM_BASE + STM32MP_NS_SYSRAM_SIZE) == + (STM32MP_SYSRAM_BASE + STM32MP_SYSRAM_SIZE)), + assert_non_secure_sysram_fits_at_end_of_sysram); + +CASSERT((STM32MP_NS_SYSRAM_BASE & GENMASK(11, 0)) == 0, + assert_non_secure_sysram_base_is_4kbyte_aligned); + +/* Last 4kByte page (12 bit wide) of SYSRAM is non-secure */ +#define TZMA1_SECURE_RANGE \ + (((STM32MP_NS_SYSRAM_BASE - STM32MP_SYSRAM_BASE) >> 12) - 1U) +#else +/* STM32MP_NS_SYSRAM_BASE not defined means all SYSRAM is secure */ +#define TZMA1_SECURE_RANGE STM32MP1_ETZPC_TZMA_ALL_SECURE +#endif /* STM32MP_NS_SYSRAM_BASE */ + +#define TZMA0_SECURE_RANGE STM32MP1_ETZPC_TZMA_ALL_SECURE + +static void stm32mp1_etzpc_early_setup(void) +{ + etzpc_init(); + etzpc_configure_tzma(0U, TZMA0_SECURE_RANGE); + etzpc_configure_tzma(1U, TZMA1_SECURE_RANGE); +} + /******************************************************************************* * 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) { +#if STM32MP_UART_PROGRAMMER + uint32_t boot_itf, boot_instance; +#endif struct dt_node_info dt_uart_info; int result; bl_params_t *params_from_bl2 = (bl_params_t *)arg0; @@ -126,9 +358,22 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, panic(); } + if (etzpc_init() != 0) { + panic(); + } + + stm32mp1_etzpc_early_setup(); + result = dt_get_stdout_uart_info(&dt_uart_info); +#if STM32MP_UART_PROGRAMMER + stm32_get_boot_interface(&boot_itf, &boot_instance); + if ((result > 0) && (dt_uart_info.status != 0U) && + !((boot_itf == BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART) && + (get_uart_address(boot_instance) == dt_uart_info.base))) { +#else if ((result > 0) && (dt_uart_info.status != 0U)) { +#endif unsigned int console_flags; if (console_stm32_register(dt_uart_info.base, 0, @@ -144,6 +389,50 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, #endif console_set_scope(&console.console, console_flags); } + + if (dt_pmic_status() > 0) { + initialize_pmic(); + } + + initialize_pll1_settings(); + + stm32mp1_init_lp_states(); +} + +/******************************************************************************* + * Set security setup in sp_min + ******************************************************************************/ +static void stm32mp1_sp_min_security_setup(void) +{ + uint32_t filter_conf = 0; + uint32_t active_conf = 0; + int ret; + + /* Init rtc driver */ + ret = stm32_rtc_init(); + if (ret < 0) { + WARN("RTC driver init error %i\n", ret); + } + + /* 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(); + } } /******************************************************************************* @@ -151,26 +440,27 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, ******************************************************************************/ void sp_min_platform_setup(void) { + ddr_save_sr_mode(); + /* Initialize tzc400 after DDR initialization */ stm32mp1_security_setup(); generic_delay_timer_init(); - stm32mp1_gic_init(); + stm32_gic_init(); - /* Unlock ETZPC securable peripherals */ -#define STM32MP1_ETZPC_BASE 0x5C007000U -#define ETZPC_DECPROT0 0x010U - mmio_write_32(STM32MP1_ETZPC_BASE + ETZPC_DECPROT0, 0xFFFFFFFF); - - /* Set GPIO bank Z as non secure */ - for (uint32_t pin = 0U; pin < STM32MP_GPIOZ_PIN_MAX_COUNT; pin++) { - set_gpio_secure_cfg(GPIO_BANK_Z, pin, false); - } + /* Update security settings */ + stm32mp1_sp_min_security_setup(); if (stm32_iwdg_init() < 0) { panic(); } + + configure_wakeup_interrupt(); + + stm32mp_lock_periph_registering(); + + stm32mp1_init_scmi_server(); } void sp_min_plat_arch_setup(void) diff --git a/plat/st/stm32mp1/stm32mp1_boot_device.c b/plat/st/stm32mp1/stm32mp1_boot_device.c new file mode 100644 index 000000000..ee64b58c3 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_boot_device.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include + +#define SZ_512 0x200U +#define SZ_64M 0x4000000U + +#if STM32MP_RAW_NAND || STM32MP_SPI_NAND +static int get_data_from_otp(struct nand_device *nand_dev, bool is_slc) +{ + uint32_t nand_param; + + /* Check if NAND parameters are stored in OTP */ + if (stm32_get_otp_value(NAND_OTP, &nand_param) != 0) { + ERROR("BSEC: NAND_OTP Error\n"); + return -EACCES; + } + + if (nand_param == 0U) { + return 0; + } + + if ((nand_param & NAND_PARAM_STORED_IN_OTP) == 0U) { + goto ecc; + } + + /* NAND parameter shall be read from OTP */ + if ((nand_param & NAND_WIDTH_MASK) != 0U) { + nand_dev->buswidth = NAND_BUS_WIDTH_16; + } else { + nand_dev->buswidth = NAND_BUS_WIDTH_8; + } + + switch ((nand_param & NAND_PAGE_SIZE_MASK) >> NAND_PAGE_SIZE_SHIFT) { + case NAND_PAGE_SIZE_2K: + nand_dev->page_size = 0x800U; + break; + + case NAND_PAGE_SIZE_4K: + nand_dev->page_size = 0x1000U; + break; + + case NAND_PAGE_SIZE_8K: + nand_dev->page_size = 0x2000U; + break; + + default: + ERROR("Cannot read NAND page size\n"); + return -EINVAL; + } + + switch ((nand_param & NAND_BLOCK_SIZE_MASK) >> NAND_BLOCK_SIZE_SHIFT) { + case NAND_BLOCK_SIZE_64_PAGES: + nand_dev->block_size = 64U * nand_dev->page_size; + break; + + case NAND_BLOCK_SIZE_128_PAGES: + nand_dev->block_size = 128U * nand_dev->page_size; + break; + + case NAND_BLOCK_SIZE_256_PAGES: + nand_dev->block_size = 256U * nand_dev->page_size; + break; + + default: + ERROR("Cannot read NAND block size\n"); + return -EINVAL; + } + + nand_dev->size = ((nand_param & NAND_BLOCK_NB_MASK) >> + NAND_BLOCK_NB_SHIFT) * + NAND_BLOCK_NB_UNIT * nand_dev->block_size; + +ecc: + if (is_slc) { + switch ((nand_param & NAND_ECC_BIT_NB_MASK) >> + NAND_ECC_BIT_NB_SHIFT) { + case NAND_ECC_BIT_NB_1_BITS: + nand_dev->ecc.max_bit_corr = 1U; + break; + + case NAND_ECC_BIT_NB_4_BITS: + nand_dev->ecc.max_bit_corr = 4U; + break; + + case NAND_ECC_BIT_NB_8_BITS: + nand_dev->ecc.max_bit_corr = 8U; + break; + + case NAND_ECC_ON_DIE: + nand_dev->ecc.mode = NAND_ECC_ONDIE; + break; + + default: + if (nand_dev->ecc.max_bit_corr == 0U) { + ERROR("No valid eccbit number\n"); + return -EINVAL; + } + } + } else { + /* Selected multiple plane NAND */ + if ((nand_param & NAND_PLANE_BIT_NB_MASK) != 0U) { + nand_dev->nb_planes = 2U; + } else { + nand_dev->nb_planes = 1U; + } + } + + VERBOSE("OTP: Block %i Page %i Size %lli\n", nand_dev->block_size, + nand_dev->page_size, nand_dev->size); + + return 0; +} +#endif /* STM32MP_RAW_NAND || STM32MP_SPI_NAND */ + +#if STM32MP_RAW_NAND +int plat_get_raw_nand_data(struct rawnand_device *device) +{ + device->nand_dev->ecc.mode = NAND_ECC_HW; + device->nand_dev->ecc.size = SZ_512; + + return get_data_from_otp(device->nand_dev, true); +} +#endif + +#if STM32MP_SPI_NAND +int plat_get_spi_nand_data(struct spinand_device *device) +{ + zeromem(&device->spi_read_cache_op, sizeof(struct spi_mem_op)); + device->spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE_4X; + device->spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->spi_read_cache_op.addr.nbytes = 2U; + device->spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->spi_read_cache_op.dummy.nbytes = 1U; + device->spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_4_LINE; + device->spi_read_cache_op.data.dir = SPI_MEM_DATA_IN; + + return get_data_from_otp(device->nand_dev, false); +} +#endif + +#if STM32MP_SPI_NOR +int plat_get_nor_data(struct nor_device *device) +{ + device->size = SZ_64M; + + zeromem(&device->read_op, sizeof(struct spi_mem_op)); + device->read_op.cmd.opcode = SPI_NOR_OP_READ_1_1_4; + device->read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->read_op.addr.nbytes = 3U; + device->read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->read_op.dummy.nbytes = 1U; + device->read_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->read_op.data.buswidth = SPI_MEM_BUSWIDTH_4_LINE; + device->read_op.data.dir = SPI_MEM_DATA_IN; + + return 0; +} +#endif diff --git a/plat/st/stm32mp1/stm32mp1_context.c b/plat/st/stm32mp1/stm32mp1_context.c index cf8a91eb4..e77b8a79f 100644 --- a/plat/st/stm32mp1/stm32mp1_context.c +++ b/plat/st/stm32mp1/stm32mp1_context.c @@ -5,19 +5,343 @@ */ #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 +#define TRAINING_AREA_SIZE 64 + +#ifdef AARCH32_SP_OPTEE +/* + * OPTEE_MAILBOX_MAGIC relates to struct backup_data_s as defined + * + * OPTEE_MAILBOX_MAGIC_V1: + * Context provides magic, resume entry, zq0cr0 zdata and DDR training buffer. + * + * OPTEE_MAILBOX_MAGIC_V2: + * Context provides magic, resume entry, zq0cr0 zdata, DDR training buffer + * and PLL1 dual OPP settings structure (86 bytes). + */ +#define OPTEE_MAILBOX_MAGIC_V1 (0x0001 << 16) +#define OPTEE_MAILBOX_MAGIC_V2 (0x0002 << 16) +#define OPTEE_MAILBOX_MAGIC (OPTEE_MAILBOX_MAGIC_V2 | \ + 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 OPTEE_MAILBOX_MAGIC_V1 does not support expected PLL1 settings +#endif +#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 { +#ifdef AARCH32_SP_OPTEE + 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]; +#else + smc_ctx_t saved_smc_context[PLATFORM_CORE_COUNT]; + cpu_context_t saved_cpu_context[PLATFORM_CORE_COUNT]; + uint32_t zq0cr0_zdata; + struct stm32_rtc_calendar rtc; + uint8_t ddr_training_backup[TRAINING_AREA_SIZE]; + uint8_t pll1_settings[PLL1_SETTINGS_SIZE]; + unsigned long long stgen; + uint8_t clock_cfg[CLOCK_CONTEXT_SIZE]; + uint8_t scmi_context[SCMI_CONTEXT_SIZE]; +#endif +}; + +#ifdef AARCH32_SP_OPTEE +uint32_t stm32_pm_get_optee_ep(void) +{ + struct backup_data_s *backup_data; + uint32_t ep; + + stm32mp_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 OPTEE_MAILBOX_MAGIC_V1: + case OPTEE_MAILBOX_MAGIC_V2: + 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; + + stm32mp_clk_disable(BKPSRAM); + + return ep; +} +#else /*AARCH32_SP_OPTEE*/ +void stm32_clean_context(void) +{ + stm32mp_clk_enable(BKPSRAM); + + zeromem((void *)STM32MP_BACKUP_RAM_BASE, sizeof(struct backup_data_s)); + + stm32mp_clk_disable(BKPSRAM); +} + +void stm32mp1_pm_save_clock_cfg(size_t offset, uint8_t *data, size_t size) +{ + struct backup_data_s *backup_data; + + if (offset + size > sizeof(backup_data->clock_cfg)) { + panic(); + } + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + stm32mp_clk_enable(BKPSRAM); + + memcpy(backup_data->clock_cfg + offset, data, size); + + stm32mp_clk_disable(BKPSRAM); +} + +void stm32mp1_pm_restore_clock_cfg(size_t offset, uint8_t *data, size_t size) +{ + struct backup_data_s *backup_data; + + if (offset + size > sizeof(backup_data->clock_cfg)) + panic(); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + stm32mp_clk_enable(BKPSRAM); + + memcpy(data, backup_data->clock_cfg + offset, size); + + stm32mp_clk_disable(BKPSRAM); +} + +int stm32_save_context(uint32_t zq0cr0_zdata) +{ + void *smc_context; + void *cpu_context; + struct backup_data_s *backup_data; + + stm32mp1_clock_suspend(); + + stm32mp_clk_enable(BKPSRAM); + + /* Context & Data to be saved at the beginning of Backup SRAM */ + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + /* 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_data->saved_smc_context[0], smc_context, + sizeof(smc_ctx_t) * PLATFORM_CORE_COUNT); + memcpy(&backup_data->saved_cpu_context[0], cpu_context, + sizeof(cpu_context_t) * PLATFORM_CORE_COUNT); + + backup_data->zq0cr0_zdata = zq0cr0_zdata; + + stm32_rtc_get_calendar(&backup_data->rtc); + backup_data->stgen = stm32mp_stgen_get_counter(); + + stm32mp1_clk_lp_save_opp_pll1_settings(backup_data->pll1_settings, + sizeof(backup_data->pll1_settings)); + + stm32mp1_pm_save_scmi_state(backup_data->scmi_context, + sizeof(backup_data->scmi_context)); + + save_clock_pm_context(); + + stm32mp_clk_disable(BKPSRAM); + + return 0; +} + +int stm32_restore_context(void) +{ + void *smc_context; + void *cpu_context; + struct backup_data_s *backup_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; + + /* Retrieve smc context struct address */ + smc_context = smc_get_ctx(NON_SECURE); + + /* Retrieve smc context struct address */ + cpu_context = cm_get_context(NON_SECURE); + + stm32mp_clk_enable(BKPSRAM); + + restore_clock_pm_context(); + + stm32mp1_pm_restore_scmi_state(backup_data->scmi_context, + sizeof(backup_data->scmi_context)); + + /* Restore data from Backup SRAM */ + memcpy(smc_context, backup_data->saved_smc_context, + sizeof(smc_ctx_t) * PLATFORM_CORE_COUNT); + memcpy(cpu_context, backup_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_data->rtc); + stm32mp_stgen_restore_counter(backup_data->stgen, stdby_time_in_ms); + + stm32mp1_clk_lp_load_opp_pll1_settings(backup_data->pll1_settings, + sizeof(backup_data->pll1_settings)); + + stm32mp_clk_disable(BKPSRAM); + + stm32mp1_clock_resume(); + + return 0; +} + +unsigned long long stm32_get_stgen_from_context(void) +{ + struct backup_data_s *backup_data; + unsigned long long stgen_cnt; + + stm32mp_clk_enable(BKPSRAM); + + backup_data = (struct backup_data_s *)STM32MP_BACKUP_RAM_BASE; + + stgen_cnt = backup_data->stgen; + + stm32mp_clk_disable(BKPSRAM); + + return stgen_cnt; +} +#endif /*AARCH32_SP_OPTEE*/ + +uint32_t stm32_get_zdata_from_context(void) +{ + struct backup_data_s *backup_data; + uint32_t zdata; + + stm32mp_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; + + stm32mp_clk_disable(BKPSRAM); + + return zdata; +} + +#ifdef AARCH32_SP_OPTEE +static int pll1_settings_in_context(struct backup_data_s *backup_data) +{ + switch (MAGIC_ID(backup_data->magic)) { + case OPTEE_MAILBOX_MAGIC_V1: + return -ENOENT; + case OPTEE_MAILBOX_MAGIC_V2: + assert(MAGIC_AREA_SIZE(backup_data->magic) == + TRAINING_AREA_SIZE); + return 0; + default: + panic(); + } +} +#else +static int pll1_settings_in_context(struct backup_data_s *backup_data) +{ + return 0; +} +#endif + +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; + + stm32mp_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); + } + + stm32mp_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; + + stm32mp_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); + + stm32mp_clk_disable(BKPSRAM); + + return 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); @@ -33,3 +357,68 @@ int stm32_save_boot_interface(uint32_t interface, uint32_t instance) return 0; } + +int stm32_get_boot_interface(uint32_t *interface, uint32_t *instance) +{ + uint32_t backup_reg_itf; + uint32_t bkpr_itf_idx = tamp_bkpr(TAMP_BOOT_ITF_BACKUP_REG_ID); + + stm32mp_clk_enable(RTCAPB); + + backup_reg_itf = (mmio_read_32(bkpr_itf_idx) & + TAMP_BOOT_ITF_MASK) >> TAMP_BOOT_ITF_SHIFT; + + stm32mp_clk_disable(RTCAPB); + + *interface = backup_reg_itf >> 4; + *instance = backup_reg_itf & 0xFU; + + return 0; +} + +#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; + + stm32mp_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); + + stm32mp_clk_disable(BKPSRAM); +} +#endif + +void stm32_restore_ddr_training_area(void) +{ + struct backup_data_s *backup_data; + + stm32mp_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(); + + stm32mp_clk_disable(BKPSRAM); +} diff --git a/plat/st/stm32mp1/stm32mp1_dbgmcu.c b/plat/st/stm32mp1/stm32mp1_dbgmcu.c index d0264968c..b9f94af0a 100644 --- a/plat/st/stm32mp1/stm32mp1_dbgmcu.c +++ b/plat/st/stm32mp1/stm32mp1_dbgmcu.c @@ -4,12 +4,14 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include #include #include +#include #include #include #include @@ -33,19 +35,11 @@ static uintptr_t get_rcc_base(void) 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); @@ -53,8 +47,15 @@ static int stm32mp1_dbgmcu_init(void) 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 +66,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 a40852bde..d458805a1 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -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 */ @@ -11,33 +11,51 @@ #include #include #include +#include #include #include #ifndef __ASSEMBLER__ #include +#include #include +#include +#include +#include #include -#include #include #include #include +#include +#include #include #include +#include +#include +#include #endif /******************************************************************************* * 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) #define STM32MP153A_PART_NB U(0x05000025) #define STM32MP151C_PART_NB U(0x0500002E) #define STM32MP151A_PART_NB U(0x0500002F) +#define STM32MP157F_PART_NB U(0x05000080) +#define STM32MP157D_PART_NB U(0x05000081) +#define STM32MP153F_PART_NB U(0x050000A4) +#define STM32MP153D_PART_NB U(0x050000A5) +#define STM32MP151F_PART_NB U(0x050000AE) +#define STM32MP151D_PART_NB U(0x050000AF) #define STM32MP1_REV_B U(0x2000) +#define STM32MP1_REV_Z U(0x2001) /******************************************************************************* * PACKAGE ID @@ -47,6 +65,11 @@ #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 ******************************************************************************/ @@ -56,12 +79,34 @@ #define STM32MP_SYSRAM_BASE U(0x2FFC0000) #define STM32MP_SYSRAM_SIZE U(0x00040000) +/* 384 KB (128 x 3) Non secure from MCU available for TF*/ +#define STM32MP_SRAM_MCU_BASE U(0x30000000) +#define STM32MP_SRAM_MCU_SIZE U(0x00060000) + +#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 - \ + STM32MP_NS_SYSRAM_SIZE) + +#define STM32MP_SEC_SYSRAM_BASE STM32MP_SYSRAM_BASE +#define STM32MP_SEC_SYSRAM_SIZE (STM32MP_SYSRAM_SIZE - \ + STM32MP_NS_SYSRAM_SIZE) + /* DDR configuration */ #define STM32MP_DDR_BASE U(0xC0000000) #define STM32MP_DDR_MAX_SIZE U(0x40000000) /* Max 1GB */ #ifdef AARCH32_SP_OPTEE #define STM32MP_DDR_S_SIZE U(0x01E00000) /* 30 MB */ #define STM32MP_DDR_SHMEM_SIZE U(0x00200000) /* 2 MB */ +#else +#define STM32MP_DDR_S_SIZE U(0) +#define STM32MP_DDR_SHMEM_SIZE U(0) #endif /* DDR power initializations */ @@ -74,76 +119,121 @@ enum ddr_type { #endif /* Section used inside TF binaries */ -#define STM32MP_PARAM_LOAD_SIZE U(0x00002400) /* 9 Ko for param */ +#define STM32MP_PARAM_LOAD_SIZE U(0x00002400) /* 9 KB for param */ /* 256 Octets reserved for header */ #define STM32MP_HEADER_SIZE U(0x00000100) -#define STM32MP_BINARY_BASE (STM32MP_SYSRAM_BASE + \ +#define STM32MP_BINARY_BASE (STM32MP_SEC_SYSRAM_BASE + \ STM32MP_PARAM_LOAD_SIZE + \ STM32MP_HEADER_SIZE) -#define STM32MP_BINARY_SIZE (STM32MP_SYSRAM_SIZE - \ +#define STM32MP_BINARY_SIZE (STM32MP_SEC_SYSRAM_SIZE - \ (STM32MP_PARAM_LOAD_SIZE + \ STM32MP_HEADER_SIZE)) #ifdef AARCH32_SP_OPTEE #define STM32MP_BL32_SIZE U(0) -#define STM32MP_OPTEE_BASE STM32MP_SYSRAM_BASE +#define STM32MP_OPTEE_BASE STM32MP_SEC_SYSRAM_BASE #define STM32MP_OPTEE_SIZE (STM32MP_DTB_BASE - \ STM32MP_OPTEE_BASE) #else #if STACK_PROTECTOR_ENABLED -#define STM32MP_BL32_SIZE U(0x00012000) /* 72 Ko for BL32 */ +#define STM32MP_BL32_SIZE U(0x00013000) /* 76 KB for BL32 */ #else -#define STM32MP_BL32_SIZE U(0x00011000) /* 68 Ko for BL32 */ +#define STM32MP_BL32_SIZE U(0x00012000) /* 72 KB for BL32 */ #endif #endif -#define STM32MP_BL32_BASE (STM32MP_SYSRAM_BASE + \ - STM32MP_SYSRAM_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(0x00019000) /* 100 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x0001A000) /* 104 KB for BL2 */ #else -#define STM32MP_BL2_SIZE U(0x00017000) /* 92 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x00018000) /* 96 KB for BL2 */ #endif #else #if STACK_PROTECTOR_ENABLED -#define STM32MP_BL2_SIZE U(0x00018000) /* 96 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x00019000) /* 100 KB for BL2 */ #else -#define STM32MP_BL2_SIZE U(0x00016000) /* 88 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x00017000) /* 92 KB for BL2 */ #endif #endif #define STM32MP_BL2_BASE (STM32MP_BL32_BASE - \ STM32MP_BL2_SIZE) -/* BL2 and BL32/sp_min require 5 tables */ -#define MAX_XLAT_TABLES 5 +#if STM32MP_USB_PROGRAMMER + /* BL2 and BL32/sp_min require 5 finer granularity tables */ + #define MAX_XLAT_TABLES U(5) /* 20 KB for mapping */ +#else + /* BL2 and BL32/sp_min require 4 finer granularity tables */ + #define MAX_XLAT_TABLES U(4) /* 16 KB for mapping */ +#endif /* * MAX_MMAP_REGIONS is usually: * BL stm32mp1_mmap size + mmap regions in *_plat_arch_setup */ #if defined(IMAGE_BL2) + #if STM32MP_USB_PROGRAMMER + #define MAX_MMAP_REGIONS 12 + #else #define MAX_MMAP_REGIONS 11 + #endif #endif #if defined(IMAGE_BL32) #define MAX_MMAP_REGIONS 6 #endif +#define XLAT_TABLE_OCTETSIZE U(0x1000) +#define PLAT_XLAT_SIZE (MAX_XLAT_TABLES * \ + XLAT_TABLE_OCTETSIZE) + +#define PLAT_XLAT_BASE (STM32MP_BL2_BASE - \ + PLAT_XLAT_SIZE) + /* DTB initialization value */ -#define STM32MP_DTB_SIZE U(0x00005000) /* 20Ko for DTB */ +#define STM32MP_DTB_SIZE U(0x00006000) /* 24 KB for DTB */ -#define STM32MP_DTB_BASE (STM32MP_BL2_BASE - \ +#define STM32MP_DTB_BASE (PLAT_XLAT_BASE - \ STM32MP_DTB_SIZE) #define STM32MP_BL33_BASE (STM32MP_DDR_BASE + U(0x100000)) +/* Define Temporary Stack size use during low power mode */ +#define STM32MP_INT_STACK_SIZE 0x100 + +/* 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) +#ifdef AARCH32_SP_OPTEE +#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) +#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) +#endif + /******************************************************************************* * STM32MP1 device/io map related constants (used for MMU) ******************************************************************************/ @@ -163,6 +253,32 @@ enum ddr_type { ******************************************************************************/ #define PWR_BASE U(0x50001000) +/******************************************************************************* + * STM32MP1 SYSCFG + ******************************************************************************/ +#define SYSCFG_BASE U(0x50020000) + +/******************************************************************************* + * 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 RTC + ******************************************************************************/ +#define RTC_BASE U(0x5C004000) + /******************************************************************************* * STM32MP1 GPIO ******************************************************************************/ @@ -223,6 +339,21 @@ enum ddr_type { #define DEBUG_UART_TX_EN_REG RCC_MP_APB1ENSETR #define DEBUG_UART_TX_EN RCC_MP_APB1ENSETR_UART4EN +/******************************************************************************* + * STM32MP1 ETZPC + ******************************************************************************/ +#define STM32MP1_ETZPC_BASE U(0x5C007000) +#define STM32MP1_ETZPC_SIZE U(0x000003FF) + +#define STM32MP1_ETZPC_TZMA_ROM_ID U(0) +/*SYSRAM internal RAM*/ +#define STM32MP1_ETZPC_TZMA_RAM_ID U(1) + +/* Lowest DECPROT ID for ETZPC cannot harden TZ security */ +#define STM32MP1_ETZPC_SEC_ID_LIMIT U(13) + +#define STM32MP1_ETZPC_TZMA_ALL_SECURE GENMASK_32(9, 0) + /******************************************************************************* * STM32MP1 TZC (TZ400) ******************************************************************************/ @@ -263,15 +394,20 @@ 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 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" /* OTP mask */ -/* DATA0 */ -#define DATA0_OTP_SECURED BIT(6) +/* CFG0 */ +#define CFG0_CLOSED_DEVICE BIT(6) /* PART NUMBER */ #define PART_NUMBER_OTP_PART_MASK GENMASK_32(7, 0) @@ -289,11 +425,71 @@ enum ddr_type { /* HW2 OTP */ #define HW2_OTP_PRODUCT_BELOW_2V5 BIT(13) +#define MAX_MONOTONIC_VALUE 32 + +/* NAND OTP */ +/* NAND parameter storage flag */ +#define NAND_PARAM_STORED_IN_OTP BIT(31) + +/* NAND page size in bytes */ +#define NAND_PAGE_SIZE_MASK GENMASK_32(30, 29) +#define NAND_PAGE_SIZE_SHIFT 29 +#define NAND_PAGE_SIZE_2K U(0) +#define NAND_PAGE_SIZE_4K U(1) +#define NAND_PAGE_SIZE_8K U(2) + +/* NAND block size in pages */ +#define NAND_BLOCK_SIZE_MASK GENMASK_32(28, 27) +#define NAND_BLOCK_SIZE_SHIFT 27 +#define NAND_BLOCK_SIZE_64_PAGES U(0) +#define NAND_BLOCK_SIZE_128_PAGES U(1) +#define NAND_BLOCK_SIZE_256_PAGES U(2) + +/* NAND number of block (in unit of 256 blocs) */ +#define NAND_BLOCK_NB_MASK GENMASK_32(26, 19) +#define NAND_BLOCK_NB_SHIFT 19 +#define NAND_BLOCK_NB_UNIT U(256) + +/* NAND bus width in bits */ +#define NAND_WIDTH_MASK BIT(18) +#define NAND_WIDTH_SHIFT 18 + +/* NAND number of ECC bits per 512 bytes */ +#define NAND_ECC_BIT_NB_MASK GENMASK_32(17, 15) +#define NAND_ECC_BIT_NB_SHIFT 15 +#define NAND_ECC_BIT_NB_UNSET U(0) +#define NAND_ECC_BIT_NB_1_BITS U(1) +#define NAND_ECC_BIT_NB_4_BITS U(2) +#define NAND_ECC_BIT_NB_8_BITS U(3) +#define NAND_ECC_ON_DIE U(4) + +/* 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 HASH + ******************************************************************************/ +#define HASH1_BASE U(0x54002000) +#define HASH_BASE HASH1_BASE + /******************************************************************************* * 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) @@ -302,6 +498,11 @@ static inline uint32_t tamp_bkpr(uint32_t idx) } #endif +/******************************************************************************* + * STM32MP1 USB + ******************************************************************************/ +#define USB_OTG_BASE U(0x49000000) + /******************************************************************************* * STM32MP1 DDRCTRL ******************************************************************************/ @@ -323,22 +524,69 @@ static inline uint32_t tamp_bkpr(uint32_t idx) #define IWDG2_BASE U(0x5A002000) /******************************************************************************* - * STM32MP1 I2C4 + * STM32MP1 I2C ******************************************************************************/ #define I2C4_BASE U(0x5C002000) +#define I2C6_BASE U(0x5C009000) /******************************************************************************* * STM32MP1 DBGMCU ******************************************************************************/ #define DBGMCU_BASE U(0x50081000) +/******************************************************************************* + * STM32MP1 SPI + ******************************************************************************/ +#define SPI6_BASE U(0x5C001000) + +/******************************************************************************* + * STM32MP1 RNG + ******************************************************************************/ +#define RNG1_BASE U(0x54003000) + +/******************************************************************************* + * STM32MP1 CRYP + ******************************************************************************/ +#define CRYP1_BASE U(0x54001000) + +/******************************************************************************* + * STM32MP1 STGEN + ******************************************************************************/ +#define STGEN_BASE U(0x5C008000) + +/******************************************************************************* + * 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_PWR_COMPAT "st,stm32mp1-pwr" +#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_SYSCFG_COMPAT "st,stm32mp157-syscfg" + +#define DT_PLL1_NODE_NAME "st,pll@0" #endif /* STM32MP1_DEF_H */ diff --git a/plat/st/stm32mp1/stm32mp1_gic.c b/plat/st/stm32mp1/stm32mp1_gic.c deleted file mode 100644 index 851a9cf0c..000000000 --- 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 bfcd991a3..b80716253 100644 --- a/plat/st/stm32mp1/stm32mp1_helper.S +++ b/plat/st/stm32mp1/stm32mp1_helper.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 */ @@ -10,12 +10,17 @@ #include #include #include +#include #define GPIO_TX_SHIFT (DEBUG_UART_TX_GPIO_PORT << 1) -#define GPIO_TX_ALT_SHIFT ((DEBUG_UART_TX_GPIO_PORT - GPIO_ALT_LOWER_LIMIT) << 2) .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 @@ -25,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 */ @@ -32,9 +38,138 @@ func platform_mem_init endfunc platform_mem_init func plat_report_exception +#if DEBUG + mov r8, lr + + /* Test if an abort occurred */ + cmp r0, #MODE32_abt + bne undef_inst_lbl + ldr r4, =abort_str + bl asm_print_str + b print_excpetion_info + +undef_inst_lbl: + /* Test for an undefined instruction */ + cmp r0, #MODE32_und + bne other_excpetion_lbl + ldr r4, =undefined_str + bl asm_print_str + b print_excpetion_info + +other_excpetion_lbl: + /* Other exceptions */ + mov r9, r0 + ldr r4, =exception_start_str + bl asm_print_str + mov r4, r9 + bl asm_print_hex + ldr r4, =exception_end_str + bl asm_print_str + +print_excpetion_info: + mrs r4, lr_svc + 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_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 @@ -129,10 +264,16 @@ func plat_crash_console_init bic r2, r2, #(GPIO_PULL_MASK << GPIO_TX_SHIFT) str r2, [r1, #GPIO_PUPD_OFFSET] /* Set alternate */ - ldr r2, [r1, #GPIO_AFRH_OFFSET] - bic r2, r2, #(GPIO_ALTERNATE_MASK << GPIO_TX_ALT_SHIFT) - orr r2, r2, #(DEBUG_UART_TX_GPIO_ALTERNATE << GPIO_TX_ALT_SHIFT) - str r2, [r1, #GPIO_AFRH_OFFSET] + ldr r2, =DEBUG_UART_TX_GPIO_PORT + cmp r2, #GPIO_ALT_LOWER_LIMIT + ldrge r2, [r1, #GPIO_AFRH_OFFSET] + bicge r2, r2, #(GPIO_ALTERNATE_MASK << ((DEBUG_UART_TX_GPIO_PORT - GPIO_ALT_LOWER_LIMIT) << 2)) + orrge r2, r2, #(DEBUG_UART_TX_GPIO_ALTERNATE << ((DEBUG_UART_TX_GPIO_PORT - GPIO_ALT_LOWER_LIMIT) << 2)) + strge r2, [r1, #GPIO_AFRH_OFFSET] + ldrlt r2, [r1, #GPIO_AFRL_OFFSET] + biclt r2, r2, #(GPIO_ALTERNATE_MASK << (DEBUG_UART_TX_GPIO_PORT << 2)) + orrlt r2, r2, #(DEBUG_UART_TX_GPIO_ALTERNATE << (DEBUG_UART_TX_GPIO_PORT << 2)) + strlt r2, [r1, #GPIO_AFRL_OFFSET] /* Enable UART clock, with its source */ ldr r1, =(RCC_BASE + DEBUG_UART_TX_CLKSRC_REG) mov r2, #DEBUG_UART_TX_CLKSRC @@ -174,3 +315,70 @@ func plat_crash_console_putc ldr r1, =STM32MP_DEBUG_USART_BASE b console_stm32_core_putc endfunc plat_crash_console_putc + + /* ---------------------------------------------------------- + * void plat_panic_handler(void) __dead2; + * Report exception + endless loop. + * + * r6 holds the address where the fault occurred. + * Filling lr with this value allows debuggers to reconstruct + * the backtrace. + * ---------------------------------------------------------- + */ +func plat_panic_handler + mrs r0, cpsr + and r0, #MODE32_MASK + bl plat_report_exception + mov lr, r6 + b . +endfunc plat_panic_handler + +#if DEBUG +.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 000000000..fb975f5f6 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_low_power.c @@ -0,0 +1,346 @@ +/* + * 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 + +static unsigned int gicc_pmr; +static struct stm32_rtc_calendar sleep_time; +static bool enter_cstop_done; +static uint32_t int_stack[STM32MP_INT_STACK_SIZE]; + +extern void wfi_svc_int_enable(uintptr_t stack_addr); + +struct pwr_lp_config { + uint32_t pwr_cr1; + uint32_t pwr_mpucr; + const char *regul_suspend_node_name; +}; + +#define PWR_CR1_MASK (PWR_CR1_LPDS | PWR_CR1_LPCFG | PWR_CR1_LVDS) +#define PWR_MPUCR_MASK (PWR_MPUCR_CSTDBYDIS | PWR_MPUCR_CSSF | PWR_MPUCR_PDDS) + +static const struct pwr_lp_config config_pwr[STM32_PM_MAX_SOC_MODE] = { + [STM32_PM_CSLEEP_RUN] = { + .pwr_cr1 = 0U, + .pwr_mpucr = 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) + +void stm32_apply_pmic_suspend_config(uint32_t mode) +{ + const char *node_name = config_pwr[mode].regul_suspend_node_name; + + assert(mode < ARRAY_SIZE(config_pwr)); + + 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); + + /* + * Set DDR in Self-refresh, even if no return address is given. + * This is also the procedure awaited when switching off power supply. + */ + if (ddr_standby_sr_entry(&zq0cr0_zdata) != 0) { + panic(); + } + + stm32mp_clk_enable(RTCAPB); + + mmio_write_32(bkpr_core1_addr, 0); + mmio_write_32(bkpr_core1_magic, 0); + + stm32mp1_clock_stopmode_save(); + + 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) != 0) { + panic(); + } + + /* Keep retention and backup RAM content in standby */ + mmio_setbits_32(pwr_base + PWR_CR2, PWR_CR2_BREN | + PWR_CR2_RREN); + while ((mmio_read_32(pwr_base + PWR_CR2) & + (PWR_CR2_BRRDY | PWR_CR2_RRRDY)) == 0U) { + ; + } + } + + stm32mp_clk_disable(RTCAPB); + + stm32_rtc_get_calendar(&sleep_time); + + enter_cstop_done = true; +} + +/* + * 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; + + if (ddr_sw_self_refresh_exit() != 0) { + panic(); + } + + /* Switch to memorized Self-Refresh mode */ + ddr_restore_sr_mode(); + + 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(stm32_get_stgen_from_context(), + stdby_time_in_ms); + + stm32mp1_syscfg_enable_io_compensation(); + + if (stm32mp1_clock_stopmode_resume() != 0) { + panic(); + } +} + +static void enter_shutdown(void) +{ + /* Set DDR in Self-refresh before shutting down the platform */ + if (ddr_standby_sr_entry(NULL) != 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(); +} + +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_pwr_down_wfi(void) +{ + uint32_t interrupt = GIC_SPURIOUS_INTERRUPT; + + 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(); + } +} diff --git a/plat/st/stm32mp1/stm32mp1_pm.c b/plat/st/stm32mp1/stm32mp1_pm.c index cf9fa8e69..627d209da 100644 --- a/plat/st/stm32mp1/stm32mp1_pm.c +++ b/plat/st/stm32mp1/stm32mp1_pm.c @@ -10,17 +10,24 @@ #include #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. @@ -33,11 +40,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(); @@ -64,13 +72,25 @@ 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; } - if ((stm32_sec_entrypoint < STM32MP_SYSRAM_BASE) || - (stm32_sec_entrypoint > (STM32MP_SYSRAM_BASE + - (STM32MP_SYSRAM_SIZE - 1)))) { + /* 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; } @@ -107,7 +127,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 +140,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,16 +156,59 @@ 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_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(); + + /* This shouldn't be reached */ panic(); } @@ -188,6 +253,8 @@ static int stm32_validate_ns_entrypoint(uintptr_t entrypoint) return PSCI_E_INVALID_ADDRESS; } + saved_entrypoint = entrypoint; + return PSCI_E_SUCCESS; } @@ -211,6 +278,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 +300,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 000000000..079c5eece --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_power_config.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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" + +static uint32_t deepest_system_suspend_mode; +static uint32_t system_off_mode; +static uint8_t stm32mp1_supported_soc_modes[STM32_PM_MAX_SOC_MODE]; + +static int dt_get_pwr_node(void) +{ + return dt_get_node_by_compatible(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(pwr_node, SYSTEM_SUSPEND_SUPPORTED_MODES, + &supported[0], count) < 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(); + 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; +} + +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(); + } +} + +/* 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; +} diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c index e2dcd2af7..b94857bcb 100644 --- a/plat/st/stm32mp1/stm32mp1_private.c +++ b/plat/st/stm32mp1/stm32mp1_private.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 */ @@ -10,32 +10,75 @@ #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) #define BOARD_ID_BOARD_NB_SHIFT 16 -#define BOARD_ID_VARIANT_MASK GENMASK(15, 12) -#define BOARD_ID_VARIANT_SHIFT 12 +#define BOARD_ID_VARCPN_MASK GENMASK(15, 12) +#define BOARD_ID_VARCPN_SHIFT 12 #define BOARD_ID_REVISION_MASK GENMASK(11, 8) #define BOARD_ID_REVISION_SHIFT 8 +#define BOARD_ID_VARFG_MASK GENMASK(7, 4) +#define BOARD_ID_VARFG_SHIFT 4 #define BOARD_ID_BOM_MASK GENMASK(3, 0) #define BOARD_ID2NB(_id) (((_id) & BOARD_ID_BOARD_NB_MASK) >> \ BOARD_ID_BOARD_NB_SHIFT) -#define BOARD_ID2VAR(_id) (((_id) & BOARD_ID_VARIANT_MASK) >> \ - BOARD_ID_VARIANT_SHIFT) +#define BOARD_ID2VARCPN(_id) (((_id) & BOARD_ID_VARCPN_MASK) >> \ + BOARD_ID_VARCPN_SHIFT) #define BOARD_ID2REV(_id) (((_id) & BOARD_ID_REVISION_MASK) >> \ BOARD_ID_REVISION_SHIFT) +#define BOARD_ID2VARFG(_id) (((_id) & BOARD_ID_VARFG_MASK) >> \ + BOARD_ID_VARFG_SHIFT) #define BOARD_ID2BOM(_id) ((_id) & BOARD_ID_BOM_MASK) -#define MAP_SRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ +#if defined(IMAGE_BL2) +#define MAP_SEC_SYSRAM MAP_REGION_FLAT(STM32MP_SYSRAM_BASE, \ STM32MP_SYSRAM_SIZE, \ MT_MEMORY | \ MT_RW | \ MT_SECURE | \ MT_EXECUTE_NEVER) +#elif defined(IMAGE_BL32) +#define MAP_SEC_SYSRAM MAP_REGION_FLAT(STM32MP_SEC_SYSRAM_BASE, \ + STM32MP_SEC_SYSRAM_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + +/* Non-secure SYSRAM is used a uncached memory for SCMI message transfer */ +#define MAP_NS_SYSRAM MAP_REGION_FLAT(STM32MP_NS_SYSRAM_BASE, \ + STM32MP_NS_SYSRAM_SIZE, \ + MT_DEVICE | \ + MT_RW | \ + MT_NS | \ + MT_EXECUTE_NEVER) +#endif + +#define MAP_SRAM_MCU MAP_REGION_FLAT(STM32MP_SRAM_MCU_BASE, \ + STM32MP_SRAM_MCU_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_NS | \ + MT_EXECUTE_NEVER) + +#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, \ @@ -53,7 +96,10 @@ #if defined(IMAGE_BL2) static const mmap_region_t stm32mp1_mmap[] = { - MAP_SRAM, + MAP_SEC_SYSRAM, +#if STM32MP_USB_PROGRAMMER + MAP_SRAM_MCU, +#endif MAP_DEVICE1, MAP_DEVICE2, {0} @@ -61,7 +107,8 @@ static const mmap_region_t stm32mp1_mmap[] = { #endif #if defined(IMAGE_BL32) static const mmap_region_t stm32mp1_mmap[] = { - MAP_SRAM, + MAP_SEC_SYSRAM, + MAP_NS_SYSRAM, MAP_DEVICE1, MAP_DEVICE2, {0} @@ -70,10 +117,177 @@ 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 +} + +#if STM32MP_UART_PROGRAMMER +/* + * 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 || instance_nb > ARRAY_SIZE(stm32mp1_uart_addresses)) + return 0; - enable_mmu_svc_mon(0); + return stm32mp1_uart_addresses[instance_nb - 1]; +} +#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), +}; + +#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 ((!stm32mp_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) == + TZPC_DECPROT_MCU_ISOLATION))) { + continue; + } + + if (tzc_source_ip[id].reset_id != GPU_R) { + uint32_t reset = tzc_source_ip[id].reset_id; + + if (stm32mp_reset_assert_to(reset, TIMEOUT_US_1MS)) { + panic(); + } + if (stm32mp_reset_deassert_to(reset, TIMEOUT_US_1MS)) { + 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(); } unsigned long stm32_get_gpio_bank_clock(unsigned int bank) @@ -87,59 +301,133 @@ unsigned long stm32_get_gpio_bank_clock(unsigned int bank) return GPIOA + (bank - GPIO_BANK_A); } -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 (stm32mp1_dbgmcu_get_chip_dev_id(&dev_id) < 0) { + if (bsec_find_otp_name_in_dt(otp_name, otp_idx, otp_len) != BSEC_OK) { 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(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; + } + + 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; +} + +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; - 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; +} + +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_print_cpuinfo(void) { const char *cpu_s, *cpu_r, *pkg; - uint32_t part_number; - uint32_t cpu_package; 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; @@ -158,19 +446,31 @@ void stm32mp_print_cpuinfo(void) case STM32MP151A_PART_NB: cpu_s = "151A"; break; + case STM32MP157F_PART_NB: + cpu_s = "157F"; + break; + case STM32MP157D_PART_NB: + cpu_s = "157D"; + break; + case STM32MP153F_PART_NB: + cpu_s = "153F"; + break; + case STM32MP153D_PART_NB: + cpu_s = "153D"; + break; + case STM32MP151F_PART_NB: + cpu_s = "151F"; + break; + case STM32MP151D_PART_NB: + cpu_s = "151D"; + break; default: cpu_s = "????"; break; } /* 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; @@ -191,14 +491,16 @@ void stm32mp_print_cpuinfo(void) /* REVISION */ ret = stm32mp1_dbgmcu_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) { case STM32MP1_REV_B: cpu_r = "B"; break; + case STM32MP1_REV_Z: + cpu_r = "Z"; + break; default: cpu_r = "?"; break; @@ -209,35 +511,9 @@ void stm32mp_print_cpuinfo(void) void stm32mp_print_boardinfo(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(); - } - - 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(); - } + uint32_t board_id = 0; - board_otp = fdt32_to_cpu(*cuint) / sizeof(uint32_t); - - 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; } @@ -246,9 +522,10 @@ void stm32mp_print_boardinfo(void) rev[0] = BOARD_ID2REV(board_id) - 1 + 'A'; rev[1] = '\0'; - NOTICE("Board: MB%04x Var%d Rev.%s-%02d\n", + NOTICE("Board: MB%04x Var%d.%d Rev.%s-%02d\n", BOARD_ID2NB(board_id), - BOARD_ID2VAR(board_id), + BOARD_ID2VARCPN(board_id), + BOARD_ID2VARFG(board_id), rev, BOARD_ID2BOM(board_id)); } @@ -257,25 +534,15 @@ 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; - bool ret = false; - - 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: - ret = true; - break; - + case STM32MP151D_PART_NB: + case STM32MP151F_PART_NB: + return true; default: - break; + return false; } - - return ret; } /* Return true when device is in closed state */ @@ -283,12 +550,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) @@ -308,13 +590,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(); } @@ -336,32 +612,82 @@ 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 ((flags & IWDG_DISABLE_ON_STOP) != 0U) { - otp |= BIT(iwdg_inst + HW2_OTP_IWDG_FZ_STOP_POS); + if (stm32_get_otp_value(HW2_OTP, &otp_value) != 0) { + panic(); + } + + 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; } return BSEC_OK; } #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 TZPC_DECPROT_S_RW; + case DECPROT_NS_R_S_W: + return TZPC_DECPROT_NS_R_S_W; + case DECPROT_MCU_ISOLATION: + return TZPC_DECPROT_MCU_ISOLATION; + case DECPROT_NS_RW: + return TZPC_DECPROT_NS_RW; + default: + panic(); + } +} + +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; +} diff --git a/plat/st/stm32mp1/stm32mp1_scmi.c b/plat/st/stm32mp1/stm32mp1_scmi.c new file mode 100644 index 000000000..c1e91538f --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_scmi.c @@ -0,0 +1,582 @@ +/* + * 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 + +#define TIMEOUT_US_1MS 1000U + +#define SCMI_CLOCK_NAME_SIZE 16U +#define SCMI_RD_NAME_SIZE 16U + +/* + * struct stm32_scmi_clk - Data for the exposed clock + * @clock_id: Clock identifier in RCC clock driver + * @name: Clock string ID exposed to agent + * @enabled: State of the SCMI clock + */ +struct stm32_scmi_clk { + unsigned long clock_id; + const char *name; + bool enabled; +}; + +/* + * struct stm32_scmi_rd - Data for the exposed reset controller + * @reset_id: Reset identifier in RCC reset driver + * @name: Reset string ID exposed to agent + */ +struct stm32_scmi_rd { + unsigned long reset_id; + const char *name; +}; + +/* Locate all non-secure SMT message buffers in last page of SYSRAM */ +#define SMT_BUFFER_BASE STM32MP_NS_SYSRAM_BASE +#define SMT_SLOT_SIZE 0x200U +#define SMT_BUFFER0_BASE SMT_BUFFER_BASE +#define SMT_BUFFER1_BASE (SMT_BUFFER_BASE + SMT_SLOT_SIZE) +#define SMT_BUFFER_END (SMT_BUFFER1_BASE + SMT_BUF_SLOT_SIZE) + +CASSERT(SMT_BUFFER_END < (STM32MP_NS_SYSRAM_BASE + STM32MP_NS_SYSRAM_SIZE), + assert_scmi_shm_fits_in_non_secure_sysram); + +static struct scmi_msg_channel scmi_channel[] = { + [0] = { + .shm_addr = SMT_BUFFER0_BASE, + .shm_size = SMT_BUF_SLOT_SIZE, + }, + [1] = { + .shm_addr = SMT_BUFFER1_BASE, + .shm_size = SMT_BUF_SLOT_SIZE, + }, +}; + +struct scmi_msg_channel *plat_scmi_get_channel(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(scmi_channel)); + + return &scmi_channel[agent_id]; +} + +#define CLOCK_CELL(_scmi_id, _id, _name, _init_enabled) \ + [_scmi_id] = { \ + .clock_id = _id, \ + .name = _name, \ + .enabled = _init_enabled, \ + } + +static struct stm32_scmi_clk stm32_scmi0_clock[] = { + CLOCK_CELL(CK_SCMI0_HSE, CK_HSE, "ck_hse", true), + CLOCK_CELL(CK_SCMI0_HSI, CK_HSI, "ck_hsi", true), + CLOCK_CELL(CK_SCMI0_CSI, CK_CSI, "ck_csi", true), + CLOCK_CELL(CK_SCMI0_LSE, CK_LSE, "ck_lse", true), + CLOCK_CELL(CK_SCMI0_LSI, CK_LSI, "ck_lsi", true), + CLOCK_CELL(CK_SCMI0_PLL2_Q, PLL2_Q, "pll2_q", true), + CLOCK_CELL(CK_SCMI0_PLL2_R, PLL2_R, "pll2_r", true), + CLOCK_CELL(CK_SCMI0_MPU, CK_MPU, "ck_mpu", true), + CLOCK_CELL(CK_SCMI0_AXI, CK_AXI, "ck_axi", true), + CLOCK_CELL(CK_SCMI0_BSEC, BSEC, "bsec", true), + CLOCK_CELL(CK_SCMI0_CRYP1, CRYP1, "cryp1", false), + CLOCK_CELL(CK_SCMI0_GPIOZ, GPIOZ, "gpioz", false), + CLOCK_CELL(CK_SCMI0_HASH1, HASH1, "hash1", false), + CLOCK_CELL(CK_SCMI0_I2C4, I2C4_K, "i2c4_k", false), + CLOCK_CELL(CK_SCMI0_I2C6, I2C6_K, "i2c6_k", false), + CLOCK_CELL(CK_SCMI0_IWDG1, IWDG1, "iwdg1", false), + CLOCK_CELL(CK_SCMI0_RNG1, RNG1_K, "rng1_k", true), + CLOCK_CELL(CK_SCMI0_RTC, RTC, "ck_rtc", true), + CLOCK_CELL(CK_SCMI0_RTCAPB, RTCAPB, "rtcapb", true), + CLOCK_CELL(CK_SCMI0_SPI6, SPI6_K, "spi6_k", false), + CLOCK_CELL(CK_SCMI0_USART1, USART1_K, "usart1_k", false), +}; + +static struct stm32_scmi_clk stm32_scmi1_clock[] = { + CLOCK_CELL(CK_SCMI1_PLL3_Q, PLL3_Q, "pll3_q", true), + CLOCK_CELL(CK_SCMI1_PLL3_R, PLL3_R, "pll3_r", true), + CLOCK_CELL(CK_SCMI1_MCU, CK_MCU, "ck_mcu", false), +}; + +#define RESET_CELL(_scmi_id, _id, _name) \ + [_scmi_id] = { \ + .reset_id = _id, \ + .name = _name, \ + } + +static struct stm32_scmi_rd stm32_scmi0_reset_domain[] = { + RESET_CELL(RST_SCMI0_SPI6, SPI6_R, "spi6"), + RESET_CELL(RST_SCMI0_I2C4, I2C4_R, "i2c4"), + RESET_CELL(RST_SCMI0_I2C6, I2C6_R, "i2c6"), + RESET_CELL(RST_SCMI0_USART1, USART1_R, "usart1"), + RESET_CELL(RST_SCMI0_STGEN, STGEN_R, "stgen"), + RESET_CELL(RST_SCMI0_GPIOZ, GPIOZ_R, "gpioz"), + RESET_CELL(RST_SCMI0_CRYP1, CRYP1_R, "cryp1"), + RESET_CELL(RST_SCMI0_HASH1, HASH1_R, "hash1"), + RESET_CELL(RST_SCMI0_RNG1, RNG1_R, "rng1"), + RESET_CELL(RST_SCMI0_MDMA, MDMA_R, "mdma"), + RESET_CELL(RST_SCMI0_MCU, MCU_R, "mcu"), +}; + +struct scmi_agent_resources { + struct stm32_scmi_clk *clock; + size_t clock_count; + struct stm32_scmi_rd *rd; + size_t rd_count; +}; + +static const struct scmi_agent_resources agent_resources[] = { + [0] = { + .clock = stm32_scmi0_clock, + .clock_count = ARRAY_SIZE(stm32_scmi0_clock), + .rd = stm32_scmi0_reset_domain, + .rd_count = ARRAY_SIZE(stm32_scmi0_reset_domain), + }, + [1] = { + .clock = stm32_scmi1_clock, + .clock_count = ARRAY_SIZE(stm32_scmi1_clock), + }, +}; + +static const struct scmi_agent_resources *find_resource(unsigned int agent_id) +{ + assert(agent_id < ARRAY_SIZE(agent_resources)); + + return &agent_resources[agent_id]; +} + +#if ENABLE_ASSERTIONS +static size_t plat_scmi_protocol_count_paranoid(void) +{ + unsigned int n = 0U; + unsigned int count = 0U; + + for (n = 0U; n < ARRAY_SIZE(agent_resources); n++) { + if (agent_resources[n].clock_count) { + count++; + break; + } + } + + for (n = 0U; n < ARRAY_SIZE(agent_resources); n++) { + if (agent_resources[n].rd_count) { + count++; + break; + } + } + + return count; +} +#endif + +static const char vendor[] = "ST"; +static const char sub_vendor[] = ""; + +const char *plat_scmi_vendor_name(void) +{ + return vendor; +} + +const char *plat_scmi_sub_vendor_name(void) +{ + return sub_vendor; +} + +/* Currently supporting Clocks and Reset Domains */ +static const uint8_t plat_protocol_list[] = { + SCMI_PROTOCOL_ID_CLOCK, + SCMI_PROTOCOL_ID_RESET_DOMAIN, + 0U /* Null termination */ +}; + +size_t plat_scmi_protocol_count(void) +{ + const size_t count = ARRAY_SIZE(plat_protocol_list) - 1U; + + assert(count == plat_scmi_protocol_count_paranoid()); + + return count; +} + +const uint8_t *plat_scmi_protocol_list(unsigned int agent_id __unused) +{ + assert(plat_scmi_protocol_count_paranoid() == + (ARRAY_SIZE(plat_protocol_list) - 1U)); + + return plat_protocol_list; +} + +/* + * Platform SCMI clocks + */ +static struct stm32_scmi_clk *find_clock(unsigned int agent_id, + unsigned int scmi_id) +{ + const struct scmi_agent_resources *resource = find_resource(agent_id); + size_t n = 0U; + + if (resource != NULL) { + for (n = 0U; n < resource->clock_count; n++) { + if (n == scmi_id) { + return &resource->clock[n]; + } + } + } + + return NULL; +} + +size_t plat_scmi_clock_count(unsigned int agent_id) +{ + const struct scmi_agent_resources *resource = find_resource(agent_id); + + if (resource == NULL) { + return 0U; + } + + return resource->clock_count; +} + +const char *plat_scmi_clock_get_name(unsigned int agent_id, + unsigned int scmi_id) +{ + struct stm32_scmi_clk *clock = find_clock(agent_id, scmi_id); + + if ((clock == NULL) || + !stm32mp_nsec_can_access_clock(clock->clock_id)) { + return NULL; + } + + return clock->name; +} + +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); + + 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: + /* + * 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] = stm32mp_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) +{ + int ret; + /* find_rd() returns NULL if clock exists for denied the agent */ + 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: + ret = stm32mp1_set_opp_khz(rate / 1000UL); + if (ret != 0) { + return SCMI_INVALID_PARAMETERS; + } + break; + default: + if (rate != stm32mp_clk_get_rate(clock->clock_id)) { + return SCMI_INVALID_PARAMETERS; + } + break; + } + + return SCMI_SUCCESS; +} + +unsigned long plat_scmi_clock_get_rate(unsigned int agent_id, + unsigned int scmi_id) +{ + struct stm32_scmi_clk *clock = find_clock(agent_id, scmi_id); + + if ((clock == NULL) || + !stm32mp_nsec_can_access_clock(clock->clock_id)) { + return 0U; + } + + return stm32mp_clk_get_rate(clock->clock_id); +} + +int32_t plat_scmi_clock_get_state(unsigned int agent_id, unsigned int scmi_id) +{ + struct stm32_scmi_clk *clock = find_clock(agent_id, scmi_id); + + if ((clock == NULL) || + !stm32mp_nsec_can_access_clock(clock->clock_id)) { + return 0U; + } + + return (int32_t)clock->enabled; +} + +int32_t plat_scmi_clock_set_state(unsigned int agent_id, unsigned int scmi_id, + bool enable_not_disable) +{ + 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; + } + + if (enable_not_disable) { + if (!clock->enabled) { + VERBOSE("SCMI clock %u enable\n", scmi_id); + stm32mp_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); + clock->enabled = false; + } + } + + return SCMI_SUCCESS; +} + +/* + * Platform SCMI reset domains + */ +static struct stm32_scmi_rd *find_rd(unsigned int agent_id, + unsigned int scmi_id) +{ + const struct scmi_agent_resources *resource = find_resource(agent_id); + size_t n; + + if (resource != NULL) { + for (n = 0U; n < resource->rd_count; n++) { + if (n == scmi_id) { + return &resource->rd[n]; + } + } + } + + return NULL; +} + +const char *plat_scmi_rd_get_name(unsigned int agent_id, unsigned int scmi_id) +{ + const struct stm32_scmi_rd *rd = find_rd(agent_id, scmi_id); + + if (rd == NULL) { + return NULL; + } + + return rd->name; +} + +size_t plat_scmi_rd_count(unsigned int agent_id) +{ + const struct scmi_agent_resources *resource = find_resource(agent_id); + + if (resource == NULL) { + return 0U; + } + + return resource->rd_count; +} + +int32_t plat_scmi_rd_autonomous(unsigned int agent_id, unsigned int scmi_id, + uint32_t state) +{ + const struct stm32_scmi_rd *rd = find_rd(agent_id, scmi_id); + + if (rd == NULL) { + return SCMI_NOT_FOUND; + } + + if (!stm32mp_nsec_can_access_reset(rd->reset_id)) { + return SCMI_DENIED; + } + + /* Supports only reset with context loss */ + if (state != 0U) { + return SCMI_NOT_SUPPORTED; + } + + VERBOSE("SCMI reset %lu cycle\n", rd->reset_id); + + if (stm32mp_reset_assert_to(rd->reset_id, TIMEOUT_US_1MS)) { + return SCMI_HARDWARE_ERROR; + } + + if (stm32mp_reset_deassert_to(rd->reset_id, TIMEOUT_US_1MS)) { + return SCMI_HARDWARE_ERROR; + } + + return SCMI_SUCCESS; +} + +int32_t plat_scmi_rd_set_state(unsigned int agent_id, unsigned int scmi_id, + bool assert_not_deassert) +{ + const struct stm32_scmi_rd *rd = find_rd(agent_id, scmi_id); + + if (rd == NULL) { + return SCMI_NOT_FOUND; + } + + if (!stm32mp_nsec_can_access_reset(rd->reset_id)) { + return SCMI_DENIED; + } + + if (assert_not_deassert) { + VERBOSE("SCMI reset %lu set\n", rd->reset_id); + stm32mp_reset_set(rd->reset_id); + } else { + VERBOSE("SCMI reset %lu release\n", rd->reset_id); + stm32mp_reset_release(rd->reset_id); + } + + return SCMI_SUCCESS; +} + +/* + * Initialize platform SCMI resources + */ +void stm32mp1_init_scmi_server(void) +{ + size_t i; + size_t j; + + for (i = 0U; i < ARRAY_SIZE(scmi_channel); i++) { + scmi_smt_init_agent_channel(&scmi_channel[i]); + } + + for (i = 0U; i < ARRAY_SIZE(agent_resources); i++) { + const struct scmi_agent_resources *res = &agent_resources[i]; + + for (j = 0U; j < res->clock_count; j++) { + struct stm32_scmi_clk *clk = &res->clock[j]; + + if ((clk->name == NULL) || + (strlen(clk->name) >= SCMI_CLOCK_NAME_SIZE)) { + ERROR("Invalid SCMI clock name\n"); + panic(); + } + + /* 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); + } + } + + for (j = 0U; j < res->rd_count; j++) { + struct stm32_scmi_rd *rd = &res->rd[j]; + + if ((rd->name == NULL) || + (strlen(rd->name) >= SCMI_RD_NAME_SIZE)) { + ERROR("Invalid SCMI reset domain name\n"); + panic(); + } + } + } +} + +/* + * 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 61db2e7c7..41762fa6b 100644 --- a/plat/st/stm32mp1/stm32mp1_security.c +++ b/plat/st/stm32mp1/stm32mp1_security.c @@ -86,8 +86,11 @@ static void init_tzc400(void) TZC_REGION_NSEC_ALL_ACCESS_RDWR); #endif - /* Raise an exception if a NS device tries to access secure memory */ - tzc400_set_action(TZC_ACTION_ERR); + /* + * Raise an interrupt (secure FIQ) if a NS device tries to access + * secure memory + */ + tzc400_set_action(TZC_ACTION_INT); tzc400_enable_filters(); } diff --git a/plat/st/stm32mp1/stm32mp1_shared_resources.c b/plat/st/stm32mp1/stm32mp1_shared_resources.c new file mode 100644 index 000000000..232fbebdb --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_shared_resources.c @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +static bool registering_locked; +static int8_t gpioz_nbpin = -1; + +/* + * Shared peripherals and resources. + * Defines resource that may be non secure, secure or shared. + * May be a device, a bus, a clock, a memory. + * Shared peripherals and resources registration + * + * Each resource assignation is stored in a table. The state defaults + * to SHRES_UNREGISTERED if the resource is not explicitly assigned. + * + * Each IO of the GPIOZ IO can be secure or non-secure. + */ +#define SHRES_NON_SECURE 2 +#define SHRES_SECURE 1 +#define SHRES_UNREGISTERED 0 + +static uint8_t shres_state[STM32MP1_SHRES_COUNT]; + +static const char *shres2str_id_tbl[STM32MP1_SHRES_COUNT] = { + [STM32MP1_SHRES_GPIOZ(0)] = "GPIOZ0", + [STM32MP1_SHRES_GPIOZ(1)] = "GPIOZ1", + [STM32MP1_SHRES_GPIOZ(2)] = "GPIOZ2", + [STM32MP1_SHRES_GPIOZ(3)] = "GPIOZ3", + [STM32MP1_SHRES_GPIOZ(4)] = "GPIOZ4", + [STM32MP1_SHRES_GPIOZ(5)] = "GPIOZ5", + [STM32MP1_SHRES_GPIOZ(6)] = "GPIOZ6", + [STM32MP1_SHRES_GPIOZ(7)] = "GPIOZ7", + [STM32MP1_SHRES_IWDG1] = "IWDG1", + [STM32MP1_SHRES_USART1] = "USART1", + [STM32MP1_SHRES_SPI6] = "SPI6", + [STM32MP1_SHRES_I2C4] = "I2C4", + [STM32MP1_SHRES_RNG1] = "RNG1", + [STM32MP1_SHRES_HASH1] = "HASH1", + [STM32MP1_SHRES_CRYP1] = "CRYP1", + [STM32MP1_SHRES_I2C6] = "I2C6", + [STM32MP1_SHRES_RTC] = "RTC", + [STM32MP1_SHRES_MCU] = "MCU", + [STM32MP1_SHRES_MDMA] = "MDMA", + [STM32MP1_SHRES_PLL3] = "PLL3", +}; + +static const char *shres2str_id(unsigned int id) +{ + return shres2str_id_tbl[id]; +} + +static const char *shres2str_state_tbl[4] = { + [SHRES_UNREGISTERED] = "unregistered", + [SHRES_NON_SECURE] = "non-secure", + [SHRES_SECURE] = "secure", +}; + +static const char *shres2str_state(unsigned int id) +{ + return shres2str_state_tbl[id]; +} + +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(); +} + +static unsigned int get_gpioz_nbpin(void) +{ + if (gpioz_nbpin < 0) { + gpioz_nbpin = (int8_t)fdt_get_gpioz_nbpins_from_dt(); + assert((gpioz_nbpin == 0) || + (gpioz_nbpin == STM32MP_GPIOZ_PIN_MAX_COUNT)); + } + + return (unsigned int)gpioz_nbpin; +} + +static void register_periph(unsigned int id, unsigned int state) +{ + assert((id < STM32MP1_SHRES_COUNT) && + ((state == SHRES_SECURE) || (state == SHRES_NON_SECURE))); + + if (registering_locked) { + if (shres_state[id] == state) { + return; + } + + panic(); + } + + if ((shres_state[id] != SHRES_UNREGISTERED) && + (shres_state[id] != state)) { + VERBOSE("Cannot change %s from %s to %s\n", + shres2str_id(id), + shres2str_state(shres_state[id]), + shres2str_state(state)); + panic(); + } + + shres_state[id] = (uint8_t)state; + + if (shres_state[id] == SHRES_UNREGISTERED) { + VERBOSE("Register %s as %s\n", + shres2str_id(id), shres2str_state(state)); + } + + switch (id) { + case STM32MP1_SHRES_GPIOZ(0) ... STM32MP1_SHRES_GPIOZ(7): + if ((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(); + } + break; + default: + break; + } + + /* Explore clock tree to lock dependencies */ + if (state == SHRES_SECURE) { + switch (id) { + case STM32MP1_SHRES_GPIOZ(0) ... STM32MP1_SHRES_GPIOZ(7): + stm32mp1_register_clock_parents_secure(GPIOZ); + break; + case STM32MP1_SHRES_IWDG1: + stm32mp1_register_clock_parents_secure(IWDG1); + break; + case STM32MP1_SHRES_USART1: + stm32mp1_register_clock_parents_secure(USART1_K); + break; + case STM32MP1_SHRES_SPI6: + stm32mp1_register_clock_parents_secure(SPI6_K); + break; + case STM32MP1_SHRES_I2C4: + stm32mp1_register_clock_parents_secure(I2C4_K); + break; + case STM32MP1_SHRES_RNG1: + stm32mp1_register_clock_parents_secure(RNG1_K); + break; + case STM32MP1_SHRES_HASH1: + stm32mp1_register_clock_parents_secure(HASH1); + break; + case STM32MP1_SHRES_CRYP1: + stm32mp1_register_clock_parents_secure(CRYP1); + break; + case STM32MP1_SHRES_I2C6: + stm32mp1_register_clock_parents_secure(I2C6_K); + break; + case STM32MP1_SHRES_RTC: + stm32mp1_register_clock_parents_secure(RTC); + break; + default: + /* No expected resource dependency */ + break; + } + } +} + +static bool stm32mp1_mckprot_resource(unsigned int id) +{ + switch (id) { + case STM32MP1_SHRES_MCU: + case STM32MP1_SHRES_PLL3: + return true; + default: + return false; + } +} + +/* Register resource by ID */ +void stm32mp_register_secure_periph(unsigned int id) +{ + register_periph(id, SHRES_SECURE); +} + +void stm32mp_register_non_secure_periph(unsigned int id) +{ + register_periph(id, SHRES_NON_SECURE); +} + +/* Register resource by IO memory base address */ +static void register_periph_iomem(uintptr_t base, unsigned int state) +{ + unsigned int id; + + switch (base) { + case IWDG1_BASE: + id = STM32MP1_SHRES_IWDG1; + break; + case USART1_BASE: + id = STM32MP1_SHRES_USART1; + break; + case SPI6_BASE: + id = STM32MP1_SHRES_SPI6; + break; + case I2C4_BASE: + id = STM32MP1_SHRES_I2C4; + break; + case I2C6_BASE: + id = STM32MP1_SHRES_I2C6; + break; + case RTC_BASE: + id = STM32MP1_SHRES_RTC; + break; + case RNG1_BASE: + id = STM32MP1_SHRES_RNG1; + break; + case CRYP1_BASE: + id = STM32MP1_SHRES_CRYP1; + break; + case HASH1_BASE: + id = STM32MP1_SHRES_HASH1; + break; + + case GPIOA_BASE: + case GPIOB_BASE: + case GPIOC_BASE: + case GPIOD_BASE: + case GPIOE_BASE: + case GPIOF_BASE: + case GPIOG_BASE: + case GPIOH_BASE: + case GPIOI_BASE: + case GPIOJ_BASE: + case GPIOK_BASE: + case USART2_BASE: + case USART3_BASE: + case UART4_BASE: + case UART5_BASE: + case USART6_BASE: + case UART7_BASE: + case UART8_BASE: + case IWDG2_BASE: + /* Allow drivers to register some non-secure resources */ + VERBOSE("IO for non-secure resource 0x%x\n", + (unsigned int)base); + if (state != SHRES_NON_SECURE) { + panic(); + } + + return; + + default: + panic(); + break; + } + + register_periph(id, state); +} + +void stm32mp_register_secure_periph_iomem(uintptr_t base) +{ + register_periph_iomem(base, SHRES_SECURE); +} + +void stm32mp_register_non_secure_periph_iomem(uintptr_t base) +{ + register_periph_iomem(base, SHRES_NON_SECURE); +} + +/* Register GPIO resource */ +void stm32mp_register_secure_gpio(unsigned int bank, unsigned int pin) +{ + switch (bank) { + case GPIO_BANK_Z: + register_periph(STM32MP1_SHRES_GPIOZ(pin), SHRES_SECURE); + break; + default: + ERROR("GPIO bank %u cannot be secured\n", bank); + panic(); + } +} + +void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin) +{ + switch (bank) { + case GPIO_BANK_Z: + register_periph(STM32MP1_SHRES_GPIOZ(pin), SHRES_NON_SECURE); + break; + default: + break; + } +} + +void stm32mp1_register_etzpc_decprot(unsigned int id, + enum etzpc_decprot_attributes attr) +{ + unsigned int state = SHRES_SECURE; + unsigned int id_shres; + + switch (attr) { + case TZPC_DECPROT_S_RW: + break; + case TZPC_DECPROT_NS_R_S_W: + case TZPC_DECPROT_MCU_ISOLATION: + case TZPC_DECPROT_NS_RW: + state = SHRES_NON_SECURE; + break; + default: + panic(); + } + + switch (id) { + case STM32MP1_ETZPC_STGENC_ID: + case STM32MP1_ETZPC_BKPSRAM_ID: + case STM32MP1_ETZPC_DDRCTRL_ID: + case STM32MP1_ETZPC_DDRPHYC_ID: + /* We assume these must always be assigned to secure world */ + if (state != SHRES_SECURE) { + panic(); + } + break; + default: + id_shres = decprot2shres(id); + if (id_shres == SHRES_INVALID) { + if (state == SHRES_SECURE) { + panic(); + } + } else { + register_periph(id_shres, state); + } + break; + } +} + +/* Get resource state: these accesses lock the registering support */ +static void lock_registering(void) +{ + registering_locked = true; +} + +bool stm32mp1_periph_is_non_secure(unsigned long id) +{ + lock_registering(); + + /* Resource not registered are assumed non-secure */ + return (shres_state[id] == SHRES_NON_SECURE) || + (shres_state[id] == SHRES_UNREGISTERED); +} + +bool stm32mp1_periph_is_secure(unsigned long id) +{ + lock_registering(); + + return shres_state[id] == SHRES_SECURE; +} + +bool stm32mp_gpio_bank_is_shared(unsigned int bank) +{ + unsigned int non_secure = 0; + unsigned int i; + + lock_registering(); + + if (bank != GPIO_BANK_Z) { + return false; + } + + for (i = 0U; i < get_gpioz_nbpin(); i++) { + if (!stm32mp1_periph_is_secure(STM32MP1_SHRES_GPIOZ(i))) { + non_secure++; + } + } + + return (non_secure != 0) && (non_secure < get_gpioz_nbpin()); +} + +bool stm32mp_gpio_bank_is_non_secure(unsigned int bank) +{ + unsigned int non_secure = 0; + unsigned int i; + + lock_registering(); + + if (bank != GPIO_BANK_Z) { + return true; + } + + for (i = 0U; i < get_gpioz_nbpin(); i++) { + if (!stm32mp1_periph_is_secure(STM32MP1_SHRES_GPIOZ(i))) { + non_secure++; + } + } + + return non_secure == get_gpioz_nbpin(); +} + +static bool stm32mp_gpio_bank_is_secure(unsigned int bank) +{ + unsigned int secure = 0; + unsigned int i; + + lock_registering(); + + if (bank != GPIO_BANK_Z) { + return false; + } + + for (i = 0U; i < get_gpioz_nbpin(); i++) { + if (stm32mp1_periph_is_secure(STM32MP1_SHRES_GPIOZ(i))) { + secure++; + } + } + + return secure == get_gpioz_nbpin(); +} + +CASSERT((CK_HSE == 0) && + ((CK_HSE + 1) == CK_CSI) && + ((CK_HSE + 2) == CK_LSI) && + ((CK_HSE + 3) == CK_LSE) && + ((CK_HSE + 4) == CK_HSI) && + ((CK_HSE + 5) == CK_HSE_DIV2) && + ((PLL1_P + 1) == PLL1_Q) && + ((PLL1_P + 2) == PLL1_R) && + ((PLL1_P + 3) == PLL2_P) && + ((PLL1_P + 4) == PLL2_Q) && + ((PLL1_P + 5) == PLL2_R) && + ((PLL1_P + 6) == PLL3_P) && + ((PLL1_P + 7) == PLL3_Q) && + ((PLL1_P + 8) == PLL3_R), + assert_clock_id_not_as_expected); + +bool stm32mp_nsec_can_access_clock(unsigned long clock_id) +{ + enum stm32mp_shres shres_id = STM32MP1_SHRES_COUNT; + + /* Oscillators and PLLs are visible from non-secure world */ + if ((clock_id <= CK_HSE_DIV2) || + ((clock_id >= PLL1_P) && (clock_id <= PLL3_R))) { + return true; + } + + switch (clock_id) { + case BSEC: + case CK_AXI: + case CK_MPU: + case RTCAPB: + return true; + case GPIOZ: + return !stm32mp_gpio_bank_is_secure(GPIO_BANK_Z); + case SPI6_K: + shres_id = STM32MP1_SHRES_SPI6; + break; + case I2C4_K: + shres_id = STM32MP1_SHRES_I2C4; + break; + case I2C6_K: + shres_id = STM32MP1_SHRES_I2C6; + break; + case USART1_K: + shres_id = STM32MP1_SHRES_USART1; + break; + case IWDG1: + shres_id = STM32MP1_SHRES_IWDG1; + break; + case CRYP1: + shres_id = STM32MP1_SHRES_CRYP1; + break; + case HASH1: + shres_id = STM32MP1_SHRES_HASH1; + break; + case RNG1_K: + shres_id = STM32MP1_SHRES_RNG1; + break; + case RTC: + shres_id = STM32MP1_SHRES_RTC; + break; + case CK_MCU: + shres_id = STM32MP1_SHRES_MCU; + break; + default: + return false; + } + + return !stm32mp1_periph_is_secure(shres_id); +} + +bool stm32mp_nsec_can_access_reset(unsigned int reset_id) +{ + enum stm32mp_shres shres_id = STM32MP1_SHRES_COUNT; + + switch (reset_id) { + case GPIOZ_R: + return stm32mp_gpio_bank_is_non_secure(GPIO_BANK_Z); + case SPI6_R: + shres_id = STM32MP1_SHRES_SPI6; + break; + case I2C4_R: + shres_id = STM32MP1_SHRES_I2C4; + break; + case I2C6_R: + shres_id = STM32MP1_SHRES_I2C6; + break; + case USART1_R: + shres_id = STM32MP1_SHRES_USART1; + break; + case CRYP1_R: + shres_id = STM32MP1_SHRES_CRYP1; + break; + case HASH1_R: + shres_id = STM32MP1_SHRES_HASH1; + break; + case RNG1_R: + shres_id = STM32MP1_SHRES_RNG1; + break; + case MDMA_R: + shres_id = STM32MP1_SHRES_MDMA; + break; + case MCU_R: + shres_id = STM32MP1_SHRES_MCU; + break; + default: + return false; + } + + return !stm32mp1_periph_is_secure(shres_id); +} + +/* ETZPC configuration at drivers initialization completion */ +static enum etzpc_decprot_attributes decprot_periph_attr(unsigned int id) +{ + switch (id) { + case STM32MP1_SHRES_GPIOZ(0) ... STM32MP1_SHRES_GPIOZ(7): + assert((id - STM32MP1_SHRES_GPIOZ(0)) < get_gpioz_nbpin()); + return TZPC_DECPROT_NS_RW; + default: + if (!stm32mp1_periph_is_secure(id)) { + return TZPC_DECPROT_NS_RW; + } + + return TZPC_DECPROT_S_RW; + } +} + +static bool check_decprot(unsigned int id, enum etzpc_decprot_attributes exp) +{ + enum etzpc_decprot_attributes cur = etzpc_get_decprot(id); + + if (cur == exp) { + return true; + } + + switch (exp) { + case TZPC_DECPROT_NS_RW: + if (cur == TZPC_DECPROT_S_RW) { + INFO("ETZPC: %s (%d) could be non secure\n", + decprot2str(id), id); + } + return true; + + case TZPC_DECPROT_S_RW: + ERROR("ETZPC: %s (%d) expected secure but DECPROT = %d\n", + decprot2str(id), id, cur); + break; + + case TZPC_DECPROT_NS_R_S_W: + case TZPC_DECPROT_MCU_ISOLATION: + default: + panic(); + } + + return false; +} + +static void check_etzpc_secure_configuration(void) +{ + bool error = false; + + assert(registering_locked); + + error |= !check_decprot(STM32MP1_ETZPC_STGENC_ID, TZPC_DECPROT_S_RW); + + error |= !check_decprot(STM32MP1_ETZPC_BKPSRAM_ID, TZPC_DECPROT_S_RW); + + error |= !check_decprot(STM32MP1_ETZPC_USART1_ID, + decprot_periph_attr(STM32MP1_SHRES_USART1)); + + error |= !check_decprot(STM32MP1_ETZPC_SPI6_ID, + decprot_periph_attr(STM32MP1_SHRES_SPI6)); + + error |= !check_decprot(STM32MP1_ETZPC_I2C4_ID, + decprot_periph_attr(STM32MP1_SHRES_I2C4)); + + error |= !check_decprot(STM32MP1_ETZPC_RNG1_ID, + decprot_periph_attr(STM32MP1_SHRES_RNG1)); + + error |= !check_decprot(STM32MP1_ETZPC_HASH1_ID, + decprot_periph_attr(STM32MP1_SHRES_HASH1)); + + error |= !check_decprot(STM32MP1_ETZPC_CRYP1_ID, + decprot_periph_attr(STM32MP1_SHRES_CRYP1)); + + error |= !check_decprot(STM32MP1_ETZPC_DDRCTRL_ID, TZPC_DECPROT_S_RW); + + error |= !check_decprot(STM32MP1_ETZPC_DDRPHYC_ID, TZPC_DECPROT_S_RW); + + error |= !check_decprot(STM32MP1_ETZPC_I2C6_ID, + decprot_periph_attr(STM32MP1_SHRES_I2C6)); + + if (error) { + panic(); + } +} + +static void check_rcc_secure_configuration(void) +{ + uint32_t n; + uint32_t error = 0; + bool mckprot = stm32mp1_rcc_is_mckprot(); + bool secure = stm32mp1_rcc_is_secure(); + + for (n = 0; n < ARRAY_SIZE(shres_state); n++) { + if (shres_state[n] == SHRES_SECURE) { + if ((stm32mp1_mckprot_resource(n) && (!mckprot)) || + !secure) { + ERROR("RCC %s MCKPROT %s and %s (%u) secure\n", + secure ? "secure" : "non secure", + mckprot ? "set" : "not set", + shres2str_id(n), n); + error++; + } + } + } + + if (error != 0U) { + panic(); + } +} + +static void check_gpio_secure_configuration(void) +{ + uint32_t pin; + + for (pin = 0U; pin < get_gpioz_nbpin(); pin++) { + unsigned int id = STM32MP1_SHRES_GPIOZ(pin); + bool secure = stm32mp1_periph_is_secure(id); + + set_gpio_secure_cfg(GPIO_BANK_Z, pin, secure); + } +} + +void stm32mp_lock_periph_registering(void) +{ + uint32_t __unused id; + + registering_locked = true; + + for (id = 0; id < STM32MP1_SHRES_COUNT; id++) { + uint8_t state = shres_state[id]; + + assert((state == SHRES_SECURE) || + (state == SHRES_NON_SECURE) || + (state == SHRES_UNREGISTERED)); + + if (state == SHRES_SECURE) { + INFO("stm32mp %s (%u): %s\n", + shres2str_id(id), id, + state == SHRES_SECURE ? "Secure" : + state == SHRES_NON_SECURE ? "Non-secure" : + state == SHRES_UNREGISTERED ? "Unregistered" : + ""); + } + } + + stm32mp1_dump_clocks_state(); + + check_rcc_secure_configuration(); + check_etzpc_secure_configuration(); + check_gpio_secure_configuration(); +} diff --git a/plat/st/stm32mp1/stm32mp1_syscfg.c b/plat/st/stm32mp1/stm32mp1_syscfg.c index 2fd06f38a..1585590bc 100644 --- a/plat/st/stm32mp1/stm32mp1_syscfg.c +++ b/plat/st/stm32mp1/stm32mp1_syscfg.c @@ -7,15 +7,16 @@ #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 @@ -53,6 +54,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,20 +64,20 @@ void stm32mp1_syscfg_init(void) { uint32_t bootr; - uint32_t otp = 0; + uint32_t otp_value; uint32_t vdd_voltage; - uintptr_t syscfg_base = dt_get_syscfg_base(); + bool product_below_2v5; /* * Interconnect update : select master using the port 1. * LTDC = AXI_M9. */ - mmio_write_32(syscfg_base + SYSCFG_ICNR, SYSCFG_ICNR_AXI_M9); + mmio_write_32(SYSCFG_BASE + SYSCFG_ICNR, SYSCFG_ICNR_AXI_M9); /* Disable Pull-Down for boot pin connected to VDD */ - bootr = mmio_read_32(syscfg_base + SYSCFG_BOOTR) & + bootr = mmio_read_32(SYSCFG_BASE + SYSCFG_BOOTR) & SYSCFG_BOOTR_BOOT_MASK; - mmio_clrsetbits_32(syscfg_base + SYSCFG_BOOTR, SYSCFG_BOOTR_BOOTPD_MASK, + mmio_clrsetbits_32(SYSCFG_BASE + SYSCFG_BOOTR, SYSCFG_BOOTR_BOOTPD_MASK, bootr << SYSCFG_BOOTR_BOOTPD_SHIFT); /* @@ -92,11 +95,11 @@ 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(); @@ -105,18 +108,18 @@ void stm32mp1_syscfg_init(void) if (vdd_voltage == 0U) { WARN("VDD unknown"); } else if (vdd_voltage < 2700000U) { - mmio_write_32(syscfg_base + SYSCFG_IOCTRLSETR, + mmio_write_32(SYSCFG_BASE + SYSCFG_IOCTRLSETR, SYSCFG_IOCTRLSETR_HSLVEN_TRACE | SYSCFG_IOCTRLSETR_HSLVEN_QUADSPI | SYSCFG_IOCTRLSETR_HSLVEN_ETH | 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"); @@ -129,29 +132,37 @@ void stm32mp1_syscfg_init(void) void stm32mp1_syscfg_enable_io_compensation(void) { - uintptr_t syscfg_base = dt_get_syscfg_base(); + uint64_t start; /* * 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, + mmio_setbits_32(SYSCFG_BASE + SYSCFG_CMPENSETR, SYSCFG_CMPENSETR_MPU_EN); - while ((mmio_read_32(syscfg_base + SYSCFG_CMPCR) & + 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); + mmio_clrbits_32(SYSCFG_BASE + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); } void stm32mp1_syscfg_disable_io_compensation(void) { - uintptr_t syscfg_base = dt_get_syscfg_base(); uint32_t value; /* @@ -160,21 +171,19 @@ void stm32mp1_syscfg_disable_io_compensation(void) * requested for other usages and always OFF in STANDBY. * Disable non-secure SYSCFG clock, we assume non-secure is suspended. */ - value = mmio_read_32(syscfg_base + SYSCFG_CMPCR) >> + value = mmio_read_32(SYSCFG_BASE + SYSCFG_CMPCR) >> SYSCFG_CMPCR_ANSRC_SHIFT; - mmio_clrbits_32(syscfg_base + SYSCFG_CMPCR, + mmio_clrbits_32(SYSCFG_BASE + SYSCFG_CMPCR, SYSCFG_CMPCR_RANSRC | SYSCFG_CMPCR_RAPSRC); - value = mmio_read_32(syscfg_base + SYSCFG_CMPCR) | + value = mmio_read_32(SYSCFG_BASE + SYSCFG_CMPCR) | (value << SYSCFG_CMPCR_RANSRC_SHIFT); - mmio_write_32(syscfg_base + SYSCFG_CMPCR, value); - - mmio_setbits_32(syscfg_base + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); + mmio_write_32(SYSCFG_BASE + SYSCFG_CMPCR, value | SYSCFG_CMPCR_SW_CTRL); - mmio_clrbits_32(syscfg_base + SYSCFG_CMPENSETR, + mmio_clrbits_32(SYSCFG_BASE + SYSCFG_CMPENSETR, SYSCFG_CMPENSETR_MPU_EN); - stm32mp1_clk_disable_non_secure(SYSCFG); + stm32mp1_clk_force_disable(SYSCFG); } diff --git a/plat/st/stm32mp1/stm32mp1_usb_desc.c b/plat/st/stm32mp1/stm32mp1_usb_desc.c new file mode 100644 index 000000000..3ca06922a --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_usb_desc.c @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +/* 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 */ + USBD_DFU_IF_DESC(0), /* This interface is mandatory for all devices */ + + /* Descriptor of DFU interface 0 Alternate setting 1 */ + USBD_DFU_IF_DESC(1), + + /* Descriptor of DFU interface 0 Alternate setting 2 */ + USBD_DFU_IF_DESC(2), + + /* Descriptor of DFU interface 0 Alternate setting 3 */ + USBD_DFU_IF_DESC(3), + + /* Descriptor of DFU interface 0 Alternate setting 4 */ + USBD_DFU_IF_DESC(4), + + /* Descriptor of DFU interface 0 Alternate setting 5 */ + USBD_DFU_IF_DESC(5), + + /* 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 = 0; + + for (idx = 0; idx < len; idx++) { + if (((value >> 28)) < 0xA) + pbuf[2 * idx] = (value >> 28) + '0'; + else + pbuf[2 * idx] = (value >> 28) + 'A' - 10; + value = value << 4; + pbuf[(2 * idx) + 1] = 0; + } +} + +/* + * Create the serial number string descriptor + */ +static void update_serial_num_string(void) +{ + /* serial number is set to 0*/ + uint8_t i; + 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_dfu_desc + * return Device Qualifier descriptor + * param : length : pointer data length + * return : pointer to descriptor buffer + */ +static uint8_t *stm32mp1_get_dfu_desc(uint16_t *len) +{ + *len = USB_DFU_DESC_SIZ; + return ((uint8_t *)usb_stm32mp1_config_desc + (9 * 7)); +} + +/* + * 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) + 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) +{ + stm32mp1_get_string((uint8_t *)USBD_PRODUCT_HS_STRING, + 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_HS_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_HS_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; + + if (index > (USBD_IDX_INTERFACE_STR + USBD_DESC_MAX_ITF_NUM)) + return NULL; + + switch (index) { + case 6: + stm32mp1_get_string((uint8_t *)"@Partition0 /0x00/1*256Ke", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 7: + stm32mp1_get_string((uint8_t *)"@FSBL /0x01/1*1Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 8: + stm32mp1_get_string((uint8_t *)"@Partition2 /0x02/1*1Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 9: + stm32mp1_get_string((uint8_t *)"@Partition3 /0x03/1*16Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 10: + stm32mp1_get_string((uint8_t *)"@Partition4 /0x04/1*16Me", + usb_local_string_dec, length); + ret = usb_local_string_dec; + break; + case 11: + 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_hs_config_desc = stm32mp1_get_config_desc, + .get_fs_config_desc = stm32mp1_get_config_desc, + .get_other_speed_config_desc = stm32mp1_get_config_desc, + .get_device_qualifier_desc = stm32mp1_get_qualifier_desc, + .get_dfu_desc = stm32mp1_get_dfu_desc +}; + +void stm32mp_usb_init_desc(usb_handle_t *pdev) +{ + register_platform(pdev, &dfu_desc); +} diff --git a/tools/stm32image/stm32image.c b/tools/stm32image/stm32image.c index 41024e286..209e0c9d8 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