diff --git a/.gitignore b/.gitignore index 7f8642e..78da669 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ tools/cert_create/cert_create tools/cert_create/cert_create.exe tools/doimage/doimage +tools/stm32image/*.o +tools/stm32image/stm32image +tools/stm32image/stm32image.exe # GNU GLOBAL files GPATH diff --git a/acknowledgements.rst b/acknowledgements.rst index 5686a58..4d527f4 100644 --- a/acknowledgements.rst +++ b/acknowledgements.rst @@ -16,5 +16,7 @@ Marvell International Ltd. +STMicroelectronics + Individuals ----------- diff --git a/docs/plat/stm32mp1.rst b/docs/plat/stm32mp1.rst new file mode 100644 index 0000000..9e731a4 --- /dev/null +++ b/docs/plat/stm32mp1.rst @@ -0,0 +1,82 @@ +Trusted Firmware-A for STM32MP1 +=============================== + +STM32MP1 is a microprocessor designed by STMicroelectronics +based on a dual Arm Cortex-A7. +It is an Armv7-A platform, using dedicated code from TF-A. + + +Design +------ +The STM32MP1 resets in the ROM code of the Cortex-A7. +The primary boot core (core 0) executes the boot sequence while +secondary boot core (core 1) is kept in a holding pen loop. +The ROM code boot sequence loads the TF-A binary image from boot device +to embedded SRAM. + +The TF-A image must be properly formatted with a STM32 header structure +for ROM code is able to load this image. +Tool stm32image can be used to prepend this header to the generated TF-A binary. + +At compilation step, BL2, BL32 and DTB file are linked together in a single +binary. The stm32image tool is also generated and the header is added to TF-A +binary. This binary file with header is named tf-a-stm32mp157c-ev1.stm32. +It can then be copied in the first partition of the boot device. + + +Memory mapping +~~~~~~~~~~~~~~ + +:: + + 0x00000000 +-----------------+ + | | ROM + 0x00020000 +-----------------+ + | | + | ... | + | | + 0x2FFC0000 +-----------------+ \ + | | | + | ... | | + | | | + 0x2FFD8000 +-----------------+ | + | TF-A DTB | | Embedded SRAM + 0x2FFDC000 +-----------------+ | + | BL2 | | + 0x2FFEF000 +-----------------+ | + | BL32 | | + 0x30000000 +-----------------+ / + | | + | ... | + | | + 0x40000000 +-----------------+ + | | + | | Devices + | | + 0xC0000000 +-----------------+ \ + | | | + 0xC0100000 +-----------------+ | + | BL33 | | Non-secure RAM (DDR) + | ... | | + | | | + 0xFFFFFFFF +-----------------+ / + + +Boot sequence +~~~~~~~~~~~~~ + +ROM code -> BL2 (compiled with BL2_AT_EL3) -> BL32 (SP_min) -> BL33 (U-Boot) + + +Build Instructions +------------------ + +To build: + +.. code:: bash + + make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min + +The following build options are supported: + +- ``ENABLE_STACK_PROTECTOR``: To enable the stack protection. diff --git a/drivers/st/clk/stm32mp1_clk.c b/drivers/st/clk/stm32mp1_clk.c new file mode 100644 index 0000000..7dff98b --- /dev/null +++ b/drivers/st/clk/stm32mp1_clk.c @@ -0,0 +1,1611 @@ +/* + * Copyright (C) 2018, 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 +#include +#include +#include +#include +#include + +#define MAX_HSI_HZ 64000000 + +#define TIMEOUT_200MS (plat_get_syscnt_freq2() / 5U) +#define TIMEOUT_1S plat_get_syscnt_freq2() + +#define PLLRDY_TIMEOUT TIMEOUT_200MS +#define CLKSRC_TIMEOUT TIMEOUT_200MS +#define CLKDIV_TIMEOUT TIMEOUT_200MS +#define HSIDIV_TIMEOUT TIMEOUT_200MS +#define OSCRDY_TIMEOUT TIMEOUT_1S + +enum stm32mp1_parent_id { +/* Oscillators are defined in enum stm32mp_osc_id */ + +/* Other parent source */ + _HSI_KER = NB_OSC, + _HSE_KER, + _HSE_KER_DIV2, + _CSI_KER, + _PLL1_P, + _PLL1_Q, + _PLL1_R, + _PLL2_P, + _PLL2_Q, + _PLL2_R, + _PLL3_P, + _PLL3_Q, + _PLL3_R, + _PLL4_P, + _PLL4_Q, + _PLL4_R, + _ACLK, + _PCLK1, + _PCLK2, + _PCLK3, + _PCLK4, + _PCLK5, + _HCLK6, + _HCLK2, + _CK_PER, + _CK_MPU, + _PARENT_NB, + _UNKNOWN_ID = 0xff, +}; + +enum stm32mp1_parent_sel { + _I2C46_SEL, + _UART6_SEL, + _UART24_SEL, + _UART35_SEL, + _UART78_SEL, + _SDMMC12_SEL, + _SDMMC3_SEL, + _QSPI_SEL, + _FMC_SEL, + _USBPHY_SEL, + _USBO_SEL, + _STGEN_SEL, + _PARENT_SEL_NB, + _UNKNOWN_SEL = 0xff, +}; + +enum stm32mp1_pll_id { + _PLL1, + _PLL2, + _PLL3, + _PLL4, + _PLL_NB +}; + +enum stm32mp1_div_id { + _DIV_P, + _DIV_Q, + _DIV_R, + _DIV_NB, +}; + +enum stm32mp1_clksrc_id { + CLKSRC_MPU, + CLKSRC_AXI, + CLKSRC_PLL12, + CLKSRC_PLL3, + CLKSRC_PLL4, + CLKSRC_RTC, + CLKSRC_MCO1, + CLKSRC_MCO2, + CLKSRC_NB +}; + +enum stm32mp1_clkdiv_id { + CLKDIV_MPU, + CLKDIV_AXI, + CLKDIV_APB1, + CLKDIV_APB2, + CLKDIV_APB3, + CLKDIV_APB4, + CLKDIV_APB5, + CLKDIV_RTC, + CLKDIV_MCO1, + CLKDIV_MCO2, + CLKDIV_NB +}; + +enum stm32mp1_pllcfg { + PLLCFG_M, + PLLCFG_N, + PLLCFG_P, + PLLCFG_Q, + PLLCFG_R, + PLLCFG_O, + PLLCFG_NB +}; + +enum stm32mp1_pllcsg { + PLLCSG_MOD_PER, + PLLCSG_INC_STEP, + PLLCSG_SSCG_MODE, + PLLCSG_NB +}; + +enum stm32mp1_plltype { + PLL_800, + PLL_1600, + PLL_TYPE_NB +}; + +struct stm32mp1_pll { + uint8_t refclk_min; + uint8_t refclk_max; + uint8_t divn_max; +}; + +struct stm32mp1_clk_gate { + uint16_t offset; + uint8_t bit; + uint8_t index; + uint8_t set_clr; + enum stm32mp1_parent_sel sel; + enum stm32mp1_parent_id fixed; + bool secure; +}; + +struct stm32mp1_clk_sel { + uint16_t offset; + uint8_t src; + uint8_t msk; + uint8_t nb_parent; + const uint8_t *parent; +}; + +#define REFCLK_SIZE 4 +struct stm32mp1_clk_pll { + enum stm32mp1_plltype plltype; + uint16_t rckxselr; + uint16_t pllxcfgr1; + uint16_t pllxcfgr2; + uint16_t pllxfracr; + uint16_t pllxcr; + uint16_t pllxcsgr; + enum stm32mp_osc_id refclk[REFCLK_SIZE]; +}; + +struct stm32mp1_clk_data { + const struct stm32mp1_clk_gate *gate; + const struct stm32mp1_clk_sel *sel; + const struct stm32mp1_clk_pll *pll; + const int nb_gate; +}; + +struct stm32mp1_clk_priv { + uint32_t base; + const struct stm32mp1_clk_data *data; + unsigned long osc[NB_OSC]; + uint32_t pkcs_usb_value; +}; + +#define STM32MP1_CLK(off, b, idx, s) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 0, \ + .sel = (s), \ + .fixed = _UNKNOWN_ID, \ + .secure = 0, \ + } + +#define STM32MP1_CLK_F(off, b, idx, f) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 0, \ + .sel = _UNKNOWN_SEL, \ + .fixed = (f), \ + .secure = 0, \ + } + +#define STM32MP1_CLK_SET_CLR(off, b, idx, s) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 1, \ + .sel = (s), \ + .fixed = _UNKNOWN_ID, \ + .secure = 0, \ + } + +#define STM32MP1_CLK_SET_CLR_F(off, b, idx, f) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 1, \ + .sel = _UNKNOWN_SEL, \ + .fixed = (f), \ + .secure = 0, \ + } + +#define STM32MP1_CLK_SEC_SET_CLR(off, b, idx, s) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 1, \ + .sel = (s), \ + .fixed = _UNKNOWN_ID, \ + .secure = 1, \ + } + +#define STM32MP1_CLK_PARENT(idx, off, s, m, p) \ + [(idx)] = { \ + .offset = (off), \ + .src = (s), \ + .msk = (m), \ + .parent = (p), \ + .nb_parent = ARRAY_SIZE((p)) \ + } + +#define STM32MP1_CLK_PLL(idx, type, off1, off2, off3, \ + off4, off5, off6, \ + p1, p2, p3, p4) \ + [(idx)] = { \ + .plltype = (type), \ + .rckxselr = (off1), \ + .pllxcfgr1 = (off2), \ + .pllxcfgr2 = (off3), \ + .pllxfracr = (off4), \ + .pllxcr = (off5), \ + .pllxcsgr = (off6), \ + .refclk[0] = (p1), \ + .refclk[1] = (p2), \ + .refclk[2] = (p3), \ + .refclk[3] = (p4), \ + } + +static const uint8_t stm32mp1_clks[][2] = { + {CK_PER, _CK_PER}, + {CK_MPU, _CK_MPU}, + {CK_AXI, _ACLK}, + {CK_HSE, _HSE}, + {CK_CSI, _CSI}, + {CK_LSI, _LSI}, + {CK_LSE, _LSE}, + {CK_HSI, _HSI}, + {CK_HSE_DIV2, _HSE_KER_DIV2}, +}; + +static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { + STM32MP1_CLK(RCC_DDRITFCR, 0, DDRC1, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 1, DDRC1LP, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 2, DDRC2, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 3, DDRC2LP, _UNKNOWN_SEL), + STM32MP1_CLK_F(RCC_DDRITFCR, 4, DDRPHYC, _PLL2_R), + STM32MP1_CLK(RCC_DDRITFCR, 5, DDRPHYCLP, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 6, DDRCAPB, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 7, DDRCAPBLP, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 8, AXIDCG, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 9, DDRPHYCAPB, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 10, DDRPHYCAPBLP, _UNKNOWN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 14, USART2_K, _UART24_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 15, USART3_K, _UART35_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 16, UART4_K, _UART24_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 17, UART5_K, _UART35_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 18, UART7_K, _UART78_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 19, UART8_K, _UART78_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL), + + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_APB5ENSETR, 2, I2C4_K, _I2C46_SEL), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_APB5ENSETR, 8, RTCAPB, _PCLK5), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_APB5ENSETR, 11, TZC1, _UNKNOWN_SEL), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_APB5ENSETR, 12, TZC2, _UNKNOWN_SEL), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_APB5ENSETR, 20, STGEN_K, _STGEN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 16, SDMMC3_K, _SDMMC3_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 3, GPIOD, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 4, GPIOE, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 5, GPIOF, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 6, GPIOG, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 7, GPIOH, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 8, GPIOI, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 9, GPIOJ, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_SEL), + + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_AHB5ENSETR, 0, GPIOZ, _UNKNOWN_SEL), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_AHB5ENSETR, 5, HASH1, _UNKNOWN_SEL), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_AHB5ENSETR, 6, RNG1_K, _CSI_KER), + STM32MP1_CLK_SEC_SET_CLR(RCC_MP_AHB5ENSETR, 8, BKPSRAM, _UNKNOWN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 12, FMC_K, _FMC_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 14, QSPI_K, _QSPI_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 16, SDMMC1_K, _SDMMC12_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 17, SDMMC2_K, _SDMMC12_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 24, USBH, _UNKNOWN_SEL), + + STM32MP1_CLK(RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL), +}; + +static const uint8_t i2c46_parents[] = {_PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER}; +static const uint8_t uart6_parents[] = {_PCLK2, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const uint8_t uart24_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const uint8_t uart35_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const uint8_t uart78_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const uint8_t sdmmc12_parents[] = {_HCLK6, _PLL3_R, _PLL4_P, _HSI_KER}; +static const uint8_t sdmmc3_parents[] = {_HCLK2, _PLL3_R, _PLL4_P, _HSI_KER}; +static const uint8_t qspi_parents[] = {_ACLK, _PLL3_R, _PLL4_P, _CK_PER}; +static const uint8_t fmc_parents[] = {_ACLK, _PLL3_R, _PLL4_P, _CK_PER}; +static const uint8_t usbphy_parents[] = {_HSE_KER, _PLL4_R, _HSE_KER_DIV2}; +static const uint8_t usbo_parents[] = {_PLL4_R, _USB_PHY_48}; +static const uint8_t stgen_parents[] = {_HSI_KER, _HSE_KER}; + +static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { + STM32MP1_CLK_PARENT(_I2C46_SEL, RCC_I2C46CKSELR, 0, 0x7, i2c46_parents), + STM32MP1_CLK_PARENT(_UART6_SEL, RCC_UART6CKSELR, 0, 0x7, uart6_parents), + STM32MP1_CLK_PARENT(_UART24_SEL, RCC_UART24CKSELR, 0, 0x7, + uart24_parents), + STM32MP1_CLK_PARENT(_UART35_SEL, RCC_UART35CKSELR, 0, 0x7, + uart35_parents), + STM32MP1_CLK_PARENT(_UART78_SEL, RCC_UART78CKSELR, 0, 0x7, + uart78_parents), + STM32MP1_CLK_PARENT(_SDMMC12_SEL, RCC_SDMMC12CKSELR, 0, 0x7, + sdmmc12_parents), + STM32MP1_CLK_PARENT(_SDMMC3_SEL, RCC_SDMMC3CKSELR, 0, 0x7, + sdmmc3_parents), + STM32MP1_CLK_PARENT(_QSPI_SEL, RCC_QSPICKSELR, 0, 0xf, qspi_parents), + STM32MP1_CLK_PARENT(_FMC_SEL, RCC_FMCCKSELR, 0, 0xf, fmc_parents), + STM32MP1_CLK_PARENT(_USBPHY_SEL, RCC_USBCKSELR, 0, 0x3, usbphy_parents), + STM32MP1_CLK_PARENT(_USBO_SEL, RCC_USBCKSELR, 4, 0x1, usbo_parents), + STM32MP1_CLK_PARENT(_STGEN_SEL, RCC_STGENCKSELR, 0, 0x3, stgen_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, + .refclk_max = 16, + .divn_max = 99, + }, + [PLL_1600] = { + .refclk_min = 8, + .refclk_max = 16, + .divn_max = 199, + }, +}; + +/* PLLNCFGR2 register divider by output */ +static const uint8_t pllncfgr2[_DIV_NB] = { + [_DIV_P] = RCC_PLLNCFGR2_DIVP_SHIFT, + [_DIV_Q] = RCC_PLLNCFGR2_DIVQ_SHIFT, + [_DIV_R] = RCC_PLLNCFGR2_DIVR_SHIFT +}; + +static const struct stm32mp1_clk_pll stm32mp1_clk_pll[_PLL_NB] = { + STM32MP1_CLK_PLL(_PLL1, PLL_1600, + RCC_RCK12SELR, RCC_PLL1CFGR1, RCC_PLL1CFGR2, + RCC_PLL1FRACR, RCC_PLL1CR, RCC_PLL1CSGR, + _HSI, _HSE, _UNKNOWN_OSC_ID, _UNKNOWN_OSC_ID), + STM32MP1_CLK_PLL(_PLL2, PLL_1600, + RCC_RCK12SELR, RCC_PLL2CFGR1, RCC_PLL2CFGR2, + RCC_PLL2FRACR, RCC_PLL2CR, RCC_PLL2CSGR, + _HSI, _HSE, _UNKNOWN_OSC_ID, _UNKNOWN_OSC_ID), + STM32MP1_CLK_PLL(_PLL3, PLL_800, + RCC_RCK3SELR, RCC_PLL3CFGR1, RCC_PLL3CFGR2, + RCC_PLL3FRACR, RCC_PLL3CR, RCC_PLL3CSGR, + _HSI, _HSE, _CSI, _UNKNOWN_OSC_ID), + STM32MP1_CLK_PLL(_PLL4, PLL_800, + RCC_RCK4SELR, RCC_PLL4CFGR1, RCC_PLL4CFGR2, + RCC_PLL4FRACR, RCC_PLL4CR, RCC_PLL4CSGR, + _HSI, _HSE, _CSI, _I2S_CKIN), +}; + +/* Prescaler table lookups for clock computation */ + +/* div = /1 /2 /4 /8 /16 : same divider for PMU and APBX */ +#define stm32mp1_mpu_div stm32mp1_mpu_apbx_div +#define stm32mp1_apbx_div stm32mp1_mpu_apbx_div +static const uint8_t stm32mp1_mpu_apbx_div[8] = { + 0, 1, 2, 3, 4, 4, 4, 4 +}; + +/* div = /1 /2 /3 /4 */ +static const uint8_t stm32mp1_axi_div[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + +static const struct stm32mp1_clk_data stm32mp1_data = { + .gate = stm32mp1_clk_gate, + .sel = stm32mp1_clk_sel, + .pll = stm32mp1_clk_pll, + .nb_gate = ARRAY_SIZE(stm32mp1_clk_gate), +}; + +static struct stm32mp1_clk_priv stm32mp1_clk_priv_data; + +static unsigned long stm32mp1_clk_get_fixed(struct stm32mp1_clk_priv *priv, + enum stm32mp_osc_id idx) +{ + if (idx >= NB_OSC) { + return 0; + } + + return priv->osc[idx]; +} + +static int stm32mp1_clk_get_id(struct stm32mp1_clk_priv *priv, unsigned long id) +{ + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i; + int nb_clks = priv->data->nb_gate; + + for (i = 0; i < nb_clks; i++) { + if (gate[i].index == id) { + return i; + } + } + + ERROR("%s: clk id %d not found\n", __func__, (uint32_t)id); + + return -EINVAL; +} + +static enum stm32mp1_parent_sel +stm32mp1_clk_get_sel(struct stm32mp1_clk_priv *priv, int i) +{ + const struct stm32mp1_clk_gate *gate = priv->data->gate; + + return gate[i].sel; +} + +static enum stm32mp1_parent_id +stm32mp1_clk_get_fixed_parent(struct stm32mp1_clk_priv *priv, int i) +{ + const struct stm32mp1_clk_gate *gate = priv->data->gate; + + return gate[i].fixed; +} + +static int stm32mp1_clk_get_parent(struct stm32mp1_clk_priv *priv, + unsigned long id) +{ + const struct stm32mp1_clk_sel *sel = priv->data->sel; + uint32_t j, p_sel; + int i; + enum stm32mp1_parent_id p; + enum stm32mp1_parent_sel s; + + for (j = 0; j < ARRAY_SIZE(stm32mp1_clks); j++) { + if (stm32mp1_clks[j][0] == id) { + return (int)stm32mp1_clks[j][1]; + } + } + + i = stm32mp1_clk_get_id(priv, id); + if (i < 0) { + return i; + } + + p = stm32mp1_clk_get_fixed_parent(priv, i); + if (p < _PARENT_NB) { + return (int)p; + } + + s = stm32mp1_clk_get_sel(priv, i); + if (s >= _PARENT_SEL_NB) { + return -EINVAL; + } + + p_sel = (mmio_read_32(priv->base + sel[s].offset) >> sel[s].src) & + sel[s].msk; + + if (p_sel < sel[s].nb_parent) { + return (int)sel[s].parent[p_sel]; + } + + ERROR("%s: no parents defined for clk id %ld\n", __func__, id); + + return -EINVAL; +} + +static unsigned long stm32mp1_pll_get_fref_ck(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + uint32_t selr, src; + unsigned long refclk; + + selr = mmio_read_32(priv->base + pll[pll_id].rckxselr); + src = selr & RCC_SELR_REFCLK_SRC_MASK; + + refclk = stm32mp1_clk_get_fixed(priv, pll[pll_id].refclk[src]); + + return refclk; +} + +/* + * pll_get_fvco() : return the VCO or (VCO / 2) frequency for the requested PLL + * - PLL1 & PLL2 => return VCO / 2 with Fpll_y_ck = FVCO / 2 * (DIVy + 1) + * - PLL3 & PLL4 => return VCO with Fpll_y_ck = FVCO / (DIVy + 1) + * => in all cases Fpll_y_ck = pll_get_fvco() / (DIVy + 1) + */ +static unsigned long stm32mp1_pll_get_fvco(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + unsigned long refclk, fvco; + uint32_t cfgr1, fracr, divm, divn; + + cfgr1 = mmio_read_32(priv->base + pll[pll_id].pllxcfgr1); + fracr = mmio_read_32(priv->base + pll[pll_id].pllxfracr); + + divm = (cfgr1 & (RCC_PLLNCFGR1_DIVM_MASK)) >> RCC_PLLNCFGR1_DIVM_SHIFT; + divn = cfgr1 & RCC_PLLNCFGR1_DIVN_MASK; + + refclk = stm32mp1_pll_get_fref_ck(priv, pll_id); + + /* + * With FRACV : + * Fvco = Fck_ref * ((DIVN + 1) + FRACV / 2^13) / (DIVM + 1) + * Without FRACV + * Fvco = Fck_ref * ((DIVN + 1) / (DIVM + 1) + */ + if ((fracr & RCC_PLLNFRACR_FRACLE) != 0U) { + uint32_t fracv = (fracr & RCC_PLLNFRACR_FRACV_MASK) + >> RCC_PLLNFRACR_FRACV_SHIFT; + unsigned long long numerator, denominator; + + numerator = ((unsigned long long)divn + 1U) << 13; + numerator = (refclk * numerator) + fracv; + denominator = ((unsigned long long)divm + 1U) << 13; + fvco = (unsigned long)(numerator / denominator); + } else { + fvco = (unsigned long)(refclk * (divn + 1U) / (divm + 1U)); + } + + return fvco; +} + +static unsigned long stm32mp1_read_pll_freq(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id, + enum stm32mp1_div_id div_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + unsigned long dfout; + uint32_t cfgr2, divy; + + if (div_id >= _DIV_NB) { + return 0; + } + + cfgr2 = mmio_read_32(priv->base + pll[pll_id].pllxcfgr2); + divy = (cfgr2 >> pllncfgr2[div_id]) & RCC_PLLNCFGR2_DIVX_MASK; + + dfout = stm32mp1_pll_get_fvco(priv, pll_id) / (divy + 1U); + + return dfout; +} + +static unsigned long stm32mp1_clk_get(struct stm32mp1_clk_priv *priv, int p) +{ + uint32_t reg, clkdiv; + unsigned long clock = 0; + + switch (p) { + case _CK_MPU: + /* MPU sub system */ + reg = mmio_read_32(priv->base + RCC_MPCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_MPCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_MPCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_MPCKSELR_PLL: + clock = stm32mp1_read_pll_freq(priv, _PLL1, _DIV_P); + break; + case RCC_MPCKSELR_PLL_MPUDIV: + clock = stm32mp1_read_pll_freq(priv, _PLL1, _DIV_P); + + reg = mmio_read_32(priv->base + RCC_MPCKDIVR); + clkdiv = reg & RCC_MPUDIV_MASK; + if (clkdiv != 0U) { + clock /= stm32mp1_mpu_div[clkdiv]; + } + + break; + default: + break; + } + break; + /* AXI sub system */ + case _ACLK: + case _HCLK2: + case _HCLK6: + case _PCLK4: + case _PCLK5: + reg = mmio_read_32(priv->base + RCC_ASSCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_ASSCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_ASSCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_ASSCKSELR_PLL: + clock = stm32mp1_read_pll_freq(priv, _PLL2, _DIV_P); + break; + default: + break; + } + + /* System clock divider */ + reg = mmio_read_32(priv->base + RCC_AXIDIVR); + clock /= stm32mp1_axi_div[reg & RCC_AXIDIV_MASK]; + + switch (p) { + case _PCLK4: + reg = mmio_read_32(priv->base + RCC_APB4DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + case _PCLK5: + reg = mmio_read_32(priv->base + RCC_APB5DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + default: + break; + } + break; + case _CK_PER: + reg = mmio_read_32(priv->base + RCC_CPERCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_CPERCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_CPERCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_CPERCKSELR_CSI: + clock = stm32mp1_clk_get_fixed(priv, _CSI); + break; + default: + break; + } + break; + case _HSI: + case _HSI_KER: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case _CSI: + case _CSI_KER: + clock = stm32mp1_clk_get_fixed(priv, _CSI); + break; + case _HSE: + case _HSE_KER: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case _HSE_KER_DIV2: + clock = stm32mp1_clk_get_fixed(priv, _HSE) >> 1; + break; + case _LSI: + clock = stm32mp1_clk_get_fixed(priv, _LSI); + break; + case _LSE: + clock = stm32mp1_clk_get_fixed(priv, _LSE); + break; + /* PLL */ + case _PLL1_P: + clock = stm32mp1_read_pll_freq(priv, _PLL1, _DIV_P); + break; + case _PLL1_Q: + clock = stm32mp1_read_pll_freq(priv, _PLL1, _DIV_Q); + break; + case _PLL1_R: + clock = stm32mp1_read_pll_freq(priv, _PLL1, _DIV_R); + break; + case _PLL2_P: + clock = stm32mp1_read_pll_freq(priv, _PLL2, _DIV_P); + break; + case _PLL2_Q: + clock = stm32mp1_read_pll_freq(priv, _PLL2, _DIV_Q); + break; + case _PLL2_R: + clock = stm32mp1_read_pll_freq(priv, _PLL2, _DIV_R); + break; + case _PLL3_P: + clock = stm32mp1_read_pll_freq(priv, _PLL3, _DIV_P); + break; + case _PLL3_Q: + clock = stm32mp1_read_pll_freq(priv, _PLL3, _DIV_Q); + break; + case _PLL3_R: + clock = stm32mp1_read_pll_freq(priv, _PLL3, _DIV_R); + break; + case _PLL4_P: + clock = stm32mp1_read_pll_freq(priv, _PLL4, _DIV_P); + break; + case _PLL4_Q: + clock = stm32mp1_read_pll_freq(priv, _PLL4, _DIV_Q); + break; + case _PLL4_R: + clock = stm32mp1_read_pll_freq(priv, _PLL4, _DIV_R); + break; + /* Other */ + case _USB_PHY_48: + clock = stm32mp1_clk_get_fixed(priv, _USB_PHY_48); + break; + default: + break; + } + + return clock; +} + +bool stm32mp1_clk_is_enabled(unsigned long id) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i = stm32mp1_clk_get_id(priv, id); + + if (i < 0) { + return false; + } + + return ((mmio_read_32(priv->base + gate[i].offset) & + BIT(gate[i].bit)) != 0U); +} + +int stm32mp1_clk_enable(unsigned long id) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i = stm32mp1_clk_get_id(priv, id); + + if (i < 0) { + return i; + } + + if (gate[i].set_clr != 0U) { + mmio_write_32(priv->base + gate[i].offset, BIT(gate[i].bit)); + } else { + mmio_setbits_32(priv->base + gate[i].offset, BIT(gate[i].bit)); + } + + return 0; +} + +int stm32mp1_clk_disable(unsigned long id) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i = stm32mp1_clk_get_id(priv, id); + + if (i < 0) { + return i; + } + + if (gate[i].set_clr != 0U) { + mmio_write_32(priv->base + gate[i].offset + + RCC_MP_ENCLRR_OFFSET, + BIT(gate[i].bit)); + } else { + mmio_clrbits_32(priv->base + gate[i].offset, BIT(gate[i].bit)); + } + + return 0; +} + +unsigned long stm32mp1_clk_get_rate(unsigned long id) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + int p = stm32mp1_clk_get_parent(priv, id); + unsigned long rate; + + if (p < 0) { + return 0; + } + + rate = stm32mp1_clk_get(priv, p); + + return rate; +} + +static void stm32mp1_ls_osc_set(int enable, uint32_t rcc, uint32_t offset, + uint32_t mask_on) +{ + uint32_t address = rcc + offset; + + if (enable != 0) { + mmio_setbits_32(address, mask_on); + } else { + mmio_clrbits_32(address, mask_on); + } +} + +static void stm32mp1_hs_ocs_set(int enable, uint32_t rcc, uint32_t mask_on) +{ + if (enable != 0) { + mmio_setbits_32(rcc + RCC_OCENSETR, mask_on); + } else { + mmio_setbits_32(rcc + RCC_OCENCLRR, mask_on); + } +} + +static int stm32mp1_osc_wait(int enable, uint32_t rcc, uint32_t offset, + uint32_t mask_rdy) +{ + unsigned long start; + uint32_t mask_test; + uint32_t address = rcc + offset; + + if (enable != 0) { + mask_test = mask_rdy; + } else { + mask_test = 0; + } + + start = get_timer(0); + while ((mmio_read_32(address) & mask_rdy) != mask_test) { + if (get_timer(start) > OSCRDY_TIMEOUT) { + ERROR("OSC %x @ %x timeout for enable=%d : 0x%x\n", + mask_rdy, address, enable, mmio_read_32(address)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void stm32mp1_lse_enable(uint32_t rcc, bool bypass, uint32_t lsedrv) +{ + uint32_t value; + + if (bypass) { + mmio_setbits_32(rcc + RCC_BDCR, RCC_BDCR_LSEBYP); + } + + /* + * Warning: not recommended to switch directly from "high drive" + * to "medium low drive", and vice-versa. + */ + value = (mmio_read_32(rcc + RCC_BDCR) & RCC_BDCR_LSEDRV_MASK) >> + RCC_BDCR_LSEDRV_SHIFT; + + while (value != lsedrv) { + if (value > lsedrv) { + value--; + } else { + value++; + } + + mmio_clrsetbits_32(rcc + RCC_BDCR, + RCC_BDCR_LSEDRV_MASK, + value << RCC_BDCR_LSEDRV_SHIFT); + } + + stm32mp1_ls_osc_set(1, rcc, RCC_BDCR, RCC_BDCR_LSEON); +} + +static void stm32mp1_lse_wait(uint32_t rcc) +{ + if (stm32mp1_osc_wait(1, rcc, RCC_BDCR, RCC_BDCR_LSERDY) != 0) { + VERBOSE("%s: failed\n", __func__); + } +} + +static void stm32mp1_lsi_set(uint32_t rcc, int enable) +{ + stm32mp1_ls_osc_set(enable, rcc, RCC_RDLSICR, RCC_RDLSICR_LSION); + if (stm32mp1_osc_wait(enable, rcc, RCC_RDLSICR, RCC_RDLSICR_LSIRDY) != + 0) { + VERBOSE("%s: failed\n", __func__); + } +} + +static void stm32mp1_hse_enable(uint32_t rcc, bool bypass, bool css) +{ + if (bypass) { + mmio_setbits_32(rcc + RCC_OCENSETR, RCC_OCENR_HSEBYP); + } + + stm32mp1_hs_ocs_set(1, rcc, RCC_OCENR_HSEON); + if (stm32mp1_osc_wait(1, rcc, RCC_OCRDYR, RCC_OCRDYR_HSERDY) != + 0) { + VERBOSE("%s: failed\n", __func__); + } + + if (css) { + mmio_setbits_32(rcc + RCC_OCENSETR, RCC_OCENR_HSECSSON); + } +} + +static void stm32mp1_csi_set(uint32_t rcc, int enable) +{ + stm32mp1_ls_osc_set(enable, rcc, RCC_OCENSETR, RCC_OCENR_CSION); + if (stm32mp1_osc_wait(enable, rcc, RCC_OCRDYR, RCC_OCRDYR_CSIRDY) != + 0) { + VERBOSE("%s: failed\n", __func__); + } +} + +static void stm32mp1_hsi_set(uint32_t rcc, int enable) +{ + stm32mp1_hs_ocs_set(enable, rcc, RCC_OCENR_HSION); + if (stm32mp1_osc_wait(enable, rcc, RCC_OCRDYR, RCC_OCRDYR_HSIRDY) != + 0) { + VERBOSE("%s: failed\n", __func__); + } +} + +static int stm32mp1_set_hsidiv(uint32_t rcc, uint8_t hsidiv) +{ + unsigned long start; + uint32_t address = rcc + RCC_OCRDYR; + + mmio_clrsetbits_32(rcc + RCC_HSICFGR, + RCC_HSICFGR_HSIDIV_MASK, + RCC_HSICFGR_HSIDIV_MASK & (uint32_t)hsidiv); + + start = get_timer(0); + while ((mmio_read_32(address) & RCC_OCRDYR_HSIDIVRDY) == 0U) { + if (get_timer(start) > HSIDIV_TIMEOUT) { + ERROR("HSIDIV failed @ 0x%x: 0x%x\n", + address, mmio_read_32(address)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stm32mp1_hsidiv(uint32_t rcc, unsigned long hsifreq) +{ + uint8_t hsidiv; + uint32_t hsidivfreq = MAX_HSI_HZ; + + for (hsidiv = 0; hsidiv < 4U; hsidiv++) { + if (hsidivfreq == hsifreq) { + break; + } + + hsidivfreq /= 2U; + } + + if (hsidiv == 4U) { + ERROR("Invalid clk-hsi frequency\n"); + return -1; + } + + if (hsidiv != 0U) { + return stm32mp1_set_hsidiv(rcc, hsidiv); + } + + return 0; +} + +static void stm32mp1_pll_start(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + + mmio_write_32(priv->base + pll[pll_id].pllxcr, RCC_PLLNCR_PLLON); +} + +static int stm32mp1_pll_output(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id, uint32_t output) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + uint32_t pllxcr = priv->base + pll[pll_id].pllxcr; + unsigned long start; + + start = get_timer(0); + /* Wait PLL lock */ + while ((mmio_read_32(pllxcr) & RCC_PLLNCR_PLLRDY) == 0U) { + if (get_timer(start) > PLLRDY_TIMEOUT) { + ERROR("PLL%d start failed @ 0x%x: 0x%x\n", + pll_id, pllxcr, mmio_read_32(pllxcr)); + return -ETIMEDOUT; + } + } + + /* Start the requested output */ + mmio_setbits_32(pllxcr, output << RCC_PLLNCR_DIVEN_SHIFT); + + return 0; +} + +static int stm32mp1_pll_stop(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + uint32_t pllxcr = priv->base + pll[pll_id].pllxcr; + unsigned long start; + + /* Stop all output */ + mmio_clrbits_32(pllxcr, RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | + RCC_PLLNCR_DIVREN); + + /* Stop PLL */ + mmio_clrbits_32(pllxcr, RCC_PLLNCR_PLLON); + + start = get_timer(0); + /* Wait PLL stopped */ + while ((mmio_read_32(pllxcr) & RCC_PLLNCR_PLLRDY) != 0U) { + if (get_timer(start) > PLLRDY_TIMEOUT) { + ERROR("PLL%d stop failed @ 0x%x: 0x%x\n", + pll_id, pllxcr, mmio_read_32(pllxcr)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void stm32mp1_pll_config_output(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id, + uint32_t *pllcfg) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + uint32_t rcc = priv->base; + uint32_t value; + + value = (pllcfg[PLLCFG_P] << RCC_PLLNCFGR2_DIVP_SHIFT) & + RCC_PLLNCFGR2_DIVP_MASK; + value |= (pllcfg[PLLCFG_Q] << RCC_PLLNCFGR2_DIVQ_SHIFT) & + RCC_PLLNCFGR2_DIVQ_MASK; + value |= (pllcfg[PLLCFG_R] << RCC_PLLNCFGR2_DIVR_SHIFT) & + RCC_PLLNCFGR2_DIVR_MASK; + mmio_write_32(rcc + pll[pll_id].pllxcfgr2, value); +} + +static int stm32mp1_pll_config(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id, + uint32_t *pllcfg, uint32_t fracv) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + uint32_t rcc = priv->base; + enum stm32mp1_plltype type = pll[pll_id].plltype; + unsigned long refclk; + uint32_t ifrge = 0; + uint32_t src, value; + + src = mmio_read_32(priv->base + pll[pll_id].rckxselr) & + RCC_SELR_REFCLK_SRC_MASK; + + refclk = stm32mp1_clk_get_fixed(priv, pll[pll_id].refclk[src]) / + (pllcfg[PLLCFG_M] + 1U); + + if ((refclk < (stm32mp1_pll[type].refclk_min * 1000000U)) || + (refclk > (stm32mp1_pll[type].refclk_max * 1000000U))) { + return -EINVAL; + } + + if ((type == PLL_800) && (refclk >= 8000000U)) { + 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; + mmio_write_32(rcc + pll[pll_id].pllxcfgr1, value); + + /* Fractional configuration */ + value = 0; + mmio_write_32(rcc + pll[pll_id].pllxfracr, value); + + value = fracv << RCC_PLLNFRACR_FRACV_SHIFT; + mmio_write_32(rcc + pll[pll_id].pllxfracr, value); + + value |= RCC_PLLNFRACR_FRACLE; + mmio_write_32(rcc + pll[pll_id].pllxfracr, value); + + stm32mp1_pll_config_output(priv, pll_id, pllcfg); + + return 0; +} + +static void stm32mp1_pll_csg(struct stm32mp1_clk_priv *priv, + enum stm32mp1_pll_id pll_id, + uint32_t *csg) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + uint32_t pllxcsg = 0; + + pllxcsg |= (csg[PLLCSG_MOD_PER] << RCC_PLLNCSGR_MOD_PER_SHIFT) & + RCC_PLLNCSGR_MOD_PER_MASK; + + pllxcsg |= (csg[PLLCSG_INC_STEP] << RCC_PLLNCSGR_INC_STEP_SHIFT) & + RCC_PLLNCSGR_INC_STEP_MASK; + + pllxcsg |= (csg[PLLCSG_SSCG_MODE] << RCC_PLLNCSGR_SSCG_MODE_SHIFT) & + RCC_PLLNCSGR_SSCG_MODE_MASK; + + mmio_write_32(priv->base + pll[pll_id].pllxcsgr, pllxcsg); +} + +static int stm32mp1_set_clksrc(struct stm32mp1_clk_priv *priv, + unsigned int clksrc) +{ + uint32_t address = priv->base + (clksrc >> 4); + unsigned long start; + + mmio_clrsetbits_32(address, RCC_SELR_SRC_MASK, + clksrc & RCC_SELR_SRC_MASK); + + start = get_timer(0); + while ((mmio_read_32(address) & RCC_SELR_SRCRDY) == 0U) { + if (get_timer(start) > CLKSRC_TIMEOUT) { + ERROR("CLKSRC %x start failed @ 0x%x: 0x%x\n", + clksrc, address, mmio_read_32(address)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stm32mp1_set_clkdiv(unsigned int clkdiv, uint32_t address) +{ + unsigned long start; + + mmio_clrsetbits_32(address, RCC_DIVR_DIV_MASK, + clkdiv & RCC_DIVR_DIV_MASK); + + start = get_timer(0); + while ((mmio_read_32(address) & RCC_DIVR_DIVRDY) == 0U) { + if (get_timer(start) > CLKDIV_TIMEOUT) { + ERROR("CLKDIV %x start failed @ 0x%x: 0x%x\n", + clkdiv, address, mmio_read_32(address)); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void stm32mp1_mco_csg(struct stm32mp1_clk_priv *priv, + uint32_t clksrc, uint32_t clkdiv) +{ + uint32_t address = priv->base + (clksrc >> 4); + + /* + * Binding clksrc : + * bit15-4 offset + * bit3: disable + * bit2-0: MCOSEL[2:0] + */ + if ((clksrc & 0x8U) != 0U) { + mmio_clrbits_32(address, RCC_MCOCFG_MCOON); + } else { + mmio_clrsetbits_32(address, + RCC_MCOCFG_MCOSRC_MASK, + clksrc & RCC_MCOCFG_MCOSRC_MASK); + mmio_clrsetbits_32(address, + RCC_MCOCFG_MCODIV_MASK, + clkdiv << RCC_MCOCFG_MCODIV_SHIFT); + mmio_setbits_32(address, RCC_MCOCFG_MCOON); + } +} + +static void stm32mp1_set_rtcsrc(struct stm32mp1_clk_priv *priv, + unsigned int clksrc, bool lse_css) +{ + uint32_t address = priv->base + RCC_BDCR; + + if (((mmio_read_32(address) & RCC_BDCR_RTCCKEN) == 0U) || + (clksrc != (uint32_t)CLK_RTC_DISABLED)) { + mmio_clrsetbits_32(address, + RCC_BDCR_RTCSRC_MASK, + clksrc << RCC_BDCR_RTCSRC_SHIFT); + + mmio_setbits_32(address, RCC_BDCR_RTCCKEN); + } + + if (lse_css) { + mmio_setbits_32(address, RCC_BDCR_LSECSSON); + } +} + +#define CNTCVL_OFF 0x008 +#define CNTCVU_OFF 0x00C + +static void stm32mp1_stgen_config(struct stm32mp1_clk_priv *priv) +{ + uintptr_t stgen; + int p; + uint32_t cntfid0; + unsigned long rate; + + stgen = fdt_get_stgen_base(); + + cntfid0 = mmio_read_32(stgen + CNTFID_OFF); + p = stm32mp1_clk_get_parent(priv, STGEN_K); + rate = stm32mp1_clk_get(priv, p); + + if (cntfid0 != rate) { + unsigned long long counter; + + 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); + + /* Need to update timer with new frequency */ + generic_delay_timer_init(); + } +} + +void stm32mp1_stgen_increment(unsigned long long offset_in_ms) +{ + uintptr_t stgen; + unsigned long long cnt; + + stgen = fdt_get_stgen_base(); + + cnt = ((unsigned long long)mmio_read_32(stgen + CNTCVU_OFF) << 32) | + mmio_read_32(stgen + CNTCVL_OFF); + + cnt += (offset_in_ms * mmio_read_32(stgen + CNTFID_OFF)) / 1000U; + + 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); +} + +static void stm32mp1_pkcs_config(struct stm32mp1_clk_priv *priv, uint32_t pkcs) +{ + uint32_t address = priv->base + ((pkcs >> 4) & 0xFFFU); + uint32_t value = pkcs & 0xFU; + uint32_t mask = 0xFU; + + if ((pkcs & BIT(31)) != 0U) { + mask <<= 4; + value <<= 4; + } + + mmio_clrsetbits_32(address, mask, value); +} + +int stm32mp1_clk_init(void) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + uint32_t rcc = priv->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; + const uint32_t *pkcs_cell; + + /* Check status field to disable security */ + if (!fdt_get_rcc_secure_status()) { + mmio_write_32(rcc + 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]; + + sprintf(name, "st,pll@%d", i); + plloff[i] = fdt_rcc_subnode_offset(name); + + if (!fdt_check_node(plloff[i])) { + continue; + } + + ret = fdt_read_uint32_array(plloff[i], "cfg", + pllcfg[i], (int)PLLCFG_NB); + if (ret < 0) { + return -FDT_ERR_NOTFOUND; + } + } + + stm32mp1_mco_csg(priv, clksrc[CLKSRC_MCO1], clkdiv[CLKDIV_MCO1]); + stm32mp1_mco_csg(priv, clksrc[CLKSRC_MCO2], clkdiv[CLKDIV_MCO2]); + + /* + * Switch ON oscillator found in device-tree. + * Note: HSI already ON after BootROM stage. + */ + if (priv->osc[_LSI] != 0U) { + stm32mp1_lsi_set(rcc, 1); + } + if (priv->osc[_LSE] != 0U) { + bool bypass; + uint32_t lsedrv; + + bypass = fdt_osc_read_bool(_LSE, "st,bypass"); + lse_css = fdt_osc_read_bool(_LSE, "st,css"); + lsedrv = fdt_osc_read_uint32_default(_LSE, "st,drive", + LSEDRV_MEDIUM_HIGH); + stm32mp1_lse_enable(rcc, bypass, lsedrv); + } + if (priv->osc[_HSE] != 0U) { + bool bypass, css; + + bypass = fdt_osc_read_bool(_LSE, "st,bypass"); + css = fdt_osc_read_bool(_LSE, "st,css"); + stm32mp1_hse_enable(rcc, bypass, 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(rcc, 1); + + /* Come back to HSI */ + ret = stm32mp1_set_clksrc(priv, CLK_MPU_HSI); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clksrc(priv, CLK_AXI_HSI); + if (ret != 0) { + return ret; + } + + for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { + if (i == _PLL4) + continue; + ret = stm32mp1_pll_stop(priv, i); + if (ret != 0) { + return ret; + } + } + + /* Configure HSIDIV */ + if (priv->osc[_HSI] != 0U) { + ret = stm32mp1_hsidiv(rcc, priv->osc[_HSI]); + if (ret != 0) { + return ret; + } + stm32mp1_stgen_config(priv); + } + + /* Select DIV */ + /* No ready bit when MPUSRC != CLK_MPU_PLL1P_DIV, MPUDIV is disabled */ + mmio_write_32(rcc + RCC_MPCKDIVR, + clkdiv[CLKDIV_MPU] & RCC_DIVR_DIV_MASK); + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_AXI], rcc + RCC_AXIDIVR); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB4], rcc + RCC_APB4DIVR); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB5], rcc + RCC_APB5DIVR); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB1], rcc + RCC_APB1DIVR); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB2], rcc + RCC_APB2DIVR); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clkdiv(clkdiv[CLKDIV_APB3], rcc + RCC_APB3DIVR); + if (ret != 0) { + return ret; + } + + /* No ready bit for RTC */ + mmio_write_32(rcc + RCC_RTCDIVR, + clkdiv[CLKDIV_RTC] & RCC_DIVR_DIV_MASK); + + /* Configure PLLs source */ + ret = stm32mp1_set_clksrc(priv, clksrc[CLKSRC_PLL12]); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clksrc(priv, clksrc[CLKSRC_PLL3]); + if (ret != 0) { + return ret; + } + + ret = stm32mp1_set_clksrc(priv, clksrc[CLKSRC_PLL4]); + if (ret != 0) { + return ret; + } + + /* Configure and start PLLs */ + for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) { + uint32_t fracv; + uint32_t csg[PLLCSG_NB]; + + if (!fdt_check_node(plloff[i])) { + continue; + } + + fracv = fdt_read_uint32_default(plloff[i], "frac", 0); + + ret = stm32mp1_pll_config(priv, i, pllcfg[i], fracv); + if (ret != 0) { + return ret; + } + ret = fdt_read_uint32_array(plloff[i], "csg", csg, + (uint32_t)PLLCSG_NB); + if (ret == 0) { + stm32mp1_pll_csg(priv, i, csg); + } else if (ret != -FDT_ERR_NOTFOUND) { + return ret; + } + + stm32mp1_pll_start(priv, 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])) { + continue; + } + + ret = stm32mp1_pll_output(priv, i, pllcfg[i][PLLCFG_O]); + if (ret != 0) { + return ret; + } + } + /* Wait LSE ready before to use it */ + if (priv->osc[_LSE] != 0U) { + stm32mp1_lse_wait(rcc); + } + + /* Configure with expected clock source */ + ret = stm32mp1_set_clksrc(priv, clksrc[CLKSRC_MPU]); + if (ret != 0) { + return ret; + } + ret = stm32mp1_set_clksrc(priv, clksrc[CLKSRC_AXI]); + if (ret != 0) { + return ret; + } + stm32mp1_set_rtcsrc(priv, clksrc[CLKSRC_RTC], lse_css); + + /* Configure PKCK */ + pkcs_cell = fdt_rcc_read_prop("st,pkcs", &len); + if (pkcs_cell != NULL) { + bool ckper_disabled = false; + uint32_t j; + + priv->pkcs_usb_value = 0; + + for (j = 0; j < ((uint32_t)len / sizeof(uint32_t)); j++) { + uint32_t pkcs = (uint32_t)fdt32_to_cpu(pkcs_cell[j]); + + if (pkcs == (uint32_t)CLK_CKPER_DISABLED) { + ckper_disabled = true; + continue; + } + stm32mp1_pkcs_config(priv, pkcs); + } + + /* + * CKPER is source for some peripheral clocks + * (FMC-NAND / QPSI-NOR) and switching source is allowed + * only if previous clock is still ON + * => deactivated CKPER only after switching clock + */ + if (ckper_disabled) { + stm32mp1_pkcs_config(priv, CLK_CKPER_DISABLED); + } + } + + /* Switch OFF HSI if not found in device-tree */ + if (priv->osc[_HSI] == 0U) { + stm32mp1_hsi_set(rcc, 0); + } + stm32mp1_stgen_config(priv); + + /* Software Self-Refresh mode (SSR) during DDR initilialization */ + mmio_clrsetbits_32(priv->base + RCC_DDRITFCR, + RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_SSR << + RCC_DDRITFCR_DDRCKMOD_SHIFT); + + return 0; +} + +static void stm32mp1_osc_clk_init(const char *name, + struct stm32mp1_clk_priv *priv, + enum stm32mp_osc_id index) +{ + uint32_t frequency; + + priv->osc[index] = 0; + + if (fdt_osc_read_freq(name, &frequency) != 0) { + ERROR("%s frequency request failed\n", name); + panic(); + } else { + priv->osc[index] = frequency; + } +} + +static void stm32mp1_osc_init(void) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + enum stm32mp_osc_id i; + + for (i = (enum stm32mp_osc_id)0 ; i < NB_OSC; i++) { + stm32mp1_osc_clk_init(stm32mp_osc_node_label[i], priv, i); + } +} + +int stm32mp1_clk_probe(void) +{ + struct stm32mp1_clk_priv *priv = &stm32mp1_clk_priv_data; + + priv->base = fdt_rcc_read_addr(); + if (priv->base == 0U) { + return -EINVAL; + } + + priv->data = &stm32mp1_data; + + if ((priv->data->gate == NULL) || (priv->data->sel == NULL) || + (priv->data->pll == NULL)) { + return -EINVAL; + } + + stm32mp1_osc_init(); + + return 0; +} diff --git a/drivers/st/clk/stm32mp1_clkfunc.c b/drivers/st/clk/stm32mp1_clkfunc.c new file mode 100644 index 0000000..d4c69cb --- /dev/null +++ b/drivers/st/clk/stm32mp1_clkfunc.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +#define DT_RCC_NODE_NAME "rcc@50000000" +#define DT_RCC_CLK_COMPAT "st,stm32mp1-rcc" +#define DT_RCC_COMPAT "syscon" +#define DT_STGEN_COMPAT "st,stm32-stgen" +#define DT_UART_COMPAT "st,stm32h7-uart" +#define DT_USART_COMPAT "st,stm32h7-usart" + +const char *stm32mp_osc_node_label[NB_OSC] = { + [_LSI] = "clk-lsi", + [_LSE] = "clk-lse", + [_HSI] = "clk-hsi", + [_HSE] = "clk-hse", + [_CSI] = "clk-csi", + [_I2S_CKIN] = "i2s_ckin", + [_USB_PHY_48] = "ck_usbo_48m" +}; + +/******************************************************************************* + * This function reads the frequency of an oscillator from its name. + * It reads the value indicated inside the device tree. + * Returns 0 if success, and a negative value else. + * If success, value is stored in the second parameter. + ******************************************************************************/ +int fdt_osc_read_freq(const char *name, uint32_t *freq) +{ + int node, subnode; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_path_offset(fdt, "/clocks"); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_subnode(subnode, fdt, node) { + const char *cchar; + int ret; + + cchar = fdt_get_name(fdt, subnode, &ret); + if (cchar == NULL) { + return ret; + } + + if (strncmp(cchar, name, (size_t)ret) == 0) { + const fdt32_t *cuint; + + cuint = fdt_getprop(fdt, subnode, "clock-frequency", + &ret); + if (cuint == NULL) { + return ret; + } + + *freq = fdt32_to_cpu(*cuint); + + return 0; + } + } + + /* Oscillator not found, freq=0 */ + *freq = 0; + return 0; +} + +/******************************************************************************* + * This function checks the presence of an oscillator property from its id. + * The search is done inside the device tree. + * Returns true/false regarding search result. + ******************************************************************************/ +bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name) +{ + int node, subnode; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return false; + } + + if (osc_id >= NB_OSC) { + return false; + } + + node = fdt_path_offset(fdt, "/clocks"); + if (node < 0) { + return false; + } + + fdt_for_each_subnode(subnode, fdt, node) { + const char *cchar; + int ret; + + cchar = fdt_get_name(fdt, subnode, &ret); + if (cchar == NULL) { + return false; + } + + if (strncmp(cchar, stm32mp_osc_node_label[osc_id], + (size_t)ret) != 0) { + continue; + } + + if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) { + return true; + } + } + + return false; +} + +/******************************************************************************* + * This function reads a value of a oscillator property from its id. + * Returns value if success, and a default value if property not found. + * Default value is passed as parameter. + ******************************************************************************/ +uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, + const char *prop_name, uint32_t dflt_value) +{ + int node, subnode; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return dflt_value; + } + + if (osc_id >= NB_OSC) { + return dflt_value; + } + + node = fdt_path_offset(fdt, "/clocks"); + if (node < 0) { + return dflt_value; + } + + fdt_for_each_subnode(subnode, fdt, node) { + const char *cchar; + int ret; + + cchar = fdt_get_name(fdt, subnode, &ret); + if (cchar == NULL) { + return dflt_value; + } + + if (strncmp(cchar, stm32mp_osc_node_label[osc_id], + (size_t)ret) != 0) { + continue; + } + + return fdt_read_uint32_default(subnode, prop_name, dflt_value); + } + + return dflt_value; +} + +/******************************************************************************* + * This function reads the rcc base address. + * It reads the value indicated inside the device tree. + * Returns address if success, and 0 value else. + ******************************************************************************/ +uint32_t fdt_rcc_read_addr(void) +{ + int node, subnode; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return 0; + } + + node = fdt_path_offset(fdt, "/soc"); + if (node < 0) { + return 0; + } + + fdt_for_each_subnode(subnode, fdt, node) { + const char *cchar; + int ret; + + cchar = fdt_get_name(fdt, subnode, &ret); + if (cchar == NULL) { + return 0; + } + + if (strncmp(cchar, DT_RCC_NODE_NAME, (size_t)ret) == 0) { + const fdt32_t *cuint; + + cuint = fdt_getprop(fdt, subnode, "reg", NULL); + if (cuint == NULL) { + return 0; + } + + return fdt32_to_cpu(*cuint); + } + } + + return 0; +} + +/******************************************************************************* + * This function reads a series of parameters in rcc-clk section. + * It reads the values indicated inside the device tree, from property name. + * The number of parameters is also indicated as entry parameter. + * Returns 0 if success, and a negative value else. + * If success, values are stored at the second parameter address. + ******************************************************************************/ +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_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + return fdt_read_uint32_array(node, prop_name, array, count); +} + +/******************************************************************************* + * This function gets the subnode offset in rcc-clk section from its name. + * It reads the values indicated inside the device tree. + * Returns offset if success, and a negative value else. + ******************************************************************************/ +int fdt_rcc_subnode_offset(const char *name) +{ + int node, subnode; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + subnode = fdt_subnode_offset(fdt, node, name); + if (subnode <= 0) { + return -FDT_ERR_NOTFOUND; + } + + return subnode; +} + +/******************************************************************************* + * This function gets the pointer to a rcc-clk property from its name. + * It reads the values indicated inside the device tree. + * Length of the property is stored in the second parameter. + * Returns pointer if success, and NULL value else. + ******************************************************************************/ +const uint32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) +{ + const uint32_t *cuint; + int node, len; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return NULL; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + if (node < 0) { + return NULL; + } + + cuint = fdt_getprop(fdt, node, prop_name, &len); + if (cuint == NULL) { + return NULL; + } + + *lenp = len; + return cuint; +} + +/******************************************************************************* + * This function gets the secure status for rcc node. + * It reads secure-status in device tree. + * Returns 1 if rcc is available from secure world, 0 else. + ******************************************************************************/ +bool fdt_get_rcc_secure_status(void) +{ + int node; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return false; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_COMPAT); + if (node < 0) { + return false; + } + + return fdt_check_secure_status(node); +} + +/******************************************************************************* + * This function reads the stgen base address. + * It reads the value indicated inside the device tree. + * Returns address if success, and NULL value else. + ******************************************************************************/ +uintptr_t fdt_get_stgen_base(void) +{ + int node; + const fdt32_t *cuint; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return 0; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_STGEN_COMPAT); + if (node < 0) { + return 0; + } + + cuint = fdt_getprop(fdt, node, "reg", NULL); + if (cuint == NULL) { + return 0; + } + + return fdt32_to_cpu(*cuint); +} + +/******************************************************************************* + * This function gets the clock ID of the given node. + * It reads the value indicated inside the device tree. + * Returns ID if success, and a negative value else. + ******************************************************************************/ +int fdt_get_clock_id(int node) +{ + const fdt32_t *cuint; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + cuint = fdt_getprop(fdt, node, "clocks", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + cuint++; + return (int)fdt32_to_cpu(*cuint); +} diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c new file mode 100644 index 0000000..eed1d76 --- /dev/null +++ b/drivers/st/ddr/stm32mp1_ddr.c @@ -0,0 +1,895 @@ +/* + * Copyright (C) 2018, 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 +#include +#include + +struct reg_desc { + const char *name; + uint16_t offset; /* Offset for base address */ + uint8_t par_offset; /* Offset for parameter array */ +}; + +#define INVALID_OFFSET 0xFFU + +#define TIMESLOT_1US (plat_get_syscnt_freq2() / 1000000U) + +#define DDRCTL_REG(x, y) \ + { \ + .name = #x, \ + .offset = offsetof(struct stm32mp1_ddrctl, x), \ + .par_offset = offsetof(struct y, x) \ + } + +#define DDRPHY_REG(x, y) \ + { \ + .name = #x, \ + .offset = offsetof(struct stm32mp1_ddrphy, x), \ + .par_offset = offsetof(struct y, x) \ + } + +#define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg) +static const struct reg_desc ddr_reg[] = { + DDRCTL_REG_REG(mstr), + DDRCTL_REG_REG(mrctrl0), + DDRCTL_REG_REG(mrctrl1), + DDRCTL_REG_REG(derateen), + DDRCTL_REG_REG(derateint), + DDRCTL_REG_REG(pwrctl), + DDRCTL_REG_REG(pwrtmg), + DDRCTL_REG_REG(hwlpctl), + DDRCTL_REG_REG(rfshctl0), + DDRCTL_REG_REG(rfshctl3), + DDRCTL_REG_REG(crcparctl0), + DDRCTL_REG_REG(zqctl0), + DDRCTL_REG_REG(dfitmg0), + DDRCTL_REG_REG(dfitmg1), + DDRCTL_REG_REG(dfilpcfg0), + DDRCTL_REG_REG(dfiupd0), + DDRCTL_REG_REG(dfiupd1), + DDRCTL_REG_REG(dfiupd2), + DDRCTL_REG_REG(dfiphymstr), + DDRCTL_REG_REG(odtmap), + DDRCTL_REG_REG(dbg0), + DDRCTL_REG_REG(dbg1), + DDRCTL_REG_REG(dbgcmd), + DDRCTL_REG_REG(poisoncfg), + DDRCTL_REG_REG(pccfg), +}; + +#define DDRCTL_REG_TIMING(x) DDRCTL_REG(x, stm32mp1_ddrctrl_timing) +static const struct reg_desc ddr_timing[] = { + DDRCTL_REG_TIMING(rfshtmg), + DDRCTL_REG_TIMING(dramtmg0), + DDRCTL_REG_TIMING(dramtmg1), + DDRCTL_REG_TIMING(dramtmg2), + DDRCTL_REG_TIMING(dramtmg3), + DDRCTL_REG_TIMING(dramtmg4), + DDRCTL_REG_TIMING(dramtmg5), + DDRCTL_REG_TIMING(dramtmg6), + DDRCTL_REG_TIMING(dramtmg7), + DDRCTL_REG_TIMING(dramtmg8), + DDRCTL_REG_TIMING(dramtmg14), + DDRCTL_REG_TIMING(odtcfg), +}; + +#define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp1_ddrctrl_map) +static const struct reg_desc ddr_map[] = { + DDRCTL_REG_MAP(addrmap1), + DDRCTL_REG_MAP(addrmap2), + DDRCTL_REG_MAP(addrmap3), + DDRCTL_REG_MAP(addrmap4), + DDRCTL_REG_MAP(addrmap5), + DDRCTL_REG_MAP(addrmap6), + DDRCTL_REG_MAP(addrmap9), + DDRCTL_REG_MAP(addrmap10), + DDRCTL_REG_MAP(addrmap11), +}; + +#define DDRCTL_REG_PERF(x) DDRCTL_REG(x, stm32mp1_ddrctrl_perf) +static const struct reg_desc ddr_perf[] = { + DDRCTL_REG_PERF(sched), + DDRCTL_REG_PERF(sched1), + DDRCTL_REG_PERF(perfhpr1), + DDRCTL_REG_PERF(perflpr1), + DDRCTL_REG_PERF(perfwr1), + DDRCTL_REG_PERF(pcfgr_0), + DDRCTL_REG_PERF(pcfgw_0), + DDRCTL_REG_PERF(pcfgqos0_0), + DDRCTL_REG_PERF(pcfgqos1_0), + DDRCTL_REG_PERF(pcfgwqos0_0), + DDRCTL_REG_PERF(pcfgwqos1_0), + DDRCTL_REG_PERF(pcfgr_1), + DDRCTL_REG_PERF(pcfgw_1), + DDRCTL_REG_PERF(pcfgqos0_1), + DDRCTL_REG_PERF(pcfgqos1_1), + DDRCTL_REG_PERF(pcfgwqos0_1), + DDRCTL_REG_PERF(pcfgwqos1_1), +}; + +#define DDRPHY_REG_REG(x) DDRPHY_REG(x, stm32mp1_ddrphy_reg) +static const struct reg_desc ddrphy_reg[] = { + DDRPHY_REG_REG(pgcr), + DDRPHY_REG_REG(aciocr), + DDRPHY_REG_REG(dxccr), + DDRPHY_REG_REG(dsgcr), + DDRPHY_REG_REG(dcr), + DDRPHY_REG_REG(odtcr), + DDRPHY_REG_REG(zq0cr1), + DDRPHY_REG_REG(dx0gcr), + DDRPHY_REG_REG(dx1gcr), + DDRPHY_REG_REG(dx2gcr), + DDRPHY_REG_REG(dx3gcr), +}; + +#define DDRPHY_REG_TIMING(x) DDRPHY_REG(x, stm32mp1_ddrphy_timing) +static const struct reg_desc ddrphy_timing[] = { + DDRPHY_REG_TIMING(ptr0), + DDRPHY_REG_TIMING(ptr1), + DDRPHY_REG_TIMING(ptr2), + DDRPHY_REG_TIMING(dtpr0), + DDRPHY_REG_TIMING(dtpr1), + DDRPHY_REG_TIMING(dtpr2), + DDRPHY_REG_TIMING(mr0), + DDRPHY_REG_TIMING(mr1), + DDRPHY_REG_TIMING(mr2), + DDRPHY_REG_TIMING(mr3), +}; + +#define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal) +static const struct reg_desc ddrphy_cal[] = { + DDRPHY_REG_CAL(dx0dllcr), + DDRPHY_REG_CAL(dx0dqtr), + DDRPHY_REG_CAL(dx0dqstr), + DDRPHY_REG_CAL(dx1dllcr), + DDRPHY_REG_CAL(dx1dqtr), + DDRPHY_REG_CAL(dx1dqstr), + DDRPHY_REG_CAL(dx2dllcr), + DDRPHY_REG_CAL(dx2dqtr), + DDRPHY_REG_CAL(dx2dqstr), + DDRPHY_REG_CAL(dx3dllcr), + DDRPHY_REG_CAL(dx3dqtr), + 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), +}; + +enum reg_type { + REG_REG, + REG_TIMING, + REG_PERF, + REG_MAP, + 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 +}; + +enum base_type { + DDR_BASE, + DDRPHY_BASE, + NONE_BASE +}; + +struct ddr_reg_info { + const char *name; + const struct reg_desc *desc; + uint8_t size; + enum base_type base; +}; + +static const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { + [REG_REG] = { + "static", ddr_reg, ARRAY_SIZE(ddr_reg), DDR_BASE + }, + [REG_TIMING] = { + "timing", ddr_timing, ARRAY_SIZE(ddr_timing), DDR_BASE + }, + [REG_PERF] = { + "perf", ddr_perf, ARRAY_SIZE(ddr_perf), DDR_BASE + }, + [REG_MAP] = { + "map", ddr_map, ARRAY_SIZE(ddr_map), DDR_BASE + }, + [REGPHY_REG] = { + "static", ddrphy_reg, ARRAY_SIZE(ddrphy_reg), DDRPHY_BASE + }, + [REGPHY_TIMING] = { + "timing", ddrphy_timing, ARRAY_SIZE(ddrphy_timing), DDRPHY_BASE + }, + [REGPHY_CAL] = { + "cal", ddrphy_cal, ARRAY_SIZE(ddrphy_cal), DDRPHY_BASE + }, + [REG_DYN] = { + "dyn", ddr_dyn, ARRAY_SIZE(ddr_dyn), DDR_BASE + }, + [REGPHY_DYN] = { + "dyn", ddrphy_dyn, ARRAY_SIZE(ddrphy_dyn), DDRPHY_BASE + }, +}; + +static uint32_t get_base_addr(const struct ddr_info *priv, enum base_type base) +{ + if (base == DDRPHY_BASE) { + return (uint32_t)priv->phy; + } else { + return (uint32_t)priv->ctl; + } +} + +static void set_reg(const struct ddr_info *priv, + enum reg_type type, + const void *param) +{ + unsigned int i; + unsigned int *ptr, value; + enum base_type base = ddr_registers[type].base; + uint32_t base_addr = get_base_addr(priv, base); + const struct reg_desc *desc = ddr_registers[type].desc; + + VERBOSE("init %s\n", ddr_registers[type].name); + for (i = 0; i < ddr_registers[type].size; i++) { + ptr = (unsigned int *)(base_addr + desc[i].offset); + if (desc[i].par_offset == INVALID_OFFSET) { + ERROR("invalid parameter offset for %s", desc[i].name); + panic(); + } else { + value = *((uint32_t *)((uint32_t)param + + desc[i].par_offset)); + mmio_write_32((uint32_t)ptr, value); + } + } +} + +static void stm32mp1_ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) +{ + uint32_t pgsr; + int error = 0; + unsigned long start; + unsigned long time0, time; + + start = get_timer(0); + time0 = start; + + do { + pgsr = mmio_read_32((uint32_t)&phy->pgsr); + time = get_timer(start); + if (time != time0) { + VERBOSE(" > [0x%x] pgsr = 0x%x &\n", + (uint32_t)&phy->pgsr, pgsr); + VERBOSE(" [0x%x] pir = 0x%x (time=%x)\n", + (uint32_t)&phy->pir, + mmio_read_32((uint32_t)&phy->pir), + (uint32_t)time); + } + + time0 = time; + if (time > plat_get_syscnt_freq2()) { + panic(); + } + if ((pgsr & DDRPHYC_PGSR_DTERR) != 0U) { + VERBOSE("DQS Gate Trainig Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_DTIERR) != 0U) { + VERBOSE("DQS Gate Trainig Intermittent Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_DFTERR) != 0U) { + VERBOSE("DQS Drift Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_RVERR) != 0U) { + VERBOSE("Read Valid Training Error\n"); + error++; + } + if ((pgsr & DDRPHYC_PGSR_RVEIRR) != 0U) { + VERBOSE("Read Valid Training Intermittent Error\n"); + error++; + } + } while ((pgsr & DDRPHYC_PGSR_IDONE) == 0U && error == 0); + VERBOSE("\n[0x%x] pgsr = 0x%x\n", + (uint32_t)&phy->pgsr, pgsr); +} + +static void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, uint32_t pir) +{ + uint32_t pir_init = pir | DDRPHYC_PIR_INIT; + + mmio_write_32((uint32_t)&phy->pir, pir_init); + VERBOSE("[0x%x] pir = 0x%x -> 0x%x\n", + (uint32_t)&phy->pir, pir_init, + mmio_read_32((uint32_t)&phy->pir)); + + /* Need to wait 10 configuration clock before start polling */ + udelay(10); + + /* Wait DRAM initialization and Gate Training Evaluation complete */ + stm32mp1_ddrphy_idone_wait(phy); +} + +/* Start quasi dynamic register update */ +static void stm32mp1_start_sw_done(struct stm32mp1_ddrctl *ctl) +{ + mmio_clrbits_32((uint32_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + VERBOSE("[0x%x] swctl = 0x%x\n", + (uint32_t)&ctl->swctl, mmio_read_32((uint32_t)&ctl->swctl)); +} + +/* Wait quasi dynamic register update */ +static void stm32mp1_wait_sw_done_ack(struct stm32mp1_ddrctl *ctl) +{ + unsigned long start; + uint32_t swstat; + + mmio_setbits_32((uint32_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + VERBOSE("[0x%x] swctl = 0x%x\n", + (uint32_t)&ctl->swctl, mmio_read_32((uint32_t)&ctl->swctl)); + + start = get_timer(0); + do { + swstat = mmio_read_32((uint32_t)&ctl->swstat); + VERBOSE("[0x%x] swstat = 0x%x ", + (uint32_t)&ctl->swstat, swstat); + VERBOSE("timer in ms 0x%x = start 0x%lx\r", + get_timer(0), start); + if (get_timer(start) > plat_get_syscnt_freq2()) { + panic(); + } + } while ((swstat & DDRCTRL_SWSTAT_SW_DONE_ACK) == 0U); + + VERBOSE("[0x%x] swstat = 0x%x\n", + (uint32_t)&ctl->swstat, swstat); +} + +/* Wait quasi dynamic register update */ +static void stm32mp1_wait_operating_mode(struct ddr_info *priv, uint32_t mode) +{ + unsigned long start; + uint32_t stat; + uint32_t operating_mode; + uint32_t selref_type; + int break_loop = 0; + + start = get_timer(0); + for ( ; ; ) { + stat = mmio_read_32((uint32_t)&priv->ctl->stat); + operating_mode = stat & DDRCTRL_STAT_OPERATING_MODE_MASK; + selref_type = stat & DDRCTRL_STAT_SELFREF_TYPE_MASK; + VERBOSE("[0x%x] stat = 0x%x\n", + (uint32_t)&priv->ctl->stat, stat); + VERBOSE("timer in ms 0x%x = start 0x%lx\r", + get_timer(0), start); + if (get_timer(start) > plat_get_syscnt_freq2()) { + panic(); + } + + if (mode == DDRCTRL_STAT_OPERATING_MODE_SR) { + /* + * Self-refresh due to software + * => checking also STAT.selfref_type. + */ + if ((operating_mode == + DDRCTRL_STAT_OPERATING_MODE_SR) && + (selref_type == DDRCTRL_STAT_SELFREF_TYPE_SR)) { + break_loop = 1; + } + } else if (operating_mode == mode) { + break_loop = 1; + } else if ((mode == DDRCTRL_STAT_OPERATING_MODE_NORMAL) && + (operating_mode == DDRCTRL_STAT_OPERATING_MODE_SR) && + (selref_type == DDRCTRL_STAT_SELFREF_TYPE_ASR)) { + /* Normal mode: handle also automatic self refresh */ + break_loop = 1; + } + + if (break_loop == 1) { + break; + } + } + + VERBOSE("[0x%x] stat = 0x%x\n", + (uint32_t)&priv->ctl->stat, stat); +} + +/* Mode Register Writes (MRW or MRS) */ +static void stm32mp1_mode_register_write(struct ddr_info *priv, uint8_t addr, + uint32_t data) +{ + uint32_t mrctrl0; + + VERBOSE("MRS: %d = %x\n", addr, data); + + /* + * 1. Poll MRSTAT.mr_wr_busy until it is '0'. + * This checks that there is no outstanding MR transaction. + * No write should be performed to MRCTRL0 and MRCTRL1 + * if MRSTAT.mr_wr_busy = 1. + */ + while ((mmio_read_32((uint32_t)&priv->ctl->mrstat) & + DDRCTRL_MRSTAT_MR_WR_BUSY) != 0U) { + ; + } + + /* + * 2. Write the MRCTRL0.mr_type, MRCTRL0.mr_addr, MRCTRL0.mr_rank + * and (for MRWs) MRCTRL1.mr_data to define the MR transaction. + */ + mrctrl0 = DDRCTRL_MRCTRL0_MR_TYPE_WRITE | + DDRCTRL_MRCTRL0_MR_RANK_ALL | + (((uint32_t)addr << DDRCTRL_MRCTRL0_MR_ADDR_SHIFT) & + DDRCTRL_MRCTRL0_MR_ADDR_MASK); + mmio_write_32((uint32_t)&priv->ctl->mrctrl0, mrctrl0); + VERBOSE("[0x%x] mrctrl0 = 0x%x (0x%x)\n", + (uint32_t)&priv->ctl->mrctrl0, + mmio_read_32((uint32_t)&priv->ctl->mrctrl0), mrctrl0); + mmio_write_32((uint32_t)&priv->ctl->mrctrl1, data); + VERBOSE("[0x%x] mrctrl1 = 0x%x\n", + (uint32_t)&priv->ctl->mrctrl1, + mmio_read_32((uint32_t)&priv->ctl->mrctrl1)); + + /* + * 3. In a separate APB transaction, write the MRCTRL0.mr_wr to 1. This + * bit is self-clearing, and triggers the MR transaction. + * The uMCTL2 then asserts the MRSTAT.mr_wr_busy while it performs + * the MR transaction to SDRAM, and no further access can be + * initiated until it is deasserted. + */ + mrctrl0 |= DDRCTRL_MRCTRL0_MR_WR; + mmio_write_32((uint32_t)&priv->ctl->mrctrl0, mrctrl0); + + while ((mmio_read_32((uint32_t)&priv->ctl->mrstat) & + DDRCTRL_MRSTAT_MR_WR_BUSY) != 0U) { + ; + } + + VERBOSE("[0x%x] mrctrl0 = 0x%x\n", + (uint32_t)&priv->ctl->mrctrl0, mrctrl0); +} + +/* Switch DDR3 from DLL-on to DLL-off */ +static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) +{ + uint32_t mr1 = mmio_read_32((uint32_t)&priv->phy->mr1); + uint32_t mr2 = mmio_read_32((uint32_t)&priv->phy->mr2); + uint32_t dbgcam; + + VERBOSE("mr1: 0x%x\n", mr1); + VERBOSE("mr2: 0x%x\n", mr2); + + /* + * 1. Set the DBG1.dis_hif = 1. + * This prevents further reads/writes being received on the HIF. + */ + mmio_setbits_32((uint32_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%x] dbg1 = 0x%x\n", + (uint32_t)&priv->ctl->dbg1, + mmio_read_32((uint32_t)&priv->ctl->dbg1)); + + /* + * 2. Ensure all commands have been flushed from the uMCTL2 by polling + * DBGCAM.wr_data_pipeline_empty = 1, + * DBGCAM.rd_data_pipeline_empty = 1, + * DBGCAM.dbg_wr_q_depth = 0 , + * DBGCAM.dbg_lpr_q_depth = 0, and + * DBGCAM.dbg_hpr_q_depth = 0. + */ + do { + dbgcam = mmio_read_32((uint32_t)&priv->ctl->dbgcam); + VERBOSE("[0x%x] dbgcam = 0x%x\n", + (uint32_t)&priv->ctl->dbgcam, dbgcam); + } while ((((dbgcam & DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY) == + DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY)) && + ((dbgcam & DDRCTRL_DBGCAM_DBG_Q_DEPTH) == 0U)); + + /* + * 3. Perform an MRS command (using MRCTRL0 and MRCTRL1 registers) + * to disable RTT_NOM: + * a. DDR3: Write to MR1[9], MR1[6] and MR1[2] + * b. DDR4: Write to MR1[10:8] + */ + mr1 &= ~(BIT(9) | BIT(6) | BIT(2)); + stm32mp1_mode_register_write(priv, 1, mr1); + + /* + * 4. For DDR4 only: Perform an MRS command + * (using MRCTRL0 and MRCTRL1 registers) to write to MR5[8:6] + * to disable RTT_PARK + */ + + /* + * 5. Perform an MRS command (using MRCTRL0 and MRCTRL1 registers) + * to write to MR2[10:9], to disable RTT_WR + * (and therefore disable dynamic ODT). + * This applies for both DDR3 and DDR4. + */ + mr2 &= ~GENMASK(10, 9); + stm32mp1_mode_register_write(priv, 2, mr2); + + /* + * 6. Perform an MRS command (using MRCTRL0 and MRCTRL1 registers) + * to disable the DLL. The timing of this MRS is automatically + * handled by the uMCTL2. + * a. DDR3: Write to MR1[0] + * b. DDR4: Write to MR1[0] + */ + mr1 |= BIT(0); + stm32mp1_mode_register_write(priv, 1, mr1); + + /* + * 7. Put the SDRAM into self-refresh mode by setting + * PWRCTL.selfref_sw = 1, and polling STAT.operating_mode to ensure + * the DDRC has entered self-refresh. + */ + mmio_setbits_32((uint32_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + VERBOSE("[0x%x] pwrctl = 0x%x\n", + (uint32_t)&priv->ctl->pwrctl, + mmio_read_32((uint32_t)&priv->ctl->pwrctl)); + + /* + * 8. Wait until STAT.operating_mode[1:0]==11 indicating that the + * DWC_ddr_umctl2 core is in self-refresh mode. + * Ensure transition to self-refresh was due to software + * by checking that STAT.selfref_type[1:0]=2. + */ + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_SR); + + /* + * 9. Set the MSTR.dll_off_mode = 1. + * warning: MSTR.dll_off_mode is a quasi-dynamic type 2 field + */ + stm32mp1_start_sw_done(priv->ctl); + + mmio_setbits_32((uint32_t)&priv->ctl->mstr, DDRCTRL_MSTR_DLL_OFF_MODE); + VERBOSE("[0x%x] mstr = 0x%x\n", + (uint32_t)&priv->ctl->mstr, + mmio_read_32((uint32_t)&priv->ctl->mstr)); + + stm32mp1_wait_sw_done_ack(priv->ctl); + + /* 10. Change the clock frequency to the desired value. */ + + /* + * 11. Update any registers which may be required to change for the new + * frequency. This includes static and dynamic registers. + * This includes both uMCTL2 registers and PHY registers. + */ + + /* Change Bypass Mode Frequency Range */ + if (stm32mp1_clk_get_rate(DDRPHYC) < 100000000U) { + mmio_clrbits_32((uint32_t)&priv->phy->dllgcr, + DDRPHYC_DLLGCR_BPS200); + } else { + mmio_setbits_32((uint32_t)&priv->phy->dllgcr, + DDRPHYC_DLLGCR_BPS200); + } + + mmio_setbits_32((uint32_t)&priv->phy->acdllcr, DDRPHYC_ACDLLCR_DLLDIS); + + mmio_setbits_32((uint32_t)&priv->phy->dx0dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + mmio_setbits_32((uint32_t)&priv->phy->dx1dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + mmio_setbits_32((uint32_t)&priv->phy->dx2dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + mmio_setbits_32((uint32_t)&priv->phy->dx3dllcr, + DDRPHYC_DXNDLLCR_DLLDIS); + + /* 12. Exit the self-refresh state by setting PWRCTL.selfref_sw = 0. */ + mmio_clrbits_32((uint32_t)&priv->ctl->pwrctl, + DDRCTRL_PWRCTL_SELFREF_SW); + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); + + /* + * 13. If ZQCTL0.dis_srx_zqcl = 0, the uMCTL2 performs a ZQCL command + * at this point. + */ + + /* + * 14. Perform MRS commands as required to re-program timing registers + * in the SDRAM for the new frequency + * (in particular, CL, CWL and WR may need to be changed). + */ + + /* 15. Write DBG1.dis_hif = 0 to re-enable reads and writes. */ + mmio_clrbits_32((uint32_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%x] dbg1 = 0x%x\n", + (uint32_t)&priv->ctl->dbg1, + mmio_read_32((uint32_t)&priv->ctl->dbg1)); +} + +static void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) +{ + stm32mp1_start_sw_done(ctl); + /* Quasi-dynamic register update*/ + mmio_setbits_32((uint32_t)&ctl->rfshctl3, + DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + mmio_clrbits_32((uint32_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + mmio_clrbits_32((uint32_t)&ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + stm32mp1_wait_sw_done_ack(ctl); +} + +static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, + uint32_t rfshctl3, uint32_t pwrctl) +{ + stm32mp1_start_sw_done(ctl); + if ((rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH) == 0U) { + mmio_clrbits_32((uint32_t)&ctl->rfshctl3, + DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + } + if ((pwrctl & DDRCTRL_PWRCTL_POWERDOWN_EN) != 0U) { + mmio_setbits_32((uint32_t)&ctl->pwrctl, + DDRCTRL_PWRCTL_POWERDOWN_EN); + } + mmio_setbits_32((uint32_t)&ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + stm32mp1_wait_sw_done_ack(ctl); +} + +static int board_ddr_power_init(enum ddr_type ddr_type) +{ + if (dt_check_pmic()) { + return pmic_ddr_power_init(ddr_type); + } + + return 0; +} + +void stm32mp1_ddr_init(struct ddr_info *priv, + struct stm32mp1_ddr_config *config) +{ + uint32_t pir; + int ret; + + if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { + ret = board_ddr_power_init(STM32MP_DDR3); + } else { + ret = board_ddr_power_init(STM32MP_LPDDR2); + } + + if (ret != 0) { + panic(); + } + + VERBOSE("name = %s\n", config->info.name); + VERBOSE("speed = %d MHz\n", config->info.speed); + VERBOSE("size = 0x%x\n", config->info.size); + + /* DDR INIT SEQUENCE */ + + /* + * 1. Program the DWC_ddr_umctl2 registers + * nota: check DFIMISC.dfi_init_complete = 0 + */ + + /* 1.1 RESETS: presetn, core_ddrc_rstn, aresetn */ + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST); + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST); + + /* 1.2. start CLOCK */ + if (stm32mp1_ddr_clk_enable(priv, config->info.speed) != 0) { + panic(); + } + + /* 1.3. deassert reset */ + /* De-assert PHY rstn and ctl_rstn via DPHYRST and DPHYCTLRST. */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST); + /* + * De-assert presetn once the clocks are active + * and stable via DDRCAPBRST bit. + */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); + + /* 1.4. wait 128 cycles to permit initialization of end logic */ + udelay(2); + /* For PCLK = 133MHz => 1 us is enough, 2 to allow lower frequency */ + + /* 1.5. initialize registers ddr_umctl2 */ + /* Stop uMCTL2 before PHY is ready */ + mmio_clrbits_32((uint32_t)&priv->ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + VERBOSE("[0x%x] dfimisc = 0x%x\n", + (uint32_t)&priv->ctl->dfimisc, + mmio_read_32((uint32_t)&priv->ctl->dfimisc)); + + set_reg(priv, REG_REG, &config->c_reg); + + /* DDR3 = don't set DLLOFF for init mode */ + if ((config->c_reg.mstr & + (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) + == (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) { + VERBOSE("deactivate DLL OFF in mstr\n"); + mmio_clrbits_32((uint32_t)&priv->ctl->mstr, + DDRCTRL_MSTR_DLL_OFF_MODE); + VERBOSE("[0x%x] mstr = 0x%x\n", + (uint32_t)&priv->ctl->mstr, + mmio_read_32((uint32_t)&priv->ctl->mstr)); + } + + set_reg(priv, REG_TIMING, &config->c_timing); + set_reg(priv, REG_MAP, &config->c_map); + + /* Skip CTRL init, SDRAM init is done by PHY PUBL */ + mmio_clrsetbits_32((uint32_t)&priv->ctl->init0, + DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK, + DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL); + VERBOSE("[0x%x] init0 = 0x%x\n", + (uint32_t)&priv->ctl->init0, + mmio_read_32((uint32_t)&priv->ctl->init0)); + + set_reg(priv, REG_PERF, &config->c_perf); + + /* 2. deassert reset signal core_ddrc_rstn, aresetn and presetn */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST); + + /* + * 3. start PHY init by accessing relevant PUBL registers + * (DXGCR, DCR, PTR*, MR*, DTPR*) + */ + set_reg(priv, REGPHY_REG, &config->p_reg); + set_reg(priv, REGPHY_TIMING, &config->p_timing); + set_reg(priv, REGPHY_CAL, &config->p_cal); + + /* DDR3 = don't set DLLOFF for init mode */ + if ((config->c_reg.mstr & + (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) + == (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) { + VERBOSE("deactivate DLL OFF in mr1\n"); + mmio_clrbits_32((uint32_t)&priv->phy->mr1, BIT(0)); + VERBOSE("[0x%x] mr1 = 0x%x\n", + (uint32_t)&priv->phy->mr1, + mmio_read_32((uint32_t)&priv->phy->mr1)); + } + + /* + * 4. Monitor PHY init status by polling PUBL register PGSR.IDONE + * Perform DDR PHY DRAM initialization and Gate Training Evaluation + */ + stm32mp1_ddrphy_idone_wait(priv->phy); + + /* + * 5. Indicate to PUBL that controller performs SDRAM initialization + * by setting PIR.INIT and PIR CTLDINIT and pool PGSR.IDONE + * DRAM init is done by PHY, init0.skip_dram.init = 1 + */ + + pir = DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | DDRPHYC_PIR_ZCAL | + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_DRAMINIT | DDRPHYC_PIR_ICPC; + + if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { + pir |= DDRPHYC_PIR_DRAMRST; /* Only for DDR3 */ + } + + stm32mp1_ddrphy_init(priv->phy, pir); + + /* + * 6. SET DFIMISC.dfi_init_complete_en to 1 + * Enable quasi-dynamic register programming. + */ + stm32mp1_start_sw_done(priv->ctl); + + mmio_setbits_32((uint32_t)&priv->ctl->dfimisc, + DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + VERBOSE("[0x%x] dfimisc = 0x%x\n", + (uint32_t)&priv->ctl->dfimisc, + mmio_read_32((uint32_t)&priv->ctl->dfimisc)); + + stm32mp1_wait_sw_done_ack(priv->ctl); + + /* + * 7. Wait for DWC_ddr_umctl2 to move to normal operation mode + * by monitoring STAT.operating_mode signal + */ + + /* Wait uMCTL2 ready */ + stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); + + /* Switch to DLL OFF mode */ + if ((config->c_reg.mstr & DDRCTRL_MSTR_DLL_OFF_MODE) != 0U) { + 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); + + /* + * 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((uint32_t)&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%x] pctrl_0 = 0x%x\n", + (uint32_t)&priv->ctl->pctrl_0, + mmio_read_32((uint32_t)&priv->ctl->pctrl_0)); + + /* Enable uMCTL2 AXI port 1 */ + mmio_setbits_32((uint32_t)&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%x] pctrl_1 = 0x%x\n", + (uint32_t)&priv->ctl->pctrl_1, + mmio_read_32((uint32_t)&priv->ctl->pctrl_1)); +} diff --git a/drivers/st/ddr/stm32mp1_ddr_helpers.c b/drivers/st/ddr/stm32mp1_ddr_helpers.c new file mode 100644 index 0000000..325c0b8 --- /dev/null +++ b/drivers/st/ddr/stm32mp1_ddr_helpers.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +void ddr_enable_clock(void) +{ + mmio_setbits_32(RCC_BASE + RCC_DDRITFCR, + RCC_DDRITFCR_DDRC1EN | + RCC_DDRITFCR_DDRC2EN | + RCC_DDRITFCR_DDRPHYCEN | + RCC_DDRITFCR_DDRPHYCAPBEN | + RCC_DDRITFCR_DDRCAPBEN); +} diff --git a/drivers/st/ddr/stm32mp1_ram.c b/drivers/st/ddr/stm32mp1_ram.c new file mode 100644 index 0000000..6d515ec --- /dev/null +++ b/drivers/st/ddr/stm32mp1_ram.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2018, 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 +#include + +#define DDR_PATTERN 0xAAAAAAAAU +#define DDR_ANTIPATTERN 0x55555555U + +static struct ddr_info ddr_priv_data; + +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) +{ + unsigned long ddrphy_clk, ddr_clk, mem_speed_hz; + + ddr_enable_clock(); + + ddrphy_clk = stm32mp1_clk_get_rate(DDRPHYC); + + VERBOSE("DDR: mem_speed (%d MHz), RCC %ld MHz\n", + mem_speed, ddrphy_clk / 1000U / 1000U); + + mem_speed_hz = (uint32_t)mem_speed * 1000U * 1000U; + + /* Max 10% frequency delta */ + if (ddrphy_clk > mem_speed_hz) { + ddr_clk = ddrphy_clk - mem_speed_hz; + } else { + ddr_clk = mem_speed_hz - ddrphy_clk; + } + if (ddr_clk > mem_speed_hz) { + ERROR("DDR expected freq %d MHz, current is %ld MHz\n", + mem_speed, ddrphy_clk / 1000U / 1000U); + return -1; + } + return 0; +} + +/******************************************************************************* + * This function tests the DDR data bus wiring. + * This is inspired from the Data Bus Test algorithm written by Michael Barr + * in "Programming Embedded Systems in C and C++" book. + * resources.oreilly.com/examples/9781565923546/blob/master/Chapter6/ + * File: memtest.c - This source code belongs to Public Domain. + * Returns 0 if success, and address value else. + ******************************************************************************/ +static uint32_t ddr_test_data_bus(void) +{ + uint32_t pattern; + + for (pattern = 1U; pattern != 0U; pattern <<= 1) { + mmio_write_32(STM32MP1_DDR_BASE, pattern); + + if (mmio_read_32(STM32MP1_DDR_BASE) != pattern) { + return (uint32_t)STM32MP1_DDR_BASE; + } + } + + return 0; +} + +/******************************************************************************* + * This function tests the DDR address bus wiring. + * This is inspired from the Data Bus Test algorithm written by Michael Barr + * in "Programming Embedded Systems in C and C++" book. + * resources.oreilly.com/examples/9781565923546/blob/master/Chapter6/ + * File: memtest.c - This source code belongs to Public Domain. + * Returns 0 if success, and address value else. + ******************************************************************************/ +static uint32_t ddr_test_addr_bus(void) +{ + uint64_t addressmask = (ddr_priv_data.info.size - 1U); + uint64_t offset; + uint64_t testoffset = 0; + + /* Write the default pattern at each of the power-of-two offsets. */ + for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + offset <<= 1) { + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)offset, + DDR_PATTERN); + } + + /* Check for address bits stuck high. */ + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, + DDR_ANTIPATTERN); + + for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + offset <<= 1) { + if (mmio_read_32(STM32MP1_DDR_BASE + (uint32_t)offset) != + DDR_PATTERN) { + return (uint32_t)(STM32MP1_DDR_BASE + offset); + } + } + + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, DDR_PATTERN); + + /* Check for address bits stuck low or shorted. */ + for (testoffset = sizeof(uint32_t); (testoffset & addressmask) != 0U; + testoffset <<= 1) { + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, + DDR_ANTIPATTERN); + + if (mmio_read_32(STM32MP1_DDR_BASE) != DDR_PATTERN) { + return STM32MP1_DDR_BASE; + } + + for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; + offset <<= 1) { + if ((mmio_read_32(STM32MP1_DDR_BASE + + (uint32_t)offset) != DDR_PATTERN) && + (offset != testoffset)) { + return (uint32_t)(STM32MP1_DDR_BASE + offset); + } + } + + mmio_write_32(STM32MP1_DDR_BASE + (uint32_t)testoffset, + DDR_PATTERN); + } + + return 0; +} + +/******************************************************************************* + * This function checks the DDR size. It has to be run with Data Cache off. + * This test is run before data have been put in DDR, and is only done for + * cold boot. The DDR data can then be overwritten, and it is not useful to + * restore its content. + * Returns DDR computed size. + ******************************************************************************/ +static uint32_t ddr_check_size(void) +{ + uint32_t offset = sizeof(uint32_t); + + mmio_write_32(STM32MP1_DDR_BASE, DDR_PATTERN); + + while (offset < STM32MP1_DDR_MAX_SIZE) { + mmio_write_32(STM32MP1_DDR_BASE + offset, DDR_ANTIPATTERN); + dsb(); + + if (mmio_read_32(STM32MP1_DDR_BASE) != DDR_PATTERN) { + break; + } + + offset <<= 1; + } + + INFO("Memory size = 0x%x (%d MB)\n", offset, offset / (1024U * 1024U)); + + return offset; +} + +static int stm32mp1_ddr_setup(void) +{ + struct ddr_info *priv = &ddr_priv_data; + int ret; + struct stm32mp1_ddr_config config; + int node, len; + uint32_t tamp_clk_off = 0, uret, idx; + void *fdt; + +#define PARAM(x, y) \ + { \ + .name = x, \ + .offset = offsetof(struct stm32mp1_ddr_config, y), \ + .size = sizeof(config.y) / sizeof(uint32_t) \ + } + +#define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x) +#define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x) + + const struct { + const char *name; /* Name in DT */ + const uint32_t offset; /* Offset in config struct */ + const uint32_t size; /* Size of parameters */ + } param[] = { + CTL_PARAM(reg), + CTL_PARAM(timing), + CTL_PARAM(map), + CTL_PARAM(perf), + PHY_PARAM(reg), + PHY_PARAM(timing), + PHY_PARAM(cal) + }; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); + if (node < 0) { + ERROR("%s: Cannot read DDR node in DT\n", __func__); + return -EINVAL; + } + + config.info.speed = + (uint16_t)fdt_read_uint32_default(node, "st,mem-speed", + STM32MP1_DDR_SPEED_DFLT); + config.info.size = fdt_read_uint32_default(node, "st,mem-size", + STM32MP1_DDR_SIZE_DFLT); + config.info.name = fdt_getprop(fdt, node, "st,mem-name", &len); + if (config.info.name == NULL) { + VERBOSE("%s: no st,mem-name\n", __func__); + return -EINVAL; + } + INFO("RAM: %s\n", config.info.name); + + for (idx = 0; idx < ARRAY_SIZE(param); idx++) { + ret = fdt_read_uint32_array(node, param[idx].name, + (void *)((uint32_t)&config + + param[idx].offset), + param[idx].size); + + 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); + return -EINVAL; + } + } + + if (!stm32mp1_clk_is_enabled(RTCAPB)) { + tamp_clk_off = 1; + if (stm32mp1_clk_enable(RTCAPB) != 0) { + return -EINVAL; + } + } + + if (tamp_clk_off != 0U) { + if (stm32mp1_clk_disable(RTCAPB) != 0) { + return -EINVAL; + } + } + + /* Disable axidcg clock gating during init */ + mmio_clrbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + stm32mp1_ddr_init(priv, &config); + + /* Enable axidcg clock gating */ + mmio_setbits_32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_AXIDCGEN); + + priv->info.size = config.info.size; + + VERBOSE("%s : ram size(%x, %x)\n", __func__, + (uint32_t)priv->info.base, (uint32_t)priv->info.size); + + dcsw_op_all(DC_OP_CISW); + write_sctlr(read_sctlr() & ~SCTLR_C_BIT); + + 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_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); + + return 0; +} + +int stm32mp1_ddr_probe(void) +{ + struct ddr_info *priv = &ddr_priv_data; + + VERBOSE("STM32MP DDR probe\n"); + + priv->ctl = (struct stm32mp1_ddrctl *)DDRCTRL_BASE; + priv->phy = (struct stm32mp1_ddrphy *)DDRPHYC_BASE; + priv->pwr = PWR_BASE; + priv->rcc = RCC_BASE; + + priv->info.base = STM32MP1_DDR_BASE; + priv->info.size = 0; + + return stm32mp1_ddr_setup(); +} diff --git a/drivers/st/gpio/stm32_gpio.c b/drivers/st/gpio/stm32_gpio.c new file mode 100644 index 0000000..200b473 --- /dev/null +++ b/drivers/st/gpio/stm32_gpio.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static bool check_gpio(uint32_t bank, uint32_t pin) +{ + if (pin > GPIO_PIN_MAX) { + ERROR("%s: wrong pin number (%d)\n", __func__, pin); + return false; + } + + if ((bank > GPIO_BANK_K) && (bank != GPIO_BANK_Z)) { + ERROR("%s: wrong GPIO bank number (%d)\n", __func__, bank); + return false; + } + + return true; +} + +void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, + uint32_t pull, uint32_t alternate) +{ + volatile uint32_t bank_address; + + if (!check_gpio(bank, pin)) { + return; + } + + if (bank == GPIO_BANK_Z) { + bank_address = STM32_GPIOZ_BANK; + } else { + bank_address = STM32_GPIOA_BANK + + (bank * STM32_GPIO_BANK_OFFSET); + } + + mmio_clrbits_32(bank_address + GPIO_MODE_OFFSET, + ((uint32_t)GPIO_MODE_MASK << (pin << 1))); + mmio_setbits_32(bank_address + GPIO_MODE_OFFSET, + (mode & ~GPIO_OPEN_DRAIN) << (pin << 1)); + + if ((mode & GPIO_OPEN_DRAIN) != 0U) { + mmio_setbits_32(bank_address + GPIO_TYPE_OFFSET, + BIT(pin)); + } + + mmio_clrbits_32(bank_address + GPIO_SPEED_OFFSET, + ((uint32_t)GPIO_SPEED_MASK << (pin << 1))); + mmio_setbits_32(bank_address + GPIO_SPEED_OFFSET, speed << (pin << 1)); + + mmio_clrbits_32(bank_address + GPIO_PUPD_OFFSET, + ((uint32_t)GPIO_PULL_MASK << (pin << 1))); + mmio_setbits_32(bank_address + GPIO_PUPD_OFFSET, pull << (pin << 1)); + + if (pin < GPIO_ALT_LOWER_LIMIT) { + mmio_clrbits_32(bank_address + GPIO_AFRL_OFFSET, + ((uint32_t)GPIO_ALTERNATE_MASK << (pin << 2))); + mmio_setbits_32(bank_address + GPIO_AFRL_OFFSET, + alternate << (pin << 2)); + } else { + mmio_clrbits_32(bank_address + GPIO_AFRH_OFFSET, + ((uint32_t)GPIO_ALTERNATE_MASK << + ((pin - GPIO_ALT_LOWER_LIMIT) << 2))); + mmio_setbits_32(bank_address + GPIO_AFRH_OFFSET, + alternate << ((pin - GPIO_ALT_LOWER_LIMIT) << + 2)); + } + + VERBOSE("GPIO %u mode set to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_MODE_OFFSET)); + VERBOSE("GPIO %u speed set to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_SPEED_OFFSET)); + VERBOSE("GPIO %u mode pull to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_PUPD_OFFSET)); + VERBOSE("GPIO %u mode alternate low to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_AFRL_OFFSET)); + VERBOSE("GPIO %u mode alternate high to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_AFRH_OFFSET)); +} diff --git a/drivers/st/pmic/stm32_i2c.c b/drivers/st/pmic/stm32_i2c.c new file mode 100644 index 0000000..0980139 --- /dev/null +++ b/drivers/st/pmic/stm32_i2c.c @@ -0,0 +1,851 @@ +/* + * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +/* STM32 I2C registers offsets */ +#define I2C_CR1 0x00U +#define I2C_CR2 0x04U +#define I2C_OAR1 0x08U +#define I2C_OAR2 0x0CU +#define I2C_TIMINGR 0x10U +#define I2C_TIMEOUTR 0x14U +#define I2C_ISR 0x18U +#define I2C_ICR 0x1CU +#define I2C_PECR 0x20U +#define I2C_RXDR 0x24U +#define I2C_TXDR 0x28U + +#define MAX_DELAY 0xFFFFFFFFU + +/* I2C TIMING clear register Mask */ +#define TIMING_CLEAR_MASK 0xF0FFFFFFU +/* Timeout 25 ms */ +#define I2C_TIMEOUT_BUSY 25U + +#define MAX_NBYTE_SIZE 255U + +static int i2c_request_memory_write(struct i2c_handle_s *hi2c, + uint16_t dev_addr, uint16_t mem_addr, + uint16_t mem_add_size, uint32_t timeout, + uint32_t tick_start); +static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t mem_addr, uint16_t mem_add_size, + uint32_t timeout, uint32_t tick_start); + +/* Private functions to handle flags during polling transfer */ +static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag, + uint8_t awaited_value, uint32_t timeout, + uint32_t tick_start); +static int i2c_wait_txis(struct i2c_handle_s *hi2c, uint32_t timeout, + uint32_t tick_start); +static int i2c_wait_stop(struct i2c_handle_s *hi2c, uint32_t timeout, + uint32_t tick_start); +static int i2c_ack_failed(struct i2c_handle_s *hi2c, uint32_t timeout, + uint32_t tick_start); + +/* Private function to flush TXDR register */ +static void i2c_flush_txdr(struct i2c_handle_s *hi2c); + +/* Private function to start, restart or stop a transfer */ +static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t size, uint32_t i2c_mode, + uint32_t request); + +/* + * @brief Initialize the I2C device. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @retval 0 if OK, negative value else + */ +int stm32_i2c_init(struct i2c_handle_s *hi2c) +{ + if (hi2c == NULL) { + return -ENOENT; + } + + if (hi2c->i2c_state == I2C_STATE_RESET) { + hi2c->lock = 0; + } + + hi2c->i2c_state = I2C_STATE_BUSY; + + /* Disable the selected I2C peripheral */ + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE); + + /* Configure I2Cx: Frequency range */ + mmio_write_32(hi2c->i2c_base_addr + I2C_TIMINGR, + hi2c->i2c_init.timing & TIMING_CLEAR_MASK); + + /* Disable Own Address1 before set the Own Address1 configuration */ + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_OAR1, I2C_OAR1_OA1EN); + + /* Configure I2Cx: Own Address1 and ack own address1 mode */ + if (hi2c->i2c_init.addressing_mode == I2C_ADDRESSINGMODE_7BIT) { + mmio_write_32(hi2c->i2c_base_addr + I2C_OAR1, + I2C_OAR1_OA1EN | hi2c->i2c_init.own_address1); + } else { /* I2C_ADDRESSINGMODE_10BIT */ + mmio_write_32(hi2c->i2c_base_addr + I2C_OAR1, + I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE | + hi2c->i2c_init.own_address1); + } + + /* Configure I2Cx: Addressing Master mode */ + if (hi2c->i2c_init.addressing_mode == I2C_ADDRESSINGMODE_10BIT) { + mmio_write_32(hi2c->i2c_base_addr + I2C_CR2, I2C_CR2_ADD10); + } + + /* + * Enable the AUTOEND by default, and enable NACK + * (should be disable only during Slave process) + */ + mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2, + I2C_CR2_AUTOEND | I2C_CR2_NACK); + + /* Disable Own Address2 before set the Own Address2 configuration */ + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_OAR2, I2C_DUALADDRESS_ENABLE); + + /* Configure I2Cx: Dual mode and Own Address2 */ + mmio_write_32(hi2c->i2c_base_addr + I2C_OAR2, + hi2c->i2c_init.dual_address_mode | + hi2c->i2c_init.own_address2 | + (hi2c->i2c_init.own_address2_masks << 8)); + + /* Configure I2Cx: Generalcall and NoStretch mode */ + mmio_write_32(hi2c->i2c_base_addr + I2C_CR1, + hi2c->i2c_init.general_call_mode | + hi2c->i2c_init.no_stretch_mode); + + /* Enable the selected I2C peripheral */ + mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE); + + hi2c->i2c_err = I2C_ERROR_NONE; + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + return 0; +} + +/* + * @brief Write an amount of data in blocking mode to a specific memory address + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param dev_addr: Target device address + * @param mem_addr: Internal memory address + * @param mem_add_size: size of internal memory address + * @param p_data: Pointer to data buffer + * @param size: Amount of data to be sent + * @param timeout: timeout duration + * @retval 0 if OK, negative value else + */ +int stm32_i2c_mem_write(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t mem_addr, uint16_t mem_add_size, + uint8_t *p_data, uint16_t size, uint32_t timeout) +{ + uint32_t tickstart; + + if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) { + return -EBUSY; + } + + if ((p_data == NULL) || (size == 0U)) { + return -EINVAL; + } + + hi2c->lock = 1; + + tickstart = (uint32_t)read_cntpct_el0(); + + if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, I2C_TIMEOUT_BUSY, + tickstart) != 0) { + return -EIO; + } + + hi2c->i2c_state = I2C_STATE_BUSY_TX; + hi2c->i2c_mode = I2C_MODE_MEM; + hi2c->i2c_err = I2C_ERROR_NONE; + + hi2c->p_buff = p_data; + hi2c->xfer_count = size; + + /* Send Slave Address and Memory Address */ + if (i2c_request_memory_write(hi2c, dev_addr, mem_addr, mem_add_size, + timeout, tickstart) != 0) { + hi2c->lock = 0; + return -EIO; + } + + /* + * Set NBYTES to write and reload + * if hi2c->xfer_count > MAX_NBYTE_SIZE + */ + if (hi2c->xfer_count > MAX_NBYTE_SIZE) { + hi2c->xfer_size = MAX_NBYTE_SIZE; + i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size, + I2C_RELOAD_MODE, I2C_NO_STARTSTOP); + } else { + hi2c->xfer_size = hi2c->xfer_count; + i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size, + I2C_AUTOEND_MODE, I2C_NO_STARTSTOP); + } + + do { + if (i2c_wait_txis(hi2c, timeout, tickstart) != 0) { + return -EIO; + } + + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, *hi2c->p_buff); + hi2c->p_buff++; + hi2c->xfer_count--; + hi2c->xfer_size--; + + if ((hi2c->xfer_count != 0U) && (hi2c->xfer_size == 0U)) { + /* Wait until TCR flag is set */ + if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout, + tickstart) != 0) { + return -EIO; + } + + if (hi2c->xfer_count > MAX_NBYTE_SIZE) { + hi2c->xfer_size = MAX_NBYTE_SIZE; + i2c_transfer_config(hi2c, dev_addr, + hi2c->xfer_size, + I2C_RELOAD_MODE, + I2C_NO_STARTSTOP); + } else { + hi2c->xfer_size = hi2c->xfer_count; + i2c_transfer_config(hi2c, dev_addr, + hi2c->xfer_size, + I2C_AUTOEND_MODE, + I2C_NO_STARTSTOP); + } + } + + } while (hi2c->xfer_count > 0U); + + /* + * No need to Check TC flag, with AUTOEND mode the stop + * is automatically generated. + * Wait until STOPF flag is reset. + */ + if (i2c_wait_stop(hi2c, timeout, tickstart) != 0) { + return -EIO; + } + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF); + + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2); + + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + + return 0; +} + +/* + * @brief Read an amount of data in blocking mode from a specific memory + * address + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param dev_addr: Target device address + * @param mem_addr: Internal memory address + * @param mem_add_size: size of internal memory address + * @param p_data: Pointer to data buffer + * @param size: Amount of data to be sent + * @param timeout: timeout duration + * @retval 0 if OK, negative value else + */ +int stm32_i2c_mem_read(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t mem_addr, uint16_t mem_add_size, + uint8_t *p_data, uint16_t size, uint32_t timeout) +{ + uint32_t tickstart; + + if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) { + return -EBUSY; + } + + if ((p_data == NULL) || (size == 0U)) { + return -EINVAL; + } + + hi2c->lock = 1; + + tickstart = (uint32_t)read_cntpct_el0(); + + if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, I2C_TIMEOUT_BUSY, + tickstart) != 0) { + return -EIO; + } + + hi2c->i2c_state = I2C_STATE_BUSY_RX; + hi2c->i2c_mode = I2C_MODE_MEM; + hi2c->i2c_err = I2C_ERROR_NONE; + + hi2c->p_buff = p_data; + hi2c->xfer_count = size; + + /* Send Slave Address and Memory Address */ + if (i2c_request_memory_read(hi2c, dev_addr, mem_addr, mem_add_size, + timeout, tickstart) != 0) { + hi2c->lock = 0; + return -EIO; + } + + /* + * Send Slave Address. + * Set NBYTES to write and reload if hi2c->xfer_count > MAX_NBYTE_SIZE + * and generate RESTART. + */ + if (hi2c->xfer_count > MAX_NBYTE_SIZE) { + hi2c->xfer_size = MAX_NBYTE_SIZE; + i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size, + I2C_RELOAD_MODE, I2C_GENERATE_START_READ); + } else { + hi2c->xfer_size = hi2c->xfer_count; + i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size, + I2C_AUTOEND_MODE, I2C_GENERATE_START_READ); + } + + do { + if (i2c_wait_flag(hi2c, I2C_FLAG_RXNE, 0, timeout, + tickstart) != 0) { + return -EIO; + } + + *hi2c->p_buff = mmio_read_8(hi2c->i2c_base_addr + I2C_RXDR); + hi2c->p_buff++; + hi2c->xfer_size--; + hi2c->xfer_count--; + + if ((hi2c->xfer_count != 0U) && (hi2c->xfer_size == 0U)) { + if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout, + tickstart) != 0) { + return -EIO; + } + + if (hi2c->xfer_count > MAX_NBYTE_SIZE) { + hi2c->xfer_size = MAX_NBYTE_SIZE; + i2c_transfer_config(hi2c, dev_addr, + hi2c->xfer_size, + I2C_RELOAD_MODE, + I2C_NO_STARTSTOP); + } else { + hi2c->xfer_size = hi2c->xfer_count; + i2c_transfer_config(hi2c, dev_addr, + hi2c->xfer_size, + I2C_AUTOEND_MODE, + I2C_NO_STARTSTOP); + } + } + } while (hi2c->xfer_count > 0U); + + /* + * No need to Check TC flag, with AUTOEND mode the stop + * is automatically generated + * Wait until STOPF flag is reset + */ + if (i2c_wait_stop(hi2c, timeout, tickstart) != 0) { + return -EIO; + } + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF); + + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2); + + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + + return 0; +} + +/* + * @brief Checks if target device is ready for communication. + * @note This function is used with Memory devices + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param dev_addr: Target device address + * @param trials: Number of trials + * @param timeout: timeout duration + * @retval 0 if OK, negative value else + */ +int stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c, + uint16_t dev_addr, uint32_t trials, + uint32_t timeout) +{ + uint32_t i2c_trials = 0U; + + if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) { + return -EBUSY; + } + + if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_BUSY) != + 0U) { + return -EBUSY; + } + + hi2c->lock = 1; + + hi2c->i2c_state = I2C_STATE_BUSY; + hi2c->i2c_err = I2C_ERROR_NONE; + + do { + uint32_t tickstart; + + /* Generate Start */ + if (hi2c->i2c_init.addressing_mode == I2C_ADDRESSINGMODE_7BIT) { + mmio_write_32(hi2c->i2c_base_addr + I2C_CR2, + (((uint32_t)dev_addr & I2C_CR2_SADD) | + I2C_CR2_START | I2C_CR2_AUTOEND) & + ~I2C_CR2_RD_WRN); + } else { + mmio_write_32(hi2c->i2c_base_addr + I2C_CR2, + (((uint32_t)dev_addr & I2C_CR2_SADD) | + I2C_CR2_START | I2C_CR2_ADD10) & + ~I2C_CR2_RD_WRN); + } + + /* + * No need to Check TC flag, with AUTOEND mode the stop + * is automatically generated + * Wait until STOPF flag is set or a NACK flag is set + */ + tickstart = (uint32_t)read_cntpct_el0(); + while (((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & + (I2C_FLAG_STOPF | I2C_FLAG_AF)) == 0U) && + (hi2c->i2c_state != I2C_STATE_TIMEOUT)) { + if (timeout != MAX_DELAY) { + if ((((uint32_t)read_cntpct_el0() - tickstart) > + timeout) || (timeout == 0U)) { + hi2c->i2c_state = I2C_STATE_READY; + + hi2c->i2c_err |= + I2C_ERROR_TIMEOUT; + + hi2c->lock = 0; + + return -EIO; + } + } + } + + /* Check if the NACKF flag has not been set */ + if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & + I2C_FLAG_AF) == 0U) { + if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout, + tickstart) != 0) { + return -EIO; + } + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, + I2C_FLAG_STOPF); + + hi2c->i2c_state = I2C_STATE_READY; + + hi2c->lock = 0; + + return 0; + } + + if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout, + tickstart) != 0) { + return -EIO; + } + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_AF); + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF); + + if (i2c_trials == trials) { + mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2, + I2C_CR2_STOP); + + if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout, + tickstart) != 0) { + return -EIO; + } + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, + I2C_FLAG_STOPF); + } + + i2c_trials++; + } while (i2c_trials < trials); + + hi2c->i2c_state = I2C_STATE_READY; + + hi2c->i2c_err |= I2C_ERROR_TIMEOUT; + + hi2c->lock = 0; + + return -EIO; +} + +/* + * @brief Master sends target device address followed by internal memory + * address for write request. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param dev_addr: Target device address + * @param mem_addr: Internal memory address + * @param mem_add_size: size of internal memory address + * @param timeout: timeout duration + * @param tick_start Tick start value + * @retval 0 if OK, negative value else + */ +static int i2c_request_memory_write(struct i2c_handle_s *hi2c, + uint16_t dev_addr, uint16_t mem_addr, + uint16_t mem_add_size, uint32_t timeout, + uint32_t tick_start) +{ + i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_RELOAD_MODE, + I2C_GENERATE_START_WRITE); + + if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) { + return -EIO; + } + + if (mem_add_size == I2C_MEMADD_SIZE_8BIT) { + /* Send Memory Address */ + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, + (uint8_t)(mem_addr & 0x00FFU)); + } else { + /* Send MSB of Memory Address */ + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, + (uint8_t)((mem_addr & 0xFF00U) >> 8)); + + /* Wait until TXIS flag is set */ + if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) { + return -EIO; + } + + /* Send LSB of Memory Address */ + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, + (uint8_t)(mem_addr & 0x00FFU)); + } + + if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout, tick_start) != + 0) { + return -EIO; + } + + return 0; +} + +/* + * @brief Master sends target device address followed by internal memory + * address for read request. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param dev_addr: Target device address + * @param mem_addr: Internal memory address + * @param mem_add_size: size of internal memory address + * @param timeout: timeout duration + * @param tick_start Tick start value + * @retval 0 if OK, negative value else + */ +static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t mem_addr, uint16_t mem_add_size, + uint32_t timeout, uint32_t tick_start) +{ + i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_SOFTEND_MODE, + I2C_GENERATE_START_WRITE); + + if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) { + return -EIO; + } + + if (mem_add_size == I2C_MEMADD_SIZE_8BIT) { + /* Send Memory Address */ + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, + (uint8_t)(mem_addr & 0x00FFU)); + } else { + /* Send MSB of Memory Address */ + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, + (uint8_t)((mem_addr & 0xFF00U) >> 8)); + + /* Wait until TXIS flag is set */ + if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) { + return -EIO; + } + + /* Send LSB of Memory Address */ + mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, + (uint8_t)(mem_addr & 0x00FFU)); + } + + if (i2c_wait_flag(hi2c, I2C_FLAG_TC, 0, timeout, tick_start) != 0) { + return -EIO; + } + + return 0; +} + +/* + * @brief I2C Tx data register flush process. + * @param hi2c: I2C handle. + * @retval None + */ +static void i2c_flush_txdr(struct i2c_handle_s *hi2c) +{ + /* + * If a pending TXIS flag is set, + * write a dummy data in TXDR to clear it. + */ + if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_TXIS) != + 0U) { + mmio_write_32(hi2c->i2c_base_addr + I2C_TXDR, 0); + } + + /* Flush TX register if not empty */ + if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_TXE) == + 0U) { + mmio_setbits_32(hi2c->i2c_base_addr + I2C_ISR, + I2C_FLAG_TXE); + } +} + +/* + * @brief This function handles I2C Communication timeout. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param flag: Specifies the I2C flag to check. + * @param awaited_value: The awaited bit value for the flag (0 or 1). + * @param timeout: timeout duration + * @param tick_start: Tick start value + * @retval 0 if OK, negative value else + */ +static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag, + uint8_t awaited_value, uint32_t timeout, + uint32_t tick_start) +{ + uint8_t flag_check; + + do { + flag_check = ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & + flag) == flag) ? 1U : 0U; + + if (timeout != MAX_DELAY) { + if ((((uint32_t)read_cntpct_el0() - tick_start) > + timeout) || (timeout == 0U)) { + hi2c->i2c_err |= I2C_ERROR_TIMEOUT; + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + return -EIO; + } + } + } while (flag_check == awaited_value); + + return 0; +} + +/* + * @brief This function handles I2C Communication timeout for specific usage + * of TXIS flag. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param timeout: timeout duration + * @param tick_start: Tick start value + * @retval 0 if OK, negative value else + */ +static int i2c_wait_txis(struct i2c_handle_s *hi2c, uint32_t timeout, + uint32_t tick_start) +{ + while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & + I2C_FLAG_TXIS) == 0U) { + if (i2c_ack_failed(hi2c, timeout, tick_start) != 0) { + return -EIO; + } + + if (timeout != MAX_DELAY) { + if ((((uint32_t)read_cntpct_el0() - tick_start) > + timeout) || (timeout == 0U)) { + hi2c->i2c_err |= I2C_ERROR_TIMEOUT; + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + + return -EIO; + } + } + } + + return 0; +} + +/* + * @brief This function handles I2C Communication timeout for specific + * usage of STOP flag. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param timeout: timeout duration + * @param tick_start: Tick start value + * @retval 0 if OK, negative value else + */ +static int i2c_wait_stop(struct i2c_handle_s *hi2c, uint32_t timeout, + uint32_t tick_start) +{ + while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & + I2C_FLAG_STOPF) == 0U) { + if (i2c_ack_failed(hi2c, timeout, tick_start) != 0) { + return -EIO; + } + + if ((((uint32_t)read_cntpct_el0() - tick_start) > timeout) || + (timeout == 0U)) { + hi2c->i2c_err |= I2C_ERROR_TIMEOUT; + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + + return -EIO; + } + } + + return 0; +} + +/* + * @brief This function handles Acknowledge failed detection during + * an I2C Communication. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2C. + * @param timeout: timeout duration + * @param tick_start: Tick start value + * @retval 0 if OK, negative value else + */ +static int i2c_ack_failed(struct i2c_handle_s *hi2c, uint32_t timeout, + uint32_t tick_start) +{ + if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_AF) == 0U) { + return 0; + } + + /* + * Wait until STOP Flag is reset. + * AutoEnd should be initiate after AF. + */ + while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & + I2C_FLAG_STOPF) == 0U) { + if (timeout != MAX_DELAY) { + if ((((uint32_t)read_cntpct_el0() - tick_start) > + timeout) || (timeout == 0U)) { + hi2c->i2c_err |= I2C_ERROR_TIMEOUT; + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + + return -EIO; + } + } + } + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_AF); + + mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF); + + i2c_flush_txdr(hi2c); + + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2); + + hi2c->i2c_err |= I2C_ERROR_AF; + hi2c->i2c_state = I2C_STATE_READY; + hi2c->i2c_mode = I2C_MODE_NONE; + + hi2c->lock = 0; + + return -EIO; +} + +/* + * @brief Handles I2Cx communication when starting transfer or during transfer + * (TC or TCR flag are set). + * @param hi2c: I2C handle. + * @param dev_addr: Specifies the slave address to be programmed. + * @param size: Specifies the number of bytes to be programmed. + * This parameter must be a value between 0 and 255. + * @param i2c_mode: New state of the I2C START condition generation. + * This parameter can be one of the following values: + * @arg @ref I2C_RELOAD_MODE: Enable Reload mode . + * @arg @ref I2C_AUTOEND_MODE: Enable Automatic end mode. + * @arg @ref I2C_SOFTEND_MODE: Enable Software end mode. + * @param request: New state of the I2C START condition generation. + * This parameter can be one of the following values: + * @arg @ref I2C_NO_STARTSTOP: Don't Generate stop and start condition. + * @arg @ref I2C_GENERATE_STOP: Generate stop condition + * (size should be set to 0). + * @arg @ref I2C_GENERATE_START_READ: Generate Restart for read request. + * @arg @ref I2C_GENERATE_START_WRITE: Generate Restart for write request. + * @retval None + */ +static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t size, uint32_t i2c_mode, + uint32_t request) +{ + uint32_t clr_value, set_value; + + clr_value = (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | + I2C_CR2_AUTOEND | I2C_CR2_START | I2C_CR2_STOP) | + (I2C_CR2_RD_WRN & (request >> (31U - I2C_CR2_RD_WRN_OFFSET))); + + set_value = ((uint32_t)dev_addr & I2C_CR2_SADD) | + (((uint32_t)size << I2C_CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES) | + i2c_mode | request; + + mmio_clrsetbits_32(hi2c->i2c_base_addr + I2C_CR2, clr_value, set_value); +} + +/* + * @brief Configure I2C Analog noise filter. + * @param hi2c: Pointer to a struct i2c_handle_s structure that contains + * the configuration information for the specified I2Cx peripheral + * @param analog_filter: New state of the Analog filter. + * @retval 0 if OK, negative value else + */ +int stm32_i2c_config_analog_filter(struct i2c_handle_s *hi2c, + uint32_t analog_filter) +{ + if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) { + return -EBUSY; + } + + hi2c->lock = 1; + + hi2c->i2c_state = I2C_STATE_BUSY; + + /* Disable the selected I2C peripheral */ + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE); + + /* Reset I2Cx ANOFF bit */ + mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_ANFOFF); + + /* Set analog filter bit*/ + mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, analog_filter); + + /* Enable the selected I2C peripheral */ + mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE); + + hi2c->i2c_state = I2C_STATE_READY; + + hi2c->lock = 0; + + return 0; +} diff --git a/drivers/st/pmic/stm32mp1_pmic.c b/drivers/st/pmic/stm32mp1_pmic.c new file mode 100644 index 0000000..958de08 --- /dev/null +++ b/drivers/st/pmic/stm32mp1_pmic.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C Timing hard-coded value, for I2C clock source is HSI at 64MHz */ +#define I2C_TIMING 0x10D07DB5 + +#define I2C_TIMEOUT 0xFFFFF + +#define MASK_RESET_BUCK3 BIT(2) + +#define STPMU1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2)) +#define STPMU1_LDO12356_OUTPUT_SHIFT 2 +#define STPMU1_LDO3_MODE (uint8_t)(BIT(7)) +#define STPMU1_LDO3_DDR_SEL 31U +#define STPMU1_LDO3_1800000 (9U << STPMU1_LDO12356_OUTPUT_SHIFT) + +#define STPMU1_BUCK_OUTPUT_SHIFT 2 +#define STPMU1_BUCK3_1V8 (39U << STPMU1_BUCK_OUTPUT_SHIFT) + +#define STPMU1_DEFAULT_START_UP_DELAY_MS 1 + +static struct i2c_handle_s i2c_handle; +static uint32_t pmic_i2c_addr; + +static int dt_get_pmic_node(void *fdt) +{ + return fdt_node_offset_by_compatible(fdt, -1, "st,stpmu1"); +} + +bool dt_check_pmic(void) +{ + int node; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return false; + } + + node = dt_get_pmic_node(fdt); + if (node < 0) { + VERBOSE("%s: No PMIC node found in DT\n", __func__); + return false; + } + + return fdt_check_status(node); +} + +static int dt_pmic_i2c_config(struct dt_node_info *i2c_info) +{ + int pmic_node, i2c_node; + void *fdt; + const fdt32_t *cuint; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, pmic_node, "reg", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1; + if (pmic_i2c_addr > UINT16_MAX) { + return -EINVAL; + } + + i2c_node = fdt_parent_offset(fdt, pmic_node); + if (i2c_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + dt_fill_device_info(i2c_info, i2c_node); + if (i2c_info->base == 0U) { + return -FDT_ERR_NOTFOUND; + } + + return dt_set_pinctrl_config(i2c_node); +} + +int dt_pmic_enable_boot_on_regulators(void) +{ + int pmic_node, regulators_node, regulator_node; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); + + fdt_for_each_subnode(regulator_node, fdt, regulators_node) { + const fdt32_t *cuint; + const char *node_name; + uint16_t voltage; + + if (fdt_getprop(fdt, regulator_node, "regulator-boot-on", + NULL) == NULL) { + continue; + } + + cuint = fdt_getprop(fdt, regulator_node, + "regulator-min-microvolt", NULL); + if (cuint == NULL) { + continue; + } + + /* DT uses microvolts, whereas driver awaits millivolts */ + voltage = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + node_name = fdt_get_name(fdt, regulator_node, NULL); + + if (stpmu1_is_regulator_enabled(node_name) == 0U) { + int status; + + status = stpmu1_regulator_voltage_set(node_name, + voltage); + if (status != 0) { + return status; + } + + status = stpmu1_regulator_enable(node_name); + if (status != 0) { + return status; + } + } + } + + return 0; +} + +void initialize_pmic_i2c(void) +{ + int ret; + struct dt_node_info i2c_info; + + if (dt_pmic_i2c_config(&i2c_info) != 0) { + ERROR("I2C configuration failed\n"); + panic(); + } + + if (stm32mp1_clk_enable((uint32_t)i2c_info.clock) < 0) { + ERROR("I2C clock enable failed\n"); + panic(); + } + + /* Initialize PMIC I2C */ + i2c_handle.i2c_base_addr = i2c_info.base; + i2c_handle.i2c_init.timing = I2C_TIMING; + i2c_handle.i2c_init.own_address1 = pmic_i2c_addr; + i2c_handle.i2c_init.addressing_mode = I2C_ADDRESSINGMODE_7BIT; + i2c_handle.i2c_init.dual_address_mode = I2C_DUALADDRESS_DISABLE; + i2c_handle.i2c_init.own_address2 = 0; + i2c_handle.i2c_init.own_address2_masks = I2C_OAR2_OA2NOMASK; + i2c_handle.i2c_init.general_call_mode = I2C_GENERALCALL_DISABLE; + i2c_handle.i2c_init.no_stretch_mode = I2C_NOSTRETCH_DISABLE; + + ret = stm32_i2c_init(&i2c_handle); + if (ret != 0) { + ERROR("Cannot initialize I2C %x (%d)\n", + i2c_handle.i2c_base_addr, ret); + panic(); + } + + ret = stm32_i2c_config_analog_filter(&i2c_handle, + I2C_ANALOGFILTER_ENABLE); + if (ret != 0) { + ERROR("Cannot initialize I2C analog filter (%d)\n", ret); + panic(); + } + + ret = stm32_i2c_is_device_ready(&i2c_handle, (uint16_t)pmic_i2c_addr, 1, + I2C_TIMEOUT); + if (ret != 0) { + ERROR("I2C device not ready (%d)\n", ret); + panic(); + } + + stpmu1_bind_i2c(&i2c_handle, (uint16_t)pmic_i2c_addr); +} + +void initialize_pmic(void) +{ + int status; + uint8_t read_val; + + initialize_pmic_i2c(); + + status = stpmu1_register_read(VERSION_STATUS_REG, &read_val); + if (status != 0) { + panic(); + } + + INFO("PMIC version = 0x%x\n", read_val); + + /* Keep VDD on during the reset cycle */ + status = stpmu1_register_update(MASK_RESET_BUCK_REG, + MASK_RESET_BUCK3, + MASK_RESET_BUCK3); + if (status != 0) { + panic(); + } +} + +int pmic_ddr_power_init(enum ddr_type ddr_type) +{ + bool buck3_at_1v8 = false; + uint8_t read_val; + int status; + + switch (ddr_type) { + case STM32MP_DDR3: + /* Set LDO3 to sync mode */ + status = stpmu1_register_read(LDO3_CONTROL_REG, &read_val); + if (status != 0) { + return status; + } + + read_val &= ~STPMU1_LDO3_MODE; + read_val &= ~STPMU1_LDO12356_OUTPUT_MASK; + read_val |= STPMU1_LDO3_DDR_SEL << STPMU1_LDO12356_OUTPUT_SHIFT; + + status = stpmu1_register_write(LDO3_CONTROL_REG, read_val); + if (status != 0) { + return status; + } + + status = stpmu1_regulator_voltage_set("buck2", 1350); + if (status != 0) { + return status; + } + + status = stpmu1_regulator_enable("buck2"); + if (status != 0) { + return status; + } + + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + status = stpmu1_regulator_enable("vref_ddr"); + if (status != 0) { + return status; + } + + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + status = stpmu1_regulator_enable("ldo3"); + if (status != 0) { + return status; + } + + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + break; + + case STM32MP_LPDDR2: + /* + * Set LDO3 to 1.8V + * Set LDO3 to bypass mode if BUCK3 = 1.8V + * Set LDO3 to normal mode if BUCK3 != 1.8V + */ + status = stpmu1_register_read(BUCK3_CONTROL_REG, &read_val); + if (status != 0) { + return status; + } + + if ((read_val & STPMU1_BUCK3_1V8) == STPMU1_BUCK3_1V8) { + buck3_at_1v8 = true; + } + + status = stpmu1_register_read(LDO3_CONTROL_REG, &read_val); + if (status != 0) { + return status; + } + + read_val &= ~STPMU1_LDO3_MODE; + read_val &= ~STPMU1_LDO12356_OUTPUT_MASK; + read_val |= STPMU1_LDO3_1800000; + if (buck3_at_1v8) { + read_val |= STPMU1_LDO3_MODE; + } + + status = stpmu1_register_write(LDO3_CONTROL_REG, read_val); + if (status != 0) { + return status; + } + + status = stpmu1_regulator_voltage_set("buck2", 1200); + if (status != 0) { + return status; + } + + status = stpmu1_regulator_enable("ldo3"); + if (status != 0) { + return status; + } + + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + status = stpmu1_regulator_enable("buck2"); + if (status != 0) { + return status; + } + + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + status = stpmu1_regulator_enable("vref_ddr"); + if (status != 0) { + return status; + } + + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + break; + + default: + break; + }; + + return 0; +} diff --git a/drivers/st/pmic/stpmu1.c b/drivers/st/pmic/stpmu1.c new file mode 100644 index 0000000..5951899 --- /dev/null +++ b/drivers/st/pmic/stpmu1.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +struct regul_struct { + const char *dt_node_name; + const uint16_t *voltage_table; + uint8_t voltage_table_size; + uint8_t control_reg; + uint8_t low_power_reg; +}; + +static struct i2c_handle_s *stpmu_i2c_handle; +static uint16_t stpmu_i2c_addr; + +/* Voltage tables in mV */ +static const uint16_t buck1_voltage_table[] = { + 600, + 625, + 650, + 675, + 700, + 725, + 750, + 775, + 800, + 825, + 850, + 875, + 900, + 925, + 950, + 975, + 1000, + 1025, + 1050, + 1075, + 1100, + 1125, + 1150, + 1175, + 1200, + 1225, + 1250, + 1275, + 1300, + 1325, + 1350, + 1350, +}; + +static const uint16_t buck2_voltage_table[] = { + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1050, + 1050, + 1100, + 1100, + 1150, + 1150, + 1200, + 1200, + 1250, + 1250, + 1300, + 1300, + 1350, + 1350, + 1400, + 1400, + 1450, + 1450, + 1500, +}; + +static const uint16_t buck3_voltage_table[] = { + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1000, + 1100, + 1100, + 1100, + 1100, + 1200, + 1200, + 1200, + 1200, + 1300, + 1300, + 1300, + 1300, + 1400, + 1400, + 1400, + 1400, + 1500, + 1600, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, + 3400, +}; + +static const uint16_t buck4_voltage_table[] = { + 600, + 625, + 650, + 675, + 700, + 725, + 750, + 775, + 800, + 825, + 850, + 875, + 900, + 925, + 950, + 975, + 1000, + 1025, + 1050, + 1075, + 1100, + 1125, + 1150, + 1175, + 1200, + 1225, + 1250, + 1275, + 1300, + 1300, + 1350, + 1350, + 1400, + 1400, + 1450, + 1450, + 1500, + 1600, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, + 3400, + 3500, + 3600, + 3700, + 3800, + 3900, +}; + +static const uint16_t ldo1_voltage_table[] = { + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, +}; + +static const uint16_t ldo2_voltage_table[] = { + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, +}; + +static const uint16_t ldo3_voltage_table[] = { + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, + 3300, + 3300, + 3300, + 3300, + 3300, + 3300, + 0xFFFF, /* VREFDDR */ +}; + +static const uint16_t ldo5_voltage_table[] = { + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, + 3400, + 3500, + 3600, + 3700, + 3800, + 3900, +}; + +static const uint16_t ldo6_voltage_table[] = { + 900, + 1000, + 1100, + 1200, + 1300, + 1400, + 1500, + 1600, + 1700, + 1800, + 1900, + 2000, + 2100, + 2200, + 2300, + 2400, + 2500, + 2600, + 2700, + 2800, + 2900, + 3000, + 3100, + 3200, + 3300, +}; + +static const uint16_t ldo4_voltage_table[] = { + 3300, +}; + +static const uint16_t vref_ddr_voltage_table[] = { + 3300, +}; + +/* Table of Regulators in PMIC SoC */ +static const struct regul_struct regulators_table[] = { + { + .dt_node_name = "buck1", + .voltage_table = buck1_voltage_table, + .voltage_table_size = ARRAY_SIZE(buck1_voltage_table), + .control_reg = BUCK1_CONTROL_REG, + .low_power_reg = BUCK1_PWRCTRL_REG, + }, + { + .dt_node_name = "buck2", + .voltage_table = buck2_voltage_table, + .voltage_table_size = ARRAY_SIZE(buck2_voltage_table), + .control_reg = BUCK2_CONTROL_REG, + .low_power_reg = BUCK2_PWRCTRL_REG, + }, + { + .dt_node_name = "buck3", + .voltage_table = buck3_voltage_table, + .voltage_table_size = ARRAY_SIZE(buck3_voltage_table), + .control_reg = BUCK3_CONTROL_REG, + .low_power_reg = BUCK3_PWRCTRL_REG, + }, + { + .dt_node_name = "buck4", + .voltage_table = buck4_voltage_table, + .voltage_table_size = ARRAY_SIZE(buck4_voltage_table), + .control_reg = BUCK4_CONTROL_REG, + .low_power_reg = BUCK4_PWRCTRL_REG, + }, + { + .dt_node_name = "ldo1", + .voltage_table = ldo1_voltage_table, + .voltage_table_size = ARRAY_SIZE(ldo1_voltage_table), + .control_reg = LDO1_CONTROL_REG, + .low_power_reg = LDO1_PWRCTRL_REG, + }, + { + .dt_node_name = "ldo2", + .voltage_table = ldo2_voltage_table, + .voltage_table_size = ARRAY_SIZE(ldo2_voltage_table), + .control_reg = LDO2_CONTROL_REG, + .low_power_reg = LDO2_PWRCTRL_REG, + }, + { + .dt_node_name = "ldo3", + .voltage_table = ldo3_voltage_table, + .voltage_table_size = ARRAY_SIZE(ldo3_voltage_table), + .control_reg = LDO3_CONTROL_REG, + .low_power_reg = LDO3_PWRCTRL_REG, + }, + { + .dt_node_name = "ldo4", + .voltage_table = ldo4_voltage_table, + .voltage_table_size = ARRAY_SIZE(ldo4_voltage_table), + .control_reg = LDO4_CONTROL_REG, + .low_power_reg = LDO4_PWRCTRL_REG, + }, + { + .dt_node_name = "ldo5", + .voltage_table = ldo5_voltage_table, + .voltage_table_size = ARRAY_SIZE(ldo5_voltage_table), + .control_reg = LDO5_CONTROL_REG, + .low_power_reg = LDO5_PWRCTRL_REG, + }, + { + .dt_node_name = "ldo6", + .voltage_table = ldo6_voltage_table, + .voltage_table_size = ARRAY_SIZE(ldo6_voltage_table), + .control_reg = LDO6_CONTROL_REG, + .low_power_reg = LDO6_PWRCTRL_REG, + }, + { + .dt_node_name = "vref_ddr", + .voltage_table = vref_ddr_voltage_table, + .voltage_table_size = ARRAY_SIZE(vref_ddr_voltage_table), + .control_reg = VREF_DDR_CONTROL_REG, + .low_power_reg = VREF_DDR_PWRCTRL_REG, + }, +}; + +#define MAX_REGUL ARRAY_SIZE(regulators_table) + +static const struct regul_struct *stpmu1_get_regulator_data(const char *name) +{ + uint8_t i; + + for (i = 0 ; i < MAX_REGUL ; i++) { + if (strncmp(name, regulators_table[i].dt_node_name, + strlen(regulators_table[i].dt_node_name)) == 0) { + return ®ulators_table[i]; + } + } + + /* Regulator not found */ + panic(); + return NULL; +} + +static uint8_t stpmu1_voltage_find_index(const char *name, + uint16_t millivolts) +{ + const struct regul_struct *regul = stpmu1_get_regulator_data(name); + uint8_t i; + + for (i = 0 ; i < regul->voltage_table_size ; i++) { + if (regul->voltage_table[i] == millivolts) { + return i; + } + } + + /* Voltage not found */ + panic(); + + return 0; +} + +int stpmu1_switch_off(void) +{ + return stpmu1_register_update(MAIN_CONTROL_REG, 1, + SOFTWARE_SWITCH_OFF_ENABLED); +} + +int stpmu1_regulator_enable(const char *name) +{ + const struct regul_struct *regul = stpmu1_get_regulator_data(name); + + return stpmu1_register_update(regul->control_reg, BIT(0), BIT(0)); +} + +int stpmu1_regulator_disable(const char *name) +{ + const struct regul_struct *regul = stpmu1_get_regulator_data(name); + + return stpmu1_register_update(regul->control_reg, 0, BIT(0)); +} + +uint8_t stpmu1_is_regulator_enabled(const char *name) +{ + uint8_t val; + const struct regul_struct *regul = stpmu1_get_regulator_data(name); + + if (stpmu1_register_read(regul->control_reg, &val) != 0) { + panic(); + } + + return (val & 0x1U); +} + +int stpmu1_regulator_voltage_set(const char *name, uint16_t millivolts) +{ + uint8_t voltage_index = stpmu1_voltage_find_index(name, millivolts); + const struct regul_struct *regul = stpmu1_get_regulator_data(name); + + return stpmu1_register_update(regul->control_reg, voltage_index << 2, + 0xFC); +} + +int stpmu1_register_read(uint8_t register_id, uint8_t *value) +{ + return stm32_i2c_mem_read(stpmu_i2c_handle, stpmu_i2c_addr, + (uint16_t)register_id, I2C_MEMADD_SIZE_8BIT, + value, 1, 100000); +} + +int stpmu1_register_write(uint8_t register_id, uint8_t value) +{ + int status; + + status = stm32_i2c_mem_write(stpmu_i2c_handle, stpmu_i2c_addr, + (uint16_t)register_id, + I2C_MEMADD_SIZE_8BIT, &value, 1, 100000); + + if (status != 0) { + return status; + } + + if ((register_id != WATCHDOG_CONTROL_REG) && (register_id <= 0x40U)) { + uint8_t readval; + + status = stpmu1_register_read(register_id, &readval); + if (status != 0) { + return status; + } + + if (readval != value) { + return -1; + } + } + + return 0; +} + +int stpmu1_register_update(uint8_t register_id, uint8_t value, uint8_t mask) +{ + int status; + uint8_t val; + + status = stpmu1_register_read(register_id, &val); + if (status != 0) { + return status; + } + + /* Clear bits to update */ + val &= ~mask; + + /* Update appropriate bits*/ + val |= (value & mask); + + /* Send new value on I2C Bus */ + return stpmu1_register_write(register_id, val); +} + +void stpmu1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr) +{ + stpmu_i2c_handle = i2c_handle; + stpmu_i2c_addr = i2c_addr; +} diff --git a/drivers/st/reset/stm32mp1_reset.c b/drivers/st/reset/stm32mp1_reset.c new file mode 100644 index 0000000..106bbfe --- /dev/null +++ b/drivers/st/reset/stm32mp1_reset.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RST_CLR_OFFSET 4U + +void stm32mp1_reset_assert(uint32_t id) +{ + uint32_t offset = (id / (uint32_t)__LONG_BIT) * sizeof(uintptr_t); + uint32_t bit = id % (uint32_t)__LONG_BIT; + + mmio_write_32(RCC_BASE + offset, BIT(bit)); + while ((mmio_read_32(RCC_BASE + offset) & BIT(bit)) == 0U) { + ; + } +} + +void stm32mp1_reset_deassert(uint32_t id) +{ + uint32_t offset = ((id / (uint32_t)__LONG_BIT) * sizeof(uintptr_t)) + + RST_CLR_OFFSET; + uint32_t bit = id % (uint32_t)__LONG_BIT; + + mmio_write_32(RCC_BASE + offset, BIT(bit)); + while ((mmio_read_32(RCC_BASE + offset) & BIT(bit)) != 0U) { + ; + } +} diff --git a/drivers/st/uart/aarch32/stm32_console.S b/drivers/st/uart/aarch32/stm32_console.S new file mode 100644 index 0000000..792703a --- /dev/null +++ b/drivers/st/uart/aarch32/stm32_console.S @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +#define USART_TIMEOUT 0x1000 + +#define USART_CR1 0x00 +#define USART_CR1_UE 0x00000001 +#define USART_CR1_TE 0x00000008 +#define USART_CR1_FIFOEN 0x20000000 + +#define USART_CR2 0x04 +#define USART_CR2_STOP 0x00003000 + +#define USART_BRR 0x0C + +#define USART_ISR 0x1C +#define USART_ISR_TC 0x00000040 +#define USART_ISR_TXE 0x00000080 +#define USART_ISR_TEACK 0x00200000 + +#define USART_TDR 0x28 + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + .globl console_core_flush + + /* ----------------------------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, + * unsigned int baud_rate) + * + * Function to initialize the console without a C Runtime to print + * debug information. This function will be accessed by console_init + * and crash reporting. + * + * In: r0 - console base address + * r1 - Uart clock in Hz + * r2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : r1, r2, r3 + * ----------------------------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cmp r0, #0 + beq core_init_fail +#if defined(IMAGE_BL2) + /* Check baud rate and uart clock for sanity */ + cmp r1, #0 + beq core_init_fail + cmp r2, #0 + beq core_init_fail + /* Disable UART */ + ldr r3, [r0, #USART_CR1] + bic r3, r3, #USART_CR1_UE + str r3, [r0, #USART_CR1] + /* Configure UART */ + orr r3, r3, #(USART_CR1_TE | USART_CR1_FIFOEN) + str r3, [r0, #USART_CR1] + ldr r3, [r0, #USART_CR2] + bic r3, r3, #USART_CR2_STOP + str r3, [r0, #USART_CR2] + /* Divisor = (Uart clock + (baudrate / 2)) / baudrate */ + lsl r3, r2, #1 + add r3, r1, r3 + udiv r3, r3, r2 + str r3, [r0, #USART_BRR] + /* Enable UART */ + ldr r3, [r0, #USART_CR1] + orr r3, r3, #USART_CR1_UE + str r3, [r0, #USART_CR1] + /* Check TEACK bit */ + mov r2, #USART_TIMEOUT +teack_loop: + subs r2, r2, #1 + beq core_init_fail + ldr r3, [r0, #USART_ISR] + tst r3, #USART_ISR_TEACK + beq teack_loop +#endif /* IMAGE_BL2 */ + mov r0, #1 + bx lr +core_init_fail: + mov r0, #0 + bx lr +endfunc console_core_init + + /* --------------------------------------------------------------- + * 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. + * + * In : r0 - character to be printed + * r1 - console base address + * Out : return -1 on error else return character. + * Clobber list : r2 + * --------------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cmp r1, #0 + beq putc_error + /* Prepend '\r' to '\n' */ + cmp r0, #0xA + bne 2f +1: + /* Check Transmit Data Register Empty */ +txe_loop_1: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TXE + beq txe_loop_1 + mov r2, #0xD + str r2, [r1, #USART_TDR] + /* Check transmit complete flag */ +tc_loop_1: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TC + beq tc_loop_1 +2: + /* Check Transmit Data Register Empty */ +txe_loop_2: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TXE + beq txe_loop_2 + str r0, [r1, #USART_TDR] + /* Check transmit complete flag */ +tc_loop_2: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TC + beq tc_loop_2 + bx lr +putc_error: + mov r0, #-1 + bx lr +endfunc console_core_putc + + /* ----------------------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * + * Function to get a character from the console. + * It returns the character grabbed on success or -1 on error. + * + * In : r0 - console base address + * Out : return -1. + * Clobber list : r0, r1 + * ----------------------------------------------------------- + */ +func console_core_getc + /* Not supported */ + mov r0, #-1 + bx lr +endfunc console_core_getc + + /* --------------------------------------------------------------- + * int console_core_flush(uintptr_t base_addr) + * + * Function to force a write of all buffered data that hasn't been + * output. + * + * In : r0 - console base address + * Out : return -1 on error else return 0. + * Clobber list : r0, r1 + * --------------------------------------------------------------- + */ +func console_core_flush + cmp r0, #0 + beq flush_error + /* Check Transmit Data Register Empty */ +txe_loop_3: + ldr r1, [r0, #USART_ISR] + tst r1, #USART_ISR_TXE + beq txe_loop_3 + mov r0, #0 + bx lr +flush_error: + mov r0, #-1 + bx lr +endfunc console_core_flush diff --git a/fdts/stm32mp15-ddr.dtsi b/fdts/stm32mp15-ddr.dtsi new file mode 100644 index 0000000..be4e2c3 --- /dev/null +++ b/fdts/stm32mp15-ddr.dtsi @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +/ { + soc { + ddr: ddr@0x5A003000{ + + compatible = "st,stm32mp1-ddr"; + + reg = <0x5A003000 0x550 + 0x5A004000 0x234>; + + clocks = <&rcc AXIDCG>, + <&rcc DDRC1>, + <&rcc DDRC2>, + <&rcc DDRPHYC>, + <&rcc DDRCAPB>, + <&rcc DDRPHYCAPB>; + + clock-names = "axidcg", + "ddrc1", + "ddrc2", + "ddrphyc", + "ddrcapb", + "ddrphycapb"; + + st,mem-name = DDR_MEM_NAME; + st,mem-speed = ; + st,mem-size = ; + + st,ctl-reg = < + DDR_MSTR + DDR_MRCTRL0 + DDR_MRCTRL1 + DDR_DERATEEN + DDR_DERATEINT + DDR_PWRCTL + DDR_PWRTMG + DDR_HWLPCTL + DDR_RFSHCTL0 + DDR_RFSHCTL3 + DDR_CRCPARCTL0 + DDR_ZQCTL0 + DDR_DFITMG0 + DDR_DFITMG1 + DDR_DFILPCFG0 + DDR_DFIUPD0 + DDR_DFIUPD1 + DDR_DFIUPD2 + DDR_DFIPHYMSTR + DDR_ODTMAP + DDR_DBG0 + DDR_DBG1 + DDR_DBGCMD + DDR_POISONCFG + DDR_PCCFG + >; + + st,ctl-timing = < + DDR_RFSHTMG + DDR_DRAMTMG0 + DDR_DRAMTMG1 + DDR_DRAMTMG2 + DDR_DRAMTMG3 + DDR_DRAMTMG4 + DDR_DRAMTMG5 + DDR_DRAMTMG6 + DDR_DRAMTMG7 + DDR_DRAMTMG8 + DDR_DRAMTMG14 + DDR_ODTCFG + >; + + st,ctl-map = < + DDR_ADDRMAP1 + DDR_ADDRMAP2 + DDR_ADDRMAP3 + DDR_ADDRMAP4 + DDR_ADDRMAP5 + DDR_ADDRMAP6 + DDR_ADDRMAP9 + DDR_ADDRMAP10 + DDR_ADDRMAP11 + >; + + st,ctl-perf = < + DDR_SCHED + DDR_SCHED1 + DDR_PERFHPR1 + DDR_PERFLPR1 + DDR_PERFWR1 + DDR_PCFGR_0 + DDR_PCFGW_0 + DDR_PCFGQOS0_0 + DDR_PCFGQOS1_0 + DDR_PCFGWQOS0_0 + DDR_PCFGWQOS1_0 + DDR_PCFGR_1 + DDR_PCFGW_1 + DDR_PCFGQOS0_1 + DDR_PCFGQOS1_1 + DDR_PCFGWQOS0_1 + DDR_PCFGWQOS1_1 + >; + + st,phy-reg = < + DDR_PGCR + DDR_ACIOCR + DDR_DXCCR + DDR_DSGCR + DDR_DCR + DDR_ODTCR + DDR_ZQ0CR1 + DDR_DX0GCR + DDR_DX1GCR + DDR_DX2GCR + DDR_DX3GCR + >; + + st,phy-timing = < + DDR_PTR0 + DDR_PTR1 + DDR_PTR2 + DDR_DTPR0 + DDR_DTPR1 + DDR_DTPR2 + DDR_MR0 + DDR_MR1 + DDR_MR2 + DDR_MR3 + >; + + st,phy-cal = < + DDR_DX0DLLCR + DDR_DX0DQTR + DDR_DX0DQSTR + DDR_DX1DLLCR + DDR_DX1DQTR + DDR_DX1DQSTR + DDR_DX2DLLCR + DDR_DX2DQTR + DDR_DX2DQSTR + DDR_DX3DLLCR + DDR_DX3DQTR + DDR_DX3DQSTR + >; + + status = "okay"; + }; + }; +}; diff --git a/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi b/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi new file mode 100644 index 0000000..58a4cdc --- /dev/null +++ b/fdts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +/* STM32MP157C ED1 and ED2 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 + */ + +#define DDR_MEM_NAME "DDR3-1066 bin G 2x4Gb 533MHz v1.39" +#define DDR_MEM_SPEED 533 +#define DDR_MEM_SIZE 0x40000000 + +#define DDR_MSTR 0x00040401 +#define DDR_MRCTRL0 0x00000010 +#define DDR_MRCTRL1 0x00000000 +#define DDR_DERATEEN 0x00000000 +#define DDR_DERATEINT 0x00800000 +#define DDR_PWRCTL 0x00000000 +#define DDR_PWRTMG 0x00400010 +#define DDR_HWLPCTL 0x00000000 +#define DDR_RFSHCTL0 0x00210000 +#define DDR_RFSHCTL3 0x00000000 +#define DDR_RFSHTMG 0x0081008B +#define DDR_CRCPARCTL0 0x00000000 +#define DDR_DRAMTMG0 0x121B2414 +#define DDR_DRAMTMG1 0x000A041C +#define DDR_DRAMTMG2 0x0608090F +#define DDR_DRAMTMG3 0x0050400C +#define DDR_DRAMTMG4 0x08040608 +#define DDR_DRAMTMG5 0x06060403 +#define DDR_DRAMTMG6 0x02020002 +#define DDR_DRAMTMG7 0x00000202 +#define DDR_DRAMTMG8 0x00001005 +#define DDR_DRAMTMG14 0x000000A0 +#define DDR_ZQCTL0 0xC2000040 +#define DDR_DFITMG0 0x02060105 +#define DDR_DFITMG1 0x00000202 +#define DDR_DFILPCFG0 0x07000000 +#define DDR_DFIUPD0 0xC0400003 +#define DDR_DFIUPD1 0x00000000 +#define DDR_DFIUPD2 0x00000000 +#define DDR_DFIPHYMSTR 0x00000000 +#define DDR_ADDRMAP1 0x00080808 +#define DDR_ADDRMAP2 0x00000000 +#define DDR_ADDRMAP3 0x00000000 +#define DDR_ADDRMAP4 0x00001F1F +#define DDR_ADDRMAP5 0x07070707 +#define DDR_ADDRMAP6 0x0F070707 +#define DDR_ADDRMAP9 0x00000000 +#define DDR_ADDRMAP10 0x00000000 +#define DDR_ADDRMAP11 0x00000000 +#define DDR_ODTCFG 0x06000600 +#define DDR_ODTMAP 0x00000001 +#define DDR_SCHED 0x00001201 +#define DDR_SCHED1 0x00000000 +#define DDR_PERFHPR1 0x01000001 +#define DDR_PERFLPR1 0x08000200 +#define DDR_PERFWR1 0x08000400 +#define DDR_DBG0 0x00000000 +#define DDR_DBG1 0x00000000 +#define DDR_DBGCMD 0x00000000 +#define DDR_POISONCFG 0x00000000 +#define DDR_PCCFG 0x00000010 +#define DDR_PCFGR_0 0x00010000 +#define DDR_PCFGW_0 0x00000000 +#define DDR_PCFGQOS0_0 0x02100B03 +#define DDR_PCFGQOS1_0 0x00800100 +#define DDR_PCFGWQOS0_0 0x01100B03 +#define DDR_PCFGWQOS1_0 0x01000200 +#define DDR_PCFGR_1 0x00010000 +#define DDR_PCFGW_1 0x00000000 +#define DDR_PCFGQOS0_1 0x02100B03 +#define DDR_PCFGQOS1_1 0x00800000 +#define DDR_PCFGWQOS0_1 0x01100B03 +#define DDR_PCFGWQOS1_1 0x01000200 +#define DDR_PGCR 0x01442E02 +#define DDR_PTR0 0x0022AA5B +#define DDR_PTR1 0x04841104 +#define DDR_PTR2 0x042DA068 +#define DDR_ACIOCR 0x10400812 +#define DDR_DXCCR 0x00000C40 +#define DDR_DSGCR 0xF200001F +#define DDR_DCR 0x0000000B +#define DDR_DTPR0 0x38D488D0 +#define DDR_DTPR1 0x098B00D8 +#define DDR_DTPR2 0x10023600 +#define DDR_MR0 0x00000840 +#define DDR_MR1 0x00000000 +#define DDR_MR2 0x00000208 +#define DDR_MR3 0x00000000 +#define DDR_ODTCR 0x00010000 +#define DDR_ZQ0CR1 0x00000038 +#define DDR_DX0GCR 0x0000CE81 +#define DDR_DX0DLLCR 0x40000000 +#define DDR_DX0DQTR 0xFFFFFFFF +#define DDR_DX0DQSTR 0x3DB02000 +#define DDR_DX1GCR 0x0000CE81 +#define DDR_DX1DLLCR 0x40000000 +#define DDR_DX1DQTR 0xFFFFFFFF +#define DDR_DX1DQSTR 0x3DB02000 +#define DDR_DX2GCR 0x0000CE81 +#define DDR_DX2DLLCR 0x40000000 +#define DDR_DX2DQTR 0xFFFFFFFF +#define DDR_DX2DQSTR 0x3DB02000 +#define DDR_DX3GCR 0x0000CE81 +#define DDR_DX3DLLCR 0x40000000 +#define DDR_DX3DQTR 0xFFFFFFFF +#define DDR_DX3DQSTR 0x3DB02000 + +#include "stm32mp15-ddr.dtsi" diff --git a/fdts/stm32mp157-pinctrl.dtsi b/fdts/stm32mp157-pinctrl.dtsi new file mode 100644 index 0000000..21bd34e --- /dev/null +++ b/fdts/stm32mp157-pinctrl.dtsi @@ -0,0 +1,250 @@ +// 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 { + #address-cells = <1>; + #size-cells = <1>; + 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"; + }; + + uart4_pins_a: uart4@0 { + pins1 { + pinmux = ; /* UART4_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART4_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; + }; + }; + + sdmmc1_b4_pins_a: sdmmc1-b4@0 { + pins { + pinmux = , /* SDMMC1_D0 */ + , /* SDMMC1_D1 */ + , /* SDMMC1_D2 */ + , /* SDMMC1_D3 */ + , /* SDMMC1_CK */ + ; /* SDMMC1_CMD */ + slew-rate = <3>; + drive-push-pull; + bias-disable; + }; + }; + + sdmmc1_dir_pins_a: sdmmc1-dir@0 { + pins1 { + pinmux = , /* SDMMC1_D0DIR */ + , /* SDMMC1_D123DIR */ + ; /* SDMMC1_CDIR */ + slew-rate = <3>; + drive-push-pull; + bias-pull-up; + }; + pins2{ + pinmux = ; /* SDMMC1_CKIN */ + bias-pull-up; + }; + }; + + sdmmc2_b4_pins_a: sdmmc2-b4@0 { + pins { + pinmux = , /* SDMMC2_D0 */ + , /* SDMMC2_D1 */ + , /* SDMMC2_D2 */ + , /* SDMMC2_D3 */ + , /* SDMMC2_CK */ + ; /* SDMMC2_CMD */ + slew-rate = <3>; + 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 = <3>; + drive-push-pull; + bias-pull-up; + }; + }; + }; + + pinctrl_z: pin-controller-z { + #address-cells = <1>; + #size-cells = <1>; + 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/stm32mp157c-ed1.dts b/fdts/stm32mp157c-ed1.dts new file mode 100644 index 0000000..e3dabe8 --- /dev/null +++ b/fdts/stm32mp157c-ed1.dts @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ + +/dts-v1/; + +#include "stm32mp157c.dtsi" +#include "stm32mp157caa-pinctrl.dtsi" + +/ { + model = "STMicroelectronics STM32MP157C-ED1 pmic eval daughter"; + compatible = "st,stm32mp157c-ed1", "st,stm32mp157"; + + chosen { + bootargs = "earlyprintk console=ttyS3,115200 root=/dev/ram"; + stdout-path = "serial3:115200n8"; + }; +}; + +&i2c4 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c4_pins_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + status = "okay"; + + pmic: stpmu1@33 { + compatible = "st,stpmu1"; + reg = <0x33>; + status = "okay"; + + st,main_control_register = <0x04>; + st,vin_control_register = <0xc0>; + st,usb_control_register = <0x30>; + + regulators { + compatible = "st,stpmu1-regulators"; + + v3v3: buck4 { + regulator-name = "v3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-over-current-protection; + regulator-initial-mode = <8>; + + regulator-state-standby { + regulator-suspend-microvolt = <3300000>; + regulator-unchanged-in-suspend; + regulator-mode = <8>; + }; + regulator-state-mem { + regulator-off-in-suspend; + }; + regulator-state-disk { + regulator-off-in-suspend; + }; + }; + + vdd_sd: ldo5 { + regulator-name = "vdd_sd"; + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; + regulator-boot-on; + + regulator-state-standby { + regulator-suspend-microvolt = <2900000>; + regulator-unchanged-in-suspend; + }; + regulator-state-mem { + regulator-off-in-suspend; + }; + regulator-state-disk { + regulator-off-in-suspend; + }; + }; + }; + }; +}; + +&iwdg2 { + instance = <2>; + timeout-sec = <32>; + status = "okay"; +}; + +&rng1 { + status = "okay"; +}; + +&sdmmc1 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>; + broken-cd; + st,dirpol; + st,negedge; + st,pin-ckin; + bus-width = <4>; + 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,dirpol; + st,negedge; + bus-width = <8>; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins_a>; + resets = <&rcc UART4_R>; + status = "okay"; +}; + +/* ATF Specific */ +#include +#include "stm32mp15-ddr3-2x4Gb-1066-binG.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; + }; + + soc { + stgen: stgen@5C008000 { + compatible = "st,stm32-stgen"; + reg = <0x5C008000 0x1000>; + status = "okay"; + }; + }; +}; + +/* CLOCK init */ +&rcc { + st,clksrc = < + CLK_MPU_PLL1P + CLK_AXI_PLL2P + CLK_PLL12_HSE + CLK_PLL3_HSE + CLK_PLL4_HSE + CLK_RTC_LSE + CLK_MCO1_DISABLED + CLK_MCO2_DISABLED + >; + + st,clkdiv = < + 1 /*MPU*/ + 0 /*AXI*/ + 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_PLL3R + 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_PLL3R + 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_PLL3Q + CLK_FDCAN_PLL4Q + CLK_SAI1_PLL3Q + CLK_SAI2_PLL3Q + CLK_SAI3_PLL3Q + CLK_SAI4_PLL3Q + CLK_RNG1_CSI + CLK_RNG2_CSI + CLK_LPTIM1_PCLK1 + CLK_LPTIM23_PCLK3 + CLK_LPTIM45_PCLK3 + >; + + /* 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 = 786.4 MHz => P = 197, Q = 49, R = 98 */ + pll3: st,pll@2 { + cfg = < 2 97 3 15 7 PQR(1,1,1) >; + frac = < 0x9ba >; + }; + + /* VCO = 508.0 MHz => P = 56, Q = 56, R = 56 */ + pll4: st,pll@3 { + cfg = < 5 126 8 8 8 PQR(1,1,1) >; + }; +}; + +/delete-node/ &clk_csi; diff --git a/fdts/stm32mp157c-ev1.dts b/fdts/stm32mp157c-ev1.dts new file mode 100644 index 0000000..98a9d35 --- /dev/null +++ b/fdts/stm32mp157c-ev1.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ + +/dts-v1/; +#include "stm32mp157c-ed1.dts" + +/ { + model = "STMicroelectronics STM32MP157C-EV1 pmic eval daughter on eval mother"; + compatible = "st,stm32mp157c-ev1", "st,stm32mp157c-ed1", "st,stm32mp157"; + + chosen { + bootargs = "earlyprintk console=ttyS3,115200 root=/dev/ram"; + stdout-path = "serial3:115200n8"; + }; +}; + +&usart3 { + pinctrl-names = "default"; + pinctrl-0 = <&usart3_pins_a>; + resets = <&rcc USART3_R>; + status = "disabled"; +}; diff --git a/fdts/stm32mp157c.dtsi b/fdts/stm32mp157c.dtsi new file mode 100644 index 0000000..8b13c0e --- /dev/null +++ b/fdts/stm32mp157c.dtsi @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ + +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + serial0 = &usart1; + serial1 = &usart2; + serial2 = &usart3; + serial3 = &uart4; + serial4 = &uart5; + serial5 = &usart6; + serial6 = &uart7; + serial7 = &uart8; + }; + + 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 = <64000000>; + }; + + clk_dsi_phy: ck_dsi_phy { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <0>; + }; + + clk_usbo_48m: ck_usbo_48m { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <48000000>; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + usart2: serial@4000e000 { + compatible = "st,stm32h7-usart"; + reg = <0x4000e000 0x400>; + clocks = <&rcc USART2_K>; + status = "disabled"; + }; + + usart3: serial@4000f000 { + compatible = "st,stm32h7-usart"; + reg = <0x4000f000 0x400>; + clocks = <&rcc USART3_K>; + status = "disabled"; + }; + + uart4: serial@40010000 { + compatible = "st,stm32h7-uart"; + reg = <0x40010000 0x400>; + clocks = <&rcc UART4_K>; + status = "disabled"; + }; + + uart5: serial@40011000 { + compatible = "st,stm32h7-uart"; + reg = <0x40011000 0x400>; + clocks = <&rcc UART5_K>; + status = "disabled"; + }; + + + uart7: serial@40018000 { + compatible = "st,stm32h7-uart"; + reg = <0x40018000 0x400>; + clocks = <&rcc UART7_K>; + status = "disabled"; + }; + + uart8: serial@40019000 { + compatible = "st,stm32h7-uart"; + reg = <0x40019000 0x400>; + clocks = <&rcc UART8_K>; + status = "disabled"; + }; + + usart6: serial@44003000 { + compatible = "st,stm32h7-usart"; + reg = <0x44003000 0x400>; + clocks = <&rcc USART6_K>; + status = "disabled"; + }; + + sdmmc3: sdmmc@48004000 { + compatible = "st,stm32-sdmmc2"; + reg = <0x48004000 0x400>, <0x48005000 0x400>; + reg-names = "sdmmc", "delay"; + clocks = <&rcc SDMMC3_K>; + resets = <&rcc SDMMC3_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + + rcc: rcc@50000000 { + compatible = "syscon", "st,stm32mp1-rcc"; + #clock-cells = <1>; + #reset-cells = <1>; + reg = <0x50000000 0x1000>; + }; + + rcc_reboot: rcc-reboot@50000000 { + compatible = "syscon-reboot"; + regmap = <&rcc>; + offset = <0x404>; + mask = <0x1>; + }; + + rng1: rng@54003000 { + compatible = "st,stm32-rng"; + reg = <0x54003000 0x400>; + clocks = <&rcc RNG1_K>; + resets = <&rcc RNG1_R>; + status = "disabled"; + }; + + fmc_nand: fmc_nand@58002000 { + compatible = "st,stm32mp1-fmc"; + reg = <0x58002000 0x1000>, + <0x80000000 0x40000>, + <0x81000000 0x40000>, + <0x88000000 0x40000>, + <0x89000000 0x40000>; + clocks = <&rcc FMC_K>; + resets = <&rcc FMC_R>; + status = "disabled"; + }; + + qspi: qspi@58003000 { + compatible = "st,stm32f469-qspi"; + reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; + clocks = <&rcc QSPI_K>; + status = "disabled"; + }; + + sdmmc1: sdmmc@58005000 { + compatible = "st,stm32-sdmmc2"; + reg = <0x58005000 0x1000>, <0x58006000 0x1000>; + reg-names = "sdmmc", "delay"; + clocks = <&rcc SDMMC1_K>; + resets = <&rcc SDMMC1_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + + sdmmc2: sdmmc@58007000 { + compatible = "st,stm32-sdmmc2"; + reg = <0x58007000 0x1000>, <0x58008000 0x1000>; + reg-names = "sdmmc", "delay"; + clocks = <&rcc SDMMC2_K>; + resets = <&rcc SDMMC2_R>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + status = "disabled"; + }; + + iwdg2: iwdg@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-usart"; + reg = <0x5c000000 0x400>; + clocks = <&rcc USART1_K>; + status = "disabled"; + }; + + i2c4: i2c@5c002000 { + compatible = "st,stm32f7-i2c"; + reg = <0x5c002000 0x400>; + 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"; + }; + }; +}; diff --git a/fdts/stm32mp157caa-pinctrl.dtsi b/fdts/stm32mp157caa-pinctrl.dtsi new file mode 100644 index 0000000..774561a --- /dev/null +++ b/fdts/stm32mp157caa-pinctrl.dtsi @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Alexandre Torgue + */ + +#include "stm32mp157-pinctrl.dtsi" +/ { + soc { + pinctrl: pin-controller { + compatible = "st,stm32mp157caa-pinctrl"; + + 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 { + compatible = "st,stm32mp157caa-z-pinctrl"; + + gpioz: gpio@54004000 { + status = "okay"; + ngpios = <8>; + gpio-ranges = <&pinctrl_z 0 400 8>; + }; + }; + }; +}; diff --git a/include/drivers/st/stm32_gpio.h b/include/drivers/st/stm32_gpio.h new file mode 100644 index 0000000..7a5ccd3 --- /dev/null +++ b/include/drivers/st/stm32_gpio.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLAT_GPIO_H__ +#define __PLAT_GPIO_H__ + +#include + +#define STM32_GPIOA_BANK U(0x50002000) +#define STM32_GPIOZ_BANK U(0x54004000) +#define STM32_GPIO_BANK_OFFSET U(0x1000) + +#define GPIO_MODE_OFFSET U(0x00) +#define GPIO_TYPE_OFFSET U(0x04) +#define GPIO_SPEED_OFFSET U(0x08) +#define GPIO_PUPD_OFFSET U(0x0C) +#define GPIO_BSRR_OFFSET U(0x18) +#define GPIO_AFRL_OFFSET U(0x20) +#define GPIO_AFRH_OFFSET U(0x24) + +#define GPIO_ALT_LOWER_LIMIT U(0x08) + +#define GPIO_BANK_A U(0x00) +#define GPIO_BANK_B U(0x01) +#define GPIO_BANK_C U(0x02) +#define GPIO_BANK_D U(0x03) +#define GPIO_BANK_E U(0x04) +#define GPIO_BANK_F U(0x05) +#define GPIO_BANK_G U(0x06) +#define GPIO_BANK_H U(0x07) +#define GPIO_BANK_I U(0x08) +#define GPIO_BANK_J U(0x09) +#define GPIO_BANK_K U(0x0A) +#define GPIO_BANK_Z U(0x19) + +#define GPIO_PIN_0 U(0x00) +#define GPIO_PIN_1 U(0x01) +#define GPIO_PIN_2 U(0x02) +#define GPIO_PIN_3 U(0x03) +#define GPIO_PIN_4 U(0x04) +#define GPIO_PIN_5 U(0x05) +#define GPIO_PIN_6 U(0x06) +#define GPIO_PIN_7 U(0x07) +#define GPIO_PIN_8 U(0x08) +#define GPIO_PIN_9 U(0x09) +#define GPIO_PIN_10 U(0x0A) +#define GPIO_PIN_11 U(0x0B) +#define GPIO_PIN_12 U(0x0C) +#define GPIO_PIN_13 U(0x0D) +#define GPIO_PIN_14 U(0x0E) +#define GPIO_PIN_15 U(0x0F) +#define GPIO_PIN_MAX GPIO_PIN_15 + +#define GPIO_ALTERNATE_0 0x00 +#define GPIO_ALTERNATE_1 0x01 +#define GPIO_ALTERNATE_2 0x02 +#define GPIO_ALTERNATE_3 0x03 +#define GPIO_ALTERNATE_4 0x04 +#define GPIO_ALTERNATE_5 0x05 +#define GPIO_ALTERNATE_6 0x06 +#define GPIO_ALTERNATE_7 0x07 +#define GPIO_ALTERNATE_8 0x08 +#define GPIO_ALTERNATE_9 0x09 +#define GPIO_ALTERNATE_10 0x0A +#define GPIO_ALTERNATE_11 0x0B +#define GPIO_ALTERNATE_12 0x0C +#define GPIO_ALTERNATE_13 0x0D +#define GPIO_ALTERNATE_14 0x0E +#define GPIO_ALTERNATE_15 0x0F +#define GPIO_ALTERNATE_MASK U(0x0F) + +#define GPIO_MODE_INPUT 0x00 +#define GPIO_MODE_OUTPUT 0x01 +#define GPIO_MODE_ALTERNATE 0x02 +#define GPIO_MODE_ANALOG 0x03 +#define GPIO_MODE_MASK U(0x03) + +#define GPIO_OPEN_DRAIN U(0x10) + +#define GPIO_SPEED_LOW 0x00 +#define GPIO_SPEED_MEDIUM 0x01 +#define GPIO_SPEED_FAST 0x02 +#define GPIO_SPEED_HIGH 0x03 +#define GPIO_SPEED_MASK U(0x03) + +#define GPIO_NO_PULL 0x00 +#define GPIO_PULL_UP 0x01 +#define GPIO_PULL_DOWN 0x02 +#define GPIO_PULL_MASK U(0x03) + +#ifndef __ASSEMBLY__ +#include + +void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, + uint32_t pull, uint32_t alternate); +#endif /*__ASSEMBLY__*/ + +#endif /*__PLAT_GPIO_H__*/ diff --git a/include/drivers/st/stm32_i2c.h b/include/drivers/st/stm32_i2c.h new file mode 100644 index 0000000..29b9d34 --- /dev/null +++ b/include/drivers/st/stm32_i2c.h @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_I2C_H +#define __STM32MP1_I2C_H + +#include +#include + +/* Bit definition for I2C_CR1 register */ +#define I2C_CR1_PE BIT(0) +#define I2C_CR1_TXIE BIT(1) +#define I2C_CR1_RXIE BIT(2) +#define I2C_CR1_ADDRIE BIT(3) +#define I2C_CR1_NACKIE BIT(4) +#define I2C_CR1_STOPIE BIT(5) +#define I2C_CR1_TCIE BIT(6) +#define I2C_CR1_ERRIE BIT(7) +#define I2C_CR1_DNF GENMASK(11, 8) +#define I2C_CR1_ANFOFF BIT(12) +#define I2C_CR1_SWRST BIT(13) +#define I2C_CR1_TXDMAEN BIT(14) +#define I2C_CR1_RXDMAEN BIT(15) +#define I2C_CR1_SBC BIT(16) +#define I2C_CR1_NOSTRETCH BIT(17) +#define I2C_CR1_WUPEN BIT(18) +#define I2C_CR1_GCEN BIT(19) +#define I2C_CR1_SMBHEN BIT(22) +#define I2C_CR1_SMBDEN BIT(21) +#define I2C_CR1_ALERTEN BIT(22) +#define I2C_CR1_PECEN BIT(23) + +/* Bit definition for I2C_CR2 register */ +#define I2C_CR2_SADD GENMASK(9, 0) +#define I2C_CR2_RD_WRN BIT(10) +#define I2C_CR2_RD_WRN_OFFSET 10U +#define I2C_CR2_ADD10 BIT(11) +#define I2C_CR2_HEAD10R BIT(12) +#define I2C_CR2_START BIT(13) +#define I2C_CR2_STOP BIT(14) +#define I2C_CR2_NACK BIT(15) +#define I2C_CR2_NBYTES GENMASK(23, 16) +#define I2C_CR2_NBYTES_OFFSET 16U +#define I2C_CR2_RELOAD BIT(24) +#define I2C_CR2_AUTOEND BIT(25) +#define I2C_CR2_PECBYTE BIT(26) + +/* Bit definition for I2C_OAR1 register */ +#define I2C_OAR1_OA1 GENMASK(9, 0) +#define I2C_OAR1_OA1MODE BIT(10) +#define I2C_OAR1_OA1EN BIT(15) + +/* Bit definition for I2C_OAR2 register */ +#define I2C_OAR2_OA2 GENMASK(7, 1) +#define I2C_OAR2_OA2MSK GENMASK(10, 8) +#define I2C_OAR2_OA2NOMASK 0 +#define I2C_OAR2_OA2MASK01 BIT(8) +#define I2C_OAR2_OA2MASK02 BIT(9) +#define I2C_OAR2_OA2MASK03 GENMASK(9, 8) +#define I2C_OAR2_OA2MASK04 BIT(10) +#define I2C_OAR2_OA2MASK05 (BIT(8) | BIT(10)) +#define I2C_OAR2_OA2MASK06 (BIT(9) | BIT(10)) +#define I2C_OAR2_OA2MASK07 GENMASK(10, 8) +#define I2C_OAR2_OA2EN BIT(15) + +/* Bit definition for I2C_TIMINGR register */ +#define I2C_TIMINGR_SCLL GENMASK(7, 0) +#define I2C_TIMINGR_SCLH GENMASK(15, 8) +#define I2C_TIMINGR_SDADEL GENMASK(19, 16) +#define I2C_TIMINGR_SCLDEL GENMASK(23, 20) +#define I2C_TIMINGR_PRESC GENMASK(31, 28) + +/* Bit definition for I2C_TIMEOUTR register */ +#define I2C_TIMEOUTR_TIMEOUTA GENMASK(11, 0) +#define I2C_TIMEOUTR_TIDLE BIT(12) +#define I2C_TIMEOUTR_TIMOUTEN BIT(15) +#define I2C_TIMEOUTR_TIMEOUTB GENMASK(27, 16) +#define I2C_TIMEOUTR_TEXTEN BIT(31) + +/* Bit definition for I2C_ISR register */ +#define I2C_ISR_TXE BIT(0) +#define I2C_ISR_TXIS BIT(1) +#define I2C_ISR_RXNE BIT(2) +#define I2C_ISR_ADDR BIT(3) +#define I2C_ISR_NACKF BIT(4) +#define I2C_ISR_STOPF BIT(5) +#define I2C_ISR_TC BIT(6) +#define I2C_ISR_TCR BIT(7) +#define I2C_ISR_BERR BIT(8) +#define I2C_ISR_ARLO BIT(9) +#define I2C_ISR_OVR BIT(10) +#define I2C_ISR_PECERR BIT(11) +#define I2C_ISR_TIMEOUT BIT(12) +#define I2C_ISR_ALERT BIT(13) +#define I2C_ISR_BUSY BIT(15) +#define I2C_ISR_DIR BIT(16) +#define I2C_ISR_ADDCODE GENMASK(23, 17) + +/* Bit definition for I2C_ICR register */ +#define I2C_ICR_ADDRCF BIT(3) +#define I2C_ICR_NACKCF BIT(4) +#define I2C_ICR_STOPCF BIT(5) +#define I2C_ICR_BERRCF BIT(8) +#define I2C_ICR_ARLOCF BIT(9) +#define I2C_ICR_OVRCF BIT(10) +#define I2C_ICR_PECCF BIT(11) +#define I2C_ICR_TIMOUTCF BIT(12) +#define I2C_ICR_ALERTCF BIT(13) + +struct stm32_i2c_init_s { + uint32_t timing; /* Specifies the I2C_TIMINGR_register value + * This parameter is calculated by referring + * to I2C initialization section in Reference + * manual. + */ + + uint32_t own_address1; /* Specifies the first device own address. + * This parameter can be a 7-bit or 10-bit + * address. + */ + + uint32_t addressing_mode; /* Specifies if 7-bit or 10-bit addressing + * mode is selected. + * This parameter can be a value of @ref + * I2C_ADDRESSING_MODE. + */ + + uint32_t dual_address_mode; /* Specifies if dual addressing mode is + * selected. + * This parameter can be a value of @ref + * I2C_DUAL_ADDRESSING_MODE. + */ + + uint32_t own_address2; /* Specifies the second device own address + * if dual addressing mode is selected. + * This parameter can be a 7-bit address. + */ + + uint32_t own_address2_masks; /* Specifies the acknowledge mask address + * second device own address if dual + * addressing mode is selected. + * This parameter can be a value of @ref + * I2C_OWN_ADDRESS2_MASKS. + */ + + uint32_t general_call_mode; /* Specifies if general call mode is + * selected. + * This parameter can be a value of @ref + * I2C_GENERAL_CALL_ADDRESSING_MODE. + */ + + uint32_t no_stretch_mode; /* Specifies if nostretch mode is + * selected. + * This parameter can be a value of @ref + * I2C_NOSTRETCH_MODE. + */ + +}; + +enum i2c_state_e { + I2C_STATE_RESET = 0x00U, /* Peripheral is not yet + * initialized. + */ + I2C_STATE_READY = 0x20U, /* Peripheral Initialized + * and ready for use. + */ + I2C_STATE_BUSY = 0x24U, /* An internal process is + * ongoing. + */ + I2C_STATE_BUSY_TX = 0x21U, /* Data Transmission process + * is ongoing. + */ + I2C_STATE_BUSY_RX = 0x22U, /* Data Reception process + * is ongoing. + */ + I2C_STATE_LISTEN = 0x28U, /* Address Listen Mode is + * ongoing. + */ + I2C_STATE_BUSY_TX_LISTEN = 0x29U, /* Address Listen Mode + * and Data Transmission + * process is ongoing. + */ + I2C_STATE_BUSY_RX_LISTEN = 0x2AU, /* Address Listen Mode + * and Data Reception + * process is ongoing. + */ + I2C_STATE_ABORT = 0x60U, /* Abort user request ongoing. */ + I2C_STATE_TIMEOUT = 0xA0U, /* Timeout state. */ + I2C_STATE_ERROR = 0xE0U /* Error. */ + +}; + +enum i2c_mode_e { + I2C_MODE_NONE = 0x00U, /* No I2C communication on going. */ + I2C_MODE_MASTER = 0x10U, /* I2C communication is in Master Mode. */ + I2C_MODE_SLAVE = 0x20U, /* I2C communication is in Slave Mode. */ + I2C_MODE_MEM = 0x40U /* I2C communication is in Memory Mode. */ + +}; + +#define I2C_ERROR_NONE 0x00000000U /* No error */ +#define I2C_ERROR_BERR 0x00000001U /* BERR error */ +#define I2C_ERROR_ARLO 0x00000002U /* ARLO error */ +#define I2C_ERROR_AF 0x00000004U /* ACKF error */ +#define I2C_ERROR_OVR 0x00000008U /* OVR error */ +#define I2C_ERROR_DMA 0x00000010U /* DMA transfer error */ +#define I2C_ERROR_TIMEOUT 0x00000020U /* Timeout error */ +#define I2C_ERROR_SIZE 0x00000040U /* Size Management error */ + +struct i2c_handle_s { + uint32_t i2c_base_addr; /* Registers base address */ + + struct stm32_i2c_init_s i2c_init; /* Communication parameters */ + + uint8_t *p_buff; /* Pointer to transfer buffer */ + + uint16_t xfer_size; /* Transfer size */ + + uint16_t xfer_count; /* Transfer counter */ + + uint32_t prev_state; /* Communication previous + * state + */ + + uint8_t lock; /* Locking object */ + + enum i2c_state_e i2c_state; /* Communication state */ + + enum i2c_mode_e i2c_mode; /* Communication mode */ + + uint32_t i2c_err; /* Error code */ +}; + +#define I2C_ADDRESSINGMODE_7BIT 0x00000001U +#define I2C_ADDRESSINGMODE_10BIT 0x00000002U + +#define I2C_DUALADDRESS_DISABLE 0x00000000U +#define I2C_DUALADDRESS_ENABLE I2C_OAR2_OA2EN + +#define I2C_GENERALCALL_DISABLE 0x00000000U +#define I2C_GENERALCALL_ENABLE I2C_CR1_GCEN + +#define I2C_NOSTRETCH_DISABLE 0x00000000U +#define I2C_NOSTRETCH_ENABLE I2C_CR1_NOSTRETCH + +#define I2C_MEMADD_SIZE_8BIT 0x00000001U +#define I2C_MEMADD_SIZE_16BIT 0x00000002U + +#define I2C_RELOAD_MODE I2C_CR2_RELOAD +#define I2C_AUTOEND_MODE I2C_CR2_AUTOEND +#define I2C_SOFTEND_MODE 0x00000000U + +#define I2C_NO_STARTSTOP 0x00000000U +#define I2C_GENERATE_STOP (BIT(31) | I2C_CR2_STOP) +#define I2C_GENERATE_START_READ (BIT(31) | I2C_CR2_START | \ + I2C_CR2_RD_WRN) +#define I2C_GENERATE_START_WRITE (BIT(31) | I2C_CR2_START) + +#define I2C_FLAG_TXE I2C_ISR_TXE +#define I2C_FLAG_TXIS I2C_ISR_TXIS +#define I2C_FLAG_RXNE I2C_ISR_RXNE +#define I2C_FLAG_ADDR I2C_ISR_ADDR +#define I2C_FLAG_AF I2C_ISR_NACKF +#define I2C_FLAG_STOPF I2C_ISR_STOPF +#define I2C_FLAG_TC I2C_ISR_TC +#define I2C_FLAG_TCR I2C_ISR_TCR +#define I2C_FLAG_BERR I2C_ISR_BERR +#define I2C_FLAG_ARLO I2C_ISR_ARLO +#define I2C_FLAG_OVR I2C_ISR_OVR +#define I2C_FLAG_PECERR I2C_ISR_PECERR +#define I2C_FLAG_TIMEOUT I2C_ISR_TIMEOUT +#define I2C_FLAG_ALERT I2C_ISR_ALERT +#define I2C_FLAG_BUSY I2C_ISR_BUSY +#define I2C_FLAG_DIR I2C_ISR_DIR + +#define I2C_RESET_CR2 (I2C_CR2_SADD | I2C_CR2_HEAD10R | \ + I2C_CR2_NBYTES | I2C_CR2_RELOAD | \ + I2C_CR2_RD_WRN) + +#define I2C_ANALOGFILTER_ENABLE ((uint32_t)0x00000000U) +#define I2C_ANALOGFILTER_DISABLE I2C_CR1_ANFOFF + +int stm32_i2c_init(struct i2c_handle_s *hi2c); + +int stm32_i2c_mem_write(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t mem_addr, uint16_t mem_add_size, + uint8_t *p_data, uint16_t size, uint32_t timeout); +int stm32_i2c_mem_read(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint16_t mem_addr, uint16_t mem_add_size, + uint8_t *p_data, uint16_t size, uint32_t timeout); +int stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c, uint16_t dev_addr, + uint32_t trials, uint32_t timeout); + +int stm32_i2c_config_analog_filter(struct i2c_handle_s *hi2c, + uint32_t analog_filter); + +#endif /* __STM32MP1_I2C_H */ diff --git a/include/drivers/st/stm32mp1_clk.h b/include/drivers/st/stm32mp1_clk.h new file mode 100644 index 0000000..85a1eb8 --- /dev/null +++ b/include/drivers/st/stm32mp1_clk.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_CLK_H__ +#define __STM32MP1_CLK_H__ + +#include +#include + +int stm32mp1_clk_probe(void); +int stm32mp1_clk_init(void); +bool stm32mp1_clk_is_enabled(unsigned long id); +int stm32mp1_clk_enable(unsigned long id); +int stm32mp1_clk_disable(unsigned long id); +unsigned long stm32mp1_clk_get_rate(unsigned long id); +void stm32mp1_stgen_increment(unsigned long long offset_in_ms); + +static inline uint32_t get_timer(uint32_t base) +{ + if (base == 0U) { + return (uint32_t)(~read_cntpct_el0()); + } + + return base - (uint32_t)(~read_cntpct_el0()); +} + +#endif /* __STM32MP1_CLK_H__ */ diff --git a/include/drivers/st/stm32mp1_clkfunc.h b/include/drivers/st/stm32mp1_clkfunc.h new file mode 100644 index 0000000..635a9cd --- /dev/null +++ b/include/drivers/st/stm32mp1_clkfunc.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_CLKFUNC_H__ +#define __STM32MP1_CLKFUNC_H__ + +#include + +enum stm32mp_osc_id { + _HSI, + _HSE, + _CSI, + _LSI, + _LSE, + _I2S_CKIN, + _USB_PHY_48, + NB_OSC, + _UNKNOWN_OSC_ID = 0xFF +}; + +extern const char *stm32mp_osc_node_label[NB_OSC]; + +int fdt_osc_read_freq(const char *name, uint32_t *freq); +bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name); +uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, + const char *prop_name, + uint32_t dflt_value); + +uint32_t fdt_rcc_read_addr(void); +int fdt_rcc_read_uint32_array(const char *prop_name, + uint32_t *array, uint32_t count); +int fdt_rcc_subnode_offset(const char *name); +const uint32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp); +bool fdt_get_rcc_secure_status(void); + +uintptr_t fdt_get_stgen_base(void); +int fdt_get_clock_id(int node); + +#endif /* __STM32MP1_CLKFUNC_H__ */ diff --git a/include/drivers/st/stm32mp1_ddr.h b/include/drivers/st/stm32mp1_ddr.h new file mode 100644 index 0000000..0765664 --- /dev/null +++ b/include/drivers/st/stm32mp1_ddr.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef _STM32MP1_DDR_H +#define _STM32MP1_DDR_H + +#include + +#define DT_DDR_COMPAT "st,stm32mp1-ddr" + +struct stm32mp1_ddr_size { + uint64_t base; + uint64_t size; +}; + +/** + * struct ddr_info + * + * @dev: pointer for the device + * @info: UCLASS RAM information + * @ctl: DDR controleur base address + * @phy: DDR PHY base address + * @syscfg: syscfg base address + */ +struct ddr_info { + struct stm32mp1_ddr_size info; + struct stm32mp1_ddrctl *ctl; + struct stm32mp1_ddrphy *phy; + uintptr_t pwr; + uintptr_t rcc; +}; + +struct stm32mp1_ddrctrl_reg { + uint32_t mstr; + uint32_t mrctrl0; + uint32_t mrctrl1; + uint32_t derateen; + uint32_t derateint; + uint32_t pwrctl; + uint32_t pwrtmg; + uint32_t hwlpctl; + uint32_t rfshctl0; + uint32_t rfshctl3; + uint32_t crcparctl0; + uint32_t zqctl0; + uint32_t dfitmg0; + uint32_t dfitmg1; + uint32_t dfilpcfg0; + uint32_t dfiupd0; + uint32_t dfiupd1; + uint32_t dfiupd2; + uint32_t dfiphymstr; + uint32_t odtmap; + uint32_t dbg0; + uint32_t dbg1; + uint32_t dbgcmd; + uint32_t poisoncfg; + uint32_t pccfg; +}; + +struct stm32mp1_ddrctrl_timing { + uint32_t rfshtmg; + uint32_t dramtmg0; + uint32_t dramtmg1; + uint32_t dramtmg2; + uint32_t dramtmg3; + uint32_t dramtmg4; + uint32_t dramtmg5; + uint32_t dramtmg6; + uint32_t dramtmg7; + uint32_t dramtmg8; + uint32_t dramtmg14; + uint32_t odtcfg; +}; + +struct stm32mp1_ddrctrl_map { + uint32_t addrmap1; + uint32_t addrmap2; + uint32_t addrmap3; + uint32_t addrmap4; + uint32_t addrmap5; + uint32_t addrmap6; + uint32_t addrmap9; + uint32_t addrmap10; + uint32_t addrmap11; +}; + +struct stm32mp1_ddrctrl_perf { + uint32_t sched; + uint32_t sched1; + uint32_t perfhpr1; + uint32_t perflpr1; + uint32_t perfwr1; + uint32_t pcfgr_0; + uint32_t pcfgw_0; + uint32_t pcfgqos0_0; + uint32_t pcfgqos1_0; + uint32_t pcfgwqos0_0; + uint32_t pcfgwqos1_0; + uint32_t pcfgr_1; + uint32_t pcfgw_1; + uint32_t pcfgqos0_1; + uint32_t pcfgqos1_1; + uint32_t pcfgwqos0_1; + uint32_t pcfgwqos1_1; +}; + +struct stm32mp1_ddrphy_reg { + uint32_t pgcr; + uint32_t aciocr; + uint32_t dxccr; + uint32_t dsgcr; + uint32_t dcr; + uint32_t odtcr; + uint32_t zq0cr1; + uint32_t dx0gcr; + uint32_t dx1gcr; + uint32_t dx2gcr; + uint32_t dx3gcr; +}; + +struct stm32mp1_ddrphy_timing { + uint32_t ptr0; + uint32_t ptr1; + uint32_t ptr2; + uint32_t dtpr0; + uint32_t dtpr1; + uint32_t dtpr2; + uint32_t mr0; + uint32_t mr1; + uint32_t mr2; + uint32_t mr3; +}; + +struct stm32mp1_ddrphy_cal { + uint32_t dx0dllcr; + uint32_t dx0dqtr; + uint32_t dx0dqstr; + uint32_t dx1dllcr; + uint32_t dx1dqtr; + uint32_t dx1dqstr; + uint32_t dx2dllcr; + uint32_t dx2dqtr; + uint32_t dx2dqstr; + uint32_t dx3dllcr; + uint32_t dx3dqtr; + uint32_t dx3dqstr; +}; + +struct stm32mp1_ddr_info { + const char *name; + uint16_t speed; /* in MHZ */ + uint32_t size; /* Memory size in byte = col * row * width */ +}; + +struct stm32mp1_ddr_config { + struct stm32mp1_ddr_info info; + struct stm32mp1_ddrctrl_reg c_reg; + struct stm32mp1_ddrctrl_timing c_timing; + struct stm32mp1_ddrctrl_map c_map; + struct stm32mp1_ddrctrl_perf c_perf; + struct stm32mp1_ddrphy_reg p_reg; + struct stm32mp1_ddrphy_timing p_timing; + struct stm32mp1_ddrphy_cal p_cal; +}; + +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_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 new file mode 100644 index 0000000..298a080 --- /dev/null +++ b/include/drivers/st/stm32mp1_ddr_helpers.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_DDR_HELPERS_H__ +#define __STM32MP1_DDR_HELPERS_H__ + +void ddr_enable_clock(void); + +#endif /* __STM32MP1_DDR_HELPERS_H__ */ diff --git a/include/drivers/st/stm32mp1_ddr_regs.h b/include/drivers/st/stm32mp1_ddr_regs.h new file mode 100644 index 0000000..64ad965 --- /dev/null +++ b/include/drivers/st/stm32mp1_ddr_regs.h @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef _RAM_STM32MP1_DDR_REGS_H +#define _RAM_STM32MP1_DDR_REGS_H + +#include + +/* DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL) registers */ +struct stm32mp1_ddrctl { + uint32_t mstr ; /* 0x0 Master */ + uint32_t stat; /* 0x4 Operating Mode Status */ + uint8_t reserved008[0x10 - 0x8]; + uint32_t mrctrl0; /* 0x10 Control 0 */ + uint32_t mrctrl1; /* 0x14 Control 1 */ + uint32_t mrstat; /* 0x18 Status */ + uint32_t reserved01c; /* 0x1c */ + uint32_t derateen; /* 0x20 Temperature Derate Enable */ + uint32_t derateint; /* 0x24 Temperature Derate Interval */ + uint8_t reserved028[0x30 - 0x28]; + uint32_t pwrctl; /* 0x30 Low Power Control */ + uint32_t pwrtmg; /* 0x34 Low Power Timing */ + uint32_t hwlpctl; /* 0x38 Hardware Low Power Control */ + uint8_t reserved03c[0x50 - 0x3C]; + uint32_t rfshctl0; /* 0x50 Refresh Control 0 */ + uint32_t reserved054; /* 0x54 Refresh Control 1 */ + uint32_t reserved058; /* 0x58 Refresh Control 2 */ + uint32_t reserved05C; + uint32_t rfshctl3; /* 0x60 Refresh Control 0 */ + uint32_t rfshtmg; /* 0x64 Refresh Timing */ + uint8_t reserved068[0xc0 - 0x68]; + uint32_t crcparctl0; /* 0xc0 CRC Parity Control0 */ + uint32_t reserved0c4; /* 0xc4 CRC Parity Control1 */ + uint32_t reserved0c8; /* 0xc8 CRC Parity Control2 */ + uint32_t crcparstat; /* 0xcc CRC Parity Status */ + uint32_t init0; /* 0xd0 SDRAM Initialization 0 */ + uint32_t init1; /* 0xd4 SDRAM Initialization 1 */ + uint32_t init2; /* 0xd8 SDRAM Initialization 2 */ + uint32_t init3; /* 0xdc SDRAM Initialization 3 */ + uint32_t init4; /* 0xe0 SDRAM Initialization 4 */ + uint32_t init5; /* 0xe4 SDRAM Initialization 5 */ + uint32_t reserved0e8; + uint32_t reserved0ec; + uint32_t dimmctl; /* 0xf0 DIMM Control */ + uint8_t reserved0f4[0x100 - 0xf4]; + uint32_t dramtmg0; /* 0x100 SDRAM Timing 0 */ + uint32_t dramtmg1; /* 0x104 SDRAM Timing 1 */ + uint32_t dramtmg2; /* 0x108 SDRAM Timing 2 */ + uint32_t dramtmg3; /* 0x10c SDRAM Timing 3 */ + uint32_t dramtmg4; /* 0x110 SDRAM Timing 4 */ + uint32_t dramtmg5; /* 0x114 SDRAM Timing 5 */ + uint32_t dramtmg6; /* 0x118 SDRAM Timing 6 */ + uint32_t dramtmg7; /* 0x11c SDRAM Timing 7 */ + uint32_t dramtmg8; /* 0x120 SDRAM Timing 8 */ + uint8_t reserved124[0x138 - 0x124]; + uint32_t dramtmg14; /* 0x138 SDRAM Timing 14 */ + uint32_t dramtmg15; /* 0x13C SDRAM Timing 15 */ + uint8_t reserved140[0x180 - 0x140]; + uint32_t zqctl0; /* 0x180 ZQ Control 0 */ + uint32_t zqctl1; /* 0x184 ZQ Control 1 */ + uint32_t zqctl2; /* 0x188 ZQ Control 2 */ + uint32_t zqstat; /* 0x18c ZQ Status */ + uint32_t dfitmg0; /* 0x190 DFI Timing 0 */ + uint32_t dfitmg1; /* 0x194 DFI Timing 1 */ + uint32_t dfilpcfg0; /* 0x198 DFI Low Power Configuration 0 */ + uint32_t reserved19c; + uint32_t dfiupd0; /* 0x1a0 DFI Update 0 */ + uint32_t dfiupd1; /* 0x1a4 DFI Update 1 */ + uint32_t dfiupd2; /* 0x1a8 DFI Update 2 */ + uint32_t reserved1ac; + uint32_t dfimisc; /* 0x1b0 DFI Miscellaneous Control */ + uint8_t reserved1b4[0x1bc - 0x1b4]; + uint32_t dfistat; /* 0x1bc DFI Miscellaneous Control */ + uint8_t reserved1c0[0x1c4 - 0x1c0]; + uint32_t dfiphymstr; /* 0x1c4 DFI PHY Master interface */ + uint8_t reserved1c8[0x204 - 0x1c8]; + uint32_t addrmap1; /* 0x204 Address Map 1 */ + uint32_t addrmap2; /* 0x208 Address Map 2 */ + uint32_t addrmap3; /* 0x20c Address Map 3 */ + uint32_t addrmap4; /* 0x210 Address Map 4 */ + uint32_t addrmap5; /* 0x214 Address Map 5 */ + uint32_t addrmap6; /* 0x218 Address Map 6 */ + uint8_t reserved21c[0x224 - 0x21c]; + uint32_t addrmap9; /* 0x224 Address Map 9 */ + uint32_t addrmap10; /* 0x228 Address Map 10 */ + uint32_t addrmap11; /* 0x22C Address Map 11 */ + uint8_t reserved230[0x240 - 0x230]; + uint32_t odtcfg; /* 0x240 ODT Configuration */ + uint32_t odtmap; /* 0x244 ODT/Rank Map */ + uint8_t reserved248[0x250 - 0x248]; + uint32_t sched; /* 0x250 Scheduler Control */ + uint32_t sched1; /* 0x254 Scheduler Control 1 */ + uint32_t reserved258; + uint32_t perfhpr1; /* 0x25c High Priority Read CAM 1 */ + uint32_t reserved260; + uint32_t perflpr1; /* 0x264 Low Priority Read CAM 1 */ + uint32_t reserved268; + uint32_t perfwr1; /* 0x26c Write CAM 1 */ + uint8_t reserved27c[0x300 - 0x270]; + uint32_t dbg0; /* 0x300 Debug 0 */ + uint32_t dbg1; /* 0x304 Debug 1 */ + uint32_t dbgcam; /* 0x308 CAM Debug */ + uint32_t dbgcmd; /* 0x30c Command Debug */ + uint32_t dbgstat; /* 0x310 Status Debug */ + uint8_t reserved314[0x320 - 0x314]; + uint32_t swctl; /* 0x320 Software Programming Control Enable */ + uint32_t swstat; /* 0x324 Software Programming Control Status */ + uint8_t reserved328[0x36c - 0x328]; + uint32_t poisoncfg; /* 0x36c AXI Poison Configuration Register */ + uint32_t poisonstat; /* 0x370 AXI Poison Status Register */ + uint8_t reserved374[0x3fc - 0x374]; + + /* Multi Port registers */ + uint32_t pstat; /* 0x3fc Port Status */ + uint32_t pccfg; /* 0x400 Port Common Configuration */ + + /* PORT 0 */ + uint32_t pcfgr_0; /* 0x404 Configuration Read */ + uint32_t pcfgw_0; /* 0x408 Configuration Write */ + uint8_t reserved40c[0x490 - 0x40c]; + uint32_t pctrl_0; /* 0x490 Port Control Register */ + uint32_t pcfgqos0_0; /* 0x494 Read QoS Configuration 0 */ + uint32_t pcfgqos1_0; /* 0x498 Read QoS Configuration 1 */ + uint32_t pcfgwqos0_0; /* 0x49c Write QoS Configuration 0 */ + uint32_t pcfgwqos1_0; /* 0x4a0 Write QoS Configuration 1 */ + uint8_t reserved4a4[0x4b4 - 0x4a4]; + + /* PORT 1 */ + uint32_t pcfgr_1; /* 0x4b4 Configuration Read */ + uint32_t pcfgw_1; /* 0x4b8 Configuration Write */ + uint8_t reserved4bc[0x540 - 0x4bc]; + uint32_t pctrl_1; /* 0x540 Port 2 Control Register */ + uint32_t pcfgqos0_1; /* 0x544 Read QoS Configuration 0 */ + uint32_t pcfgqos1_1; /* 0x548 Read QoS Configuration 1 */ + uint32_t pcfgwqos0_1; /* 0x54c Write QoS Configuration 0 */ + uint32_t pcfgwqos1_1; /* 0x550 Write QoS Configuration 1 */ +} __packed; + +/* DDR Physical Interface Control (DDRPHYC) registers*/ +struct stm32mp1_ddrphy { + uint32_t ridr; /* 0x00 R Revision Identification */ + uint32_t pir; /* 0x04 R/W PHY Initialization */ + uint32_t pgcr; /* 0x08 R/W PHY General Configuration */ + uint32_t pgsr; /* 0x0C PHY General Status */ + uint32_t dllgcr; /* 0x10 R/W DLL General Control */ + uint32_t acdllcr; /* 0x14 R/W AC DLL Control */ + uint32_t ptr0; /* 0x18 R/W PHY Timing 0 */ + uint32_t ptr1; /* 0x1C R/W PHY Timing 1 */ + uint32_t ptr2; /* 0x20 R/W PHY Timing 2 */ + uint32_t aciocr; /* 0x24 AC I/O Configuration */ + uint32_t dxccr; /* 0x28 DATX8 Common Configuration */ + uint32_t dsgcr; /* 0x2C DDR System General Configuration */ + uint32_t dcr; /* 0x30 DRAM Configuration */ + uint32_t dtpr0; /* 0x34 DRAM Timing Parameters0 */ + uint32_t dtpr1; /* 0x38 DRAM Timing Parameters1 */ + uint32_t dtpr2; /* 0x3C DRAM Timing Parameters2 */ + uint32_t mr0; /* 0x40 Mode 0 */ + uint32_t mr1; /* 0x44 Mode 1 */ + uint32_t mr2; /* 0x48 Mode 2 */ + uint32_t mr3; /* 0x4C Mode 3 */ + uint32_t odtcr; /* 0x50 ODT Configuration */ + uint32_t dtar; /* 0x54 data training address */ + uint32_t dtdr0; /* 0x58 */ + uint32_t dtdr1; /* 0x5c */ + uint8_t res1[0x0c0 - 0x060]; /* 0x60 */ + uint32_t dcuar; /* 0xc0 Address */ + uint32_t dcudr; /* 0xc4 DCU Data */ + uint32_t dcurr; /* 0xc8 DCU Run */ + uint32_t dculr; /* 0xcc DCU Loop */ + uint32_t dcugcr; /* 0xd0 DCU General Configuration */ + uint32_t dcutpr; /* 0xd4 DCU Timing Parameters */ + uint32_t dcusr0; /* 0xd8 DCU Status 0 */ + uint32_t dcusr1; /* 0xdc DCU Status 1 */ + uint8_t res2[0x100 - 0xe0]; /* 0xe0 */ + uint32_t bistrr; /* 0x100 BIST Run */ + uint32_t bistmskr0; /* 0x104 BIST Mask 0 */ + uint32_t bistmskr1; /* 0x108 BIST Mask 0 */ + uint32_t bistwcr; /* 0x10c BIST Word Count */ + uint32_t bistlsr; /* 0x110 BIST LFSR Seed */ + uint32_t bistar0; /* 0x114 BIST Address 0 */ + uint32_t bistar1; /* 0x118 BIST Address 1 */ + uint32_t bistar2; /* 0x11c BIST Address 2 */ + uint32_t bistupdr; /* 0x120 BIST User Data Pattern */ + uint32_t bistgsr; /* 0x124 BIST General Status */ + uint32_t bistwer; /* 0x128 BIST Word Error */ + uint32_t bistber0; /* 0x12c BIST Bit Error 0 */ + uint32_t bistber1; /* 0x130 BIST Bit Error 1 */ + uint32_t bistber2; /* 0x134 BIST Bit Error 2 */ + uint32_t bistwcsr; /* 0x138 BIST Word Count Status */ + uint32_t bistfwr0; /* 0x13c BIST Fail Word 0 */ + uint32_t bistfwr1; /* 0x140 BIST Fail Word 1 */ + uint8_t res3[0x178 - 0x144]; /* 0x144 */ + uint32_t gpr0; /* 0x178 General Purpose 0 (GPR0) */ + uint32_t gpr1; /* 0x17C General Purpose 1 (GPR1) */ + uint32_t zq0cr0; /* 0x180 zq 0 control 0 */ + uint32_t zq0cr1; /* 0x184 zq 0 control 1 */ + uint32_t zq0sr0; /* 0x188 zq 0 status 0 */ + uint32_t zq0sr1; /* 0x18C zq 0 status 1 */ + uint8_t res4[0x1C0 - 0x190]; /* 0x190 */ + uint32_t dx0gcr; /* 0x1c0 Byte lane 0 General Configuration */ + uint32_t dx0gsr0; /* 0x1c4 Byte lane 0 General Status 0 */ + uint32_t dx0gsr1; /* 0x1c8 Byte lane 0 General Status 1 */ + uint32_t dx0dllcr; /* 0x1cc Byte lane 0 DLL Control */ + uint32_t dx0dqtr; /* 0x1d0 Byte lane 0 DQ Timing */ + uint32_t dx0dqstr; /* 0x1d4 Byte lane 0 DQS Timing */ + uint8_t res5[0x200 - 0x1d8]; /* 0x1d8 */ + uint32_t dx1gcr; /* 0x200 Byte lane 1 General Configuration */ + uint32_t dx1gsr0; /* 0x204 Byte lane 1 General Status 0 */ + uint32_t dx1gsr1; /* 0x208 Byte lane 1 General Status 1 */ + uint32_t dx1dllcr; /* 0x20c Byte lane 1 DLL Control */ + uint32_t dx1dqtr; /* 0x210 Byte lane 1 DQ Timing */ + uint32_t dx1dqstr; /* 0x214 Byte lane 1 QS Timing */ + uint8_t res6[0x240 - 0x218]; /* 0x218 */ + uint32_t dx2gcr; /* 0x240 Byte lane 2 General Configuration */ + uint32_t dx2gsr0; /* 0x244 Byte lane 2 General Status 0 */ + uint32_t dx2gsr1; /* 0x248 Byte lane 2 General Status 1 */ + uint32_t dx2dllcr; /* 0x24c Byte lane 2 DLL Control */ + uint32_t dx2dqtr; /* 0x250 Byte lane 2 DQ Timing */ + uint32_t dx2dqstr; /* 0x254 Byte lane 2 QS Timing */ + uint8_t res7[0x280 - 0x258]; /* 0x258 */ + uint32_t dx3gcr; /* 0x280 Byte lane 3 General Configuration */ + uint32_t dx3gsr0; /* 0x284 Byte lane 3 General Status 0 */ + uint32_t dx3gsr1; /* 0x288 Byte lane 3 General Status 1 */ + uint32_t dx3dllcr; /* 0x28c Byte lane 3 DLL Control */ + uint32_t dx3dqtr; /* 0x290 Byte lane 3 DQ Timing */ + uint32_t dx3dqstr; /* 0x294 Byte lane 3 QS Timing */ +} __packed; + +/* DDR Controller registers offsets */ +#define DDRCTRL_MSTR 0x000 +#define DDRCTRL_STAT 0x004 +#define DDRCTRL_MRCTRL0 0x010 +#define DDRCTRL_MRSTAT 0x018 +#define DDRCTRL_PWRCTL 0x030 +#define DDRCTRL_PWRTMG 0x034 +#define DDRCTRL_HWLPCTL 0x038 +#define DDRCTRL_RFSHCTL3 0x060 +#define DDRCTRL_RFSHTMG 0x064 +#define DDRCTRL_INIT0 0x0D0 +#define DDRCTRL_DFIMISC 0x1B0 +#define DDRCTRL_DBG1 0x304 +#define DDRCTRL_DBGCAM 0x308 +#define DDRCTRL_DBGCMD 0x30C +#define DDRCTRL_DBGSTAT 0x310 +#define DDRCTRL_SWCTL 0x320 +#define DDRCTRL_SWSTAT 0x324 +#define DDRCTRL_PCTRL_0 0x490 +#define DDRCTRL_PCTRL_1 0x540 + +/* DDR Controller Register fields */ +#define DDRCTRL_MSTR_DDR3 BIT(0) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK(13, 12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_FULL 0 +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF BIT(12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER BIT(13) +#define DDRCTRL_MSTR_DLL_OFF_MODE BIT(15) + +#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0) +#define DDRCTRL_STAT_OPERATING_MODE_NORMAL BIT(0) +#define DDRCTRL_STAT_OPERATING_MODE_SR (BIT(0) | BIT(1)) +#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) +#define DDRCTRL_STAT_SELFREF_TYPE_ASR (BIT(4) | BIT(5)) +#define DDRCTRL_STAT_SELFREF_TYPE_SR BIT(5) + +#define DDRCTRL_MRCTRL0_MR_TYPE_WRITE U(0) +/* Only one rank supported */ +#define DDRCTRL_MRCTRL0_MR_RANK_SHIFT 4 +#define DDRCTRL_MRCTRL0_MR_RANK_ALL \ + (0x1U << DDRCTRL_MRCTRL0_MR_RANK_SHIFT) +#define DDRCTRL_MRCTRL0_MR_ADDR_SHIFT 12 +#define DDRCTRL_MRCTRL0_MR_ADDR_MASK GENMASK(15, 12) +#define DDRCTRL_MRCTRL0_MR_WR BIT(31) + +#define DDRCTRL_MRSTAT_MR_WR_BUSY BIT(0) + +#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0) +#define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) +#define DDRCTRL_PWRCTL_EN_DFI_DRAM_CLK_DISABLE BIT(3) +#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) + +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_MASK GENMASK(19, 12) +#define DDRCTRL_PWRTMG_SELFREF_TO_X32_0 BIT(16) + +#define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) + +#define DDRCTRL_HWLPCTL_HW_LP_EN BIT(0) + +#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) +#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT 16 + +#define DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK GENMASK(31, 30) +#define DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL BIT(30) + +#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) + +#define DDRCTRL_DBG1_DIS_HIF BIT(1) + +#define DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY BIT(29) +#define DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY BIT(28) +#define DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY BIT(26) +#define DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH GENMASK(12, 8) +#define DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH GENMASK(4, 0) +#define DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY \ + (DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY | \ + DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY) +#define DDRCTRL_DBGCAM_DBG_Q_DEPTH \ + (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \ + DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH | \ + DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH) + +#define DDRCTRL_DBGCMD_RANK0_REFRESH BIT(0) + +#define DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY BIT(0) + +#define DDRCTRL_SWCTL_SW_DONE BIT(0) + +#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0) + +#define DDRCTRL_PCTRL_N_PORT_EN BIT(0) + +/* DDR PHY registers offsets */ +#define DDRPHYC_PIR 0x004 +#define DDRPHYC_PGCR 0x008 +#define DDRPHYC_PGSR 0x00C +#define DDRPHYC_DLLGCR 0x010 +#define DDRPHYC_ACDLLCR 0x014 +#define DDRPHYC_PTR0 0x018 +#define DDRPHYC_ACIOCR 0x024 +#define DDRPHYC_DXCCR 0x028 +#define DDRPHYC_DSGCR 0x02C +#define DDRPHYC_ZQ0CR0 0x180 +#define DDRPHYC_DX0GCR 0x1C0 +#define DDRPHYC_DX0DLLCR 0x1CC +#define DDRPHYC_DX1GCR 0x200 +#define DDRPHYC_DX1DLLCR 0x20C +#define DDRPHYC_DX2GCR 0x240 +#define DDRPHYC_DX2DLLCR 0x24C +#define DDRPHYC_DX3GCR 0x280 +#define DDRPHYC_DX3DLLCR 0x28C + +/* DDR PHY Register fields */ +#define DDRPHYC_PIR_INIT BIT(0) +#define DDRPHYC_PIR_DLLSRST BIT(1) +#define DDRPHYC_PIR_DLLLOCK BIT(2) +#define DDRPHYC_PIR_ZCAL BIT(3) +#define DDRPHYC_PIR_ITMSRST BIT(4) +#define DDRPHYC_PIR_DRAMRST BIT(5) +#define DDRPHYC_PIR_DRAMINIT BIT(6) +#define DDRPHYC_PIR_QSTRN BIT(7) +#define DDRPHYC_PIR_ICPC BIT(16) +#define DDRPHYC_PIR_ZCALBYP BIT(30) +#define DDRPHYC_PIR_INITSTEPS_MASK GENMASK(31, 7) + +#define DDRPHYC_PGCR_DFTCMP BIT(2) +#define DDRPHYC_PGCR_PDDISDX BIT(24) +#define DDRPHYC_PGCR_RFSHDT_MASK GENMASK(28, 25) + +#define DDRPHYC_PGSR_IDONE BIT(0) +#define DDRPHYC_PGSR_DTERR BIT(5) +#define DDRPHYC_PGSR_DTIERR BIT(6) +#define DDRPHYC_PGSR_DFTERR BIT(7) +#define DDRPHYC_PGSR_RVERR BIT(8) +#define DDRPHYC_PGSR_RVEIRR BIT(9) + +#define DDRPHYC_DLLGCR_BPS200 BIT(23) + +#define DDRPHYC_ACDLLCR_DLLDIS BIT(31) + +#define DDRPHYC_PTR0_TDLLSRST_OFFSET 0 +#define DDRPHYC_PTR0_TDLLSRST_MASK GENMASK(5, 0) +#define DDRPHYC_PTR0_TDLLLOCK_OFFSET 6 +#define DDRPHYC_PTR0_TDLLLOCK_MASK GENMASK(17, 6) +#define DDRPHYC_PTR0_TITMSRST_OFFSET 18 +#define DDRPHYC_PTR0_TITMSRST_MASK GENMASK(21, 18) + +#define DDRPHYC_ACIOCR_ACPDD BIT(3) +#define DDRPHYC_ACIOCR_ACPDR BIT(4) +#define DDRPHYC_ACIOCR_CKPDD_MASK GENMASK(10, 8) +#define DDRPHYC_ACIOCR_CKPDD_0 BIT(8) +#define DDRPHYC_ACIOCR_CKPDR_MASK GENMASK(13, 11) +#define DDRPHYC_ACIOCR_CKPDR_0 BIT(11) +#define DDRPHYC_ACIOCR_CSPDD_MASK GENMASK(21, 18) +#define DDRPHYC_ACIOCR_CSPDD_0 BIT(18) +#define DDRPHYC_ACIOCR_RSTPDD BIT(27) +#define DDRPHYC_ACIOCR_RSTPDR BIT(28) + +#define DDRPHYC_DXCCR_DXPDD BIT(2) +#define DDRPHYC_DXCCR_DXPDR BIT(3) + +#define DDRPHYC_DSGCR_CKEPDD_MASK GENMASK(19, 16) +#define DDRPHYC_DSGCR_CKEPDD_0 BIT(16) +#define DDRPHYC_DSGCR_ODTPDD_MASK GENMASK(23, 20) +#define DDRPHYC_DSGCR_ODTPDD_0 BIT(20) +#define DDRPHYC_DSGCR_NL2PD BIT(24) + +#define DDRPHYC_ZQ0CRN_ZDATA_MASK GENMASK(27, 0) +#define DDRPHYC_ZQ0CRN_ZDATA_SHIFT 0 +#define DDRPHYC_ZQ0CRN_ZDEN BIT(28) +#define DDRPHYC_ZQ0CRN_ZQPD BIT(31) + +#define DDRPHYC_DXNGCR_DXEN BIT(0) + +#define DDRPHYC_DXNDLLCR_DLLSRST BIT(30) +#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) +#define DDRPHYC_DXNDLLCR_SDPHASE_MASK GENMASK(17, 14) +#define DDRPHYC_DXNDLLCR_SDPHASE_SHIFT 14 + +void ddr_enable_clock(void); + +#endif /* _RAM_STM32MP1_DDR_REGS_H */ diff --git a/include/drivers/st/stm32mp1_pmic.h b/include/drivers/st/stm32mp1_pmic.h new file mode 100644 index 0000000..5d94b40 --- /dev/null +++ b/include/drivers/st/stm32mp1_pmic.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_PMIC_H__ +#define __STM32MP1_PMIC_H__ + +#include + +bool dt_check_pmic(void); +int dt_pmic_enable_boot_on_regulators(void); +void initialize_pmic_i2c(void); +void initialize_pmic(void); +int pmic_ddr_power_init(enum ddr_type ddr_type); + +#endif /* __STM32MP1_PMIC_H__ */ diff --git a/include/drivers/st/stm32mp1_pwr.h b/include/drivers/st/stm32mp1_pwr.h new file mode 100644 index 0000000..e567042 --- /dev/null +++ b/include/drivers/st/stm32mp1_pwr.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_PWR_H__ +#define __STM32MP1_PWR_H__ + +#include + +#define PWR_CR1 U(0x00) +#define PWR_CR2 U(0x08) +#define PWR_CR3 U(0x0C) +#define PWR_MPUCR U(0x10) +#define PWR_WKUPCR U(0x20) +#define PWR_MPUWKUPENR U(0x28) + +#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_CR3_DDRSREN BIT(10) +#define PWR_CR3_DDRSRDIS BIT(11) +#define PWR_CR3_DDRRETEN BIT(12) + +#define PWR_MPUCR_PDDS BIT(0) +#define PWR_MPUCR_CSTDBYDIS BIT(3) +#define PWR_MPUCR_CSSF BIT(9) + +#endif /* __STM32MP1_PWR_H__ */ diff --git a/include/drivers/st/stm32mp1_ram.h b/include/drivers/st/stm32mp1_ram.h new file mode 100644 index 0000000..af96177 --- /dev/null +++ b/include/drivers/st/stm32mp1_ram.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _STM32MP1_RAM_H +#define _STM32MP1_RAM_H + +int stm32mp1_ddr_probe(void); + +#endif /* _STM32MP1_RAM_H */ diff --git a/include/drivers/st/stm32mp1_rcc.h b/include/drivers/st/stm32mp1_rcc.h new file mode 100644 index 0000000..e28ca97 --- /dev/null +++ b/include/drivers/st/stm32mp1_rcc.h @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_RCC_H__ +#define __STM32MP1_RCC_H__ + +#include + +#define RCC_TZCR U(0x00) +#define RCC_OCENSETR U(0x0C) +#define RCC_OCENCLRR U(0x10) +#define RCC_HSICFGR U(0x18) +#define RCC_CSICFGR U(0x1C) +#define RCC_MPCKSELR U(0x20) +#define RCC_ASSCKSELR U(0x24) +#define RCC_RCK12SELR U(0x28) +#define RCC_MPCKDIVR U(0x2C) +#define RCC_AXIDIVR U(0x30) +#define RCC_APB4DIVR U(0x3C) +#define RCC_APB5DIVR U(0x40) +#define RCC_RTCDIVR U(0x44) +#define RCC_MSSCKSELR U(0x48) +#define RCC_PLL1CR U(0x80) +#define RCC_PLL1CFGR1 U(0x84) +#define RCC_PLL1CFGR2 U(0x88) +#define RCC_PLL1FRACR U(0x8C) +#define RCC_PLL1CSGR U(0x90) +#define RCC_PLL2CR U(0x94) +#define RCC_PLL2CFGR1 U(0x98) +#define RCC_PLL2CFGR2 U(0x9C) +#define RCC_PLL2FRACR U(0xA0) +#define RCC_PLL2CSGR U(0xA4) +#define RCC_I2C46CKSELR U(0xC0) +#define RCC_SPI6CKSELR U(0xC4) +#define RCC_UART1CKSELR U(0xC8) +#define RCC_RNG1CKSELR U(0xCC) +#define RCC_CPERCKSELR U(0xD0) +#define RCC_STGENCKSELR U(0xD4) +#define RCC_DDRITFCR U(0xD8) +#define RCC_MP_BOOTCR U(0x100) +#define RCC_MP_SREQSETR U(0x104) +#define RCC_MP_SREQCLRR U(0x108) +#define RCC_MP_GCR U(0x10C) +#define RCC_MP_APRSTCR U(0x110) +#define RCC_MP_APRSTSR U(0x114) +#define RCC_BDCR U(0x140) +#define RCC_RDLSICR U(0x144) +#define RCC_APB4RSTSETR U(0x180) +#define RCC_APB4RSTCLRR U(0x184) +#define RCC_APB5RSTSETR U(0x188) +#define RCC_APB5RSTCLRR U(0x18C) +#define RCC_AHB5RSTSETR U(0x190) +#define RCC_AHB5RSTCLRR U(0x194) +#define RCC_AHB6RSTSETR U(0x198) +#define RCC_AHB6RSTCLRR U(0x19C) +#define RCC_TZAHB6RSTSETR U(0x1A0) +#define RCC_TZAHB6RSTCLRR U(0x1A4) +#define RCC_MP_APB4ENSETR U(0x200) +#define RCC_MP_APB4ENCLRR U(0x204) +#define RCC_MP_APB5ENSETR U(0x208) +#define RCC_MP_APB5ENCLRR U(0x20C) +#define RCC_MP_AHB5ENSETR U(0x210) +#define RCC_MP_AHB5ENCLRR U(0x214) +#define RCC_MP_AHB6ENSETR U(0x218) +#define RCC_MP_AHB6ENCLRR U(0x21C) +#define RCC_MP_TZAHB6ENSETR U(0x220) +#define RCC_MP_TZAHB6ENCLRR U(0x224) +#define RCC_MP_APB4LPENSETR U(0x300) +#define RCC_MP_APB4LPENCLRR U(0x304) +#define RCC_MP_APB5LPENSETR U(0x308) +#define RCC_MP_APB5LPENCLRR U(0x30C) +#define RCC_MP_AHB5LPENSETR U(0x310) +#define RCC_MP_AHB5LPENCLRR U(0x314) +#define RCC_MP_AHB6LPENSETR U(0x318) +#define RCC_MP_AHB6LPENCLRR U(0x31C) +#define RCC_MP_TZAHB6LPENSETR U(0x320) +#define RCC_MP_TZAHB6LPENCLRR U(0x324) +#define RCC_BR_RSTSCLRR U(0x400) +#define RCC_MP_GRSTCSETR U(0x404) +#define RCC_MP_RSTSCLRR U(0x408) +#define RCC_MP_IWDGFZSETR U(0x40C) +#define RCC_MP_IWDGFZCLRR U(0x410) +#define RCC_MP_CIER U(0x414) +#define RCC_MP_CIFR U(0x418) +#define RCC_PWRLPDLYCR U(0x41C) +#define RCC_MP_RSTSSETR U(0x420) +#define RCC_MCO1CFGR U(0x800) +#define RCC_MCO2CFGR U(0x804) +#define RCC_OCRDYR U(0x808) +#define RCC_DBGCFGR U(0x80C) +#define RCC_RCK3SELR U(0x820) +#define RCC_RCK4SELR U(0x824) +#define RCC_TIMG1PRER U(0x828) +#define RCC_TIMG2PRER U(0x82C) +#define RCC_APB1DIVR U(0x834) +#define RCC_APB2DIVR U(0x838) +#define RCC_APB3DIVR U(0x83C) +#define RCC_PLL3CR U(0x880) +#define RCC_PLL3CFGR1 U(0x884) +#define RCC_PLL3CFGR2 U(0x888) +#define RCC_PLL3FRACR U(0x88C) +#define RCC_PLL3CSGR U(0x890) +#define RCC_PLL4CR U(0x894) +#define RCC_PLL4CFGR1 U(0x898) +#define RCC_PLL4CFGR2 U(0x89C) +#define RCC_PLL4FRACR U(0x8A0) +#define RCC_PLL4CSGR U(0x8A4) +#define RCC_I2C12CKSELR U(0x8C0) +#define RCC_I2C35CKSELR U(0x8C4) +#define RCC_SAI1CKSELR U(0x8C8) +#define RCC_SAI2CKSELR U(0x8CC) +#define RCC_SAI3CKSELR U(0x8D0) +#define RCC_SAI4CKSELR U(0x8D4) +#define RCC_SPI2S1CKSELR U(0x8D8) +#define RCC_SPI2S23CKSELR U(0x8DC) +#define RCC_SPI45CKSELR U(0x8E0) +#define RCC_UART6CKSELR U(0x8E4) +#define RCC_UART24CKSELR U(0x8E8) +#define RCC_UART35CKSELR U(0x8EC) +#define RCC_UART78CKSELR U(0x8F0) +#define RCC_SDMMC12CKSELR U(0x8F4) +#define RCC_SDMMC3CKSELR U(0x8F8) +#define RCC_ETHCKSELR U(0x8FC) +#define RCC_QSPICKSELR U(0x900) +#define RCC_FMCCKSELR U(0x904) +#define RCC_FDCANCKSELR U(0x90C) +#define RCC_SPDIFCKSELR U(0x914) +#define RCC_CECCKSELR U(0x918) +#define RCC_USBCKSELR U(0x91C) +#define RCC_RNG2CKSELR U(0x920) +#define RCC_DSICKSELR U(0x924) +#define RCC_ADCCKSELR U(0x928) +#define RCC_LPTIM45CKSELR U(0x92C) +#define RCC_LPTIM23CKSELR U(0x930) +#define RCC_LPTIM1CKSELR U(0x934) +#define RCC_APB1RSTSETR U(0x980) +#define RCC_APB1RSTCLRR U(0x984) +#define RCC_APB2RSTSETR U(0x988) +#define RCC_APB2RSTCLRR U(0x98C) +#define RCC_APB3RSTSETR U(0x990) +#define RCC_APB3RSTCLRR U(0x994) +#define RCC_AHB2RSTSETR U(0x998) +#define RCC_AHB2RSTCLRR U(0x99C) +#define RCC_AHB3RSTSETR U(0x9A0) +#define RCC_AHB3RSTCLRR U(0x9A4) +#define RCC_AHB4RSTSETR U(0x9A8) +#define RCC_AHB4RSTCLRR U(0x9AC) +#define RCC_MP_APB1ENSETR U(0xA00) +#define RCC_MP_APB1ENCLRR U(0xA04) +#define RCC_MP_APB2ENSETR U(0xA08) +#define RCC_MP_APB2ENCLRR U(0xA0C) +#define RCC_MP_APB3ENSETR U(0xA10) +#define RCC_MP_APB3ENCLRR U(0xA14) +#define RCC_MP_AHB2ENSETR U(0xA18) +#define RCC_MP_AHB2ENCLRR U(0xA1C) +#define RCC_MP_AHB3ENSETR U(0xA20) +#define RCC_MP_AHB3ENCLRR U(0xA24) +#define RCC_MP_AHB4ENSETR U(0xA28) +#define RCC_MP_AHB4ENCLRR U(0xA2C) +#define RCC_MP_MLAHBENSETR U(0xA38) +#define RCC_MP_MLAHBENCLRR U(0xA3C) +#define RCC_MP_APB1LPENSETR U(0xB00) +#define RCC_MP_APB1LPENCLRR U(0xB04) +#define RCC_MP_APB2LPENSETR U(0xB08) +#define RCC_MP_APB2LPENCLRR U(0xB0C) +#define RCC_MP_APB3LPENSETR U(0xB10) +#define RCC_MP_APB3LPENCLRR U(0xB14) +#define RCC_MP_AHB2LPENSETR U(0xB18) +#define RCC_MP_AHB2LPENCLRR U(0xB1C) +#define RCC_MP_AHB3LPENSETR U(0xB20) +#define RCC_MP_AHB3LPENCLRR U(0xB24) +#define RCC_MP_AHB4LPENSETR U(0xB28) +#define RCC_MP_AHB4LPENCLRR U(0xB2C) +#define RCC_MP_AXIMLPENSETR U(0xB30) +#define RCC_MP_AXIMLPENCLRR U(0xB34) +#define RCC_MP_MLAHBLPENSETR U(0xB38) +#define RCC_MP_MLAHBLPENCLRR U(0xB3C) +#define RCC_VERR U(0xFF4) +#define RCC_IDR U(0xFF8) +#define RCC_SIDR U(0xFFC) + +/* Values for RCC_TZCR register */ +#define RCC_TZCR_TZEN BIT(0) + +/* Used for most of RCC_SELR registers */ +#define RCC_SELR_SRC_MASK GENMASK(2, 0) +#define RCC_SELR_REFCLK_SRC_MASK GENMASK(1, 0) +#define RCC_SELR_SRCRDY BIT(31) + +/* Values of RCC_MPCKSELR register */ +#define RCC_MPCKSELR_HSI 0x00000000 +#define RCC_MPCKSELR_HSE 0x00000001 +#define RCC_MPCKSELR_PLL 0x00000002 +#define RCC_MPCKSELR_PLL_MPUDIV 0x00000003 + +/* Values of RCC_ASSCKSELR register */ +#define RCC_ASSCKSELR_HSI 0x00000000 +#define RCC_ASSCKSELR_HSE 0x00000001 +#define RCC_ASSCKSELR_PLL 0x00000002 + +/* Values of RCC_MSSCKSELR register */ +#define RCC_MSSCKSELR_HSI 0x00000000 +#define RCC_MSSCKSELR_HSE 0x00000001 +#define RCC_MSSCKSELR_CSI 0x00000002 +#define RCC_MSSCKSELR_PLL 0x00000003 + +/* Values of RCC_CPERCKSELR register */ +#define RCC_CPERCKSELR_HSI 0x00000000 +#define RCC_CPERCKSELR_CSI 0x00000001 +#define RCC_CPERCKSELR_HSE 0x00000002 + +/* Used for most of DIVR register: max div for RTC */ +#define RCC_DIVR_DIV_MASK GENMASK(5, 0) +#define RCC_DIVR_DIVRDY BIT(31) + +/* Masks for specific DIVR registers */ +#define RCC_APBXDIV_MASK GENMASK(2, 0) +#define RCC_MPUDIV_MASK GENMASK(2, 0) +#define RCC_AXIDIV_MASK GENMASK(2, 0) + +/* Offset between RCC_MP_xxxENSETR and RCC_MP_xxxENCLRR registers */ +#define RCC_MP_ENCLRR_OFFSET U(4) + +/* Fields of RCC_BDCR register */ +#define RCC_BDCR_LSEON BIT(0) +#define RCC_BDCR_LSEBYP BIT(1) +#define RCC_BDCR_LSERDY BIT(2) +#define RCC_BDCR_LSEDRV_MASK GENMASK(5, 4) +#define RCC_BDCR_LSEDRV_SHIFT 4 +#define RCC_BDCR_LSECSSON BIT(8) +#define RCC_BDCR_RTCCKEN BIT(20) +#define RCC_BDCR_RTCSRC_MASK GENMASK(17, 16) +#define RCC_BDCR_RTCSRC_SHIFT 16 +#define RCC_BDCR_VSWRST BIT(31) + +/* Fields of RCC_RDLSICR register */ +#define RCC_RDLSICR_LSION BIT(0) +#define RCC_RDLSICR_LSIRDY BIT(1) + +/* Used for all RCC_PLLCR registers */ +#define RCC_PLLNCR_PLLON BIT(0) +#define RCC_PLLNCR_PLLRDY BIT(1) +#define RCC_PLLNCR_DIVPEN BIT(4) +#define RCC_PLLNCR_DIVQEN BIT(5) +#define RCC_PLLNCR_DIVREN BIT(6) +#define RCC_PLLNCR_DIVEN_SHIFT 4 + +/* Used for all RCC_PLLCFGR1 registers */ +#define RCC_PLLNCFGR1_DIVM_SHIFT 16 +#define RCC_PLLNCFGR1_DIVM_MASK GENMASK(21, 16) +#define RCC_PLLNCFGR1_DIVN_SHIFT 0 +#define RCC_PLLNCFGR1_DIVN_MASK GENMASK(8, 0) +/* Only for PLL3 and PLL4 */ +#define RCC_PLLNCFGR1_IFRGE_SHIFT 24 +#define RCC_PLLNCFGR1_IFRGE_MASK GENMASK(25, 24) + +/* Used for all RCC_PLLCFGR2 registers */ +#define RCC_PLLNCFGR2_DIVX_MASK GENMASK(6, 0) +#define RCC_PLLNCFGR2_DIVP_SHIFT 0 +#define RCC_PLLNCFGR2_DIVP_MASK GENMASK(6, 0) +#define RCC_PLLNCFGR2_DIVQ_SHIFT 8 +#define RCC_PLLNCFGR2_DIVQ_MASK GENMASK(14, 8) +#define RCC_PLLNCFGR2_DIVR_SHIFT 16 +#define RCC_PLLNCFGR2_DIVR_MASK GENMASK(22, 16) + +/* Used for all RCC_PLLFRACR registers */ +#define RCC_PLLNFRACR_FRACV_SHIFT 3 +#define RCC_PLLNFRACR_FRACV_MASK GENMASK(15, 3) +#define RCC_PLLNFRACR_FRACLE BIT(16) + +/* Used for all RCC_PLLCSGR registers */ +#define RCC_PLLNCSGR_INC_STEP_SHIFT 16 +#define RCC_PLLNCSGR_INC_STEP_MASK GENMASK(30, 16) +#define RCC_PLLNCSGR_MOD_PER_SHIFT 0 +#define RCC_PLLNCSGR_MOD_PER_MASK GENMASK(12, 0) +#define RCC_PLLNCSGR_SSCG_MODE_SHIFT 15 +#define RCC_PLLNCSGR_SSCG_MODE_MASK BIT(15) + +/* Used for RCC_OCENSETR and RCC_OCENCLRR registers */ +#define RCC_OCENR_HSION BIT(0) +#define RCC_OCENR_CSION BIT(4) +#define RCC_OCENR_HSEON BIT(8) +#define RCC_OCENR_HSEBYP BIT(10) +#define RCC_OCENR_HSECSSON BIT(11) + +/* Fields of RCC_OCRDYR register */ +#define RCC_OCRDYR_HSIRDY BIT(0) +#define RCC_OCRDYR_HSIDIVRDY BIT(2) +#define RCC_OCRDYR_CSIRDY BIT(4) +#define RCC_OCRDYR_HSERDY BIT(8) + +/* Fields of RCC_DDRITFCR register */ +#define RCC_DDRITFCR_DDRC1EN BIT(0) +#define RCC_DDRITFCR_DDRC1LPEN BIT(1) +#define RCC_DDRITFCR_DDRC2EN BIT(2) +#define RCC_DDRITFCR_DDRC2LPEN BIT(3) +#define RCC_DDRITFCR_DDRPHYCEN BIT(4) +#define RCC_DDRITFCR_DDRPHYCLPEN BIT(5) +#define RCC_DDRITFCR_DDRCAPBEN BIT(6) +#define RCC_DDRITFCR_DDRCAPBLPEN BIT(7) +#define RCC_DDRITFCR_AXIDCGEN BIT(8) +#define RCC_DDRITFCR_DDRPHYCAPBEN BIT(9) +#define RCC_DDRITFCR_DDRPHYCAPBLPEN BIT(10) +#define RCC_DDRITFCR_DDRCAPBRST BIT(14) +#define RCC_DDRITFCR_DDRCAXIRST BIT(15) +#define RCC_DDRITFCR_DDRCORERST BIT(16) +#define RCC_DDRITFCR_DPHYAPBRST BIT(17) +#define RCC_DDRITFCR_DPHYRST BIT(18) +#define RCC_DDRITFCR_DPHYCTLRST BIT(19) +#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) +#define RCC_DDRITFCR_DDRCKMOD_SHIFT 20 +#define RCC_DDRITFCR_DDRCKMOD_SSR 0 +#define RCC_DDRITFCR_DDRCKMOD_ASR1 BIT(20) +#define RCC_DDRITFCR_DDRCKMOD_HSR1 BIT(21) +#define RCC_DDRITFCR_GSKPCTRL BIT(24) + +/* Fields of RCC_HSICFGR register */ +#define RCC_HSICFGR_HSIDIV_MASK GENMASK(1, 0) + +/* Used for RCC_MCO related operations */ +#define RCC_MCOCFG_MCOON BIT(12) +#define RCC_MCOCFG_MCODIV_MASK GENMASK(7, 4) +#define RCC_MCOCFG_MCODIV_SHIFT 4 +#define RCC_MCOCFG_MCOSRC_MASK GENMASK(2, 0) + +/* Fields of RCC_DBGCFGR register */ +#define RCC_DBGCFGR_DBGCKEN BIT(8) + +/* RCC register fields for reset reasons */ +#define RCC_MP_RSTSCLRR_PORRSTF BIT(0) +#define RCC_MP_RSTSCLRR_BORRSTF BIT(1) +#define RCC_MP_RSTSCLRR_PADRSTF BIT(2) +#define RCC_MP_RSTSCLRR_HCSSRSTF BIT(3) +#define RCC_MP_RSTSCLRR_VCORERSTF BIT(4) +#define RCC_MP_RSTSCLRR_MPSYSRSTF BIT(6) +#define RCC_MP_RSTSCLRR_IWDG1RSTF BIT(8) +#define RCC_MP_RSTSCLRR_IWDG2RSTF BIT(9) +#define RCC_MP_RSTSCLRR_STDBYRSTF BIT(11) +#define RCC_MP_RSTSCLRR_CSTDBYRSTF BIT(12) + +/* Global Reset Register */ +#define RCC_MP_GRSTCSETR_MPSYSRST BIT(0) + +/* Clock Source Interrupt Flag Register */ +#define RCC_MP_CIFR_MASK U(0x110F1F) +#define RCC_MP_CIFR_WKUPF BIT(20) + +/* Stop Request Set Register */ +#define RCC_MP_SREQSETR_STPREQ_P0 BIT(0) +#define RCC_MP_SREQSETR_STPREQ_P1 BIT(1) + +/* Stop Request Clear Register */ +#define RCC_MP_SREQCLRR_STPREQ_P0 BIT(0) +#define RCC_MP_SREQCLRR_STPREQ_P1 BIT(1) + +/* Values of RCC_UART24CKSELR register */ +#define RCC_UART24CKSELR_HSI 0x00000002 + +/* Values of RCC_MP_APB1ENSETR register */ +#define RCC_MP_APB1ENSETR_UART4EN BIT(16) + +/* Values of RCC_MP_AHB4ENSETR register */ +#define RCC_MP_AHB4ENSETR_GPIOGEN BIT(6) + +#endif /* __STM32MP1_RCC_H__ */ diff --git a/include/drivers/st/stm32mp1_reset.h b/include/drivers/st/stm32mp1_reset.h new file mode 100644 index 0000000..76ee09d --- /dev/null +++ b/include/drivers/st/stm32mp1_reset.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_RESET_H__ +#define __STM32MP1_RESET_H__ + +#include + +void stm32mp1_reset_assert(uint32_t reset_id); +void stm32mp1_reset_deassert(uint32_t reset_id); + +#endif /* __STM32MP1_RESET_H__ */ diff --git a/include/drivers/st/stpmu1.h b/include/drivers/st/stpmu1.h new file mode 100644 index 0000000..1b93ab2 --- /dev/null +++ b/include/drivers/st/stpmu1.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#ifndef __STPMU1_H__ +#define __STPMU1_H__ + +#include +#include + +#define TURN_ON_REG 0x1U +#define TURN_OFF_REG 0x2U +#define ICC_LDO_TURN_OFF_REG 0x3U +#define ICC_BUCK_TURN_OFF_REG 0x4U +#define RESET_STATUS_REG 0x5U +#define VERSION_STATUS_REG 0x6U +#define MAIN_CONTROL_REG 0x10U +#define PADS_PULL_REG 0x11U +#define BUCK_PULL_DOWN_REG 0x12U +#define LDO14_PULL_DOWN_REG 0x13U +#define LDO56_PULL_DOWN_REG 0x14U +#define VIN_CONTROL_REG 0x15U +#define PONKEY_TIMER_REG 0x16U +#define MASK_RANK_BUCK_REG 0x17U +#define MASK_RESET_BUCK_REG 0x18U +#define MASK_RANK_LDO_REG 0x19U +#define MASK_RESET_LDO_REG 0x1AU +#define WATCHDOG_CONTROL_REG 0x1BU +#define WATCHDOG_TIMER_REG 0x1CU +#define BUCK_ICC_TURNOFF_REG 0x1DU +#define LDO_ICC_TURNOFF_REG 0x1EU +#define BUCK_APM_CONTROL_REG 0x1FU +#define BUCK1_CONTROL_REG 0x20U +#define BUCK2_CONTROL_REG 0x21U +#define BUCK3_CONTROL_REG 0x22U +#define BUCK4_CONTROL_REG 0x23U +#define VREF_DDR_CONTROL_REG 0x24U +#define LDO1_CONTROL_REG 0x25U +#define LDO2_CONTROL_REG 0x26U +#define LDO3_CONTROL_REG 0x27U +#define LDO4_CONTROL_REG 0x28U +#define LDO5_CONTROL_REG 0x29U +#define LDO6_CONTROL_REG 0x2AU +#define BUCK1_PWRCTRL_REG 0x30U +#define BUCK2_PWRCTRL_REG 0x31U +#define BUCK3_PWRCTRL_REG 0x32U +#define BUCK4_PWRCTRL_REG 0x33U +#define VREF_DDR_PWRCTRL_REG 0x34U +#define LDO1_PWRCTRL_REG 0x35U +#define LDO2_PWRCTRL_REG 0x36U +#define LDO3_PWRCTRL_REG 0x37U +#define LDO4_PWRCTRL_REG 0x38U +#define LDO5_PWRCTRL_REG 0x39U +#define LDO6_PWRCTRL_REG 0x3AU +#define FREQUENCY_SPREADING_REG 0x3BU +#define USB_CONTROL_REG 0x40U +#define ITLATCH1_REG 0x50U +#define ITLATCH2_REG 0x51U +#define ITLATCH3_REG 0x52U +#define ITLATCH4_REG 0x53U +#define ITSETLATCH1_REG 0x60U +#define ITSETLATCH2_REG 0x61U +#define ITSETLATCH3_REG 0x62U +#define ITSETLATCH4_REG 0x63U +#define ITCLEARLATCH1_REG 0x70U +#define ITCLEARLATCH2_REG 0x71U +#define ITCLEARLATCH3_REG 0x72U +#define ITCLEARLATCH4_REG 0x73U +#define ITMASK1_REG 0x80U +#define ITMASK2_REG 0x81U +#define ITMASK3_REG 0x82U +#define ITMASK4_REG 0x83U +#define ITSETMASK1_REG 0x90U +#define ITSETMASK2_REG 0x91U +#define ITSETMASK3_REG 0x92U +#define ITSETMASK4_REG 0x93U +#define ITCLEARMASK1_REG 0xA0U +#define ITCLEARMASK2_REG 0xA1U +#define ITCLEARMASK3_REG 0xA2U +#define ITCLEARMASK4_REG 0xA3U +#define ITSOURCE1_REG 0xB0U +#define ITSOURCE2_REG 0xB1U +#define ITSOURCE3_REG 0xB2U +#define ITSOURCE4_REG 0xB3U +#define LDO_VOLTAGE_MASK 0x7CU +#define BUCK_VOLTAGE_MASK 0xFCU +#define LDO_BUCK_VOLTAGE_SHIFT 2 +#define LDO_ENABLE_MASK 0x01U +#define BUCK_ENABLE_MASK 0x01U +#define BUCK_HPLP_ENABLE_MASK 0x02U +#define LDO_HPLP_ENABLE_MASK 0x02U +#define LDO_BUCK_HPLP_SHIFT 1 +#define LDO_BUCK_RANK_MASK 0x01U +#define LDO_BUCK_RESET_MASK 0x01U +#define LDO_BUCK_PULL_DOWN_MASK 0x03U + +/* Main PMIC Control Register (MAIN_CONTROL_REG) */ +#define ICC_EVENT_ENABLED BIT(4) +#define PWRCTRL_POLARITY_HIGH BIT(3) +#define PWRCTRL_PIN_VALID BIT(2) +#define RESTART_REQUEST_ENABLED BIT(1) +#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0) + +/* Main PMIC PADS Control Register (PADS_PULL_REG) */ +#define WAKEUP_DETECTOR_DISABLED BIT(4) +#define PWRCTRL_PD_ACTIVE BIT(3) +#define PWRCTRL_PU_ACTIVE BIT(2) +#define WAKEUP_PD_ACTIVE BIT(1) +#define PONKEY_PU_ACTIVE BIT(0) + +/* Main PMIC VINLOW Control Register (VIN_CONTROL_REGC DMSC) */ +#define SWIN_DETECTOR_ENABLED BIT(7) +#define SWOUT_DETECTOR_ENABLED BIT(6) +#define VINLOW_HYST_MASK 0x3 +#define VINLOW_HYST_SHIFT 4 +#define VINLOW_THRESHOLD_MASK 0x7 +#define VINLOW_THRESHOLD_SHIFT 1 +#define VINLOW_ENABLED 0x01 +#define VINLOW_CTRL_REG_MASK 0xFF + +/* USB Control Register */ +#define BOOST_OVP_DISABLED BIT(7) +#define VBUS_OTG_DETECTION_DISABLED BIT(6) +#define OCP_LIMIT_HIGH BIT(3) +#define SWIN_SWOUT_ENABLED BIT(2) +#define USBSW_OTG_SWITCH_ENABLED BIT(1) + +int stpmu1_switch_off(void); +int stpmu1_register_read(uint8_t register_id, uint8_t *value); +int stpmu1_register_write(uint8_t register_id, uint8_t value); +int stpmu1_register_update(uint8_t register_id, uint8_t value, uint8_t mask); +int stpmu1_regulator_enable(const char *name); +int stpmu1_regulator_disable(const char *name); +uint8_t stpmu1_is_regulator_enabled(const char *name); +int stpmu1_regulator_voltage_set(const char *name, uint16_t millivolts); +void stpmu1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr); + +#endif /* __STPMU1_H__ */ diff --git a/include/dt-bindings/clock/stm32mp1-clks.h b/include/dt-bindings/clock/stm32mp1-clks.h new file mode 100644 index 0000000..18bdb57 --- /dev/null +++ b/include/dt-bindings/clock/stm32mp1-clks.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Gabriel Fernandez for STMicroelectronics. + */ + +#ifndef _DT_BINDINGS_STM32MP1_CLKS_H_ +#define _DT_BINDINGS_STM32MP1_CLKS_H_ + +/* OSCILLATOR clocks */ +#define CK_HSE 0 +#define CK_CSI 1 +#define CK_LSI 2 +#define CK_LSE 3 +#define CK_HSI 4 +#define CK_HSE_DIV2 5 + +/* Bus clocks */ +#define TIM2 6 +#define TIM3 7 +#define TIM4 8 +#define TIM5 9 +#define TIM6 10 +#define TIM7 11 +#define TIM12 12 +#define TIM13 13 +#define TIM14 14 +#define LPTIM1 15 +#define SPI2 16 +#define SPI3 17 +#define USART2 18 +#define USART3 19 +#define UART4 20 +#define UART5 21 +#define UART7 22 +#define UART8 23 +#define I2C1 24 +#define I2C2 25 +#define I2C3 26 +#define I2C5 27 +#define SPDIF 28 +#define CEC 29 +#define DAC12 30 +#define MDIO 31 +#define TIM1 32 +#define TIM8 33 +#define TIM15 34 +#define TIM16 35 +#define TIM17 36 +#define SPI1 37 +#define SPI4 38 +#define SPI5 39 +#define USART6 40 +#define SAI1 41 +#define SAI2 42 +#define SAI3 43 +#define DFSDM 44 +#define FDCAN 45 +#define LPTIM2 46 +#define LPTIM3 47 +#define LPTIM4 48 +#define LPTIM5 49 +#define SAI4 50 +#define SYSCFG 51 +#define VREF 52 +#define TMPSENS 53 +#define PMBCTRL 54 +#define HDP 55 +#define LTDC 56 +#define DSI 57 +#define IWDG2 58 +#define USBPHY 59 +#define STGENRO 60 +#define SPI6 61 +#define I2C4 62 +#define I2C6 63 +#define USART1 64 +#define RTCAPB 65 +#define TZC1 66 +#define TZPC 67 +#define IWDG1 68 +#define BSEC 69 +#define STGEN 70 +#define DMA1 71 +#define DMA2 72 +#define DMAMUX 73 +#define ADC12 74 +#define USBO 75 +#define SDMMC3 76 +#define DCMI 77 +#define CRYP2 78 +#define HASH2 79 +#define RNG2 80 +#define CRC2 81 +#define HSEM 82 +#define IPCC 83 +#define GPIOA 84 +#define GPIOB 85 +#define GPIOC 86 +#define GPIOD 87 +#define GPIOE 88 +#define GPIOF 89 +#define GPIOG 90 +#define GPIOH 91 +#define GPIOI 92 +#define GPIOJ 93 +#define GPIOK 94 +#define GPIOZ 95 +#define CRYP1 96 +#define HASH1 97 +#define RNG1 98 +#define BKPSRAM 99 +#define MDMA 100 +#define GPU 101 +#define ETHCK 102 +#define ETHTX 103 +#define ETHRX 104 +#define ETHMAC 105 +#define FMC 106 +#define QSPI 107 +#define SDMMC1 108 +#define SDMMC2 109 +#define CRC1 110 +#define USBH 111 +#define ETHSTP 112 +#define TZC2 113 + +/* Kernel clocks */ +#define SDMMC1_K 118 +#define SDMMC2_K 119 +#define SDMMC3_K 120 +#define FMC_K 121 +#define QSPI_K 122 +#define ETHCK_K 123 +#define RNG1_K 124 +#define RNG2_K 125 +#define GPU_K 126 +#define USBPHY_K 127 +#define STGEN_K 128 +#define SPDIF_K 129 +#define SPI1_K 130 +#define SPI2_K 131 +#define SPI3_K 132 +#define SPI4_K 133 +#define SPI5_K 134 +#define SPI6_K 135 +#define CEC_K 136 +#define I2C1_K 137 +#define I2C2_K 138 +#define I2C3_K 139 +#define I2C4_K 140 +#define I2C5_K 141 +#define I2C6_K 142 +#define LPTIM1_K 143 +#define LPTIM2_K 144 +#define LPTIM3_K 145 +#define LPTIM4_K 146 +#define LPTIM5_K 147 +#define USART1_K 148 +#define USART2_K 149 +#define USART3_K 150 +#define UART4_K 151 +#define UART5_K 152 +#define USART6_K 153 +#define UART7_K 154 +#define UART8_K 155 +#define DFSDM_K 156 +#define FDCAN_K 157 +#define SAI1_K 158 +#define SAI2_K 159 +#define SAI3_K 160 +#define SAI4_K 161 +#define ADC12_K 162 +#define DSI_K 163 +#define DSI_PX 164 +#define ADFSDM_K 165 +#define USBO_K 166 +#define LTDC_PX 167 +#define DAC12_K 168 +#define ETHPTP_K 169 + +/* PLL */ +#define PLL1 176 +#define PLL2 177 +#define PLL3 178 +#define PLL4 179 + +/* ODF */ +#define PLL1_P 180 +#define PLL1_Q 181 +#define PLL1_R 182 +#define PLL2_P 183 +#define PLL2_Q 184 +#define PLL2_R 185 +#define PLL3_P 186 +#define PLL3_Q 187 +#define PLL3_R 188 +#define PLL4_P 189 +#define PLL4_Q 190 +#define PLL4_R 191 + +/* AUX */ +#define RTC 192 + +/* MCLK */ +#define CK_PER 193 +#define CK_MPU 194 +#define CK_AXI 195 +#define CK_MCU 196 + +/* Time base */ +#define TIM2_K 197 +#define TIM3_K 198 +#define TIM4_K 199 +#define TIM5_K 200 +#define TIM6_K 201 +#define TIM7_K 202 +#define TIM12_K 203 +#define TIM13_K 204 +#define TIM14_K 205 +#define TIM1_K 206 +#define TIM8_K 207 +#define TIM15_K 208 +#define TIM16_K 209 +#define TIM17_K 210 + +/* MCO clocks */ +#define CK_MCO1 211 +#define CK_MCO2 212 + +/* TRACE & DEBUG clocks */ +#define CK_DBG 214 +#define CK_TRACE 215 + +/* DDR */ +#define DDRC1 220 +#define DDRC1LP 221 +#define DDRC2 222 +#define DDRC2LP 223 +#define DDRPHYC 224 +#define DDRPHYCLP 225 +#define DDRCAPB 226 +#define DDRCAPBLP 227 +#define AXIDCG 228 +#define DDRPHYCAPB 229 +#define DDRPHYCAPBLP 230 +#define DDRPERFM 231 + +#define STM32MP1_LAST_CLK 232 + +#endif /* _DT_BINDINGS_STM32MP1_CLKS_H_ */ diff --git a/include/dt-bindings/clock/stm32mp1-clksrc.h b/include/dt-bindings/clock/stm32mp1-clksrc.h new file mode 100644 index 0000000..818f4b7 --- /dev/null +++ b/include/dt-bindings/clock/stm32mp1-clksrc.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + */ + +#ifndef _DT_BINDINGS_CLOCK_STM32MP1_CLKSRC_H_ +#define _DT_BINDINGS_CLOCK_STM32MP1_CLKSRC_H_ + +/* PLL output is enable when x=1, with x=p,q or r */ +#define PQR(p, q, r) (((p) & 1) | (((q) & 1) << 1) | (((r) & 1) << 2)) + +/* st,clksrc: mandatory clock source */ + +#define CLK_MPU_HSI 0x00000200 +#define CLK_MPU_HSE 0x00000201 +#define CLK_MPU_PLL1P 0x00000202 +#define CLK_MPU_PLL1P_DIV 0x00000203 + +#define CLK_AXI_HSI 0x00000240 +#define CLK_AXI_HSE 0x00000241 +#define CLK_AXI_PLL2P 0x00000242 + +#define CLK_MCU_HSI 0x00000480 +#define CLK_MCU_HSE 0x00000481 +#define CLK_MCU_CSI 0x00000482 +#define CLK_MCU_PLL3P 0x00000483 + +#define CLK_PLL12_HSI 0x00000280 +#define CLK_PLL12_HSE 0x00000281 + +#define CLK_PLL3_HSI 0x00008200 +#define CLK_PLL3_HSE 0x00008201 +#define CLK_PLL3_CSI 0x00008202 + +#define CLK_PLL4_HSI 0x00008240 +#define CLK_PLL4_HSE 0x00008241 +#define CLK_PLL4_CSI 0x00008242 +#define CLK_PLL4_I2SCKIN 0x00008243 + +#define CLK_RTC_DISABLED 0x00001400 +#define CLK_RTC_LSE 0x00001401 +#define CLK_RTC_LSI 0x00001402 +#define CLK_RTC_HSE 0x00001403 + +#define CLK_MCO1_HSI 0x00008000 +#define CLK_MCO1_HSE 0x00008001 +#define CLK_MCO1_CSI 0x00008002 +#define CLK_MCO1_LSI 0x00008003 +#define CLK_MCO1_LSE 0x00008004 +#define CLK_MCO1_DISABLED 0x0000800F + +#define CLK_MCO2_MPU 0x00008040 +#define CLK_MCO2_AXI 0x00008041 +#define CLK_MCO2_MCU 0x00008042 +#define CLK_MCO2_PLL4P 0x00008043 +#define CLK_MCO2_HSE 0x00008044 +#define CLK_MCO2_HSI 0x00008045 +#define CLK_MCO2_DISABLED 0x0000804F + +/* st,pkcs: peripheral kernel clock source */ + +#define CLK_I2C12_PCLK1 0x00008C00 +#define CLK_I2C12_PLL4R 0x00008C01 +#define CLK_I2C12_HSI 0x00008C02 +#define CLK_I2C12_CSI 0x00008C03 +#define CLK_I2C12_DISABLED 0x00008C07 + +#define CLK_I2C35_PCLK1 0x00008C40 +#define CLK_I2C35_PLL4R 0x00008C41 +#define CLK_I2C35_HSI 0x00008C42 +#define CLK_I2C35_CSI 0x00008C43 +#define CLK_I2C35_DISABLED 0x00008C47 + +#define CLK_I2C46_PCLK5 0x00000C00 +#define CLK_I2C46_PLL3Q 0x00000C01 +#define CLK_I2C46_HSI 0x00000C02 +#define CLK_I2C46_CSI 0x00000C03 +#define CLK_I2C46_DISABLED 0x00000C07 + +#define CLK_SAI1_PLL4Q 0x00008C80 +#define CLK_SAI1_PLL3Q 0x00008C81 +#define CLK_SAI1_I2SCKIN 0x00008C82 +#define CLK_SAI1_CKPER 0x00008C83 +#define CLK_SAI1_PLL3R 0x00008C84 +#define CLK_SAI1_DISABLED 0x00008C87 + +#define CLK_SAI2_PLL4Q 0x00008CC0 +#define CLK_SAI2_PLL3Q 0x00008CC1 +#define CLK_SAI2_I2SCKIN 0x00008CC2 +#define CLK_SAI2_CKPER 0x00008CC3 +#define CLK_SAI2_SPDIF 0x00008CC4 +#define CLK_SAI2_PLL3R 0x00008CC5 +#define CLK_SAI2_DISABLED 0x00008CC7 + +#define CLK_SAI3_PLL4Q 0x00008D00 +#define CLK_SAI3_PLL3Q 0x00008D01 +#define CLK_SAI3_I2SCKIN 0x00008D02 +#define CLK_SAI3_CKPER 0x00008D03 +#define CLK_SAI3_PLL3R 0x00008D04 +#define CLK_SAI3_DISABLED 0x00008D07 + +#define CLK_SAI4_PLL4Q 0x00008D40 +#define CLK_SAI4_PLL3Q 0x00008D41 +#define CLK_SAI4_I2SCKIN 0x00008D42 +#define CLK_SAI4_CKPER 0x00008D43 +#define CLK_SAI4_PLL3R 0x00008D44 +#define CLK_SAI4_DISABLED 0x00008D47 + +#define CLK_SPI2S1_PLL4P 0x00008D80 +#define CLK_SPI2S1_PLL3Q 0x00008D81 +#define CLK_SPI2S1_I2SCKIN 0x00008D82 +#define CLK_SPI2S1_CKPER 0x00008D83 +#define CLK_SPI2S1_PLL3R 0x00008D84 +#define CLK_SPI2S1_DISABLED 0x00008D87 + +#define CLK_SPI2S23_PLL4P 0x00008DC0 +#define CLK_SPI2S23_PLL3Q 0x00008DC1 +#define CLK_SPI2S23_I2SCKIN 0x00008DC2 +#define CLK_SPI2S23_CKPER 0x00008DC3 +#define CLK_SPI2S23_PLL3R 0x00008DC4 +#define CLK_SPI2S23_DISABLED 0x00008DC7 + +#define CLK_SPI45_PCLK2 0x00008E00 +#define CLK_SPI45_PLL4Q 0x00008E01 +#define CLK_SPI45_HSI 0x00008E02 +#define CLK_SPI45_CSI 0x00008E03 +#define CLK_SPI45_HSE 0x00008E04 +#define CLK_SPI45_DISABLED 0x00008E07 + +#define CLK_SPI6_PCLK5 0x00000C40 +#define CLK_SPI6_PLL4Q 0x00000C41 +#define CLK_SPI6_HSI 0x00000C42 +#define CLK_SPI6_CSI 0x00000C43 +#define CLK_SPI6_HSE 0x00000C44 +#define CLK_SPI6_PLL3Q 0x00000C45 +#define CLK_SPI6_DISABLED 0x00000C47 + +#define CLK_UART6_PCLK2 0x00008E40 +#define CLK_UART6_PLL4Q 0x00008E41 +#define CLK_UART6_HSI 0x00008E42 +#define CLK_UART6_CSI 0x00008E43 +#define CLK_UART6_HSE 0x00008E44 +#define CLK_UART6_DISABLED 0x00008E47 + +#define CLK_UART24_PCLK1 0x00008E80 +#define CLK_UART24_PLL4Q 0x00008E81 +#define CLK_UART24_HSI 0x00008E82 +#define CLK_UART24_CSI 0x00008E83 +#define CLK_UART24_HSE 0x00008E84 +#define CLK_UART24_DISABLED 0x00008E87 + +#define CLK_UART35_PCLK1 0x00008EC0 +#define CLK_UART35_PLL4Q 0x00008EC1 +#define CLK_UART35_HSI 0x00008EC2 +#define CLK_UART35_CSI 0x00008EC3 +#define CLK_UART35_HSE 0x00008EC4 +#define CLK_UART35_DISABLED 0x00008EC7 + +#define CLK_UART78_PCLK1 0x00008F00 +#define CLK_UART78_PLL4Q 0x00008F01 +#define CLK_UART78_HSI 0x00008F02 +#define CLK_UART78_CSI 0x00008F03 +#define CLK_UART78_HSE 0x00008F04 +#define CLK_UART78_DISABLED 0x00008F07 + +#define CLK_UART1_PCLK5 0x00000C80 +#define CLK_UART1_PLL3Q 0x00000C81 +#define CLK_UART1_HSI 0x00000C82 +#define CLK_UART1_CSI 0x00000C83 +#define CLK_UART1_PLL4Q 0x00000C84 +#define CLK_UART1_HSE 0x00000C85 +#define CLK_UART1_DISABLED 0x00000C87 + +#define CLK_SDMMC12_HCLK6 0x00008F40 +#define CLK_SDMMC12_PLL3R 0x00008F41 +#define CLK_SDMMC12_PLL4P 0x00008F42 +#define CLK_SDMMC12_HSI 0x00008F43 +#define CLK_SDMMC12_DISABLED 0x00008F47 + +#define CLK_SDMMC3_HCLK2 0x00008F80 +#define CLK_SDMMC3_PLL3R 0x00008F81 +#define CLK_SDMMC3_PLL4P 0x00008F82 +#define CLK_SDMMC3_HSI 0x00008F83 +#define CLK_SDMMC3_DISABLED 0x00008F87 + +#define CLK_ETH_PLL4P 0x00008FC0 +#define CLK_ETH_PLL3Q 0x00008FC1 +#define CLK_ETH_DISABLED 0x00008FC3 + +#define CLK_QSPI_ACLK 0x00009000 +#define CLK_QSPI_PLL3R 0x00009001 +#define CLK_QSPI_PLL4P 0x00009002 +#define CLK_QSPI_CKPER 0x00009003 + +#define CLK_FMC_ACLK 0x00009040 +#define CLK_FMC_PLL3R 0x00009041 +#define CLK_FMC_PLL4P 0x00009042 +#define CLK_FMC_CKPER 0x00009043 + +#define CLK_FDCAN_HSE 0x000090C0 +#define CLK_FDCAN_PLL3Q 0x000090C1 +#define CLK_FDCAN_PLL4Q 0x000090C2 +#define CLK_FDCAN_PLL4R 0x000090C3 + +#define CLK_SPDIF_PLL4P 0x00009140 +#define CLK_SPDIF_PLL3Q 0x00009141 +#define CLK_SPDIF_HSI 0x00009142 +#define CLK_SPDIF_DISABLED 0x00009143 + +#define CLK_CEC_LSE 0x00009180 +#define CLK_CEC_LSI 0x00009181 +#define CLK_CEC_CSI_DIV122 0x00009182 +#define CLK_CEC_DISABLED 0x00009183 + +#define CLK_USBPHY_HSE 0x000091C0 +#define CLK_USBPHY_PLL4R 0x000091C1 +#define CLK_USBPHY_HSE_DIV2 0x000091C2 +#define CLK_USBPHY_DISABLED 0x000091C3 + +#define CLK_USBO_PLL4R 0x800091C0 +#define CLK_USBO_USBPHY 0x800091C1 + +#define CLK_RNG1_CSI 0x00000CC0 +#define CLK_RNG1_PLL4R 0x00000CC1 +#define CLK_RNG1_LSE 0x00000CC2 +#define CLK_RNG1_LSI 0x00000CC3 + +#define CLK_RNG2_CSI 0x00009200 +#define CLK_RNG2_PLL4R 0x00009201 +#define CLK_RNG2_LSE 0x00009202 +#define CLK_RNG2_LSI 0x00009203 + +#define CLK_CKPER_HSI 0x00000D00 +#define CLK_CKPER_CSI 0x00000D01 +#define CLK_CKPER_HSE 0x00000D02 +#define CLK_CKPER_DISABLED 0x00000D03 + +#define CLK_STGEN_HSI 0x00000D40 +#define CLK_STGEN_HSE 0x00000D41 +#define CLK_STGEN_DISABLED 0x00000D43 + +#define CLK_DSI_DSIPLL 0x00009240 +#define CLK_DSI_PLL4P 0x00009241 + +#define CLK_ADC_PLL4R 0x00009280 +#define CLK_ADC_CKPER 0x00009281 +#define CLK_ADC_PLL3Q 0x00009282 +#define CLK_ADC_DISABLED 0x00009283 + +#define CLK_LPTIM45_PCLK3 0x000092C0 +#define CLK_LPTIM45_PLL4P 0x000092C1 +#define CLK_LPTIM45_PLL3Q 0x000092C2 +#define CLK_LPTIM45_LSE 0x000092C3 +#define CLK_LPTIM45_LSI 0x000092C4 +#define CLK_LPTIM45_CKPER 0x000092C5 +#define CLK_LPTIM45_DISABLED 0x000092C7 + +#define CLK_LPTIM23_PCLK3 0x00009300 +#define CLK_LPTIM23_PLL4Q 0x00009301 +#define CLK_LPTIM23_CKPER 0x00009302 +#define CLK_LPTIM23_LSE 0x00009303 +#define CLK_LPTIM23_LSI 0x00009304 +#define CLK_LPTIM23_DISABLED 0x00009307 + +#define CLK_LPTIM1_PCLK1 0x00009340 +#define CLK_LPTIM1_PLL4P 0x00009341 +#define CLK_LPTIM1_PLL3Q 0x00009342 +#define CLK_LPTIM1_LSE 0x00009343 +#define CLK_LPTIM1_LSI 0x00009344 +#define CLK_LPTIM1_CKPER 0x00009345 +#define CLK_LPTIM1_DISABLED 0x00009347 + +/* define for st,pll /csg */ +#define SSCG_MODE_CENTER_SPREAD 0 +#define SSCG_MODE_DOWN_SPREAD 1 + +/* define for st,drive */ +#define LSEDRV_LOWEST 0 +#define LSEDRV_MEDIUM_LOW 1 +#define LSEDRV_MEDIUM_HIGH 2 +#define LSEDRV_HIGHEST 3 + +#endif diff --git a/include/dt-bindings/pinctrl/stm32-pinfunc.h b/include/dt-bindings/pinctrl/stm32-pinfunc.h new file mode 100644 index 0000000..e2f1f1b --- /dev/null +++ b/include/dt-bindings/pinctrl/stm32-pinfunc.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Torgue Alexandre for STMicroelectronics. + */ + +#ifndef _DT_BINDINGS_STM32_PINFUNC_H +#define _DT_BINDINGS_STM32_PINFUNC_H + +/* define PIN modes */ +#define GPIO 0x0 +#define AF0 0x1 +#define AF1 0x2 +#define AF2 0x3 +#define AF3 0x4 +#define AF4 0x5 +#define AF5 0x6 +#define AF6 0x7 +#define AF7 0x8 +#define AF8 0x9 +#define AF9 0xa +#define AF10 0xb +#define AF11 0xc +#define AF12 0xd +#define AF13 0xe +#define AF14 0xf +#define AF15 0x10 +#define ANALOG 0x11 + +/* define Pins number*/ +#define PIN_NO(port, line) (((port) - 'A') * 0x10 + (line)) + +#define STM32_PINMUX(port, line, mode) (((PIN_NO(port, line)) << 8) | (mode)) + +#endif /* _DT_BINDINGS_STM32_PINFUNC_H */ diff --git a/include/dt-bindings/reset/stm32mp1-resets.h b/include/dt-bindings/reset/stm32mp1-resets.h new file mode 100644 index 0000000..f0c3aae --- /dev/null +++ b/include/dt-bindings/reset/stm32mp1-resets.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Gabriel Fernandez for STMicroelectronics. + */ + +#ifndef _DT_BINDINGS_STM32MP1_RESET_H_ +#define _DT_BINDINGS_STM32MP1_RESET_H_ + +#define LTDC_R 3072 +#define DSI_R 3076 +#define DDRPERFM_R 3080 +#define USBPHY_R 3088 +#define SPI6_R 3136 +#define I2C4_R 3138 +#define I2C6_R 3139 +#define USART1_R 3140 +#define STGEN_R 3156 +#define GPIOZ_R 3200 +#define CRYP1_R 3204 +#define HASH1_R 3205 +#define RNG1_R 3206 +#define AXIM_R 3216 +#define GPU_R 3269 +#define ETHMAC_R 3274 +#define FMC_R 3276 +#define QSPI_R 3278 +#define SDMMC1_R 3280 +#define SDMMC2_R 3281 +#define CRC1_R 3284 +#define USBH_R 3288 +#define MDMA_R 3328 +#define MCU_R 8225 +#define TIM2_R 19456 +#define TIM3_R 19457 +#define TIM4_R 19458 +#define TIM5_R 19459 +#define TIM6_R 19460 +#define TIM7_R 19461 +#define TIM12_R 16462 +#define TIM13_R 16463 +#define TIM14_R 16464 +#define LPTIM1_R 19465 +#define SPI2_R 19467 +#define SPI3_R 19468 +#define USART2_R 19470 +#define USART3_R 19471 +#define UART4_R 19472 +#define UART5_R 19473 +#define UART7_R 19474 +#define UART8_R 19475 +#define I2C1_R 19477 +#define I2C2_R 19478 +#define I2C3_R 19479 +#define I2C5_R 19480 +#define SPDIF_R 19482 +#define CEC_R 19483 +#define DAC12_R 19485 +#define MDIO_R 19847 +#define TIM1_R 19520 +#define TIM8_R 19521 +#define TIM15_R 19522 +#define TIM16_R 19523 +#define TIM17_R 19524 +#define SPI1_R 19528 +#define SPI4_R 19529 +#define SPI5_R 19530 +#define USART6_R 19533 +#define SAI1_R 19536 +#define SAI2_R 19537 +#define SAI3_R 19538 +#define DFSDM_R 19540 +#define FDCAN_R 19544 +#define LPTIM2_R 19584 +#define LPTIM3_R 19585 +#define LPTIM4_R 19586 +#define LPTIM5_R 19587 +#define SAI4_R 19592 +#define SYSCFG_R 19595 +#define VREF_R 19597 +#define TMPSENS_R 19600 +#define PMBCTRL_R 19601 +#define DMA1_R 19648 +#define DMA2_R 19649 +#define DMAMUX_R 19650 +#define ADC12_R 19653 +#define USBO_R 19656 +#define SDMMC3_R 19664 +#define CAMITF_R 19712 +#define CRYP2_R 19716 +#define HASH2_R 19717 +#define RNG2_R 19718 +#define CRC2_R 19719 +#define HSEM_R 19723 +#define MBOX_R 19724 +#define GPIOA_R 19776 +#define GPIOB_R 19777 +#define GPIOC_R 19778 +#define GPIOD_R 19779 +#define GPIOE_R 19780 +#define GPIOF_R 19781 +#define GPIOG_R 19782 +#define GPIOH_R 19783 +#define GPIOI_R 19784 +#define GPIOJ_R 19785 +#define GPIOK_R 19786 + +#endif /* _DT_BINDINGS_STM32MP1_RESET_H_ */ diff --git a/maintainers.rst b/maintainers.rst index 76fede8..28127f8 100644 --- a/maintainers.rst +++ b/maintainers.rst @@ -125,6 +125,16 @@ :G: `rockchip-linux`_ :F: plat/rockchip/ +STM32MP1 platform port +---------------------- +:M: Yann Gautier +:G: `Yann-lms`_ +:F: docs/plat/stm32mp1.rst +:F: fdts/stm32\* +:F: include/dt-bindings/\*/stm32\* +:F: plat/st/ +:F: tools/stm32image/ + Synquacer platform port ----------------------- :M: Sumit Garg @@ -184,3 +194,4 @@ .. _soby-mathew: https://github.com/soby-mathew .. _TonyXie06: https://github.com/TonyXie06 .. _vwadekar: https://github.com/vwadekar +.. _Yann-lms: https://github.com/Yann-lms diff --git a/plat/st/stm32mp1/bl2_io_storage.c b/plat/st/stm32mp1/bl2_io_storage.c new file mode 100644 index 0000000..7346c0c --- /dev/null +++ b/plat/st/stm32mp1/bl2_io_storage.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* IO devices */ +static const io_dev_connector_t *dummy_dev_con; +static uintptr_t dummy_dev_handle; +static uintptr_t dummy_dev_spec; + +static const io_block_spec_t bl32_block_spec = { + .offset = BL32_BASE, + .length = STM32MP1_BL32_SIZE +}; + +static const io_block_spec_t bl2_block_spec = { + .offset = BL2_BASE, + .length = STM32MP1_BL2_SIZE, +}; + +static int open_dummy(const uintptr_t spec); + +struct plat_io_policy { + uintptr_t *dev_handle; + uintptr_t image_spec; + int (*check)(const uintptr_t spec); +}; + +static const struct plat_io_policy policies[] = { + [BL2_IMAGE_ID] = { + .dev_handle = &dummy_dev_handle, + .image_spec = (uintptr_t)&bl2_block_spec, + .check = open_dummy + }, + [BL32_IMAGE_ID] = { + .dev_handle = &dummy_dev_handle, + .image_spec = (uintptr_t)&bl32_block_spec, + .check = open_dummy + }, +}; + +static int open_dummy(const uintptr_t spec) +{ + return io_dev_init(dummy_dev_handle, 0); +} + +static void print_boot_device(boot_api_context_t *boot_context) +{ + switch (boot_context->boot_interface_selected) { + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: + INFO("Using SDMMC\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: + INFO("Using EMMC\n"); + break; + default: + ERROR("Boot interface not found\n"); + panic(); + break; + } + + if (boot_context->boot_interface_instance != 0U) { + INFO(" Instance %d\n", boot_context->boot_interface_instance); + } +} + +static void print_reset_reason(void) +{ + uint32_t rstsr = mmio_read_32(RCC_BASE + RCC_MP_RSTSCLRR); + + if (rstsr == 0U) { + WARN("Reset reason unknown\n"); + return; + } + + INFO("Reset reason (0x%x):\n", rstsr); + + if ((rstsr & RCC_MP_RSTSCLRR_PADRSTF) == 0U) { + if ((rstsr & RCC_MP_RSTSCLRR_STDBYRSTF) != 0U) { + INFO("System exits from STANDBY\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_CSTDBYRSTF) != 0U) { + INFO("MPU exits from CSTANDBY\n"); + return; + } + } + + if ((rstsr & RCC_MP_RSTSCLRR_PORRSTF) != 0U) { + INFO(" Power-on Reset (rst_por)\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_BORRSTF) != 0U) { + INFO(" Brownout Reset (rst_bor)\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_MPSYSRSTF) != 0U) { + INFO(" System reset generated by MPU (MPSYSRST)\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_HCSSRSTF) != 0U) { + INFO(" Reset due to a clock failure on HSE\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_IWDG1RSTF) != 0U) { + INFO(" IWDG1 Reset (rst_iwdg1)\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_IWDG2RSTF) != 0U) { + INFO(" IWDG2 Reset (rst_iwdg2)\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_PADRSTF) != 0U) { + INFO(" Pad Reset from NRST\n"); + return; + } + + if ((rstsr & RCC_MP_RSTSCLRR_VCORERSTF) != 0U) { + INFO(" Reset due to a failure of VDD_CORE\n"); + return; + } + + ERROR(" Unidentified reset reason\n"); +} + +void stm32mp1_io_setup(void) +{ + int io_result __unused; + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp1_get_boot_ctx_address(); + + print_reset_reason(); + + print_boot_device(boot_context); + + if ((boot_context->boot_partition_used_toboot == 1U) || + (boot_context->boot_partition_used_toboot == 2U)) { + INFO("Boot used partition fsbl%d\n", + boot_context->boot_partition_used_toboot); + } + + io_result = register_io_dev_dummy(&dummy_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(dummy_dev_con, dummy_dev_spec, + &dummy_dev_handle); + assert(io_result == 0); +} + +/* + * Return an IO device handle and specification which can be used to access + * an image. Use this to enforce platform load policy. + */ +int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle, + uintptr_t *image_spec) +{ + int rc; + const struct plat_io_policy *policy; + + assert(image_id < ARRAY_SIZE(policies)); + + policy = &policies[image_id]; + rc = policy->check(policy->image_spec); + if (rc == 0) { + *image_spec = policy->image_spec; + *dev_handle = *(policy->dev_handle); + } + + return rc; +} diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c new file mode 100644 index 0000000..9f2d8bd --- /dev/null +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void bl2_el3_early_platform_setup(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + stm32mp1_save_boot_ctx_address(arg0); +} + +void bl2_platform_setup(void) +{ + int ret; + + if (dt_check_pmic()) { + initialize_pmic(); + } + + ret = stm32mp1_ddr_probe(); + if (ret < 0) { + ERROR("Invalid DDR init: error %d\n", ret); + panic(); + } + + INFO("BL2 runs SP_MIN setup\n"); +} + +void bl2_el3_plat_arch_setup(void) +{ + int32_t result; + struct dt_node_info dt_dev_info; + const char *board_model; + boot_api_context_t *boot_context = + (boot_api_context_t *)stm32mp1_get_boot_ctx_address(); + uint32_t clk_rate; + + /* + * Disable the backup domain write protection. + * The protection is enable at each reset by hardware + * and must be disabled by software. + */ + mmio_setbits_32(PWR_BASE + PWR_CR1, PWR_CR1_DBP); + + while ((mmio_read_32(PWR_BASE + PWR_CR1) & PWR_CR1_DBP) == 0U) { + ; + } + + /* Reset backup domain on cold boot cases */ + if ((mmio_read_32(RCC_BASE + RCC_BDCR) & RCC_BDCR_RTCSRC_MASK) == 0U) { + mmio_setbits_32(RCC_BASE + RCC_BDCR, RCC_BDCR_VSWRST); + + while ((mmio_read_32(RCC_BASE + RCC_BDCR) & RCC_BDCR_VSWRST) == + 0U) { + ; + } + + mmio_clrbits_32(RCC_BASE + RCC_BDCR, RCC_BDCR_VSWRST); + } + + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + + /* Prevent corruption of preloaded BL32 */ + mmap_add_region(BL32_BASE, BL32_BASE, + BL32_LIMIT - BL32_BASE, + MT_MEMORY | MT_RO | MT_SECURE); + + /* Prevent corruption of preloaded Device Tree */ + mmap_add_region(DTB_BASE, DTB_BASE, + DTB_LIMIT - DTB_BASE, + MT_MEMORY | MT_RO | MT_SECURE); + + configure_mmu(); + + generic_delay_timer_init(); + + if (dt_open_and_check() < 0) { + panic(); + } + + if (stm32mp1_clk_probe() < 0) { + panic(); + } + + if (stm32mp1_clk_init() < 0) { + panic(); + } + + result = dt_get_stdout_uart_info(&dt_dev_info); + + if ((result <= 0) || + (dt_dev_info.status == 0U) || + (dt_dev_info.clock < 0) || + (dt_dev_info.reset < 0)) { + goto skip_console_init; + } + + if (dt_set_stdout_pinctrl() != 0) { + goto skip_console_init; + } + + if (stm32mp1_clk_enable((unsigned long)dt_dev_info.clock) != 0) { + goto skip_console_init; + } + + stm32mp1_reset_assert((uint32_t)dt_dev_info.reset); + udelay(2); + stm32mp1_reset_deassert((uint32_t)dt_dev_info.reset); + mdelay(1); + + clk_rate = stm32mp1_clk_get_rate((unsigned long)dt_dev_info.clock); + + if (console_init(dt_dev_info.base, clk_rate, + STM32MP1_UART_BAUDRATE) == 0) { + panic(); + } + + board_model = dt_get_board_model(); + if (board_model != NULL) { + NOTICE("%s\n", board_model); + } + +skip_console_init: + + if (stm32_save_boot_interface(boot_context->boot_interface_selected, + boot_context->boot_interface_instance) != + 0) { + ERROR("Cannot save boot interface\n"); + } + + stm32mp1_arch_security_setup(); + + stm32mp1_io_setup(); +} diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h new file mode 100644 index 0000000..71c3593 --- /dev/null +++ b/plat/st/stm32mp1/include/boot_api.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __BOOT_API_H +#define __BOOT_API_H + +#include + +/* + * Possible value of boot context field 'boot_interface_sel' + */ + +/* Value of field 'boot_interface_sel' when no boot occurred */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_NO 0x0U + +/* Boot occurred on SD */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD 0x1U + +/* Boot occurred on EMMC */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC 0x2U + +/** + * @brief Possible value of boot context field 'EmmcXferStatus' + */ +/* + * Possible value of boot context field 'emmc_xfer_status' + */ +#define BOOT_API_CTX_EMMC_XFER_STATUS_NOT_STARTED 0x0U +#define BOOT_API_CTX_EMMC_XFER_STATUS_DATAEND_DETECTED 0x1U +#define BOOT_API_CTX_EMMC_XFER_STATUS_XFER_OVERALL_TIMEOUT_DETECTED 0x2U +#define BOOT_API_CTX_EMMC_XFER_STATUS_XFER_DATA_TIMEOUT 0x3U + +/* + * Possible value of boot context field 'emmc_error_status' + */ +#define BOOT_API_CTX_EMMC_ERROR_STATUS_NONE 0x0U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_CMD_TIMEOUT 0x1U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_ACK_TIMEOUT 0x2U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_DATA_CRC_FAIL 0x3U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_NOT_ENOUGH_BOOT_DATA_RX 0x4U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_HEADER_NOT_FOUND 0x5U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_HEADER_SIZE_ZERO 0x6U +#define BOOT_API_CTX_EMMC_ERROR_STATUS_IMAGE_NOT_COMPLETE 0x7U + +/* Image Header related definitions */ + +/* Definition of header version */ +#define BOOT_API_HEADER_VERSION 0x00010000U + +/* + * Magic number used to detect header in memory + * Its value must be 'S' 'T' 'M' 0x32, i.e 0x324D5453 as field + * 'bootapi_image_header_t.magic' + * This identifies the start of a boot image. + */ +#define BOOT_API_IMAGE_HEADER_MAGIC_NB 0x324D5453U + +/* Definitions related to Authentication used in image header structure */ +#define BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES 64 +#define BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES 64 +#define BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES 32 + +/* Possible values of the field 'boot_api_image_header_t.ecc_algo_type' */ +#define BOOT_API_ECDSA_ALGO_TYPE_P256NIST 1 +#define BOOT_API_ECDSA_ALGO_TYPE_BRAINPOOL256 2 + +/* + * Cores secure magic numbers + * Constant to be stored in bakcup register + * BOOT_API_MAGIC_NUMBER_TAMP_BCK_REG_IDX + */ +#define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xCA7FACE0U +#define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xCA7FACE1U + +/* + * TAMP_BCK4R register index + * This register is used to write a Magic Number in order to restart + * Cortex A7 Core 1 and make it execute @ branch address from TAMP_BCK5R + */ +#define BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX 4U + +/* + * TAMP_BCK5R register index + * This register is used to contain the branch address of + * Cortex A7 Core 1 when restarted by a TAMP_BCK4R magic number writing + */ +#define BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX 5U + +/* + * Possible value of boot context field 'hse_clock_value_in_hz' + */ +#define BOOT_API_CTX_HSE_CLOCK_VALUE_UNDEFINED 0U +#define BOOT_API_CTX_HSE_CLOCK_VALUE_24_MHZ 24000000U +#define BOOT_API_CTX_HSE_CLOCK_VALUE_25_MHZ 25000000U +#define BOOT_API_CTX_HSE_CLOCK_VALUE_26_MHZ 26000000U + +/* + * Possible value of boot context field 'boot_partition_used_toboot' + */ +#define BOOT_API_CTX_BOOT_PARTITION_UNDEFINED 0U + +/* Used FSBL1 to boot */ +#define BOOT_API_CTX_BOOT_PARTITION_FSBL1 1U + +/* Used FSBL2 to boot */ +#define BOOT_API_CTX_BOOT_PARTITION_FSBL2 2U + +/* OTP_CFG0 */ +#define BOOT_API_OTP_MODE_WORD_NB 0 +/* Closed = OTP_CFG0[6] */ +#define BOOT_API_OTP_MODE_CLOSED_BIT_POS 6 + +/* + * Boot Context related definitions + */ + +/* + * Boot core boot configuration structure + * Specifies all items of the cold boot configuration + * Memory and peripheral part. + */ +typedef struct { + /* + * Boot interface used to boot : take values from defines + * BOOT_API_CTX_BOOT_INTERFACE_SEL_XXX above + */ + uint16_t boot_interface_selected; + uint16_t boot_interface_instance; + uint32_t reserved1[13]; + uint32_t otp_afmux_values[3]; + uint32_t reserved[9]; + /* + * Information specific to an SD boot + * Updated each time an SD boot is at least attempted, + * even if not successful + * Note : This is useful to understand why an SD boot failed + * in particular + */ + uint32_t sd_err_internal_timeout_cnt; + uint32_t sd_err_dcrc_fail_cnt; + uint32_t sd_err_dtimeout_cnt; + uint32_t sd_err_ctimeout_cnt; + uint32_t sd_err_ccrc_fail_cnt; + uint32_t sd_overall_retry_cnt; + /* + * Information specific to an eMMC boot + * Updated each time an eMMC boot is at least attempted, + * even if not successful + * Note : This is useful to understand why an eMMC boot failed + * in particular + */ + uint32_t emmc_xfer_status; + uint32_t emmc_error_status; + uint32_t emmc_nbbytes_rxcopied_tosysram_download_area; + uint32_t hse_clock_value_in_hz; + /* + * Boot partition : + * ie FSBL partition on which the boot was successful + */ + uint32_t boot_partition_used_toboot; + +} __packed boot_api_context_t; + +/* + * Image Header related definitions + */ + +/* + * Structure used to define the common Header format used for FSBL, xloader, + * ... and in particular used by bootROM for FSBL header readout. + * FSBL header size is 256 Bytes = 0x100 + */ +typedef struct { + /* BOOT_API_IMAGE_HEADER_MAGIC_NB */ + uint32_t magic; + uint8_t image_signature[BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES]; + /* + * Checksum of payload + * 32-bit sum all all payload bytes considered as 8 bit unigned numbers, + * discarding any overflow bits. + * Use to check UART/USB downloaded image integrity when signature + * is not used (i.e bit 0 : 'No_sig_check' = 1 in option flags) + */ + uint32_t payload_checksum; + /* Image header version : should have value BOOT_API_HEADER_VERSION */ + uint32_t header_version; + /* Image length in bytes */ + uint32_t image_length; + /* + * Image Entry point address : should be in the SYSRAM area + * and at least within the download area range + */ + uint32_t image_entry_point; + /* Reserved */ + uint32_t reserved1; + /* + * Image load address : not used by bootROM but to be consistent + * with header format for other packages (xloader, ...) + */ + uint32_t load_address; + /* Reserved */ + uint32_t reserved2; + /* Image version to be compared by bootROM with monotonic + * counter value in OTP_CFG4 prior executing the downloaded image + */ + uint32_t image_version; + /* + * Option flags: + * Bit 0 : No signature check request : 'No_sig_check' + * value 1 : for No signature check request + * value 0 : No request to bypass the signature check + * Note : No signature check is never allowed on a Secured chip + */ + uint32_t option_flags; + /* + * Type of ECC algorithm to use : + * value 1 : for P-256 NIST algorithm + * value 2 : for Brainpool 256 algorithm + * See definitions 'BOOT_API_ECDSA_ALGO_TYPE_XXX' above. + */ + uint32_t ecc_algo_type; + /* + * OEM ECC Public Key (aka Root pubk) provided in header on 512 bits. + * The SHA-256 hash of the OEM ECC pubk must match the one stored + * in OTP cells. + */ + uint8_t ecc_pubk[BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES]; + /* Pad up to 256 byte total size */ + uint8_t pad[84]; +} __packed boot_api_image_header_t; + +#endif /* __BOOT_API_H */ diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h new file mode 100644 index 0000000..47e1ffc --- /dev/null +++ b/plat/st/stm32mp1/include/platform_def.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include +#include +#include +#include +#include "../stm32mp1_def.h" + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#if defined(IMAGE_BL32) +#define PLATFORM_STACK_SIZE 0x600 +#else +#define PLATFORM_STACK_SIZE 0xC00 +#endif + +/* SSBL = second stage boot loader */ +#define BL33_IMAGE_NAME "ssbl" + +#define STM32MP1_PRIMARY_CPU U(0x0) + +#define PLATFORM_CACHE_LINE_SIZE 64 +#define PLATFORM_CLUSTER_COUNT ULL(1) +#define PLATFORM_CLUSTER0_CORE_COUNT U(2) +#define PLATFORM_CLUSTER1_CORE_COUNT U(0) +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER1_CORE_COUNT + \ + PLATFORM_CLUSTER0_CORE_COUNT) +#define PLATFORM_MAX_CPUS_PER_CLUSTER 2 + +#define MAX_IO_DEVICES 4 +#define MAX_IO_HANDLES 4 + +/******************************************************************************* + * BL2 specific defines. + ******************************************************************************/ +/* + * Put BL2 just below BL3-1. BL2_BASE is calculated using the current BL2 debug + * size plus a little space for growth. + */ +#define BL2_BASE STM32MP1_BL2_BASE +#define BL2_LIMIT (STM32MP1_BL2_BASE + \ + STM32MP1_BL2_SIZE) + +/******************************************************************************* + * BL32 specific defines. + ******************************************************************************/ +#define BL32_BASE STM32MP1_BL32_BASE +#define BL32_LIMIT (STM32MP1_BL32_BASE + \ + STM32MP1_BL32_SIZE) + +/******************************************************************************* + * BL33 specific defines. + ******************************************************************************/ +#define BL33_BASE STM32MP1_BL33_BASE + +/* + * Load address of BL33 for this platform port + */ +#define PLAT_STM32MP1_NS_IMAGE_OFFSET BL33_BASE + +/******************************************************************************* + * DTB specific defines. + ******************************************************************************/ +#define DTB_BASE STM32MP1_DTB_BASE +#define DTB_LIMIT (STM32MP1_DTB_BASE + \ + STM32MP1_DTB_SIZE) + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) + +/******************************************************************************* + * Declarations and constants to access the mailboxes safely. Each mailbox is + * aligned on the biggest cache line size in the platform. This is known only + * to the platform as it might have a combination of integrated and external + * caches. Such alignment ensures that two maiboxes do not sit on the same cache + * line at any cache level. They could belong to different cpus/clusters & + * get written while being protected by different locks causing corruption of + * a valid mailbox address. + ******************************************************************************/ +#define CACHE_WRITEBACK_SHIFT 6 +#define CACHE_WRITEBACK_GRANULE (U(1) << CACHE_WRITEBACK_SHIFT) + +/* + * Secure Interrupt: based on the standard ARM mapping + */ +#define ARM_IRQ_SEC_PHY_TIMER U(29) + +#define ARM_IRQ_SEC_SGI_0 U(8) +#define ARM_IRQ_SEC_SGI_1 U(9) +#define ARM_IRQ_SEC_SGI_2 U(10) +#define ARM_IRQ_SEC_SGI_3 U(11) +#define ARM_IRQ_SEC_SGI_4 U(12) +#define ARM_IRQ_SEC_SGI_5 U(13) +#define ARM_IRQ_SEC_SGI_6 U(14) +#define ARM_IRQ_SEC_SGI_7 U(15) + +#define STM32MP1_IRQ_TZC400 U(36) +#define STM32MP1_IRQ_TAMPSERRS U(229) +#define STM32MP1_IRQ_AXIERRIRQ U(244) + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLATFORM_G1S_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(STM32MP1_IRQ_TAMPSERRS, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(STM32MP1_IRQ_AXIERRIRQ, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(STM32MP1_IRQ_TZC400, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE) + +#define PLATFORM_G0_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, \ + GIC_HIGHEST_SEC_PRIORITY, \ + grp, GIC_INTR_CFG_EDGE) + +/* + * Power + */ +#define PLAT_MAX_PWR_LVL U(1) + +/* Local power state for power domains in Run state. */ +#define ARM_LOCAL_STATE_RUN U(0) +/* Local power state for retention. Valid only for CPU power domains */ +#define ARM_LOCAL_STATE_RET U(1) +/* Local power state for power-down. Valid for CPU and cluster power domains */ +#define ARM_LOCAL_STATE_OFF U(2) +/* + * This macro defines the deepest retention state possible. + * A higher state id will represent an invalid or a power down state. + */ +#define PLAT_MAX_RET_STATE ARM_LOCAL_STATE_RET +/* + * This macro defines the deepest power down states possible. Any state ID + * higher than this is invalid. + */ +#define PLAT_MAX_OFF_STATE ARM_LOCAL_STATE_OFF + +/******************************************************************************* + * Size of the per-cpu data in bytes that should be reserved in the generic + * per-cpu data structure for the FVP port. + ******************************************************************************/ +#define PLAT_PCPU_DATA_SIZE 2 + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/st/stm32mp1/include/stm32mp1_context.h b/plat/st/stm32mp1/include/stm32mp1_context.h new file mode 100644 index 0000000..fd08afc --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_context.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_CONTEXT_H__ +#define __STM32MP1_CONTEXT_H__ + +#include + +int stm32_save_boot_interface(uint32_t interface, uint32_t instance); + +#endif /* __STM32MP1_CONTEXT_H__ */ diff --git a/plat/st/stm32mp1/include/stm32mp1_dt.h b/plat/st/stm32mp1/include/stm32mp1_dt.h new file mode 100644 index 0000000..58e10d1 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_dt.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_DT_H__ +#define __STM32MP1_DT_H__ + +#include + +struct dt_node_info { + uint32_t base; + int32_t clock; + int32_t reset; + bool status; + bool sec_status; +}; + +/******************************************************************************* + * Function and variable prototypes + ******************************************************************************/ +int dt_open_and_check(void); +int fdt_get_address(void **fdt_addr); +bool fdt_check_node(int node); +bool fdt_check_status(int node); +bool fdt_check_secure_status(int node); +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 dt_set_pinctrl_config(int node); +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_stdout_node_offset(void); +uint32_t dt_get_ddr_size(void); +const char *dt_get_board_model(void); + +#endif /* __STM32MP1_DT_H__ */ diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h new file mode 100644 index 0000000..a789d53 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_PRIVATE_H__ +#define __STM32MP1_PRIVATE_H__ + +void stm32mp1_io_setup(void); +void configure_mmu(void); + +void stm32mp1_arch_security_setup(void); +void stm32mp1_security_setup(void); + +void stm32mp1_save_boot_ctx_address(uintptr_t address); +uintptr_t stm32mp1_get_boot_ctx_address(void); + +void stm32mp1_gic_pcpu_init(void); +void stm32mp1_gic_init(void); + +#endif /* __STM32MP1_PRIVATE_H__ */ diff --git a/plat/st/stm32mp1/plat_bl2_mem_params_desc.c b/plat/st/stm32mp1/plat_bl2_mem_params_desc.c new file mode 100644 index 0000000..6f5bc4c --- /dev/null +++ b/plat/st/stm32mp1/plat_bl2_mem_params_desc.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +/******************************************************************************* + * Following descriptor provides BL image/ep information that gets used + * by BL2 to load the images and also subset of this information is + * passed to next BL image. The image loading sequence is managed by + * populating the images in required loading order. The image execution + * sequence is managed by populating the `next_handoff_image_id` with + * the next executable image id. + ******************************************************************************/ +static bl_mem_params_node_t bl2_mem_params_descs[] = { + /* Fill BL32 related information */ + { + .image_id = BL32_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + SECURE | EXECUTABLE | EP_FIRST_EXE), + + .ep_info.pc = BL32_BASE, + .ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, + SPSR_E_LITTLE, + DISABLE_ALL_EXCEPTIONS), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, + IMAGE_ATTRIB_PLAT_SETUP), + + .image_info.image_base = BL32_BASE, + .image_info.image_max_size = BL32_LIMIT - BL32_BASE, + + .next_handoff_image_id = BL33_IMAGE_ID, + }, + + /* Fill BL33 related information */ + { + .image_id = BL33_IMAGE_ID, + + SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, + VERSION_2, entry_point_info_t, + NON_SECURE | EXECUTABLE), + + .ep_info.pc = PLAT_STM32MP1_NS_IMAGE_OFFSET, + .ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, + SPSR_E_LITTLE, + DISABLE_ALL_EXCEPTIONS), + + SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, + VERSION_2, image_info_t, 0), + + .image_info.image_base = PLAT_STM32MP1_NS_IMAGE_OFFSET, + .image_info.image_max_size = STM32MP1_DDR_MAX_SIZE - + (PLAT_STM32MP1_NS_IMAGE_OFFSET - STM32MP1_DDR_BASE), + + .next_handoff_image_id = INVALID_IMAGE_ID, + } +}; + +REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) diff --git a/plat/st/stm32mp1/plat_image_load.c b/plat/st/stm32mp1/plat_image_load.c new file mode 100644 index 0000000..3c6d677 --- /dev/null +++ b/plat/st/stm32mp1/plat_image_load.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +/******************************************************************************* + * This function flushes the data structures so that they are visible + * in memory for the next BL image. + ******************************************************************************/ +void plat_flush_next_bl_params(void) +{ + flush_bl_params_desc(); +} + +/******************************************************************************* + * This function returns the list of loadable images. + ******************************************************************************/ +bl_load_info_t *plat_get_bl_image_load_info(void) +{ + return get_bl_load_info_from_mem_params_desc(); +} + +/******************************************************************************* + * This function returns the list of executable images. + ******************************************************************************/ +bl_params_t *plat_get_next_bl_params(void) +{ + return get_next_bl_params_from_mem_params_desc(); +} diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk new file mode 100644 index 0000000..3f938d9 --- /dev/null +++ b/plat/st/stm32mp1/platform.mk @@ -0,0 +1,148 @@ +# +# Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ARM_CORTEX_A7 := yes +ARM_WITH_NEON := yes +LOAD_IMAGE_V2 := 1 +BL2_AT_EL3 := 1 +ENABLE_PLAT_COMPAT := 0 +USE_COHERENT_MEM := 0 + +STM32_TF_VERSION ?= 0 + +# Not needed for Cortex-A7 +WORKAROUND_CVE_2017_5715:= 0 + +PLAT_INCLUDES := -Iplat/st/stm32mp1/include/ +PLAT_INCLUDES += -Iinclude/common/tbbr +PLAT_INCLUDES += -Iinclude/drivers/st + +# Device tree +STM32_DTB_FILE_NAME ?= stm32mp157c-ev1.dtb +FDT_SOURCES := $(addprefix fdts/, $(patsubst %.dtb,%.dts,$(STM32_DTB_FILE_NAME))) +DTC_FLAGS += -Wno-unit_address_vs_reg + +include lib/libfdt/libfdt.mk + +PLAT_BL_COMMON_SOURCES := plat/st/stm32mp1/stm32mp1_common.c + +PLAT_BL_COMMON_SOURCES += drivers/console/aarch32/console.S \ + drivers/st/uart/aarch32/stm32_console.S + +ifneq (${ENABLE_STACK_PROTECTOR},0) +PLAT_BL_COMMON_SOURCES += plat/st/stm32mp1/stm32mp1_stack_protector.c +endif + +include lib/xlat_tables_v2/xlat_tables.mk +PLAT_BL_COMMON_SOURCES += ${XLAT_TABLES_LIB_SRCS} + +PLAT_BL_COMMON_SOURCES += lib/cpus/aarch32/cortex_a7.S + +PLAT_BL_COMMON_SOURCES += ${LIBFDT_SRCS} \ + drivers/arm/tzc/tzc400.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + drivers/st/clk/stm32mp1_clk.c \ + drivers/st/clk/stm32mp1_clkfunc.c \ + drivers/st/ddr/stm32mp1_ddr_helpers.c \ + drivers/st/gpio/stm32_gpio.c \ + drivers/st/pmic/stm32_i2c.c \ + drivers/st/pmic/stm32mp1_pmic.c \ + drivers/st/pmic/stpmu1.c \ + drivers/st/reset/stm32mp1_reset.c \ + plat/st/stm32mp1/stm32mp1_context.c \ + plat/st/stm32mp1/stm32mp1_dt.c \ + plat/st/stm32mp1/stm32mp1_helper.S \ + plat/st/stm32mp1/stm32mp1_security.c + +BL2_SOURCES += drivers/io/io_dummy.c \ + drivers/io/io_storage.c \ + plat/st/stm32mp1/bl2_io_storage.c \ + plat/st/stm32mp1/bl2_plat_setup.c + +BL2_SOURCES += drivers/st/ddr/stm32mp1_ddr.c \ + drivers/st/ddr/stm32mp1_ram.c + +BL2_SOURCES += common/desc_image_load.c \ + plat/st/stm32mp1/plat_bl2_mem_params_desc.c \ + plat/st/stm32mp1/plat_image_load.c + +# For memory footprint optimization, build with thumb and interwork support +ASFLAGS += -mthumb -mthumb-interwork +TF_CFLAGS += -mthumb -mthumb-interwork + +# Macros and rules to build TF binary +STM32_TF_ELF_LDFLAGS := --hash-style=gnu --as-needed +STM32_DT_BASENAME := $(STM32_DTB_FILE_NAME:.dtb=) +STM32_TF_STM32 := ${BUILD_PLAT}/tf-a-${STM32_DT_BASENAME}.stm32 +STM32_TF_BINARY := $(STM32_TF_STM32:.stm32=.bin) +STM32_TF_MAPFILE := $(STM32_TF_STM32:.stm32=.map) +STM32_TF_LINKERFILE := $(STM32_TF_STM32:.stm32=.ld) +STM32_TF_ELF := $(STM32_TF_STM32:.stm32=.elf) +STM32_TF_DTBFILE := ${BUILD_PLAT}/fdts/${STM32_DTB_FILE_NAME} +STM32_TF_OBJS := ${BUILD_PLAT}/stm32mp1.o + +# Variables for use with stm32image +STM32IMAGEPATH ?= tools/stm32image +STM32IMAGE ?= ${STM32IMAGEPATH}/stm32image${BIN_EXT} + +.PHONY: ${STM32_TF_STM32} +.SUFFIXES: + +all: check_dtc_version ${STM32_TF_STM32} stm32image + +ifeq ($(AARCH32_SP),sp_min) +# BL32 is built only if using SP_MIN +BL32_DEP := bl32 +BL32_PATH := -DBL32_BIN_PATH=\"${BUILD_PLAT}/bl32.bin\" +endif + +distclean realclean clean: clean_stm32image + +stm32image: + ${Q}${MAKE} CPPFLAGS="" --no-print-directory -C ${STM32IMAGEPATH} + +clean_stm32image: + ${Q}${MAKE} --no-print-directory -C ${STM32IMAGEPATH} clean + +check_dtc_version: + $(eval DTC_V = $(shell $(DTC) -v | awk '{print $$NF}')) + $(eval DTC_VERSION = $(shell printf "%d" $(shell echo ${DTC_V} | cut -d- -f1 | sed "s/\./0/g"))) + @if [ ${DTC_VERSION} -lt 10404 ]; then \ + echo "dtc version too old (${DTC_V}), you need at least version 1.4.4"; \ + false; \ + fi + + +${STM32_TF_OBJS}: plat/st/stm32mp1/stm32mp1.S bl2 ${BL32_DEP} ${STM32_TF_DTBFILE} + @echo " AS $<" + ${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \ + ${BL32_PATH} \ + -DBL2_BIN_PATH=\"${BUILD_PLAT}/bl2.bin\" \ + -DDTB_BIN_PATH=\"${STM32_TF_DTBFILE}\" \ + -c plat/st/stm32mp1/stm32mp1.S -o $@ + +${STM32_TF_LINKERFILE}: plat/st/stm32mp1/stm32mp1.ld.S + @echo " LDS $<" + ${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} -P -E $< -o $@ + +${STM32_TF_ELF}: ${STM32_TF_OBJS} ${STM32_TF_LINKERFILE} + @echo " LDS $<" + ${Q}${LD} -o $@ ${STM32_TF_ELF_LDFLAGS} -Map=${STM32_TF_MAPFILE} --script ${STM32_TF_LINKERFILE} ${STM32_TF_OBJS} + +${STM32_TF_BINARY}: ${STM32_TF_ELF} + ${Q}${OC} -O binary ${STM32_TF_ELF} $@ + @echo + @echo "Built $@ successfully" + @echo + +${STM32_TF_STM32}: stm32image ${STM32_TF_BINARY} + @echo + @echo "Generated $@" + $(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} + @echo diff --git a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk new file mode 100644 index 0000000..9fde153 --- /dev/null +++ b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk @@ -0,0 +1,21 @@ +# +# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +SP_MIN_WITH_SECURE_FIQ := 1 + +BL32_SOURCES += plat/common/aarch32/platform_mp_stack.S \ + plat/st/stm32mp1/sp_min/sp_min_setup.c \ + plat/st/stm32mp1/stm32mp1_pm.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 + +# Generic PSCI +BL32_SOURCES += plat/common/plat_psci_common.c diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c new file mode 100644 index 0000000..1329bdb --- /dev/null +++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************** + * Placeholder variables for copying the arguments that have been passed to + * BL32 from BL2. + ******************************************************************************/ +static entry_point_info_t bl33_image_ep_info; + +/******************************************************************************* + * Interrupt handler for FIQ (secure IRQ) + ******************************************************************************/ +void sp_min_plat_fiq_handler(uint32_t id) +{ + switch (id) { + case STM32MP1_IRQ_TZC400: + ERROR("STM32MP1_IRQ_TZC400 generated\n"); + panic(); + break; + case STM32MP1_IRQ_AXIERRIRQ: + ERROR("STM32MP1_IRQ_AXIERRIRQ generated\n"); + panic(); + break; + default: + ERROR("SECURE IT handler not define for it : %i", id); + break; + } +} + +/******************************************************************************* + * Return a pointer to the 'entry_point_info' structure of the next image for + * the security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + ******************************************************************************/ +entry_point_info_t *sp_min_plat_get_bl33_ep_info(void) +{ + entry_point_info_t *next_image_info; + + next_image_info = &bl33_image_ep_info; + + if (next_image_info->pc == 0U) { + return NULL; + } + + return next_image_info; +} + +/******************************************************************************* + * Perform any BL32 specific platform actions. + ******************************************************************************/ +void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + struct dt_node_info dt_dev_info; + int result; + bl_params_t *params_from_bl2 = (bl_params_t *)arg0; + + /* Imprecise aborts can be masked in NonSecure */ + write_scr(read_scr() | SCR_AW_BIT); + + assert(params_from_bl2 != NULL); + assert(params_from_bl2->h.type == PARAM_BL_PARAMS); + assert(params_from_bl2->h.version >= VERSION_2); + + bl_params_node_t *bl_params = params_from_bl2->head; + + /* + * Copy BL33 entry point information. + * They are stored in Secure RAM, in BL2's address space. + */ + while (bl_params != NULL) { + if (bl_params->image_id == BL33_IMAGE_ID) { + bl33_image_ep_info = *bl_params->ep_info; + break; + } + + bl_params = bl_params->next_params_info; + } + + if (dt_open_and_check() < 0) { + panic(); + } + + if (stm32mp1_clk_probe() < 0) { + panic(); + } + + result = dt_get_stdout_uart_info(&dt_dev_info); + + if ((result > 0) && dt_dev_info.status) { + if (console_init(dt_dev_info.base, 0, STM32MP1_UART_BAUDRATE) + == 0) { + panic(); + } + } +} + +/******************************************************************************* + * Initialize the MMU, security and the GIC. + ******************************************************************************/ +void sp_min_platform_setup(void) +{ + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + + configure_mmu(); + + /* Initialize tzc400 after DDR initialization */ + stm32mp1_security_setup(); + + generic_delay_timer_init(); + + stm32mp1_gic_init(); +} + +void sp_min_plat_arch_setup(void) +{ +} diff --git a/plat/st/stm32mp1/stm32mp1.S b/plat/st/stm32mp1/stm32mp1.S new file mode 100644 index 0000000..7255fe5 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1.S @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifdef BL32_BIN_PATH +.section .bl32_image +.incbin BL32_BIN_PATH +#endif + +.section .bl2_image +.incbin BL2_BIN_PATH + +.section .dtb_image +.incbin DTB_BIN_PATH diff --git a/plat/st/stm32mp1/stm32mp1.ld.S b/plat/st/stm32mp1/stm32mp1.ld.S new file mode 100644 index 0000000..0d7a8bb --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1.ld.S @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __STM32MP1_LD_S__ +#define __STM32MP1_LD_S__ +#include +#include + +OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT) +OUTPUT_ARCH(PLATFORM_LINKER_ARCH) + +ENTRY(__BL2_IMAGE_START__) + +MEMORY { + HEADER (rw) : ORIGIN = 0x00000000, LENGTH = 0x3000 + RAM (rwx) : ORIGIN = STM32MP1_BINARY_BASE, LENGTH = STM32MP1_BINARY_SIZE +} + +SECTIONS +{ + /* + * TF mapping must conform to ROM code specification. + */ + .header : { + __HEADER_START__ = .; + KEEP(*(.header)) + . = ALIGN(4); + __HEADER_END__ = .; + } >HEADER + + . = STM32MP1_BINARY_BASE; + .data . : { + . = ALIGN(PAGE_SIZE); + __DATA_START__ = .; + *(.data*) + + /* + * dtb. + * The strongest and only alignment contraint is MMU 4K page. + * Indeed as images below will be removed, 4K pages will be re-used. + */ + . = ( STM32MP1_DTB_BASE - STM32MP1_BINARY_BASE ); + __DTB_IMAGE_START__ = .; + *(.dtb_image*) + __DTB_IMAGE_END__ = .; + + /* + * bl2. + * The strongest and only alignment contraint is MMU 4K page. + * Indeed as images below will be removed, 4K pages will be re-used. + */ + . = ( STM32MP1_BL2_BASE - STM32MP1_BINARY_BASE ); + __BL2_IMAGE_START__ = .; + *(.bl2_image*) + __BL2_IMAGE_END__ = .; + + /* + * bl32 will be settled by bl2. + * The strongest and only alignment constraint is 8 words to simplify + * memraise8 assembly code. + */ + . = ( STM32MP1_BL32_BASE - STM32MP1_BINARY_BASE ); + __BL32_IMAGE_START__ = .; + *(.bl32_image*) + __BL32_IMAGE_END__ = .; + + __DATA_END__ = .; + } >RAM + + __TF_END__ = .; + +} +#endif /*__STM32MP1_LD_S__*/ diff --git a/plat/st/stm32mp1/stm32mp1_common.c b/plat/st/stm32mp1/stm32mp1_common.c new file mode 100644 index 0000000..68ca7db --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_common.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAP_SRAM MAP_REGION_FLAT(STM32MP1_SRAM_BASE, \ + STM32MP1_SRAM_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + +#define MAP_DEVICE1 MAP_REGION_FLAT(STM32MP1_DEVICE1_BASE, \ + STM32MP1_DEVICE1_SIZE, \ + MT_DEVICE | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + +#define MAP_DEVICE2 MAP_REGION_FLAT(STM32MP1_DEVICE2_BASE, \ + STM32MP1_DEVICE2_SIZE, \ + MT_DEVICE | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + +#define MAP_DDR MAP_REGION_FLAT(STM32MP1_DDR_BASE, \ + STM32MP1_DDR_MAX_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_SECURE | \ + MT_EXECUTE_NEVER) + +#define MAP_DDR_NS MAP_REGION_FLAT(STM32MP1_DDR_BASE, \ + STM32MP1_DDR_MAX_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_NS | \ + MT_EXECUTE_NEVER) + +#if defined(IMAGE_BL2) +static const mmap_region_t stm32mp1_mmap[] = { + MAP_SRAM, + MAP_DEVICE1, + MAP_DEVICE2, + MAP_DDR, + {0} +}; +#endif +#if defined(IMAGE_BL32) +static const mmap_region_t stm32mp1_mmap[] = { + MAP_SRAM, + MAP_DEVICE1, + MAP_DEVICE2, + MAP_DDR_NS, + {0} +}; +#endif + +void configure_mmu(void) +{ + mmap_add(stm32mp1_mmap); + init_xlat_tables(); + + enable_mmu_secure(0); +} + +uintptr_t plat_get_ns_image_entrypoint(void) +{ + return BL33_BASE; +} + +unsigned int plat_get_syscnt_freq2(void) +{ + return read_cntfrq_el0(); +} + +/* Functions to save and get boot context address given by ROM code */ +static uintptr_t boot_ctx_address; + +void stm32mp1_save_boot_ctx_address(uintptr_t address) +{ + boot_ctx_address = address; +} + +uintptr_t stm32mp1_get_boot_ctx_address(void) +{ + return boot_ctx_address; +} diff --git a/plat/st/stm32mp1/stm32mp1_context.c b/plat/st/stm32mp1/stm32mp1_context.c new file mode 100644 index 0000000..245fd17 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_context.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 + +int stm32_save_boot_interface(uint32_t interface, uint32_t instance) +{ + uint32_t tamp_clk_off = 0; + uint32_t bkpr_itf_idx = tamp_bkpr(TAMP_BOOT_ITF_BACKUP_REG_ID); + + if (!stm32mp1_clk_is_enabled(RTCAPB)) { + tamp_clk_off = 1; + if (stm32mp1_clk_enable(RTCAPB) != 0) { + return -EINVAL; + } + } + + mmio_clrsetbits_32(bkpr_itf_idx, + TAMP_BOOT_ITF_MASK, + ((interface << 4) | (instance & 0xFU)) << + TAMP_BOOT_ITF_SHIFT); + + if (tamp_clk_off != 0U) { + if (stm32mp1_clk_disable(RTCAPB) != 0) { + return -EINVAL; + } + } + + return 0; +} diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h new file mode 100644 index 0000000..bb3fecf --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_DEF_H +#define STM32MP1_DEF_H + +#include +#include +#include + +/******************************************************************************* + * STM32MP1 memory map related constants + ******************************************************************************/ + +#define STM32MP1_SRAM_BASE U(0x2FFC0000) +#define STM32MP1_SRAM_SIZE U(0x00040000) + +/* DDR configuration */ +#define STM32MP1_DDR_BASE U(0xC0000000) +#define STM32MP1_DDR_SIZE_DFLT U(0x20000000) /* 512 MB */ +#define STM32MP1_DDR_MAX_SIZE U(0x40000000) /* Max 1GB */ +#define STM32MP1_DDR_SPEED_DFLT 528 + +/* DDR power initializations */ +#ifndef __ASSEMBLY__ +enum ddr_type { + STM32MP_DDR3, + STM32MP_LPDDR2, +}; +#endif + +/* Section used inside TF binaries */ +#define STM32MP1_PARAM_LOAD_SIZE U(0x00002400) /* 9 Ko for param */ +/* 256 Octets reserved for header */ +#define STM32MP1_HEADER_SIZE U(0x00000100) + +#define STM32MP1_BINARY_BASE (STM32MP1_SRAM_BASE + \ + STM32MP1_PARAM_LOAD_SIZE + \ + STM32MP1_HEADER_SIZE) + +#define STM32MP1_BINARY_SIZE (STM32MP1_SRAM_SIZE - \ + (STM32MP1_PARAM_LOAD_SIZE + \ + STM32MP1_HEADER_SIZE)) + +#if STACK_PROTECTOR_ENABLED +#define STM32MP1_BL32_SIZE U(0x00012000) /* 72 Ko for BL32 */ +#else +#define STM32MP1_BL32_SIZE U(0x00011000) /* 68 Ko for BL32 */ +#endif + +#define STM32MP1_BL32_BASE (STM32MP1_SRAM_BASE + \ + STM32MP1_SRAM_SIZE - \ + STM32MP1_BL32_SIZE) + +#if STACK_PROTECTOR_ENABLED +#define STM32MP1_BL2_SIZE U(0x00015000) /* 84 Ko for BL2 */ +#else +#define STM32MP1_BL2_SIZE U(0x00013000) /* 76 Ko for BL2 */ +#endif + +#define STM32MP1_BL2_BASE (STM32MP1_BL32_BASE - \ + STM32MP1_BL2_SIZE) + +/* BL2 and BL32/sp_min require 5 tables */ +#define MAX_XLAT_TABLES 5 + +/* + * MAX_MMAP_REGIONS is usually: + * BL stm32mp1_mmap size + mmap regions in *_plat_arch_setup + */ +#if defined(IMAGE_BL2) + #define MAX_MMAP_REGIONS 11 +#endif +#if defined(IMAGE_BL32) + #define MAX_MMAP_REGIONS 6 +#endif + +/* DTB initialization value */ +#define STM32MP1_DTB_SIZE U(0x00004000) /* 16Ko for DTB */ + +#define STM32MP1_DTB_BASE (STM32MP1_BL2_BASE - \ + STM32MP1_DTB_SIZE) + +#define STM32MP1_BL33_BASE (STM32MP1_DDR_BASE + U(0x100000)) + +/******************************************************************************* + * STM32MP1 device/io map related constants (used for MMU) + ******************************************************************************/ +#define STM32MP1_DEVICE1_BASE U(0x40000000) +#define STM32MP1_DEVICE1_SIZE U(0x40000000) + +#define STM32MP1_DEVICE2_BASE U(0x80000000) +#define STM32MP1_DEVICE2_SIZE U(0x40000000) + +/******************************************************************************* + * STM32MP1 RCC + ******************************************************************************/ +#define RCC_BASE U(0x50000000) + +/******************************************************************************* + * STM32MP1 PWR + ******************************************************************************/ +#define PWR_BASE U(0x50001000) + +/******************************************************************************* + * STM32MP1 UART + ******************************************************************************/ +#define USART1_BASE U(0x5C000000) +#define USART2_BASE U(0x4000E000) +#define USART3_BASE U(0x4000F000) +#define UART4_BASE U(0x40010000) +#define UART5_BASE U(0x40011000) +#define USART6_BASE U(0x44003000) +#define UART7_BASE U(0x40018000) +#define UART8_BASE U(0x40019000) +#define STM32MP1_DEBUG_USART_BASE UART4_BASE +#define STM32MP1_UART_BAUDRATE 115200 + +/******************************************************************************* + * STM32MP1 GIC-400 + ******************************************************************************/ +#define STM32MP1_GICD_BASE U(0xA0021000) +#define STM32MP1_GICC_BASE U(0xA0022000) +#define STM32MP1_GICH_BASE U(0xA0024000) +#define STM32MP1_GICV_BASE U(0xA0026000) + +/******************************************************************************* + * STM32MP1 TZC (TZ400) + ******************************************************************************/ +#define STM32MP1_TZC_BASE U(0x5C006000) + +#define STM32MP1_TZC_A7_ID U(0) +#define STM32MP1_TZC_LCD_ID U(3) +#define STM32MP1_TZC_GPU_ID U(4) +#define STM32MP1_TZC_MDMA_ID U(5) +#define STM32MP1_TZC_DMA_ID U(6) +#define STM32MP1_TZC_USB_HOST_ID U(7) +#define STM32MP1_TZC_USB_OTG_ID U(8) +#define STM32MP1_TZC_SDMMC_ID U(9) +#define STM32MP1_TZC_ETH_ID U(10) +#define STM32MP1_TZC_DAP_ID U(15) + +#define STM32MP1_MEMORY_NS 0 +#define STM32MP1_MEMORY_SECURE 1 + +#define STM32MP1_FILTER_BIT_ALL 3 + +/******************************************************************************* + * STM32MP1 SDMMC + ******************************************************************************/ +#define STM32MP1_SDMMC1_BASE U(0x58005000) +#define STM32MP1_SDMMC2_BASE U(0x58007000) +#define STM32MP1_SDMMC3_BASE U(0x48004000) + +#define STM32MP1_SD_INIT_FREQ 400000 /*400 KHz*/ +#define STM32MP1_SD_NORMAL_SPEED_MAX_FREQ 25000000 /*25 MHz*/ +#define STM32MP1_SD_HIGH_SPEED_MAX_FREQ 50000000 /*50 MHz*/ +#define STM32MP1_EMMC_INIT_FREQ STM32MP1_SD_INIT_FREQ +#define STM32MP1_EMMC_NORMAL_SPEED_MAX_FREQ 26000000 /*26 MHz*/ +#define STM32MP1_EMMC_HIGH_SPEED_MAX_FREQ 52000000 /*52 MHz*/ + +/******************************************************************************* + * STM32MP1 TAMP + ******************************************************************************/ +#define TAMP_BASE U(0x5C00A000) +#define TAMP_BKP_REGISTER_BASE (TAMP_BASE + U(0x100)) + +#if !(defined(__LINKER__) || defined(__ASSEMBLY__)) +static inline uint32_t tamp_bkpr(uint32_t idx) +{ + return TAMP_BKP_REGISTER_BASE + (idx << 2); +} +#endif + +/******************************************************************************* + * STM32MP1 DDRCTRL + ******************************************************************************/ +#define DDRCTRL_BASE U(0x5A003000) + +/******************************************************************************* + * STM32MP1 DDRPHYC + ******************************************************************************/ +#define DDRPHYC_BASE U(0x5A004000) + +/******************************************************************************* + * STM32MP1 I2C4 + ******************************************************************************/ +#define I2C4_BASE U(0x5C002000) + +#endif /* STM32MP1_DEF_H */ diff --git a/plat/st/stm32mp1/stm32mp1_dt.c b/plat/st/stm32mp1/stm32mp1_dt.c new file mode 100644 index 0000000..bde968a --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_dt.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DT_GPIO_BANK_SHIFT 12 +#define DT_GPIO_BANK_MASK 0x1F000U +#define DT_GPIO_PIN_SHIFT 8 +#define DT_GPIO_PIN_MASK 0xF00U +#define DT_GPIO_MODE_MASK 0xFFU + +static int fdt_checked; + +static void *fdt = (void *)(uintptr_t)STM32MP1_DTB_BASE; + +/******************************************************************************* + * This function gets the pin settings from DT information. + * When analyze and parsing is done, set the GPIO registers. + * Return 0 on success, else return a negative FDT_ERR_xxx error code. + ******************************************************************************/ +static int dt_set_gpio_config(int node) +{ + const fdt32_t *cuint, *slewrate; + int len, pinctrl_node, pinctrl_subnode; + uint32_t i; + uint32_t speed = GPIO_SPEED_LOW; + uint32_t pull = GPIO_NO_PULL; + + cuint = fdt_getprop(fdt, node, "pinmux", &len); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + pinctrl_node = fdt_parent_offset(fdt, fdt_parent_offset(fdt, node)); + if (pinctrl_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + slewrate = fdt_getprop(fdt, node, "slew-rate", NULL); + if (slewrate != NULL) { + speed = fdt32_to_cpu(*slewrate); + } + + if (fdt_getprop(fdt, node, "bias-pull-up", NULL) != NULL) { + pull = GPIO_PULL_UP; + } else if (fdt_getprop(fdt, node, "bias-pull-down", NULL) != NULL) { + pull = GPIO_PULL_DOWN; + } else { + VERBOSE("No bias configured in node %d\n", node); + } + + for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { + uint32_t pincfg; + uint32_t bank; + uint32_t pin; + uint32_t mode; + uint32_t alternate = GPIO_ALTERNATE_0; + + pincfg = fdt32_to_cpu(*cuint); + cuint++; + + bank = (pincfg & DT_GPIO_BANK_MASK) >> DT_GPIO_BANK_SHIFT; + + pin = (pincfg & DT_GPIO_PIN_MASK) >> DT_GPIO_PIN_SHIFT; + + mode = pincfg & DT_GPIO_MODE_MASK; + + switch (mode) { + case 0: + mode = GPIO_MODE_INPUT; + break; + case 1 ... 16: + alternate = mode - 1U; + mode = GPIO_MODE_ALTERNATE; + break; + case 17: + mode = GPIO_MODE_ANALOG; + break; + default: + mode = GPIO_MODE_OUTPUT; + break; + } + + if (fdt_getprop(fdt, node, "drive-open-drain", NULL) != NULL) { + mode |= GPIO_OPEN_DRAIN; + } + + fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) { + uint32_t bank_offset; + const fdt32_t *cuint2; + + if (fdt_getprop(fdt, pinctrl_subnode, + "gpio-controller", NULL) == NULL) { + continue; + } + + cuint2 = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL); + if (cuint2 == NULL) { + continue; + } + + if (bank == GPIO_BANK_Z) { + bank_offset = 0; + } else { + bank_offset = bank * STM32_GPIO_BANK_OFFSET; + } + + if (fdt32_to_cpu(*cuint2) == bank_offset) { + int clk_id = fdt_get_clock_id(pinctrl_subnode); + + if (clk_id < 0) { + return -FDT_ERR_NOTFOUND; + } + + if (stm32mp1_clk_enable((unsigned long)clk_id) < + 0) { + return -FDT_ERR_BADVALUE; + } + + break; + } + } + + set_gpio(bank, pin, mode, speed, pull, alternate); + } + + return 0; +} + +/******************************************************************************* + * This function checks device tree file with its header. + * Returns 0 if success, and a negative value else. + ******************************************************************************/ +int dt_open_and_check(void) +{ + int ret = fdt_check_header(fdt); + + if (ret == 0) { + fdt_checked = 1; + } + + return ret; +} + +/******************************************************************************* + * This function gets the address of the DT. + * If DT is OK, fdt_addr is filled with DT address. + * Returns 1 if success, 0 otherwise. + ******************************************************************************/ +int fdt_get_address(void **fdt_addr) +{ + if (fdt_checked == 1) { + *fdt_addr = fdt; + } + + return fdt_checked; +} + +/******************************************************************************* + * This function check the presence of a node (generic use of fdt library). + * Returns true if present, false else. + ******************************************************************************/ +bool fdt_check_node(int node) +{ + int len; + const char *cchar; + + cchar = fdt_get_name(fdt, node, &len); + + return (cchar != NULL) && (len >= 0); +} + +/******************************************************************************* + * This function check the status of a node (generic use of fdt library). + * Returns true if "okay" or missing, false else. + ******************************************************************************/ +bool fdt_check_status(int node) +{ + int len; + const char *cchar; + + cchar = fdt_getprop(fdt, node, "status", &len); + if (cchar == NULL) { + return true; + } + + return strncmp(cchar, "okay", (size_t)len) == 0; +} + +/******************************************************************************* + * This function check the secure-status of a node (generic use of fdt library). + * Returns true if "okay" or missing, false else. + ******************************************************************************/ +bool fdt_check_secure_status(int node) +{ + int len; + const char *cchar; + + cchar = fdt_getprop(fdt, node, "secure-status", &len); + if (cchar == NULL) { + return true; + } + + return strncmp(cchar, "okay", (size_t)len) == 0; +} + +/******************************************************************************* + * This function reads a value of a node property (generic use of fdt + * library). + * Returns value if success, and a default value if property not found. + * Default value is passed as parameter. + ******************************************************************************/ +uint32_t fdt_read_uint32_default(int node, const char *prop_name, + uint32_t dflt_value) +{ + const fdt32_t *cuint; + int lenp; + + cuint = fdt_getprop(fdt, node, prop_name, &lenp); + if (cuint == NULL) { + return dflt_value; + } + + return fdt32_to_cpu(*cuint); +} + +/******************************************************************************* + * This function reads a series of parameters in a node property + * (generic use of fdt library). + * It reads the values inside the device tree, from property name and node. + * The number of parameters is also indicated as entry parameter. + * Returns 0 if success, and a negative value else. + * If success, values are stored at the third parameter address. + ******************************************************************************/ +int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, + uint32_t count) +{ + const fdt32_t *cuint; + int len; + uint32_t i; + + cuint = fdt_getprop(fdt, node, prop_name, &len); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + if ((uint32_t)len != (count * sizeof(uint32_t))) { + return -FDT_ERR_BADLAYOUT; + } + + for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { + *array = fdt32_to_cpu(*cuint); + array++; + cuint++; + } + + return 0; +} + +/******************************************************************************* + * This function gets the pin settings from DT information. + * When analyze and parsing is done, set the GPIO registers. + * Returns 0 if success, and a negative value else. + ******************************************************************************/ +int dt_set_pinctrl_config(int node) +{ + const fdt32_t *cuint; + int lenp = 0; + uint32_t i; + + if (!fdt_check_status(node)) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, node, "pinctrl-0", &lenp); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + for (i = 0; i < ((uint32_t)lenp / 4U); i++) { + int phandle_node, phandle_subnode; + + phandle_node = + fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (phandle_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_subnode(phandle_subnode, fdt, phandle_node) { + int ret = dt_set_gpio_config(phandle_subnode); + + if (ret < 0) { + return ret; + } + } + + cuint++; + } + + return 0; +} + +/******************************************************************************* + * This function gets the stdout pin configuration information from the DT. + * And then calls the sub-function to treat it and set GPIO registers. + * Returns 0 if success, and a negative value else. + ******************************************************************************/ +int dt_set_stdout_pinctrl(void) +{ + int node; + + node = dt_get_stdout_node_offset(); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + return dt_set_pinctrl_config(node); +} + +/******************************************************************************* + * This function fills the generic information from a given node. + ******************************************************************************/ +void dt_fill_device_info(struct dt_node_info *info, int node) +{ + const fdt32_t *cuint; + + cuint = fdt_getprop(fdt, node, "reg", NULL); + if (cuint != NULL) { + info->base = fdt32_to_cpu(*cuint); + } else { + info->base = 0; + } + + cuint = fdt_getprop(fdt, node, "clocks", NULL); + if (cuint != NULL) { + cuint++; + info->clock = (int)fdt32_to_cpu(*cuint); + } else { + info->clock = -1; + } + + cuint = fdt_getprop(fdt, node, "resets", NULL); + if (cuint != NULL) { + cuint++; + info->reset = (int)fdt32_to_cpu(*cuint); + } else { + info->reset = -1; + } + + info->status = fdt_check_status(node); + info->sec_status = fdt_check_secure_status(node); +} + +/******************************************************************************* + * This function retrieve the generic information from DT. + * Returns node if success, and a negative value else. + ******************************************************************************/ +int dt_get_node(struct dt_node_info *info, int offset, const char *compat) +{ + int node; + + node = fdt_node_offset_by_compatible(fdt, offset, compat); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + dt_fill_device_info(info, node); + + return node; +} + +/******************************************************************************* + * This function gets the UART instance info of stdout from the DT. + * Returns node if success, and a negative value else. + ******************************************************************************/ +int dt_get_stdout_uart_info(struct dt_node_info *info) +{ + int node; + + node = dt_get_stdout_node_offset(); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + dt_fill_device_info(info, node); + + return node; +} + +/******************************************************************************* + * This function gets the stdout path node. + * It reads the value indicated inside the device tree. + * Returns node if success, and a negative value else. + ******************************************************************************/ +int dt_get_stdout_node_offset(void) +{ + int node; + const char *cchar; + + node = fdt_path_offset(fdt, "/chosen"); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + cchar = fdt_getprop(fdt, node, "stdout-path", NULL); + if (cchar == NULL) { + return -FDT_ERR_NOTFOUND; + } + + node = -FDT_ERR_NOTFOUND; + if (strchr(cchar, (int)':') != NULL) { + const char *name; + char *str = (char *)cchar; + int len = 0; + + while (strncmp(":", str, 1)) { + len++; + str++; + } + + name = fdt_get_alias_namelen(fdt, cchar, len); + + if (name != NULL) { + node = fdt_path_offset(fdt, name); + } + } else { + node = fdt_path_offset(fdt, cchar); + } + + return node; +} + +/******************************************************************************* + * This function gets DDR size information from the DT. + * Returns value in bytes if success, and STM32MP1_DDR_SIZE_DFLT else. + ******************************************************************************/ +uint32_t dt_get_ddr_size(void) +{ + int node; + + 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 STM32MP1_DDR_SIZE_DFLT; + } + + return fdt_read_uint32_default(node, "st,mem-size", + STM32MP1_DDR_SIZE_DFLT); +} + +/******************************************************************************* + * This function retrieves board model from DT + * Returns string taken from model node, NULL otherwise + ******************************************************************************/ +const char *dt_get_board_model(void) +{ + int node = fdt_path_offset(fdt, "/"); + + if (node < 0) { + return NULL; + } + + return (const char *)fdt_getprop(fdt, node, "model", NULL); +} diff --git a/plat/st/stm32mp1/stm32mp1_gic.c b/plat/st/stm32mp1/stm32mp1_gic.c new file mode 100644 index 0000000..11eb0a3 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_gic.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include + +/****************************************************************************** + * 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) +}; + +static unsigned int target_mask_array[PLATFORM_CORE_COUNT]; + +static const gicv2_driver_data_t platform_gic_data = { + .gicd_base = STM32MP1_GICD_BASE, + .gicc_base = STM32MP1_GICC_BASE, + .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), +}; + +void stm32mp1_gic_init(void) +{ + 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 new file mode 100644 index 0000000..b0ea0d8 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_helper.S @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +#define GPIO_BANK_G_ADDRESS 0x50008000 +#define GPIO_TX_PORT 11 +#define GPIO_TX_SHIFT (GPIO_TX_PORT << 1) +#define GPIO_TX_ALT_SHIFT ((GPIO_TX_PORT - GPIO_ALT_LOWER_LIMIT) << 2) +#define STM32MP1_HSI_CLK 64000000 + + .globl platform_mem_init + .globl plat_report_exception + .globl plat_get_my_entrypoint + .globl plat_secondary_cold_boot_setup + .globl plat_reset_handler + .globl plat_is_my_cpu_primary + .globl plat_my_core_pos + .globl plat_crash_console_init + .globl plat_crash_console_flush + .globl plat_crash_console_putc + .globl plat_panic_handler + +func platform_mem_init + /* Nothing to do, don't need to init SYSRAM */ + bx lr +endfunc platform_mem_init + +func plat_report_exception + bx lr +endfunc plat_report_exception + +func plat_reset_handler + bx lr +endfunc plat_reset_handler + + /* ------------------------------------------------------------------ + * unsigned long plat_get_my_entrypoint (void); + * + * Main job of this routine is to distinguish between a cold and warm + * boot. + * + * Currently supports only cold boot + * ------------------------------------------------------------------ + */ +func plat_get_my_entrypoint + mov r0, #0 + bx lr +endfunc plat_get_my_entrypoint + + /* --------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * + * Cold-booting secondary CPUs is not supported. + * --------------------------------------------- + */ +func plat_secondary_cold_boot_setup + b . +endfunc plat_secondary_cold_boot_setup + + /* ----------------------------------------------------- + * unsigned int plat_is_my_cpu_primary (void); + * + * Find out whether the current cpu is the primary cpu. + * ----------------------------------------------------- + */ +func plat_is_my_cpu_primary + ldcopr r0, MPIDR + ldr r1, =(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) + and r0, r1 + cmp r0, #STM32MP1_PRIMARY_CPU + moveq r0, #1 + movne r0, #0 + bx lr +endfunc plat_is_my_cpu_primary + + /* ------------------------------------------- + * int plat_stm32mp1_get_core_pos(int mpidr); + * + * Return CorePos = (ClusterId * 4) + CoreId + * ------------------------------------------- + */ +func plat_stm32mp1_get_core_pos + and r1, r0, #MPIDR_CPU_MASK + and r0, r0, #MPIDR_CLUSTER_MASK + add r0, r1, r0, LSR #6 + bx lr +endfunc plat_stm32mp1_get_core_pos + + /* ------------------------------------ + * unsigned int plat_my_core_pos(void) + * ------------------------------------ + */ +func plat_my_core_pos + ldcopr r0, MPIDR + b plat_stm32mp1_get_core_pos +endfunc plat_my_core_pos + + /* --------------------------------------------- + * int plat_crash_console_init(void) + * + * Initialize the crash console without a C Runtime stack. + * --------------------------------------------- + */ +func plat_crash_console_init + /* Enable GPIOs for UART4 TX */ + ldr r1, =(RCC_BASE + RCC_MP_AHB4ENSETR) + ldr r2, [r1] + /* Configure GPIO G11 */ + orr r2, r2, #RCC_MP_AHB4ENSETR_GPIOGEN + str r2, [r1] + ldr r1, =GPIO_BANK_G_ADDRESS + /* Set GPIO mode alternate */ + ldr r2, [r1, #GPIO_MODE_OFFSET] + bic r2, r2, #(GPIO_MODE_MASK << GPIO_TX_SHIFT) + orr r2, r2, #(GPIO_MODE_ALTERNATE << GPIO_TX_SHIFT) + str r2, [r1, #GPIO_MODE_OFFSET] + /* Set GPIO speed low */ + ldr r2, [r1, #GPIO_SPEED_OFFSET] + bic r2, r2, #(GPIO_SPEED_MASK << GPIO_TX_SHIFT) + str r2, [r1, #GPIO_SPEED_OFFSET] + /* Set no-pull */ + ldr r2, [r1, #GPIO_PUPD_OFFSET] + bic r2, r2, #(GPIO_PULL_MASK << GPIO_TX_SHIFT) + str r2, [r1, #GPIO_PUPD_OFFSET] + /* Set alternate AF6 */ + ldr r2, [r1, #GPIO_AFRH_OFFSET] + bic r2, r2, #(GPIO_ALTERNATE_MASK << GPIO_TX_ALT_SHIFT) + orr r2, r2, #(GPIO_ALTERNATE_6 << GPIO_TX_ALT_SHIFT) + str r2, [r1, #GPIO_AFRH_OFFSET] + + /* Enable UART clock, with HSI source */ + ldr r1, =(RCC_BASE + RCC_UART24CKSELR) + mov r2, #RCC_UART24CKSELR_HSI + str r2, [r1] + ldr r1, =(RCC_BASE + RCC_MP_APB1ENSETR) + ldr r2, [r1] + orr r2, r2, #RCC_MP_APB1ENSETR_UART4EN + str r2, [r1] + + ldr r0, =STM32MP1_DEBUG_USART_BASE + ldr r1, =STM32MP1_HSI_CLK + ldr r2, =STM32MP1_UART_BAUDRATE + b console_core_init +endfunc plat_crash_console_init + + /* --------------------------------------------- + * int plat_crash_console_flush(void) + * + * Flush the crash console without a C Runtime stack. + * --------------------------------------------- + */ +func plat_crash_console_flush + ldr r1, =STM32MP1_DEBUG_USART_BASE + b console_core_flush +endfunc plat_crash_console_flush + + /* --------------------------------------------- + * int plat_crash_console_putc(int c) + * + * Print a character on the crash console without a C Runtime stack. + * Clobber list : r1 - r3 + * + * In case of bootloading through uart, we keep console crash as this. + * Characters could be sent to the programmer, but will be ignored. + * No specific code in that case. + * --------------------------------------------- + */ +func plat_crash_console_putc + ldr r1, =STM32MP1_DEBUG_USART_BASE + b console_core_putc +endfunc plat_crash_console_putc diff --git a/plat/st/stm32mp1/stm32mp1_pm.c b/plat/st/stm32mp1/stm32mp1_pm.c new file mode 100644 index 0000000..e24af0e --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_pm.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint32_t stm32_sec_entrypoint; +static uint32_t cntfrq_core0; + +#define SEND_SECURE_IT_TO_CORE_1 0x20000U + +/******************************************************************************* + * STM32MP1 handler called when a CPU is about to enter standby. + * call by core 1 to enter in wfi + ******************************************************************************/ +static void stm32_cpu_standby(plat_local_state_t cpu_state) +{ + uint32_t interrupt = GIC_SPURIOUS_INTERRUPT; + + assert(cpu_state == ARM_LOCAL_STATE_RET); + + /* + * Enter standby state + * dsb is good practice before using wfi to enter low power states + */ + dsb(); + while (interrupt == GIC_SPURIOUS_INTERRUPT) { + wfi(); + + /* Acknoledge IT */ + interrupt = gicv2_acknowledge_interrupt(); + /* If Interrupt == 1022 it will be acknowledged by non secure */ + if ((interrupt != PENDING_G1_INTID) && + (interrupt != GIC_SPURIOUS_INTERRUPT)) { + gicv2_end_of_interrupt(interrupt); + } + } +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain is about to be turned on. The + * mpidr determines the CPU to be turned on. + * call by core 0 to activate core 1 + ******************************************************************************/ +static int stm32_pwr_domain_on(u_register_t mpidr) +{ + unsigned long current_cpu_mpidr = read_mpidr_el1(); + uint32_t tamp_clk_off = 0; + 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); + + if (mpidr == current_cpu_mpidr) { + return PSCI_E_INVALID_PARAMS; + } + + if ((stm32_sec_entrypoint < STM32MP1_SRAM_BASE) || + (stm32_sec_entrypoint > (STM32MP1_SRAM_BASE + + (STM32MP1_SRAM_SIZE - 1)))) { + return PSCI_E_INVALID_ADDRESS; + } + + if (!stm32mp1_clk_is_enabled(RTCAPB)) { + tamp_clk_off = 1; + if (stm32mp1_clk_enable(RTCAPB) != 0) { + panic(); + } + } + + cntfrq_core0 = read_cntfrq_el0(); + + /* Write entrypoint in backup RAM register */ + mmio_write_32(bkpr_core1_addr, stm32_sec_entrypoint); + + /* Write magic number in backup register */ + mmio_write_32(bkpr_core1_magic, BOOT_API_A7_CORE1_MAGIC_NUMBER); + + if (tamp_clk_off != 0U) { + if (stm32mp1_clk_disable(RTCAPB) != 0) { + panic(); + } + } + + /* Generate an IT to core 1 */ + mmio_write_32(STM32MP1_GICD_BASE + GICD_SGIR, + SEND_SECURE_IT_TO_CORE_1 | ARM_IRQ_SEC_SGI_0); + + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. + ******************************************************************************/ +static void stm32_pwr_domain_off(const psci_power_state_t *target_state) +{ + /* Nothing to do */ +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. + ******************************************************************************/ +static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + /* Nothing to do, power domain is not disabled */ +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. + * call by core 1 just after wake up + ******************************************************************************/ +static void stm32_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + stm32mp1_gic_pcpu_init(); + + write_cntfrq_el0(cntfrq_core0); +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. + ******************************************************************************/ +static void stm32_pwr_domain_suspend_finish(const psci_power_state_t + *target_state) +{ + /* Nothing to do, power domain is not disabled */ +} + +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"); + panic(); +} + +static void __dead2 stm32_system_off(void) +{ + ERROR("stm32mpu1 System Off: operation not handled.\n"); + panic(); +} + +static void __dead2 stm32_system_reset(void) +{ + mmio_setbits_32(RCC_BASE + RCC_MP_GRSTCSETR, RCC_MP_GRSTCSETR_MPSYSRST); + + /* Loop in case system reset is not immediately caught */ + for ( ; ; ) { + ; + } +} + +static int stm32_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int pstate = psci_get_pstate_type(power_state); + + if (pstate != 0) { + return PSCI_E_INVALID_PARAMS; + } + + if (psci_get_pstate_pwrlvl(power_state)) { + return PSCI_E_INVALID_PARAMS; + } + + if (psci_get_pstate_id(power_state)) { + return PSCI_E_INVALID_PARAMS; + } + + req_state->pwr_domain_state[0] = ARM_LOCAL_STATE_RET; + req_state->pwr_domain_state[1] = ARM_LOCAL_STATE_RUN; + + return PSCI_E_SUCCESS; +} + +static int stm32_validate_ns_entrypoint(uintptr_t entrypoint) +{ + /* The non-secure entry point must be in DDR */ + if (entrypoint < STM32MP1_DDR_BASE) { + return PSCI_E_INVALID_ADDRESS; + } + + return PSCI_E_SUCCESS; +} + +static int stm32_node_hw_state(u_register_t target_cpu, + unsigned int power_level) +{ + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. Only allow level 0. + */ + if (power_level != MPIDR_AFFLVL0) { + return PSCI_E_INVALID_PARAMS; + } + + /* + * From psci view the CPU 0 is always ON, + * CPU 1 can be SUSPEND or RUNNING. + * Therefore do not manage POWER OFF state and always return HW_ON. + */ + + return (int)HW_ON; +} + +/******************************************************************************* + * Export the platform handlers. The ARM Standard platform layer will take care + * of registering the handlers with PSCI. + ******************************************************************************/ +static const plat_psci_ops_t stm32_psci_ops = { + .cpu_standby = stm32_cpu_standby, + .pwr_domain_on = stm32_pwr_domain_on, + .pwr_domain_off = stm32_pwr_domain_off, + .pwr_domain_suspend = stm32_pwr_domain_suspend, + .pwr_domain_on_finish = stm32_pwr_domain_on_finish, + .pwr_domain_suspend_finish = stm32_pwr_domain_suspend_finish, + .pwr_domain_pwr_down_wfi = stm32_pwr_domain_pwr_down_wfi, + .system_off = stm32_system_off, + .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 +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + stm32_sec_entrypoint = sec_entrypoint; + *psci_ops = &stm32_psci_ops; + + return 0; +} diff --git a/plat/st/stm32mp1/stm32mp1_security.c b/plat/st/stm32mp1/stm32mp1_security.c new file mode 100644 index 0000000..e783c14 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_security.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_def.h" + +/******************************************************************************* + * Initialize the TrustZone Controller. Configure Region 0 with Secure RW access + * and allow Non-Secure masters full access. + ******************************************************************************/ +static void init_tzc400(void) +{ + unsigned long long region_base, region_top; + unsigned long long ddr_base = STM32MP1_DDR_BASE; + unsigned long long ddr_size = (unsigned long long)dt_get_ddr_size(); + + tzc400_init(STM32MP1_TZC_BASE); + + tzc400_disable_filters(); + + /* Region 1 set to cover all DRAM at 0xC000_0000. Apply the + * same configuration to all filters in the TZC. + */ + region_base = ddr_base; + region_top = ddr_base + (ddr_size - 1U); + tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 1, + region_base, + region_top, + TZC_REGION_S_RDWR, + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_GPU_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_LCD_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_MDMA_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DMA_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_HOST_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_OTG_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_ETH_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DAP_ID)); + + /* Raise an exception if a NS device tries to access secure memory */ + tzc400_set_action(TZC_ACTION_ERR); + + tzc400_enable_filters(); +} + +/******************************************************************************* + * Initialize the TrustZone Controller. + * Early initialization create only one region with full access to secure. + * This setting is used before and during DDR initialization. + ******************************************************************************/ +static void early_init_tzc400(void) +{ + uint32_t rstsr, rst_standby; + + rstsr = mmio_read_32(RCC_BASE + RCC_MP_RSTSCLRR); + + /* No warning if return from (C)STANDBY */ + rst_standby = rstsr & + (RCC_MP_RSTSCLRR_STDBYRSTF | RCC_MP_RSTSCLRR_CSTDBYRSTF); + + if (stm32mp1_clk_is_enabled(TZC1) && (rst_standby == 0U)) { + WARN("TZC400 port 1 clock already enable\n"); + } + + if (stm32mp1_clk_is_enabled(TZC2) && (rst_standby == 0U)) { + WARN("TZC400 port 2 clock already enable\n"); + } + + if (stm32mp1_clk_enable(TZC1) != 0) { + ERROR("Cannot enable TZC1 clock\n"); + panic(); + } + if (stm32mp1_clk_enable(TZC2) != 0) { + ERROR("Cannot enable TZC2 clock\n"); + panic(); + } + + tzc400_init(STM32MP1_TZC_BASE); + + tzc400_disable_filters(); + + /* + * Region 1 set to cover Non-Secure DRAM at 0x8000_0000. Apply the + * same configuration to all filters in the TZC. + */ + tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 1, + STM32MP1_DDR_BASE, + STM32MP1_DDR_BASE + + (STM32MP1_DDR_MAX_SIZE - 1U), + TZC_REGION_S_RDWR, + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID)); + + /* Raise an exception if a NS device tries to access secure memory */ + tzc400_set_action(TZC_ACTION_ERR); + + tzc400_enable_filters(); +} + +/******************************************************************************* + * Initialize the secure environment. At this moment only the TrustZone + * Controller is initialized. + ******************************************************************************/ +void stm32mp1_arch_security_setup(void) +{ + early_init_tzc400(); +} + +/******************************************************************************* + * Initialize the secure environment. At this moment only the TrustZone + * Controller is initialized. + ******************************************************************************/ +void stm32mp1_security_setup(void) +{ + init_tzc400(); +} diff --git a/plat/st/stm32mp1/stm32mp1_stack_protector.c b/plat/st/stm32mp1/stm32mp1_stack_protector.c new file mode 100644 index 0000000..c681300 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_stack_protector.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#define RANDOM_CANARY_VALUE 2144346116U + +u_register_t plat_get_stack_protector_canary(void) +{ + /* + * Ideally, a random number should be returned instead of the + * combination of a timer's value and a compile-time constant. + */ + return RANDOM_CANARY_VALUE ^ (u_register_t)read_cntpct_el0(); +} + diff --git a/plat/st/stm32mp1/stm32mp1_topology.c b/plat/st/stm32mp1/stm32mp1_topology.c new file mode 100644 index 0000000..405aa33 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_topology.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +/* 1 cluster, all cores into */ +static const unsigned char stm32mp1_power_domain_tree_desc[] = { + PLATFORM_CLUSTER_COUNT, + PLATFORM_CORE_COUNT, +}; + +/* This function returns the platform topology */ +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return stm32mp1_power_domain_tree_desc; +} + +/******************************************************************************* + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. + ******************************************************************************/ +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + unsigned int cluster_id, cpu_id; + u_register_t mpidr_copy = mpidr; + + mpidr_copy &= MPIDR_AFFINITY_MASK; + + if ((mpidr_copy & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) != 0U) { + return -1; + } + + cluster_id = (mpidr_copy >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + cpu_id = (mpidr_copy >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; + + if (cluster_id >= PLATFORM_CLUSTER_COUNT) { + return -1; + } + + /* + * Validate cpu_id by checking whether it represents a CPU in one + * of the two clusters present on the platform. + */ + if (cpu_id >= PLATFORM_CORE_COUNT) { + return -1; + } + + return (int)cpu_id; +} diff --git a/tools/stm32image/Makefile b/tools/stm32image/Makefile new file mode 100644 index 0000000..80dfbec --- /dev/null +++ b/tools/stm32image/Makefile @@ -0,0 +1,49 @@ +# +# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}build_env.mk + +PROJECT := stm32image${BIN_EXT} +OBJECTS := stm32image.o +V := 0 + +override CPPFLAGS += -D_GNU_SOURCE +CFLAGS := -Wall -Werror -pedantic -std=c99 +ifeq (${DEBUG},1) + CFLAGS += -g -O0 -DDEBUG +else + CFLAGS += -O2 +endif + +ifeq (${V},0) + Q := @ +else + Q := +endif + +CC := gcc + +.PHONY: all clean distclean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + @echo " LD $@" + ${Q}${CC} ${OBJECTS} -o $@ + @${ECHO_BLANK_LINE} + @echo "Built $@ successfully" + @${ECHO_BLANK_LINE} + +%.o: %.c %.h Makefile + @echo " CC $<" + ${Q}${CC} -c ${CFLAGS} $< -o $@ + +clean: + $(call SHELL_DELETE_ALL, ${PROJECT} ${OBJECTS}) + +distclean: clean diff --git a/tools/stm32image/stm32image.c b/tools/stm32image/stm32image.c new file mode 100644 index 0000000..2607928 --- /dev/null +++ b/tools/stm32image/stm32image.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Magic = 'S' 'T' 'M' 0x32 */ +#define HEADER_MAGIC __be32_to_cpu(0x53544D32) +#define VER_MAJOR 2 +#define VER_MINOR 1 +#define VER_VARIANT 0 +#define HEADER_VERSION_V1 0x1 +#define TF_BINARY_TYPE 0x0 + +/* Default option : bit0 => no signature */ +#define HEADER_DEFAULT_OPTION (__cpu_to_le32(0x00000001)) + +struct stm32_header { + uint32_t magic_number; + uint8_t image_signature[64]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t option_flags; + uint32_t ecdsa_algorithm; + uint8_t ecdsa_public_key[64]; + uint8_t padding[83]; + uint8_t binary_type; +}; + +static struct stm32_header stm32image_header; + +static void stm32image_default_header(struct stm32_header *ptr) +{ + if (!ptr) { + return; + } + + 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->binary_type = TF_BINARY_TYPE; +} + +static uint32_t stm32image_checksum(void *start, uint32_t len) +{ + uint32_t csum = 0; + uint32_t hdr_len = sizeof(struct stm32_header); + uint8_t *p; + + if (len < hdr_len) { + return 0; + } + + p = (unsigned char *)start + hdr_len; + len -= hdr_len; + + while (len > 0) { + csum += *p; + p++; + len--; + } + + return csum; +} + +static void stm32image_print_header(const void *ptr) +{ + struct stm32_header *stm32hdr = (struct stm32_header *)ptr; + + printf("Image Type : ST Microelectronics STM32 V%d.%d\n", + stm32hdr->header_version[VER_MAJOR], + stm32hdr->header_version[VER_MINOR]); + printf("Image Size : %lu bytes\n", + (unsigned long)__le32_to_cpu(stm32hdr->image_length)); + printf("Image Load : 0x%08x\n", + __le32_to_cpu(stm32hdr->load_address)); + printf("Entry Point : 0x%08x\n", + __le32_to_cpu(stm32hdr->image_entry_point)); + printf("Checksum : 0x%08x\n", + __le32_to_cpu(stm32hdr->image_checksum)); + printf("Option : 0x%08x\n", + __le32_to_cpu(stm32hdr->option_flags)); + printf("Version : 0x%08x\n", + __le32_to_cpu(stm32hdr->version_number)); +} + +static void stm32image_set_header(void *ptr, struct stat *sbuf, int ifd, + uint32_t loadaddr, uint32_t ep, uint32_t ver) +{ + struct stm32_header *stm32hdr = (struct stm32_header *)ptr; + + stm32image_default_header(stm32hdr); + + 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->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) +{ + int src_fd, dest_fd; + struct stat sbuf; + unsigned char *ptr; + + dest_fd = open(destname, O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0666); + if (dest_fd == -1) { + fprintf(stderr, "Can't open %s: %s\n", destname, + strerror(errno)); + return -1; + } + + src_fd = open(srcname, O_RDONLY); + if (src_fd == -1) { + fprintf(stderr, "Can't open %s: %s\n", srcname, + strerror(errno)); + return -1; + } + + if (fstat(src_fd, &sbuf) < 0) { + return -1; + } + + ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, src_fd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s\n", srcname); + return -1; + } + + memset(&stm32image_header, 0, sizeof(struct stm32_header)); + + if (write(dest_fd, &stm32image_header, sizeof(struct stm32_header)) != + sizeof(struct stm32_header)) { + fprintf(stderr, "Write error %s: %s\n", destname, + strerror(errno)); + return -1; + } + + if (write(dest_fd, ptr, sbuf.st_size) != sbuf.st_size) { + fprintf(stderr, "Write error on %s: %s\n", destname, + strerror(errno)); + return -1; + } + + munmap((void *)ptr, sbuf.st_size); + close(src_fd); + + if (fstat(dest_fd, &sbuf) < 0) { + return -1; + } + + ptr = mmap(0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + dest_fd, 0); + + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s\n", srcname); + return -1; + } + + stm32image_set_header(ptr, &sbuf, dest_fd, loadaddr, entry, version); + + stm32image_print_header(ptr); + + munmap((void *)ptr, sbuf.st_size); + close(dest_fd); + return 0; +} + +int main(int argc, char *argv[]) +{ + int opt, loadaddr = -1, entry = -1, err = 0, version = 0; + char *dest = NULL, *src = NULL; + + while ((opt = getopt(argc, argv, ":s:d:l:e:v:")) != -1) { + switch (opt) { + case 's': + src = optarg; + break; + case 'd': + dest = optarg; + break; + case 'l': + loadaddr = strtol(optarg, NULL, 16); + break; + case 'e': + entry = strtol(optarg, NULL, 16); + break; + case 'v': + version = strtol(optarg, NULL, 10); + break; + default: + fprintf(stderr, + "Usage : %s [-s srcfile] [-d destfile] [-l loadaddr] [-e entry_point]\n", + argv[0]); + return -1; + } + } + + if (!src) { + fprintf(stderr, "Missing -s option\n"); + return -1; + } + + if (!dest) { + fprintf(stderr, "Missing -d option\n"); + return -1; + } + + if (loadaddr == -1) { + fprintf(stderr, "Missing -l option\n"); + return -1; + } + + if (entry == -1) { + fprintf(stderr, "Missing -e option\n"); + return -1; + } + + err = stm32image_create_header_file(src, dest, loadaddr, + entry, version); + + return err; +}