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/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/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_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/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/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index d386202..6128c5e 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -76,5 +78,17 @@ generic_delay_timer_init(); + if (dt_open_and_check() < 0) { + panic(); + } + + if (stm32mp1_clk_probe() < 0) { + panic(); + } + + if (stm32mp1_clk_init() < 0) { + panic(); + } + stm32mp1_io_setup(); } diff --git a/plat/st/stm32mp1/include/stm32mp1_dt.h b/plat/st/stm32mp1/include/stm32mp1_dt.h new file mode 100644 index 0000000..da203a7 --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_dt.h @@ -0,0 +1,25 @@ +/* + * 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 + +/******************************************************************************* + * 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); + +#endif /* __STM32MP1_DT_H__ */ diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index ff390d5..e26504f 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -39,6 +39,10 @@ PLAT_BL_COMMON_SOURCES += ${LIBFDT_SRCS} \ 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/reset/stm32mp1_reset.c \ + plat/st/stm32mp1/stm32mp1_dt.c \ plat/st/stm32mp1/stm32mp1_helper.S BL2_SOURCES += drivers/io/io_dummy.c \ diff --git a/plat/st/stm32mp1/stm32mp1_dt.c b/plat/st/stm32mp1/stm32mp1_dt.c new file mode 100644 index 0000000..6098759 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_dt.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 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; +}