| /* |
| * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved |
| * |
| * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include <arch.h> |
| #include <arch_helpers.h> |
| #include "clk-stm32-core.h" |
| #include <common/debug.h> |
| #include <common/fdt_wrappers.h> |
| #include <drivers/clk.h> |
| #include <drivers/delay_timer.h> |
| #include <drivers/st/stm32mp13_rcc.h> |
| #include <drivers/st/stm32mp1_clk.h> |
| #include <drivers/st/stm32mp_clkfunc.h> |
| #include <dt-bindings/clock/stm32mp13-clksrc.h> |
| #include <lib/mmio.h> |
| #include <lib/spinlock.h> |
| #include <lib/utils_def.h> |
| #include <libfdt.h> |
| #include <plat/common/platform.h> |
| |
| #include <platform_def.h> |
| |
| struct stm32_osci_dt_cfg { |
| unsigned long freq; |
| bool bypass; |
| bool digbyp; |
| bool css; |
| uint32_t drive; |
| }; |
| |
| enum pll_mn { |
| PLL_CFG_M, |
| PLL_CFG_N, |
| PLL_DIV_MN_NB |
| }; |
| |
| enum pll_pqr { |
| PLL_CFG_P, |
| PLL_CFG_Q, |
| PLL_CFG_R, |
| PLL_DIV_PQR_NB |
| }; |
| |
| enum pll_csg { |
| PLL_CSG_MOD_PER, |
| PLL_CSG_INC_STEP, |
| PLL_CSG_SSCG_MODE, |
| PLL_CSG_NB |
| }; |
| |
| struct stm32_pll_vco { |
| uint32_t status; |
| uint32_t src; |
| uint32_t div_mn[PLL_DIV_MN_NB]; |
| uint32_t frac; |
| bool csg_enabled; |
| uint32_t csg[PLL_CSG_NB]; |
| }; |
| |
| struct stm32_pll_output { |
| uint32_t output[PLL_DIV_PQR_NB]; |
| }; |
| |
| struct stm32_pll_dt_cfg { |
| struct stm32_pll_vco vco; |
| struct stm32_pll_output output; |
| }; |
| |
| struct stm32_clk_platdata { |
| uint32_t nosci; |
| struct stm32_osci_dt_cfg *osci; |
| uint32_t npll; |
| struct stm32_pll_dt_cfg *pll; |
| uint32_t nclksrc; |
| uint32_t *clksrc; |
| uint32_t nclkdiv; |
| uint32_t *clkdiv; |
| }; |
| |
| enum stm32_clock { |
| /* ROOT CLOCKS */ |
| _CK_OFF, |
| _CK_HSI, |
| _CK_HSE, |
| _CK_CSI, |
| _CK_LSI, |
| _CK_LSE, |
| _I2SCKIN, |
| _CSI_DIV122, |
| _HSE_DIV, |
| _HSE_DIV2, |
| _CK_PLL1, |
| _CK_PLL2, |
| _CK_PLL3, |
| _CK_PLL4, |
| _PLL1P, |
| _PLL1P_DIV, |
| _PLL2P, |
| _PLL2Q, |
| _PLL2R, |
| _PLL3P, |
| _PLL3Q, |
| _PLL3R, |
| _PLL4P, |
| _PLL4Q, |
| _PLL4R, |
| _PCLK1, |
| _PCLK2, |
| _PCLK3, |
| _PCLK4, |
| _PCLK5, |
| _PCLK6, |
| _CKMPU, |
| _CKAXI, |
| _CKMLAHB, |
| _CKPER, |
| _CKTIMG1, |
| _CKTIMG2, |
| _CKTIMG3, |
| _USB_PHY_48, |
| _MCO1_K, |
| _MCO2_K, |
| _TRACECK, |
| /* BUS and KERNEL CLOCKS */ |
| _DDRC1, |
| _DDRC1LP, |
| _DDRPHYC, |
| _DDRPHYCLP, |
| _DDRCAPB, |
| _DDRCAPBLP, |
| _AXIDCG, |
| _DDRPHYCAPB, |
| _DDRPHYCAPBLP, |
| _SYSCFG, |
| _DDRPERFM, |
| _IWDG2APB, |
| _USBPHY_K, |
| _USBO_K, |
| _RTCAPB, |
| _TZC, |
| _ETZPC, |
| _IWDG1APB, |
| _BSEC, |
| _STGENC, |
| _USART1_K, |
| _USART2_K, |
| _I2C3_K, |
| _I2C4_K, |
| _I2C5_K, |
| _TIM12, |
| _TIM15, |
| _RTCCK, |
| _GPIOA, |
| _GPIOB, |
| _GPIOC, |
| _GPIOD, |
| _GPIOE, |
| _GPIOF, |
| _GPIOG, |
| _GPIOH, |
| _GPIOI, |
| _PKA, |
| _SAES_K, |
| _CRYP1, |
| _HASH1, |
| _RNG1_K, |
| _BKPSRAM, |
| _SDMMC1_K, |
| _SDMMC2_K, |
| _DBGCK, |
| _USART3_K, |
| _UART4_K, |
| _UART5_K, |
| _UART7_K, |
| _UART8_K, |
| _USART6_K, |
| _MCE, |
| _FMC_K, |
| _QSPI_K, |
| |
| CK_LAST |
| }; |
| |
| /* PARENT CONFIG */ |
| static const uint16_t RTC_src[] = { |
| _CK_OFF, _CK_LSE, _CK_LSI, _CK_HSE |
| }; |
| |
| static const uint16_t MCO1_src[] = { |
| _CK_HSI, _CK_HSE, _CK_CSI, _CK_LSI, _CK_LSE |
| }; |
| |
| static const uint16_t MCO2_src[] = { |
| _CKMPU, _CKAXI, _CKMLAHB, _PLL4P, _CK_HSE, _CK_HSI |
| }; |
| |
| static const uint16_t PLL12_src[] = { |
| _CK_HSI, _CK_HSE |
| }; |
| |
| static const uint16_t PLL3_src[] = { |
| _CK_HSI, _CK_HSE, _CK_CSI |
| }; |
| |
| static const uint16_t PLL4_src[] = { |
| _CK_HSI, _CK_HSE, _CK_CSI, _I2SCKIN |
| }; |
| |
| static const uint16_t MPU_src[] = { |
| _CK_HSI, _CK_HSE, _PLL1P, _PLL1P_DIV |
| }; |
| |
| static const uint16_t AXI_src[] = { |
| _CK_HSI, _CK_HSE, _PLL2P |
| }; |
| |
| static const uint16_t MLAHBS_src[] = { |
| _CK_HSI, _CK_HSE, _CK_CSI, _PLL3P |
| }; |
| |
| static const uint16_t CKPER_src[] = { |
| _CK_HSI, _CK_CSI, _CK_HSE, _CK_OFF |
| }; |
| |
| static const uint16_t I2C12_src[] = { |
| _PCLK1, _PLL4R, _CK_HSI, _CK_CSI |
| }; |
| |
| static const uint16_t I2C3_src[] = { |
| _PCLK6, _PLL4R, _CK_HSI, _CK_CSI |
| }; |
| |
| static const uint16_t I2C4_src[] = { |
| _PCLK6, _PLL4R, _CK_HSI, _CK_CSI |
| }; |
| |
| static const uint16_t I2C5_src[] = { |
| _PCLK6, _PLL4R, _CK_HSI, _CK_CSI |
| }; |
| |
| static const uint16_t SPI1_src[] = { |
| _PLL4P, _PLL3Q, _I2SCKIN, _CKPER, _PLL3R |
| }; |
| |
| static const uint16_t SPI23_src[] = { |
| _PLL4P, _PLL3Q, _I2SCKIN, _CKPER, _PLL3R |
| }; |
| |
| static const uint16_t SPI4_src[] = { |
| _PCLK6, _PLL4Q, _CK_HSI, _CK_CSI, _CK_HSE, _I2SCKIN |
| }; |
| |
| static const uint16_t SPI5_src[] = { |
| _PCLK6, _PLL4Q, _CK_HSI, _CK_CSI, _CK_HSE |
| }; |
| |
| static const uint16_t UART1_src[] = { |
| _PCLK6, _PLL3Q, _CK_HSI, _CK_CSI, _PLL4Q, _CK_HSE |
| }; |
| |
| static const uint16_t UART2_src[] = { |
| _PCLK6, _PLL3Q, _CK_HSI, _CK_CSI, _PLL4Q, _CK_HSE |
| }; |
| |
| static const uint16_t UART35_src[] = { |
| _PCLK1, _PLL4Q, _CK_HSI, _CK_CSI, _CK_HSE |
| }; |
| |
| static const uint16_t UART4_src[] = { |
| _PCLK1, _PLL4Q, _CK_HSI, _CK_CSI, _CK_HSE |
| }; |
| |
| static const uint16_t UART6_src[] = { |
| _PCLK2, _PLL4Q, _CK_HSI, _CK_CSI, _CK_HSE |
| }; |
| |
| static const uint16_t UART78_src[] = { |
| _PCLK1, _PLL4Q, _CK_HSI, _CK_CSI, _CK_HSE |
| }; |
| |
| static const uint16_t LPTIM1_src[] = { |
| _PCLK1, _PLL4P, _PLL3Q, _CK_LSE, _CK_LSI, _CKPER |
| }; |
| |
| static const uint16_t LPTIM2_src[] = { |
| _PCLK3, _PLL4Q, _CKPER, _CK_LSE, _CK_LSI |
| }; |
| |
| static const uint16_t LPTIM3_src[] = { |
| _PCLK3, _PLL4Q, _CKPER, _CK_LSE, _CK_LSI |
| }; |
| |
| static const uint16_t LPTIM45_src[] = { |
| _PCLK3, _PLL4P, _PLL3Q, _CK_LSE, _CK_LSI, _CKPER |
| }; |
| |
| static const uint16_t SAI1_src[] = { |
| _PLL4Q, _PLL3Q, _I2SCKIN, _CKPER, _PLL3R |
| }; |
| |
| static const uint16_t SAI2_src[] = { |
| _PLL4Q, _PLL3Q, _I2SCKIN, _CKPER, _NO_ID, _PLL3R |
| }; |
| |
| static const uint16_t FDCAN_src[] = { |
| _CK_HSE, _PLL3Q, _PLL4Q, _PLL4R |
| }; |
| |
| static const uint16_t SPDIF_src[] = { |
| _PLL4P, _PLL3Q, _CK_HSI |
| }; |
| |
| static const uint16_t ADC1_src[] = { |
| _PLL4R, _CKPER, _PLL3Q |
| }; |
| |
| static const uint16_t ADC2_src[] = { |
| _PLL4R, _CKPER, _PLL3Q |
| }; |
| |
| static const uint16_t SDMMC1_src[] = { |
| _CKAXI, _PLL3R, _PLL4P, _CK_HSI |
| }; |
| |
| static const uint16_t SDMMC2_src[] = { |
| _CKAXI, _PLL3R, _PLL4P, _CK_HSI |
| }; |
| |
| static const uint16_t ETH1_src[] = { |
| _PLL4P, _PLL3Q |
| }; |
| |
| static const uint16_t ETH2_src[] = { |
| _PLL4P, _PLL3Q |
| }; |
| |
| static const uint16_t USBPHY_src[] = { |
| _CK_HSE, _PLL4R, _HSE_DIV2 |
| }; |
| |
| static const uint16_t USBO_src[] = { |
| _PLL4R, _USB_PHY_48 |
| }; |
| |
| static const uint16_t QSPI_src[] = { |
| _CKAXI, _PLL3R, _PLL4P, _CKPER |
| }; |
| |
| static const uint16_t FMC_src[] = { |
| _CKAXI, _PLL3R, _PLL4P, _CKPER |
| }; |
| |
| /* Position 2 of RNG1 mux is reserved */ |
| static const uint16_t RNG1_src[] = { |
| _CK_CSI, _PLL4R, _CK_OFF, _CK_LSI |
| }; |
| |
| static const uint16_t STGEN_src[] = { |
| _CK_HSI, _CK_HSE |
| }; |
| |
| static const uint16_t DCMIPP_src[] = { |
| _CKAXI, _PLL2Q, _PLL4P, _CKPER |
| }; |
| |
| static const uint16_t SAES_src[] = { |
| _CKAXI, _CKPER, _PLL4R, _CK_LSI |
| }; |
| |
| #define MUX_CFG(id, src, _offset, _shift, _witdh)[id] = {\ |
| .id_parents = src,\ |
| .num_parents = ARRAY_SIZE(src),\ |
| .mux = &(struct mux_cfg) {\ |
| .offset = (_offset),\ |
| .shift = (_shift),\ |
| .width = (_witdh),\ |
| .bitrdy = MUX_NO_BIT_RDY,\ |
| },\ |
| } |
| |
| #define MUX_RDY_CFG(id, src, _offset, _shift, _witdh)[id] = {\ |
| .id_parents = src,\ |
| .num_parents = ARRAY_SIZE(src),\ |
| .mux = &(struct mux_cfg) {\ |
| .offset = (_offset),\ |
| .shift = (_shift),\ |
| .width = (_witdh),\ |
| .bitrdy = 31,\ |
| },\ |
| } |
| |
| static const struct parent_cfg parent_mp13[MUX_MAX] = { |
| MUX_CFG(MUX_ADC1, ADC1_src, RCC_ADC12CKSELR, 0, 2), |
| MUX_CFG(MUX_ADC2, ADC2_src, RCC_ADC12CKSELR, 2, 2), |
| MUX_RDY_CFG(MUX_AXI, AXI_src, RCC_ASSCKSELR, 0, 3), |
| MUX_CFG(MUX_CKPER, CKPER_src, RCC_CPERCKSELR, 0, 2), |
| MUX_CFG(MUX_DCMIPP, DCMIPP_src, RCC_DCMIPPCKSELR, 0, 2), |
| MUX_CFG(MUX_ETH1, ETH1_src, RCC_ETH12CKSELR, 0, 2), |
| MUX_CFG(MUX_ETH2, ETH2_src, RCC_ETH12CKSELR, 8, 2), |
| MUX_CFG(MUX_FDCAN, FDCAN_src, RCC_FDCANCKSELR, 0, 2), |
| MUX_CFG(MUX_FMC, FMC_src, RCC_FMCCKSELR, 0, 2), |
| MUX_CFG(MUX_I2C12, I2C12_src, RCC_I2C12CKSELR, 0, 3), |
| MUX_CFG(MUX_I2C3, I2C3_src, RCC_I2C345CKSELR, 0, 3), |
| MUX_CFG(MUX_I2C4, I2C4_src, RCC_I2C345CKSELR, 3, 3), |
| MUX_CFG(MUX_I2C5, I2C5_src, RCC_I2C345CKSELR, 6, 3), |
| MUX_CFG(MUX_LPTIM1, LPTIM1_src, RCC_LPTIM1CKSELR, 0, 3), |
| MUX_CFG(MUX_LPTIM2, LPTIM2_src, RCC_LPTIM23CKSELR, 0, 3), |
| MUX_CFG(MUX_LPTIM3, LPTIM3_src, RCC_LPTIM23CKSELR, 3, 3), |
| MUX_CFG(MUX_LPTIM45, LPTIM45_src, RCC_LPTIM45CKSELR, 0, 3), |
| MUX_CFG(MUX_MCO1, MCO1_src, RCC_MCO1CFGR, 0, 3), |
| MUX_CFG(MUX_MCO2, MCO2_src, RCC_MCO2CFGR, 0, 3), |
| MUX_RDY_CFG(MUX_MLAHB, MLAHBS_src, RCC_MSSCKSELR, 0, 2), |
| MUX_RDY_CFG(MUX_MPU, MPU_src, RCC_MPCKSELR, 0, 2), |
| MUX_RDY_CFG(MUX_PLL12, PLL12_src, RCC_RCK12SELR, 0, 2), |
| MUX_RDY_CFG(MUX_PLL3, PLL3_src, RCC_RCK3SELR, 0, 2), |
| MUX_RDY_CFG(MUX_PLL4, PLL4_src, RCC_RCK4SELR, 0, 2), |
| MUX_CFG(MUX_QSPI, QSPI_src, RCC_QSPICKSELR, 0, 2), |
| MUX_CFG(MUX_RNG1, RNG1_src, RCC_RNG1CKSELR, 0, 2), |
| MUX_CFG(MUX_RTC, RTC_src, RCC_BDCR, 16, 2), |
| MUX_CFG(MUX_SAES, SAES_src, RCC_SAESCKSELR, 0, 2), |
| MUX_CFG(MUX_SAI1, SAI1_src, RCC_SAI1CKSELR, 0, 3), |
| MUX_CFG(MUX_SAI2, SAI2_src, RCC_SAI2CKSELR, 0, 3), |
| MUX_CFG(MUX_SDMMC1, SDMMC1_src, RCC_SDMMC12CKSELR, 0, 3), |
| MUX_CFG(MUX_SDMMC2, SDMMC2_src, RCC_SDMMC12CKSELR, 3, 3), |
| MUX_CFG(MUX_SPDIF, SPDIF_src, RCC_SPDIFCKSELR, 0, 2), |
| MUX_CFG(MUX_SPI1, SPI1_src, RCC_SPI2S1CKSELR, 0, 3), |
| MUX_CFG(MUX_SPI23, SPI23_src, RCC_SPI2S23CKSELR, 0, 3), |
| MUX_CFG(MUX_SPI4, SPI4_src, RCC_SPI45CKSELR, 0, 3), |
| MUX_CFG(MUX_SPI5, SPI5_src, RCC_SPI45CKSELR, 3, 3), |
| MUX_CFG(MUX_STGEN, STGEN_src, RCC_STGENCKSELR, 0, 2), |
| MUX_CFG(MUX_UART1, UART1_src, RCC_UART12CKSELR, 0, 3), |
| MUX_CFG(MUX_UART2, UART2_src, RCC_UART12CKSELR, 3, 3), |
| MUX_CFG(MUX_UART35, UART35_src, RCC_UART35CKSELR, 0, 3), |
| MUX_CFG(MUX_UART4, UART4_src, RCC_UART4CKSELR, 0, 3), |
| MUX_CFG(MUX_UART6, UART6_src, RCC_UART6CKSELR, 0, 3), |
| MUX_CFG(MUX_UART78, UART78_src, RCC_UART78CKSELR, 0, 3), |
| MUX_CFG(MUX_USBO, USBO_src, RCC_USBCKSELR, 4, 1), |
| MUX_CFG(MUX_USBPHY, USBPHY_src, RCC_USBCKSELR, 0, 2), |
| }; |
| |
| /* |
| * GATE CONFIG |
| */ |
| |
| enum enum_gate_cfg { |
| GATE_ZERO, /* reserved for no gate */ |
| GATE_LSE, |
| GATE_RTCCK, |
| GATE_LSI, |
| GATE_HSI, |
| GATE_CSI, |
| GATE_HSE, |
| GATE_LSI_RDY, |
| GATE_CSI_RDY, |
| GATE_LSE_RDY, |
| GATE_HSE_RDY, |
| GATE_HSI_RDY, |
| GATE_MCO1, |
| GATE_MCO2, |
| GATE_DBGCK, |
| GATE_TRACECK, |
| GATE_PLL1, |
| GATE_PLL1_DIVP, |
| GATE_PLL1_DIVQ, |
| GATE_PLL1_DIVR, |
| GATE_PLL2, |
| GATE_PLL2_DIVP, |
| GATE_PLL2_DIVQ, |
| GATE_PLL2_DIVR, |
| GATE_PLL3, |
| GATE_PLL3_DIVP, |
| GATE_PLL3_DIVQ, |
| GATE_PLL3_DIVR, |
| GATE_PLL4, |
| GATE_PLL4_DIVP, |
| GATE_PLL4_DIVQ, |
| GATE_PLL4_DIVR, |
| GATE_DDRC1, |
| GATE_DDRC1LP, |
| GATE_DDRPHYC, |
| GATE_DDRPHYCLP, |
| GATE_DDRCAPB, |
| GATE_DDRCAPBLP, |
| GATE_AXIDCG, |
| GATE_DDRPHYCAPB, |
| GATE_DDRPHYCAPBLP, |
| GATE_TIM2, |
| GATE_TIM3, |
| GATE_TIM4, |
| GATE_TIM5, |
| GATE_TIM6, |
| GATE_TIM7, |
| GATE_LPTIM1, |
| GATE_SPI2, |
| GATE_SPI3, |
| GATE_USART3, |
| GATE_UART4, |
| GATE_UART5, |
| GATE_UART7, |
| GATE_UART8, |
| GATE_I2C1, |
| GATE_I2C2, |
| GATE_SPDIF, |
| GATE_TIM1, |
| GATE_TIM8, |
| GATE_SPI1, |
| GATE_USART6, |
| GATE_SAI1, |
| GATE_SAI2, |
| GATE_DFSDM, |
| GATE_ADFSDM, |
| GATE_FDCAN, |
| GATE_LPTIM2, |
| GATE_LPTIM3, |
| GATE_LPTIM4, |
| GATE_LPTIM5, |
| GATE_VREF, |
| GATE_DTS, |
| GATE_PMBCTRL, |
| GATE_HDP, |
| GATE_SYSCFG, |
| GATE_DCMIPP, |
| GATE_DDRPERFM, |
| GATE_IWDG2APB, |
| GATE_USBPHY, |
| GATE_STGENRO, |
| GATE_LTDC, |
| GATE_RTCAPB, |
| GATE_TZC, |
| GATE_ETZPC, |
| GATE_IWDG1APB, |
| GATE_BSEC, |
| GATE_STGENC, |
| GATE_USART1, |
| GATE_USART2, |
| GATE_SPI4, |
| GATE_SPI5, |
| GATE_I2C3, |
| GATE_I2C4, |
| GATE_I2C5, |
| GATE_TIM12, |
| GATE_TIM13, |
| GATE_TIM14, |
| GATE_TIM15, |
| GATE_TIM16, |
| GATE_TIM17, |
| GATE_DMA1, |
| GATE_DMA2, |
| GATE_DMAMUX1, |
| GATE_DMA3, |
| GATE_DMAMUX2, |
| GATE_ADC1, |
| GATE_ADC2, |
| GATE_USBO, |
| GATE_TSC, |
| GATE_GPIOA, |
| GATE_GPIOB, |
| GATE_GPIOC, |
| GATE_GPIOD, |
| GATE_GPIOE, |
| GATE_GPIOF, |
| GATE_GPIOG, |
| GATE_GPIOH, |
| GATE_GPIOI, |
| GATE_PKA, |
| GATE_SAES, |
| GATE_CRYP1, |
| GATE_HASH1, |
| GATE_RNG1, |
| GATE_BKPSRAM, |
| GATE_AXIMC, |
| GATE_MCE, |
| GATE_ETH1CK, |
| GATE_ETH1TX, |
| GATE_ETH1RX, |
| GATE_ETH1MAC, |
| GATE_FMC, |
| GATE_QSPI, |
| GATE_SDMMC1, |
| GATE_SDMMC2, |
| GATE_CRC1, |
| GATE_USBH, |
| GATE_ETH2CK, |
| GATE_ETH2TX, |
| GATE_ETH2RX, |
| GATE_ETH2MAC, |
| GATE_MDMA, |
| |
| LAST_GATE |
| }; |
| |
| #define GATE_CFG(id, _offset, _bit_idx, _offset_clr)[id] = {\ |
| .offset = (_offset),\ |
| .bit_idx = (_bit_idx),\ |
| .set_clr = (_offset_clr),\ |
| } |
| |
| static const struct gate_cfg gates_mp13[LAST_GATE] = { |
| GATE_CFG(GATE_LSE, RCC_BDCR, 0, 0), |
| GATE_CFG(GATE_RTCCK, RCC_BDCR, 20, 0), |
| GATE_CFG(GATE_LSI, RCC_RDLSICR, 0, 0), |
| GATE_CFG(GATE_HSI, RCC_OCENSETR, 0, 1), |
| GATE_CFG(GATE_CSI, RCC_OCENSETR, 4, 1), |
| GATE_CFG(GATE_HSE, RCC_OCENSETR, 8, 1), |
| GATE_CFG(GATE_LSI_RDY, RCC_RDLSICR, 1, 0), |
| GATE_CFG(GATE_CSI_RDY, RCC_OCRDYR, 4, 0), |
| GATE_CFG(GATE_LSE_RDY, RCC_BDCR, 2, 0), |
| GATE_CFG(GATE_HSE_RDY, RCC_OCRDYR, 8, 0), |
| GATE_CFG(GATE_HSI_RDY, RCC_OCRDYR, 0, 0), |
| GATE_CFG(GATE_MCO1, RCC_MCO1CFGR, 12, 0), |
| GATE_CFG(GATE_MCO2, RCC_MCO2CFGR, 12, 0), |
| GATE_CFG(GATE_DBGCK, RCC_DBGCFGR, 8, 0), |
| GATE_CFG(GATE_TRACECK, RCC_DBGCFGR, 9, 0), |
| GATE_CFG(GATE_PLL1, RCC_PLL1CR, 0, 0), |
| GATE_CFG(GATE_PLL1_DIVP, RCC_PLL1CR, 4, 0), |
| GATE_CFG(GATE_PLL1_DIVQ, RCC_PLL1CR, 5, 0), |
| GATE_CFG(GATE_PLL1_DIVR, RCC_PLL1CR, 6, 0), |
| GATE_CFG(GATE_PLL2, RCC_PLL2CR, 0, 0), |
| GATE_CFG(GATE_PLL2_DIVP, RCC_PLL2CR, 4, 0), |
| GATE_CFG(GATE_PLL2_DIVQ, RCC_PLL2CR, 5, 0), |
| GATE_CFG(GATE_PLL2_DIVR, RCC_PLL2CR, 6, 0), |
| GATE_CFG(GATE_PLL3, RCC_PLL3CR, 0, 0), |
| GATE_CFG(GATE_PLL3_DIVP, RCC_PLL3CR, 4, 0), |
| GATE_CFG(GATE_PLL3_DIVQ, RCC_PLL3CR, 5, 0), |
| GATE_CFG(GATE_PLL3_DIVR, RCC_PLL3CR, 6, 0), |
| GATE_CFG(GATE_PLL4, RCC_PLL4CR, 0, 0), |
| GATE_CFG(GATE_PLL4_DIVP, RCC_PLL4CR, 4, 0), |
| GATE_CFG(GATE_PLL4_DIVQ, RCC_PLL4CR, 5, 0), |
| GATE_CFG(GATE_PLL4_DIVR, RCC_PLL4CR, 6, 0), |
| GATE_CFG(GATE_DDRC1, RCC_DDRITFCR, 0, 0), |
| GATE_CFG(GATE_DDRC1LP, RCC_DDRITFCR, 1, 0), |
| GATE_CFG(GATE_DDRPHYC, RCC_DDRITFCR, 4, 0), |
| GATE_CFG(GATE_DDRPHYCLP, RCC_DDRITFCR, 5, 0), |
| GATE_CFG(GATE_DDRCAPB, RCC_DDRITFCR, 6, 0), |
| GATE_CFG(GATE_DDRCAPBLP, RCC_DDRITFCR, 7, 0), |
| GATE_CFG(GATE_AXIDCG, RCC_DDRITFCR, 8, 0), |
| GATE_CFG(GATE_DDRPHYCAPB, RCC_DDRITFCR, 9, 0), |
| GATE_CFG(GATE_DDRPHYCAPBLP, RCC_DDRITFCR, 10, 0), |
| GATE_CFG(GATE_TIM2, RCC_MP_APB1ENSETR, 0, 1), |
| GATE_CFG(GATE_TIM3, RCC_MP_APB1ENSETR, 1, 1), |
| GATE_CFG(GATE_TIM4, RCC_MP_APB1ENSETR, 2, 1), |
| GATE_CFG(GATE_TIM5, RCC_MP_APB1ENSETR, 3, 1), |
| GATE_CFG(GATE_TIM6, RCC_MP_APB1ENSETR, 4, 1), |
| GATE_CFG(GATE_TIM7, RCC_MP_APB1ENSETR, 5, 1), |
| GATE_CFG(GATE_LPTIM1, RCC_MP_APB1ENSETR, 9, 1), |
| GATE_CFG(GATE_SPI2, RCC_MP_APB1ENSETR, 11, 1), |
| GATE_CFG(GATE_SPI3, RCC_MP_APB1ENSETR, 12, 1), |
| GATE_CFG(GATE_USART3, RCC_MP_APB1ENSETR, 15, 1), |
| GATE_CFG(GATE_UART4, RCC_MP_APB1ENSETR, 16, 1), |
| GATE_CFG(GATE_UART5, RCC_MP_APB1ENSETR, 17, 1), |
| GATE_CFG(GATE_UART7, RCC_MP_APB1ENSETR, 18, 1), |
| GATE_CFG(GATE_UART8, RCC_MP_APB1ENSETR, 19, 1), |
| GATE_CFG(GATE_I2C1, RCC_MP_APB1ENSETR, 21, 1), |
| GATE_CFG(GATE_I2C2, RCC_MP_APB1ENSETR, 22, 1), |
| GATE_CFG(GATE_SPDIF, RCC_MP_APB1ENSETR, 26, 1), |
| GATE_CFG(GATE_TIM1, RCC_MP_APB2ENSETR, 0, 1), |
| GATE_CFG(GATE_TIM8, RCC_MP_APB2ENSETR, 1, 1), |
| GATE_CFG(GATE_SPI1, RCC_MP_APB2ENSETR, 8, 1), |
| GATE_CFG(GATE_USART6, RCC_MP_APB2ENSETR, 13, 1), |
| GATE_CFG(GATE_SAI1, RCC_MP_APB2ENSETR, 16, 1), |
| GATE_CFG(GATE_SAI2, RCC_MP_APB2ENSETR, 17, 1), |
| GATE_CFG(GATE_DFSDM, RCC_MP_APB2ENSETR, 20, 1), |
| GATE_CFG(GATE_ADFSDM, RCC_MP_APB2ENSETR, 21, 1), |
| GATE_CFG(GATE_FDCAN, RCC_MP_APB2ENSETR, 24, 1), |
| GATE_CFG(GATE_LPTIM2, RCC_MP_APB3ENSETR, 0, 1), |
| GATE_CFG(GATE_LPTIM3, RCC_MP_APB3ENSETR, 1, 1), |
| GATE_CFG(GATE_LPTIM4, RCC_MP_APB3ENSETR, 2, 1), |
| GATE_CFG(GATE_LPTIM5, RCC_MP_APB3ENSETR, 3, 1), |
| GATE_CFG(GATE_VREF, RCC_MP_APB3ENSETR, 13, 1), |
| GATE_CFG(GATE_DTS, RCC_MP_APB3ENSETR, 16, 1), |
| GATE_CFG(GATE_PMBCTRL, RCC_MP_APB3ENSETR, 17, 1), |
| GATE_CFG(GATE_HDP, RCC_MP_APB3ENSETR, 20, 1), |
| GATE_CFG(GATE_SYSCFG, RCC_MP_S_APB3ENSETR, 0, 1), |
| GATE_CFG(GATE_DCMIPP, RCC_MP_APB4ENSETR, 1, 1), |
| GATE_CFG(GATE_DDRPERFM, RCC_MP_APB4ENSETR, 8, 1), |
| GATE_CFG(GATE_IWDG2APB, RCC_MP_APB4ENSETR, 15, 1), |
| GATE_CFG(GATE_USBPHY, RCC_MP_APB4ENSETR, 16, 1), |
| GATE_CFG(GATE_STGENRO, RCC_MP_APB4ENSETR, 20, 1), |
| GATE_CFG(GATE_LTDC, RCC_MP_S_APB4ENSETR, 0, 1), |
| GATE_CFG(GATE_RTCAPB, RCC_MP_APB5ENSETR, 8, 1), |
| GATE_CFG(GATE_TZC, RCC_MP_APB5ENSETR, 11, 1), |
| GATE_CFG(GATE_ETZPC, RCC_MP_APB5ENSETR, 13, 1), |
| GATE_CFG(GATE_IWDG1APB, RCC_MP_APB5ENSETR, 15, 1), |
| GATE_CFG(GATE_BSEC, RCC_MP_APB5ENSETR, 16, 1), |
| GATE_CFG(GATE_STGENC, RCC_MP_APB5ENSETR, 20, 1), |
| GATE_CFG(GATE_USART1, RCC_MP_APB6ENSETR, 0, 1), |
| GATE_CFG(GATE_USART2, RCC_MP_APB6ENSETR, 1, 1), |
| GATE_CFG(GATE_SPI4, RCC_MP_APB6ENSETR, 2, 1), |
| GATE_CFG(GATE_SPI5, RCC_MP_APB6ENSETR, 3, 1), |
| GATE_CFG(GATE_I2C3, RCC_MP_APB6ENSETR, 4, 1), |
| GATE_CFG(GATE_I2C4, RCC_MP_APB6ENSETR, 5, 1), |
| GATE_CFG(GATE_I2C5, RCC_MP_APB6ENSETR, 6, 1), |
| GATE_CFG(GATE_TIM12, RCC_MP_APB6ENSETR, 7, 1), |
| GATE_CFG(GATE_TIM13, RCC_MP_APB6ENSETR, 8, 1), |
| GATE_CFG(GATE_TIM14, RCC_MP_APB6ENSETR, 9, 1), |
| GATE_CFG(GATE_TIM15, RCC_MP_APB6ENSETR, 10, 1), |
| GATE_CFG(GATE_TIM16, RCC_MP_APB6ENSETR, 11, 1), |
| GATE_CFG(GATE_TIM17, RCC_MP_APB6ENSETR, 12, 1), |
| GATE_CFG(GATE_DMA1, RCC_MP_AHB2ENSETR, 0, 1), |
| GATE_CFG(GATE_DMA2, RCC_MP_AHB2ENSETR, 1, 1), |
| GATE_CFG(GATE_DMAMUX1, RCC_MP_AHB2ENSETR, 2, 1), |
| GATE_CFG(GATE_DMA3, RCC_MP_AHB2ENSETR, 3, 1), |
| GATE_CFG(GATE_DMAMUX2, RCC_MP_AHB2ENSETR, 4, 1), |
| GATE_CFG(GATE_ADC1, RCC_MP_AHB2ENSETR, 5, 1), |
| GATE_CFG(GATE_ADC2, RCC_MP_AHB2ENSETR, 6, 1), |
| GATE_CFG(GATE_USBO, RCC_MP_AHB2ENSETR, 8, 1), |
| GATE_CFG(GATE_TSC, RCC_MP_AHB4ENSETR, 15, 1), |
| |
| GATE_CFG(GATE_GPIOA, RCC_MP_S_AHB4ENSETR, 0, 1), |
| GATE_CFG(GATE_GPIOB, RCC_MP_S_AHB4ENSETR, 1, 1), |
| GATE_CFG(GATE_GPIOC, RCC_MP_S_AHB4ENSETR, 2, 1), |
| GATE_CFG(GATE_GPIOD, RCC_MP_S_AHB4ENSETR, 3, 1), |
| GATE_CFG(GATE_GPIOE, RCC_MP_S_AHB4ENSETR, 4, 1), |
| GATE_CFG(GATE_GPIOF, RCC_MP_S_AHB4ENSETR, 5, 1), |
| GATE_CFG(GATE_GPIOG, RCC_MP_S_AHB4ENSETR, 6, 1), |
| GATE_CFG(GATE_GPIOH, RCC_MP_S_AHB4ENSETR, 7, 1), |
| GATE_CFG(GATE_GPIOI, RCC_MP_S_AHB4ENSETR, 8, 1), |
| |
| GATE_CFG(GATE_PKA, RCC_MP_AHB5ENSETR, 2, 1), |
| GATE_CFG(GATE_SAES, RCC_MP_AHB5ENSETR, 3, 1), |
| GATE_CFG(GATE_CRYP1, RCC_MP_AHB5ENSETR, 4, 1), |
| GATE_CFG(GATE_HASH1, RCC_MP_AHB5ENSETR, 5, 1), |
| GATE_CFG(GATE_RNG1, RCC_MP_AHB5ENSETR, 6, 1), |
| GATE_CFG(GATE_BKPSRAM, RCC_MP_AHB5ENSETR, 8, 1), |
| GATE_CFG(GATE_AXIMC, RCC_MP_AHB5ENSETR, 16, 1), |
| GATE_CFG(GATE_MCE, RCC_MP_AHB6ENSETR, 1, 1), |
| GATE_CFG(GATE_ETH1CK, RCC_MP_AHB6ENSETR, 7, 1), |
| GATE_CFG(GATE_ETH1TX, RCC_MP_AHB6ENSETR, 8, 1), |
| GATE_CFG(GATE_ETH1RX, RCC_MP_AHB6ENSETR, 9, 1), |
| GATE_CFG(GATE_ETH1MAC, RCC_MP_AHB6ENSETR, 10, 1), |
| GATE_CFG(GATE_FMC, RCC_MP_AHB6ENSETR, 12, 1), |
| GATE_CFG(GATE_QSPI, RCC_MP_AHB6ENSETR, 14, 1), |
| GATE_CFG(GATE_SDMMC1, RCC_MP_AHB6ENSETR, 16, 1), |
| GATE_CFG(GATE_SDMMC2, RCC_MP_AHB6ENSETR, 17, 1), |
| GATE_CFG(GATE_CRC1, RCC_MP_AHB6ENSETR, 20, 1), |
| GATE_CFG(GATE_USBH, RCC_MP_AHB6ENSETR, 24, 1), |
| GATE_CFG(GATE_ETH2CK, RCC_MP_AHB6ENSETR, 27, 1), |
| GATE_CFG(GATE_ETH2TX, RCC_MP_AHB6ENSETR, 28, 1), |
| GATE_CFG(GATE_ETH2RX, RCC_MP_AHB6ENSETR, 29, 1), |
| GATE_CFG(GATE_ETH2MAC, RCC_MP_AHB6ENSETR, 30, 1), |
| GATE_CFG(GATE_MDMA, RCC_MP_S_AHB6ENSETR, 0, 1), |
| }; |
| |
| /* |
| * DIV CONFIG |
| */ |
| |
| static const struct clk_div_table axi_div_table[] = { |
| { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, |
| { 4, 4 }, { 5, 4 }, { 6, 4 }, { 7, 4 }, |
| { 0 }, |
| }; |
| |
| static const struct clk_div_table mlahb_div_table[] = { |
| { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, |
| { 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 }, |
| { 8, 256 }, { 9, 512 }, { 10, 512}, { 11, 512 }, |
| { 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 }, |
| { 0 }, |
| }; |
| |
| static const struct clk_div_table apb_div_table[] = { |
| { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, |
| { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 }, |
| { 0 }, |
| }; |
| |
| #define DIV_CFG(id, _offset, _shift, _width, _flags, _table, _bitrdy)[id] = {\ |
| .offset = _offset,\ |
| .shift = _shift,\ |
| .width = _width,\ |
| .flags = _flags,\ |
| .table = _table,\ |
| .bitrdy = _bitrdy,\ |
| } |
| |
| static const struct div_cfg dividers_mp13[DIV_MAX] = { |
| DIV_CFG(DIV_PLL1DIVP, RCC_PLL1CFGR2, 0, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL2DIVP, RCC_PLL2CFGR2, 0, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL2DIVQ, RCC_PLL2CFGR2, 8, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL2DIVR, RCC_PLL2CFGR2, 16, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL3DIVP, RCC_PLL3CFGR2, 0, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL3DIVQ, RCC_PLL3CFGR2, 8, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL3DIVR, RCC_PLL3CFGR2, 16, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL4DIVP, RCC_PLL4CFGR2, 0, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL4DIVQ, RCC_PLL4CFGR2, 8, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_PLL4DIVR, RCC_PLL4CFGR2, 16, 7, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_MPU, RCC_MPCKDIVR, 0, 4, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_AXI, RCC_AXIDIVR, 0, 3, 0, axi_div_table, 31), |
| DIV_CFG(DIV_MLAHB, RCC_MLAHBDIVR, 0, 4, 0, mlahb_div_table, 31), |
| DIV_CFG(DIV_APB1, RCC_APB1DIVR, 0, 3, 0, apb_div_table, 31), |
| DIV_CFG(DIV_APB2, RCC_APB2DIVR, 0, 3, 0, apb_div_table, 31), |
| DIV_CFG(DIV_APB3, RCC_APB3DIVR, 0, 3, 0, apb_div_table, 31), |
| DIV_CFG(DIV_APB4, RCC_APB4DIVR, 0, 3, 0, apb_div_table, 31), |
| DIV_CFG(DIV_APB5, RCC_APB5DIVR, 0, 3, 0, apb_div_table, 31), |
| DIV_CFG(DIV_APB6, RCC_APB6DIVR, 0, 3, 0, apb_div_table, 31), |
| DIV_CFG(DIV_RTC, RCC_RTCDIVR, 0, 6, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_MCO1, RCC_MCO1CFGR, 4, 4, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_MCO2, RCC_MCO2CFGR, 4, 4, 0, NULL, DIV_NO_BIT_RDY), |
| |
| DIV_CFG(DIV_HSI, RCC_HSICFGR, 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_TRACE, RCC_DBGCFGR, 0, 3, CLK_DIVIDER_POWER_OF_TWO, NULL, DIV_NO_BIT_RDY), |
| |
| DIV_CFG(DIV_ETH1PTP, RCC_ETH12CKSELR, 4, 4, 0, NULL, DIV_NO_BIT_RDY), |
| DIV_CFG(DIV_ETH2PTP, RCC_ETH12CKSELR, 12, 4, 0, NULL, DIV_NO_BIT_RDY), |
| }; |
| |
| #define MAX_HSI_HZ 64000000 |
| #define USB_PHY_48_MHZ 48000000 |
| |
| #define TIMEOUT_US_200MS U(200000) |
| #define TIMEOUT_US_1S U(1000000) |
| |
| #define PLLRDY_TIMEOUT TIMEOUT_US_200MS |
| #define CLKSRC_TIMEOUT TIMEOUT_US_200MS |
| #define CLKDIV_TIMEOUT TIMEOUT_US_200MS |
| #define HSIDIV_TIMEOUT TIMEOUT_US_200MS |
| #define OSCRDY_TIMEOUT TIMEOUT_US_1S |
| |
| enum stm32_osc { |
| OSC_HSI, |
| OSC_HSE, |
| OSC_CSI, |
| OSC_LSI, |
| OSC_LSE, |
| OSC_I2SCKIN, |
| NB_OSCILLATOR |
| }; |
| |
| enum stm32mp1_pll_id { |
| _PLL1, |
| _PLL2, |
| _PLL3, |
| _PLL4, |
| _PLL_NB |
| }; |
| |
| enum stm32mp1_plltype { |
| PLL_800, |
| PLL_1600, |
| PLL_2000, |
| PLL_TYPE_NB |
| }; |
| |
| #define RCC_OFFSET_PLLXCR 0 |
| #define RCC_OFFSET_PLLXCFGR1 4 |
| #define RCC_OFFSET_PLLXCFGR2 8 |
| #define RCC_OFFSET_PLLXFRACR 12 |
| #define RCC_OFFSET_PLLXCSGR 16 |
| |
| struct stm32_clk_pll { |
| enum stm32mp1_plltype plltype; |
| uint16_t clk_id; |
| uint16_t reg_pllxcr; |
| }; |
| |
| struct stm32mp1_pll { |
| uint8_t refclk_min; |
| uint8_t refclk_max; |
| }; |
| |
| /* Define characteristic of PLL according type */ |
| static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { |
| [PLL_800] = { |
| .refclk_min = 4, |
| .refclk_max = 16, |
| }, |
| [PLL_1600] = { |
| .refclk_min = 8, |
| .refclk_max = 16, |
| }, |
| [PLL_2000] = { |
| .refclk_min = 8, |
| .refclk_max = 16, |
| }, |
| }; |
| |
| #if STM32MP_USB_PROGRAMMER |
| static bool pll4_bootrom; |
| #endif |
| |
| /* RCC clock device driver private */ |
| static uint8_t refcounts_mp13[CK_LAST]; |
| |
| static const struct stm32_clk_pll *clk_st32_pll_data(unsigned int idx); |
| |
| #if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER |
| static void clk_oscillator_check_bypass(struct stm32_clk_priv *priv, int idx, |
| bool digbyp, bool bypass) |
| { |
| struct clk_oscillator_data *osc_data = clk_oscillator_get_data(priv, idx); |
| struct stm32_clk_bypass *bypass_data = osc_data->bypass; |
| uintptr_t address; |
| |
| if (bypass_data == NULL) { |
| return; |
| } |
| |
| address = priv->base + bypass_data->offset; |
| if ((mmio_read_32(address) & RCC_OCENR_HSEBYP) && |
| (!(digbyp || bypass))) { |
| panic(); |
| } |
| } |
| #endif |
| |
| static void stm32_enable_oscillator_hse(struct stm32_clk_priv *priv) |
| { |
| struct stm32_clk_platdata *pdata = priv->pdata; |
| struct stm32_osci_dt_cfg *osci = &pdata->osci[OSC_HSE]; |
| bool digbyp = osci->digbyp; |
| bool bypass = osci->bypass; |
| bool css = osci->css; |
| |
| if (_clk_stm32_get_rate(priv, _CK_HSE) == 0U) { |
| return; |
| } |
| |
| clk_oscillator_set_bypass(priv, _CK_HSE, digbyp, bypass); |
| |
| _clk_stm32_enable(priv, _CK_HSE); |
| |
| #if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER |
| clk_oscillator_check_bypass(priv, _CK_HSE, digbyp, bypass); |
| #endif |
| |
| clk_oscillator_set_css(priv, _CK_HSE, css); |
| } |
| |
| static void stm32_enable_oscillator_lse(struct stm32_clk_priv *priv) |
| { |
| struct clk_oscillator_data *osc_data = clk_oscillator_get_data(priv, _CK_LSE); |
| struct stm32_clk_platdata *pdata = priv->pdata; |
| struct stm32_osci_dt_cfg *osci = &pdata->osci[OSC_LSE]; |
| bool digbyp = osci->digbyp; |
| bool bypass = osci->bypass; |
| uint8_t drive = osci->drive; |
| |
| if (_clk_stm32_get_rate(priv, _CK_LSE) == 0U) { |
| return; |
| } |
| |
| /* Do not reconfigure LSE if already enabled */ |
| if (_clk_stm32_gate_is_enabled(priv, osc_data->gate_id)) { |
| return; |
| } |
| |
| clk_oscillator_set_bypass(priv, _CK_LSE, digbyp, bypass); |
| |
| clk_oscillator_set_drive(priv, _CK_LSE, drive); |
| |
| _clk_stm32_gate_enable(priv, osc_data->gate_id); |
| } |
| |
| static int stm32mp1_set_hsidiv(uint8_t hsidiv) |
| { |
| uint64_t timeout; |
| uintptr_t rcc_base = stm32mp_rcc_base(); |
| uintptr_t address = rcc_base + RCC_OCRDYR; |
| |
| mmio_clrsetbits_32(rcc_base + RCC_HSICFGR, |
| RCC_HSICFGR_HSIDIV_MASK, |
| RCC_HSICFGR_HSIDIV_MASK & (uint32_t)hsidiv); |
| |
| timeout = timeout_init_us(HSIDIV_TIMEOUT); |
| while ((mmio_read_32(address) & RCC_OCRDYR_HSIDIVRDY) == 0U) { |
| if (timeout_elapsed(timeout)) { |
| EARLY_ERROR("HSIDIV failed @ 0x%lx: 0x%x\n", |
| address, mmio_read_32(address)); |
| return -ETIMEDOUT; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int stm32mp1_hsidiv(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) { |
| EARLY_ERROR("Invalid clk-hsi frequency\n"); |
| return -EINVAL; |
| } |
| |
| if (hsidiv != 0U) { |
| return stm32mp1_set_hsidiv(hsidiv); |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_oscillators_lse_set_css(struct stm32_clk_priv *priv) |
| { |
| struct stm32_clk_platdata *pdata = priv->pdata; |
| struct stm32_osci_dt_cfg *osci = &pdata->osci[OSC_LSE]; |
| |
| clk_oscillator_set_css(priv, _CK_LSE, osci->css); |
| |
| return 0; |
| } |
| |
| static int stm32mp1_come_back_to_hsi(void) |
| { |
| int ret; |
| struct stm32_clk_priv *priv = clk_stm32_get_priv(); |
| |
| /* Come back to HSI */ |
| ret = _clk_stm32_set_parent(priv, _CKMPU, _CK_HSI); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = _clk_stm32_set_parent(priv, _CKAXI, _CK_HSI); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = _clk_stm32_set_parent(priv, _CKMLAHB, _CK_HSI); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_configure_clk_get_binding_id(struct stm32_clk_priv *priv, uint32_t data) |
| { |
| unsigned long binding_id = ((unsigned long)data & CLK_ID_MASK) >> CLK_ID_SHIFT; |
| |
| return clk_get_index(priv, binding_id); |
| } |
| |
| static int stm32_clk_configure_clk(struct stm32_clk_priv *priv, uint32_t data) |
| { |
| int sel = (data & CLK_SEL_MASK) >> CLK_SEL_SHIFT; |
| int enable = (data & CLK_ON_MASK) >> CLK_ON_SHIFT; |
| int clk_id; |
| int ret; |
| |
| clk_id = stm32_clk_configure_clk_get_binding_id(priv, data); |
| if (clk_id < 0) { |
| return clk_id; |
| } |
| |
| ret = _clk_stm32_set_parent_by_index(priv, clk_id, sel); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| if (enable != 0) { |
| clk_stm32_enable_call_ops(priv, clk_id); |
| } else { |
| clk_stm32_disable_call_ops(priv, clk_id); |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_configure_mux(struct stm32_clk_priv *priv, uint32_t data) |
| { |
| int mux = (data & MUX_ID_MASK) >> MUX_ID_SHIFT; |
| int sel = (data & MUX_SEL_MASK) >> MUX_SEL_SHIFT; |
| |
| return clk_mux_set_parent(priv, mux, sel); |
| } |
| |
| static int stm32_clk_dividers_configure(struct stm32_clk_priv *priv) |
| { |
| struct stm32_clk_platdata *pdata = priv->pdata; |
| uint32_t i; |
| |
| for (i = 0; i < pdata->nclkdiv; i++) { |
| int div_id, div_n; |
| int val; |
| int ret; |
| |
| val = pdata->clkdiv[i] & CMD_DATA_MASK; |
| div_id = (val & DIV_ID_MASK) >> DIV_ID_SHIFT; |
| div_n = (val & DIV_DIVN_MASK) >> DIV_DIVN_SHIFT; |
| |
| ret = clk_stm32_set_div(priv, div_id, div_n); |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_source_configure(struct stm32_clk_priv *priv) |
| { |
| struct stm32_clk_platdata *pdata = priv->pdata; |
| bool ckper_disabled = false; |
| int clk_id; |
| int ret; |
| uint32_t i; |
| |
| for (i = 0; i < pdata->nclksrc; i++) { |
| uint32_t val = pdata->clksrc[i]; |
| uint32_t cmd, cmd_data; |
| |
| if (val == (uint32_t)CLK_CKPER_DISABLED) { |
| ckper_disabled = true; |
| continue; |
| } |
| |
| if (val == (uint32_t)CLK_RTC_DISABLED) { |
| continue; |
| } |
| |
| cmd = (val & CMD_MASK) >> CMD_SHIFT; |
| cmd_data = val & ~CMD_MASK; |
| |
| switch (cmd) { |
| case CMD_MUX: |
| ret = stm32_clk_configure_mux(priv, cmd_data); |
| break; |
| |
| case CMD_CLK: |
| clk_id = stm32_clk_configure_clk_get_binding_id(priv, cmd_data); |
| |
| if (clk_id == _RTCCK) { |
| if ((_clk_stm32_is_enabled(priv, _RTCCK) == true)) { |
| continue; |
| } |
| } |
| |
| ret = stm32_clk_configure_clk(priv, cmd_data); |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| |
| /* |
| * CKPER is source for some peripheral clocks |
| * (FMC-NAND / QPSI-NOR) and switching source is allowed |
| * only if previous clock is still ON |
| * => deactivate CKPER only after switching clock |
| */ |
| if (ckper_disabled) { |
| ret = stm32_clk_configure_mux(priv, CLK_CKPER_DISABLED); |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_stgen_configure(struct stm32_clk_priv *priv, int id) |
| { |
| unsigned long stgen_freq; |
| |
| stgen_freq = _clk_stm32_get_rate(priv, id); |
| |
| stm32mp_stgen_config(stgen_freq); |
| |
| return 0; |
| } |
| |
| #define CLK_PLL_CFG(_idx, _clk_id, _type, _reg)\ |
| [(_idx)] = {\ |
| .clk_id = (_clk_id),\ |
| .plltype = (_type),\ |
| .reg_pllxcr = (_reg),\ |
| } |
| |
| static int clk_stm32_pll_compute_cfgr1(struct stm32_clk_priv *priv, |
| const struct stm32_clk_pll *pll, |
| struct stm32_pll_vco *vco, |
| uint32_t *value) |
| { |
| uint32_t divm = vco->div_mn[PLL_CFG_M]; |
| uint32_t divn = vco->div_mn[PLL_CFG_N]; |
| unsigned long prate = 0UL; |
| unsigned long refclk = 0UL; |
| |
| prate = _clk_stm32_get_parent_rate(priv, pll->clk_id); |
| refclk = prate / (divm + 1U); |
| |
| if ((refclk < (stm32mp1_pll[pll->plltype].refclk_min * 1000000U)) || |
| (refclk > (stm32mp1_pll[pll->plltype].refclk_max * 1000000U))) { |
| return -EINVAL; |
| } |
| |
| *value = 0; |
| |
| if ((pll->plltype == PLL_800) && (refclk >= 8000000U)) { |
| *value = 1U << RCC_PLLNCFGR1_IFRGE_SHIFT; |
| } |
| |
| *value |= (divn << RCC_PLLNCFGR1_DIVN_SHIFT) & RCC_PLLNCFGR1_DIVN_MASK; |
| *value |= (divm << RCC_PLLNCFGR1_DIVM_SHIFT) & RCC_PLLNCFGR1_DIVM_MASK; |
| |
| return 0; |
| } |
| |
| static uint32_t clk_stm32_pll_compute_cfgr2(struct stm32_pll_output *out) |
| { |
| uint32_t value = 0; |
| |
| value |= (out->output[PLL_CFG_P] << RCC_PLLNCFGR2_DIVP_SHIFT) & RCC_PLLNCFGR2_DIVP_MASK; |
| value |= (out->output[PLL_CFG_Q] << RCC_PLLNCFGR2_DIVQ_SHIFT) & RCC_PLLNCFGR2_DIVQ_MASK; |
| value |= (out->output[PLL_CFG_R] << RCC_PLLNCFGR2_DIVR_SHIFT) & RCC_PLLNCFGR2_DIVR_MASK; |
| |
| return value; |
| } |
| |
| static void clk_stm32_pll_config_vco(struct stm32_clk_priv *priv, |
| const struct stm32_clk_pll *pll, |
| struct stm32_pll_vco *vco) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| uint32_t value = 0; |
| |
| if (clk_stm32_pll_compute_cfgr1(priv, pll, vco, &value) != 0) { |
| EARLY_ERROR("Invalid Vref clock !\n"); |
| panic(); |
| } |
| |
| /* Write N / M / IFREGE fields */ |
| mmio_write_32(pll_base + RCC_OFFSET_PLLXCFGR1, value); |
| |
| /* Fractional configuration */ |
| mmio_write_32(pll_base + RCC_OFFSET_PLLXFRACR, 0); |
| |
| /* Frac must be enabled only once its configuration is loaded */ |
| mmio_write_32(pll_base + RCC_OFFSET_PLLXFRACR, vco->frac << RCC_PLLNFRACR_FRACV_SHIFT); |
| mmio_setbits_32(pll_base + RCC_OFFSET_PLLXFRACR, RCC_PLLNFRACR_FRACLE); |
| } |
| |
| static void clk_stm32_pll_config_csg(struct stm32_clk_priv *priv, |
| const struct stm32_clk_pll *pll, |
| struct stm32_pll_vco *vco) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| uint32_t mod_per = 0; |
| uint32_t inc_step = 0; |
| uint32_t sscg_mode = 0; |
| uint32_t value = 0; |
| |
| if (!vco->csg_enabled) { |
| return; |
| } |
| |
| mod_per = vco->csg[PLL_CSG_MOD_PER]; |
| inc_step = vco->csg[PLL_CSG_INC_STEP]; |
| sscg_mode = vco->csg[PLL_CSG_SSCG_MODE]; |
| |
| value |= (mod_per << RCC_PLLNCSGR_MOD_PER_SHIFT) & RCC_PLLNCSGR_MOD_PER_MASK; |
| value |= (inc_step << RCC_PLLNCSGR_INC_STEP_SHIFT) & RCC_PLLNCSGR_INC_STEP_MASK; |
| value |= (sscg_mode << RCC_PLLNCSGR_SSCG_MODE_SHIFT) & RCC_PLLNCSGR_SSCG_MODE_MASK; |
| |
| mmio_write_32(pll_base + RCC_OFFSET_PLLXCSGR, value); |
| mmio_setbits_32(pll_base + RCC_OFFSET_PLLXCR, RCC_PLLNCR_SSCG_CTRL); |
| } |
| |
| static void clk_stm32_pll_config_out(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll, |
| struct stm32_pll_output *out) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| uint32_t value = 0; |
| |
| value = clk_stm32_pll_compute_cfgr2(out); |
| |
| mmio_write_32(pll_base + RCC_OFFSET_PLLXCFGR2, value); |
| } |
| |
| static inline struct stm32_pll_dt_cfg *clk_stm32_pll_get_pdata(int pll_idx) |
| { |
| struct stm32_clk_priv *priv = clk_stm32_get_priv(); |
| struct stm32_clk_platdata *pdata = priv->pdata; |
| |
| return &pdata->pll[pll_idx]; |
| } |
| |
| /* Define characteristic for PLL1 : PLL_2000 */ |
| #define POST_DIVM_MIN 8000000U |
| #define POST_DIVM_MAX 16000000U |
| #define DIVM_MIN 0U |
| #define DIVM_MAX 63U |
| #define DIVN_MIN 24U |
| #define DIVN_MAX 99U |
| #define DIVP_MIN 0U |
| #define DIVP_MAX 127U |
| #define FRAC_MAX 8192U |
| #define VCO_MIN 992000000U |
| #define VCO_MAX 2000000000U |
| |
| static int clk_compute_pll1_settings(uint32_t freq_khz) |
| { |
| struct stm32_clk_priv *priv = clk_stm32_get_priv(); |
| struct stm32_pll_dt_cfg *pll1 = clk_stm32_pll_get_pdata(_PLL1); |
| struct stm32_pll_dt_cfg *pll2 = clk_stm32_pll_get_pdata(_PLL2); |
| unsigned long long best_diff = ULLONG_MAX; |
| unsigned int divm; |
| unsigned long input_freq = 0UL; |
| uint32_t src = pll2->vco.src; |
| |
| /* PLL1 share the same clock source than PLL2 */ |
| switch (src) { |
| case CLK_PLL12_HSI: |
| input_freq = _clk_stm32_get_rate(priv, _CK_HSI); |
| break; |
| case CLK_PLL12_HSE: |
| input_freq = _clk_stm32_get_rate(priv, _CK_HSE); |
| break; |
| default: |
| break; |
| } |
| |
| if (input_freq == 0UL) { |
| panic(); |
| } |
| |
| /* Following parameters have always the same value */ |
| pll1->output.output[PLL_CFG_Q] = 0U; |
| pll1->output.output[PLL_CFG_R] = 0U; |
| |
| for (divm = (DIVM_MAX + 1U); divm != DIVM_MIN; divm--) { |
| unsigned long post_divm = input_freq / divm; |
| unsigned int divp; |
| |
| if ((post_divm < POST_DIVM_MIN) || (post_divm > POST_DIVM_MAX)) { |
| continue; |
| } |
| |
| for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { |
| unsigned long long output_freq = freq_khz * 1000ULL; |
| unsigned long long freq; |
| unsigned long long divn; |
| unsigned long long frac; |
| unsigned int i; |
| |
| freq = output_freq * divm * (divp + 1U); |
| |
| divn = (freq / input_freq) - 1U; |
| if ((divn < DIVN_MIN) || (divn > DIVN_MAX)) { |
| continue; |
| } |
| |
| frac = ((freq * FRAC_MAX) / input_freq) - ((divn + 1U) * FRAC_MAX); |
| |
| /* 2 loops to refine the fractional part */ |
| for (i = 2U; i != 0U; i--) { |
| unsigned long long diff; |
| unsigned long long vco; |
| |
| if (frac > FRAC_MAX) { |
| break; |
| } |
| |
| vco = (post_divm * (divn + 1U)) + ((post_divm * frac) / FRAC_MAX); |
| |
| if ((vco < (VCO_MIN / 2U)) || (vco > (VCO_MAX / 2U))) { |
| frac++; |
| continue; |
| } |
| |
| freq = vco / (divp + 1U); |
| if (output_freq < freq) { |
| diff = freq - output_freq; |
| } else { |
| diff = output_freq - freq; |
| } |
| |
| if (diff < best_diff) { |
| pll1->vco.src = src; |
| pll1->vco.status = RCC_PLLNCR_DIVPEN | RCC_PLLNCR_PLLON; |
| pll1->vco.div_mn[PLL_CFG_M] = divm - 1U; |
| pll1->vco.div_mn[PLL_CFG_N] = (uint32_t)divn; |
| pll1->vco.frac = (uint32_t)frac; |
| pll1->output.output[PLL_CFG_P] = divp; |
| |
| if (diff == 0U) { |
| return 0; |
| } |
| |
| best_diff = diff; |
| } |
| |
| frac++; |
| } |
| } |
| } |
| |
| if (best_diff == ULLONG_MAX) { |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static bool _clk_stm32_pll_is_enabled(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| |
| return ((mmio_read_32(pll_base) & RCC_PLLNCR_PLLON) != 0U); |
| } |
| |
| static void _clk_stm32_pll_set_on(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| |
| /* Preserve RCC_PLLNCR_SSCG_CTRL value */ |
| mmio_clrsetbits_32(pll_base, RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN, |
| RCC_PLLNCR_PLLON); |
| } |
| |
| static void _clk_stm32_pll_set_off(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| |
| /* Stop all output */ |
| mmio_clrbits_32(pll_base, RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN); |
| |
| /* Stop PLL */ |
| mmio_clrbits_32(pll_base, RCC_PLLNCR_PLLON); |
| } |
| |
| static int _clk_stm32_pll_wait_ready_on(struct stm32_clk_priv *priv, |
| const struct stm32_clk_pll *pll) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| uint64_t timeout = timeout_init_us(PLLRDY_TIMEOUT); |
| |
| /* Wait PLL lock */ |
| while ((mmio_read_32(pll_base) & RCC_PLLNCR_PLLRDY) == 0U) { |
| if (timeout_elapsed(timeout)) { |
| ERROR("PLL%d start failed @ 0x%x: 0x%x\n", |
| pll->clk_id - _CK_PLL1 + 1, pll->reg_pllxcr, mmio_read_32(pll_base)); |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int _clk_stm32_pll_wait_ready_off(struct stm32_clk_priv *priv, |
| const struct stm32_clk_pll *pll) |
| { |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| uint64_t timeout = timeout_init_us(PLLRDY_TIMEOUT); |
| |
| /* Wait PLL lock */ |
| while ((mmio_read_32(pll_base) & RCC_PLLNCR_PLLRDY) != 0U) { |
| if (timeout_elapsed(timeout)) { |
| ERROR("PLL%d stop failed @ 0x%x: 0x%x\n", |
| pll->clk_id - _CK_PLL1 + 1, pll->reg_pllxcr, mmio_read_32(pll_base)); |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int _clk_stm32_pll_enable(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) |
| { |
| if (_clk_stm32_pll_is_enabled(priv, pll)) { |
| return 0; |
| } |
| |
| /* Preserve RCC_PLLNCR_SSCG_CTRL value */ |
| _clk_stm32_pll_set_on(priv, pll); |
| |
| /* Wait PLL lock */ |
| return _clk_stm32_pll_wait_ready_on(priv, pll); |
| } |
| |
| static void _clk_stm32_pll_disable(struct stm32_clk_priv *priv, const struct stm32_clk_pll *pll) |
| { |
| if (!_clk_stm32_pll_is_enabled(priv, pll)) { |
| return; |
| } |
| |
| /* Stop all outputs and the PLL */ |
| _clk_stm32_pll_set_off(priv, pll); |
| |
| /* Wait PLL stopped */ |
| _clk_stm32_pll_wait_ready_off(priv, pll); |
| } |
| |
| static int _clk_stm32_pll_init(struct stm32_clk_priv *priv, int pll_idx, |
| struct stm32_pll_dt_cfg *pll_conf) |
| { |
| const struct stm32_clk_pll *pll = clk_st32_pll_data(pll_idx); |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| int ret = 0; |
| |
| /* Configure PLLs source */ |
| ret = stm32_clk_configure_mux(priv, pll_conf->vco.src); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| #if STM32MP_USB_PROGRAMMER |
| if ((pll_idx == _PLL4) && pll4_bootrom) { |
| clk_stm32_pll_config_out(priv, pll, &pll_conf->output); |
| |
| mmio_setbits_32(pll_base, |
| RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN); |
| |
| return 0; |
| } |
| #endif |
| /* Stop the PLL before */ |
| _clk_stm32_pll_disable(priv, pll); |
| |
| clk_stm32_pll_config_vco(priv, pll, &pll_conf->vco); |
| clk_stm32_pll_config_out(priv, pll, &pll_conf->output); |
| clk_stm32_pll_config_csg(priv, pll, &pll_conf->vco); |
| |
| ret = _clk_stm32_pll_enable(priv, pll); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| mmio_setbits_32(pll_base, RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN); |
| |
| return 0; |
| } |
| |
| static int clk_stm32_pll_init(struct stm32_clk_priv *priv, int pll_idx) |
| { |
| struct stm32_pll_dt_cfg *pll_conf = clk_stm32_pll_get_pdata(pll_idx); |
| |
| if (pll_conf->vco.status != 0U) { |
| return _clk_stm32_pll_init(priv, pll_idx, pll_conf); |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_pll_configure(struct stm32_clk_priv *priv) |
| { |
| int err = 0; |
| |
| err = clk_stm32_pll_init(priv, _PLL1); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = clk_stm32_pll_init(priv, _PLL2); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = clk_stm32_pll_init(priv, _PLL3); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = clk_stm32_pll_init(priv, _PLL4); |
| if (err != 0) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_oscillators_wait_lse_ready(struct stm32_clk_priv *priv) |
| { |
| int ret = 0; |
| |
| if (_clk_stm32_get_rate(priv, _CK_LSE) != 0U) { |
| ret = clk_oscillator_wait_ready_on(priv, _CK_LSE); |
| } |
| |
| return ret; |
| } |
| |
| static void stm32_clk_oscillators_enable(struct stm32_clk_priv *priv) |
| { |
| stm32_enable_oscillator_hse(priv); |
| stm32_enable_oscillator_lse(priv); |
| _clk_stm32_enable(priv, _CK_LSI); |
| _clk_stm32_enable(priv, _CK_CSI); |
| } |
| |
| static int stm32_clk_hsidiv_configure(struct stm32_clk_priv *priv) |
| { |
| return stm32mp1_hsidiv(_clk_stm32_get_rate(priv, _CK_HSI)); |
| } |
| |
| #if STM32MP_USB_PROGRAMMER |
| static bool stm32mp1_clk_is_pll4_used_by_bootrom(struct stm32_clk_priv *priv, int usbphy_p) |
| { |
| /* Don't initialize PLL4, when used by BOOTROM */ |
| if ((stm32mp_get_boot_itf_selected() == |
| BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB) && |
| (usbphy_p == _PLL4R)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static int stm32mp1_clk_check_usb_conflict(struct stm32_clk_priv *priv, int usbphy_p, int usbo_p) |
| { |
| int _usbo_p; |
| int _usbphy_p; |
| |
| if (!pll4_bootrom) { |
| return 0; |
| } |
| |
| _usbo_p = _clk_stm32_get_parent(priv, _USBO_K); |
| _usbphy_p = _clk_stm32_get_parent(priv, _USBPHY_K); |
| |
| if ((_usbo_p != usbo_p) || (_usbphy_p != usbphy_p)) { |
| return -FDT_ERR_BADVALUE; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static struct clk_oscillator_data stm32mp13_osc_data[NB_OSCILLATOR] = { |
| OSCILLATOR(OSC_HSI, _CK_HSI, "clk-hsi", GATE_HSI, GATE_HSI_RDY, |
| NULL, NULL, NULL), |
| |
| OSCILLATOR(OSC_LSI, _CK_LSI, "clk-lsi", GATE_LSI, GATE_LSI_RDY, |
| NULL, NULL, NULL), |
| |
| OSCILLATOR(OSC_CSI, _CK_CSI, "clk-csi", GATE_CSI, GATE_CSI_RDY, |
| NULL, NULL, NULL), |
| |
| OSCILLATOR(OSC_LSE, _CK_LSE, "clk-lse", GATE_LSE, GATE_LSE_RDY, |
| BYPASS(RCC_BDCR, 1, 3), |
| CSS(RCC_BDCR, 8), |
| DRIVE(RCC_BDCR, 4, 2, 2)), |
| |
| OSCILLATOR(OSC_HSE, _CK_HSE, "clk-hse", GATE_HSE, GATE_HSE_RDY, |
| BYPASS(RCC_OCENSETR, 10, 7), |
| CSS(RCC_OCENSETR, 11), |
| NULL), |
| |
| OSCILLATOR(OSC_I2SCKIN, _I2SCKIN, "i2s_ckin", NO_GATE, NO_GATE, |
| NULL, NULL, NULL), |
| }; |
| |
| static const char *clk_stm32_get_oscillator_name(enum stm32_osc id) |
| { |
| if (id < NB_OSCILLATOR) { |
| return stm32mp13_osc_data[id].name; |
| } |
| |
| return NULL; |
| } |
| |
| #define CLK_PLL_CFG(_idx, _clk_id, _type, _reg)\ |
| [(_idx)] = {\ |
| .clk_id = (_clk_id),\ |
| .plltype = (_type),\ |
| .reg_pllxcr = (_reg),\ |
| } |
| |
| static const struct stm32_clk_pll stm32_mp13_clk_pll[_PLL_NB] = { |
| CLK_PLL_CFG(_PLL1, _CK_PLL1, PLL_2000, RCC_PLL1CR), |
| CLK_PLL_CFG(_PLL2, _CK_PLL2, PLL_1600, RCC_PLL2CR), |
| CLK_PLL_CFG(_PLL3, _CK_PLL3, PLL_800, RCC_PLL3CR), |
| CLK_PLL_CFG(_PLL4, _CK_PLL4, PLL_800, RCC_PLL4CR), |
| }; |
| |
| static const struct stm32_clk_pll *clk_st32_pll_data(unsigned int idx) |
| { |
| return &stm32_mp13_clk_pll[idx]; |
| } |
| |
| struct stm32_pll_cfg { |
| uint8_t pll_id; |
| }; |
| |
| static unsigned long clk_stm32_pll_recalc_rate(struct stm32_clk_priv *priv, int id, |
| unsigned long prate) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, id); |
| struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; |
| const struct stm32_clk_pll *pll = clk_st32_pll_data(pll_cfg->pll_id); |
| uintptr_t pll_base = priv->base + pll->reg_pllxcr; |
| uint32_t cfgr1, fracr, divm, divn; |
| unsigned long fvco; |
| |
| cfgr1 = mmio_read_32(pll_base + RCC_OFFSET_PLLXCFGR1); |
| fracr = mmio_read_32(pll_base + RCC_OFFSET_PLLXFRACR); |
| |
| divm = (cfgr1 & (RCC_PLLNCFGR1_DIVM_MASK)) >> RCC_PLLNCFGR1_DIVM_SHIFT; |
| divn = cfgr1 & RCC_PLLNCFGR1_DIVN_MASK; |
| |
| /* |
| * 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) + fracv; |
| numerator = prate * numerator; |
| denominator = ((unsigned long long)divm + 1U) << 13; |
| fvco = (unsigned long)(numerator / denominator); |
| } else { |
| fvco = (unsigned long)(prate * (divn + 1U) / (divm + 1U)); |
| } |
| |
| return fvco; |
| }; |
| |
| static bool clk_stm32_pll_is_enabled(struct stm32_clk_priv *priv, int id) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, id); |
| struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; |
| const struct stm32_clk_pll *pll = clk_st32_pll_data(pll_cfg->pll_id); |
| |
| return _clk_stm32_pll_is_enabled(priv, pll); |
| } |
| |
| static int clk_stm32_pll_enable(struct stm32_clk_priv *priv, int id) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, id); |
| struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; |
| const struct stm32_clk_pll *pll = clk_st32_pll_data(pll_cfg->pll_id); |
| |
| return _clk_stm32_pll_enable(priv, pll); |
| } |
| |
| static void clk_stm32_pll_disable(struct stm32_clk_priv *priv, int id) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, id); |
| struct stm32_pll_cfg *pll_cfg = clk->clock_cfg; |
| const struct stm32_clk_pll *pll = clk_st32_pll_data(pll_cfg->pll_id); |
| |
| _clk_stm32_pll_disable(priv, pll); |
| } |
| |
| static const struct stm32_clk_ops clk_stm32_pll_ops = { |
| .recalc_rate = clk_stm32_pll_recalc_rate, |
| .enable = clk_stm32_pll_enable, |
| .disable = clk_stm32_pll_disable, |
| .is_enabled = clk_stm32_pll_is_enabled, |
| }; |
| |
| #define CLK_PLL(idx, _idx, _parent, _gate, _pll_id, _flags)[idx] = {\ |
| .binding = _idx,\ |
| .parent = _parent,\ |
| .flags = (_flags),\ |
| .clock_cfg = &(struct stm32_pll_cfg) {\ |
| .pll_id = _pll_id,\ |
| },\ |
| .ops = STM32_PLL_OPS,\ |
| } |
| |
| struct clk_stm32_composite_cfg { |
| uint8_t gate_id; |
| uint8_t div_id; |
| }; |
| |
| static unsigned long clk_stm32_composite_recalc_rate(struct stm32_clk_priv *priv, |
| int idx, unsigned long prate) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, idx); |
| struct clk_stm32_composite_cfg *composite_cfg = clk->clock_cfg; |
| |
| return _clk_stm32_divider_recalc(priv, composite_cfg->div_id, prate); |
| }; |
| |
| static bool clk_stm32_composite_gate_is_enabled(struct stm32_clk_priv *priv, int idx) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, idx); |
| struct clk_stm32_composite_cfg *composite_cfg = clk->clock_cfg; |
| |
| return _clk_stm32_gate_is_enabled(priv, composite_cfg->gate_id); |
| } |
| |
| static int clk_stm32_composite_gate_enable(struct stm32_clk_priv *priv, int idx) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, idx); |
| struct clk_stm32_composite_cfg *composite_cfg = clk->clock_cfg; |
| |
| return _clk_stm32_gate_enable(priv, composite_cfg->gate_id); |
| } |
| |
| static void clk_stm32_composite_gate_disable(struct stm32_clk_priv *priv, int idx) |
| { |
| const struct clk_stm32 *clk = _clk_get(priv, idx); |
| struct clk_stm32_composite_cfg *composite_cfg = clk->clock_cfg; |
| |
| _clk_stm32_gate_disable(priv, composite_cfg->gate_id); |
| } |
| |
| static const struct stm32_clk_ops clk_stm32_composite_ops = { |
| .recalc_rate = clk_stm32_composite_recalc_rate, |
| .is_enabled = clk_stm32_composite_gate_is_enabled, |
| .enable = clk_stm32_composite_gate_enable, |
| .disable = clk_stm32_composite_gate_disable, |
| }; |
| |
| #define STM32_COMPOSITE(idx, _binding, _parent, _flags, _gate_id,\ |
| _div_id)[idx] = {\ |
| .binding = (_binding),\ |
| .parent = (_parent),\ |
| .flags = (_flags),\ |
| .clock_cfg = &(struct clk_stm32_composite_cfg) {\ |
| .gate_id = (_gate_id),\ |
| .div_id = (_div_id),\ |
| },\ |
| .ops = STM32_COMPOSITE_OPS,\ |
| } |
| |
| enum { |
| STM32_PLL_OPS = STM32_LAST_OPS, |
| STM32_COMPOSITE_OPS, |
| |
| MP13_LAST_OPS |
| }; |
| |
| static const struct stm32_clk_ops *ops_array_mp13[MP13_LAST_OPS] = { |
| [NO_OPS] = NULL, |
| [FIXED_FACTOR_OPS] = &clk_fixed_factor_ops, |
| [GATE_OPS] = &clk_gate_ops, |
| [STM32_MUX_OPS] = &clk_mux_ops, |
| [STM32_DIVIDER_OPS] = &clk_stm32_divider_ops, |
| [STM32_GATE_OPS] = &clk_stm32_gate_ops, |
| [STM32_TIMER_OPS] = &clk_timer_ops, |
| [STM32_FIXED_RATE_OPS] = &clk_stm32_fixed_rate_ops, |
| [STM32_OSC_OPS] = &clk_stm32_osc_ops, |
| [STM32_OSC_NOGATE_OPS] = &clk_stm32_osc_nogate_ops, |
| |
| [STM32_PLL_OPS] = &clk_stm32_pll_ops, |
| [STM32_COMPOSITE_OPS] = &clk_stm32_composite_ops |
| }; |
| |
| static const struct clk_stm32 stm32mp13_clk[CK_LAST] = { |
| /* ROOT CLOCKS */ |
| CLK_FIXED_RATE(_CK_OFF, _NO_ID, 0), |
| CLK_OSC(_CK_HSE, CK_HSE, CLK_IS_ROOT, OSC_HSE), |
| CLK_OSC(_CK_HSI, CK_HSI, CLK_IS_ROOT, OSC_HSI), |
| CLK_OSC(_CK_CSI, CK_CSI, CLK_IS_ROOT, OSC_CSI), |
| CLK_OSC(_CK_LSI, CK_LSI, CLK_IS_ROOT, OSC_LSI), |
| CLK_OSC(_CK_LSE, CK_LSE, CLK_IS_ROOT, OSC_LSE), |
| |
| CLK_OSC_FIXED(_I2SCKIN, _NO_ID, CLK_IS_ROOT, OSC_I2SCKIN), |
| |
| CLK_FIXED_RATE(_USB_PHY_48, _NO_ID, USB_PHY_48_MHZ), |
| |
| STM32_DIV(_HSE_DIV, _NO_ID, _CK_HSE, 0, DIV_RTC), |
| |
| FIXED_FACTOR(_HSE_DIV2, CK_HSE_DIV2, _CK_HSE, 1, 2), |
| FIXED_FACTOR(_CSI_DIV122, _NO_ID, _CK_CSI, 1, 122), |
| |
| CLK_PLL(_CK_PLL1, PLL1, MUX(MUX_PLL12), GATE_PLL1, _PLL1, 0), |
| CLK_PLL(_CK_PLL2, PLL2, MUX(MUX_PLL12), GATE_PLL2, _PLL2, 0), |
| CLK_PLL(_CK_PLL3, PLL3, MUX(MUX_PLL3), GATE_PLL3, _PLL3, 0), |
| CLK_PLL(_CK_PLL4, PLL4, MUX(MUX_PLL4), GATE_PLL4, _PLL4, 0), |
| |
| STM32_COMPOSITE(_PLL1P, PLL1_P, _CK_PLL1, CLK_IS_CRITICAL, GATE_PLL1_DIVP, DIV_PLL1DIVP), |
| STM32_DIV(_PLL1P_DIV, _NO_ID, _CK_PLL1, 0, DIV_MPU), |
| |
| STM32_COMPOSITE(_PLL2P, PLL2_P, _CK_PLL2, CLK_IS_CRITICAL, GATE_PLL2_DIVP, DIV_PLL2DIVP), |
| STM32_COMPOSITE(_PLL2Q, PLL2_Q, _CK_PLL2, 0, GATE_PLL2_DIVQ, DIV_PLL2DIVQ), |
| STM32_COMPOSITE(_PLL2R, PLL2_R, _CK_PLL2, CLK_IS_CRITICAL, GATE_PLL2_DIVR, DIV_PLL2DIVR), |
| |
| STM32_COMPOSITE(_PLL3P, PLL3_P, _CK_PLL3, 0, GATE_PLL3_DIVP, DIV_PLL3DIVP), |
| STM32_COMPOSITE(_PLL3Q, PLL3_Q, _CK_PLL3, 0, GATE_PLL3_DIVQ, DIV_PLL3DIVQ), |
| STM32_COMPOSITE(_PLL3R, PLL3_R, _CK_PLL3, 0, GATE_PLL3_DIVR, DIV_PLL3DIVR), |
| |
| STM32_COMPOSITE(_PLL4P, PLL4_P, _CK_PLL4, 0, GATE_PLL4_DIVP, DIV_PLL4DIVP), |
| STM32_COMPOSITE(_PLL4Q, PLL4_Q, _CK_PLL4, 0, GATE_PLL4_DIVQ, DIV_PLL4DIVQ), |
| STM32_COMPOSITE(_PLL4R, PLL4_R, _CK_PLL4, 0, GATE_PLL4_DIVR, DIV_PLL4DIVR), |
| |
| STM32_MUX(_CKMPU, CK_MPU, MUX_MPU, 0), |
| STM32_DIV(_CKAXI, CK_AXI, MUX(MUX_AXI), 0, DIV_AXI), |
| STM32_DIV(_CKMLAHB, CK_MLAHB, MUX(MUX_MLAHB), CLK_IS_CRITICAL, DIV_MLAHB), |
| STM32_MUX(_CKPER, CK_PER, MUX(MUX_CKPER), 0), |
| |
| STM32_DIV(_PCLK1, PCLK1, _CKMLAHB, 0, DIV_APB1), |
| STM32_DIV(_PCLK2, PCLK2, _CKMLAHB, 0, DIV_APB2), |
| STM32_DIV(_PCLK3, PCLK3, _CKMLAHB, 0, DIV_APB3), |
| STM32_DIV(_PCLK4, PCLK4, _CKAXI, 0, DIV_APB4), |
| STM32_DIV(_PCLK5, PCLK5, _CKAXI, 0, DIV_APB5), |
| STM32_DIV(_PCLK6, PCLK6, _CKMLAHB, 0, DIV_APB6), |
| |
| CK_TIMER(_CKTIMG1, CK_TIMG1, _PCLK1, 0, RCC_APB1DIVR, RCC_TIMG1PRER), |
| CK_TIMER(_CKTIMG2, CK_TIMG2, _PCLK2, 0, RCC_APB2DIVR, RCC_TIMG2PRER), |
| CK_TIMER(_CKTIMG3, CK_TIMG3, _PCLK6, 0, RCC_APB6DIVR, RCC_TIMG3PRER), |
| |
| /* END ROOT CLOCKS */ |
| |
| STM32_GATE(_DDRC1, DDRC1, _CKAXI, CLK_IS_CRITICAL, GATE_DDRC1), |
| STM32_GATE(_DDRC1LP, DDRC1LP, _CKAXI, CLK_IS_CRITICAL, GATE_DDRC1LP), |
| STM32_GATE(_DDRPHYC, DDRPHYC, _PLL2R, CLK_IS_CRITICAL, GATE_DDRPHYC), |
| STM32_GATE(_DDRPHYCLP, DDRPHYCLP, _PLL2R, CLK_IS_CRITICAL, GATE_DDRPHYCLP), |
| STM32_GATE(_DDRCAPB, DDRCAPB, _PCLK4, CLK_IS_CRITICAL, GATE_DDRCAPB), |
| STM32_GATE(_DDRCAPBLP, DDRCAPBLP, _PCLK4, CLK_IS_CRITICAL, GATE_DDRCAPBLP), |
| STM32_GATE(_AXIDCG, AXIDCG, _CKAXI, CLK_IS_CRITICAL, GATE_AXIDCG), |
| STM32_GATE(_DDRPHYCAPB, DDRPHYCAPB, _PCLK4, CLK_IS_CRITICAL, GATE_DDRPHYCAPB), |
| STM32_GATE(_DDRPHYCAPBLP, DDRPHYCAPBLP, _PCLK4, CLK_IS_CRITICAL, GATE_DDRPHYCAPBLP), |
| |
| STM32_GATE(_SYSCFG, SYSCFG, _PCLK3, 0, GATE_SYSCFG), |
| STM32_GATE(_DDRPERFM, DDRPERFM, _PCLK4, 0, GATE_DDRPERFM), |
| STM32_GATE(_IWDG2APB, IWDG2, _PCLK4, 0, GATE_IWDG2APB), |
| STM32_GATE(_USBPHY_K, USBPHY_K, MUX(MUX_USBPHY), 0, GATE_USBPHY), |
| STM32_GATE(_USBO_K, USBO_K, MUX(MUX_USBO), 0, GATE_USBO), |
| |
| STM32_GATE(_RTCAPB, RTCAPB, _PCLK5, CLK_IS_CRITICAL, GATE_RTCAPB), |
| STM32_GATE(_TZC, TZC, _PCLK5, CLK_IS_CRITICAL, GATE_TZC), |
| STM32_GATE(_ETZPC, TZPC, _PCLK5, CLK_IS_CRITICAL, GATE_ETZPC), |
| STM32_GATE(_IWDG1APB, IWDG1, _PCLK5, 0, GATE_IWDG1APB), |
| STM32_GATE(_BSEC, BSEC, _PCLK5, CLK_IS_CRITICAL, GATE_BSEC), |
| STM32_GATE(_STGENC, STGEN_K, MUX(MUX_STGEN), CLK_IS_CRITICAL, GATE_STGENC), |
| |
| STM32_GATE(_USART1_K, USART1_K, MUX(MUX_UART1), 0, GATE_USART1), |
| STM32_GATE(_USART2_K, USART2_K, MUX(MUX_UART2), 0, GATE_USART2), |
| STM32_GATE(_I2C3_K, I2C3_K, MUX(MUX_I2C3), 0, GATE_I2C3), |
| STM32_GATE(_I2C4_K, I2C4_K, MUX(MUX_I2C4), 0, GATE_I2C4), |
| STM32_GATE(_I2C5_K, I2C5_K, MUX(MUX_I2C5), 0, GATE_I2C5), |
| STM32_GATE(_TIM12, TIM12_K, _CKTIMG3, 0, GATE_TIM12), |
| STM32_GATE(_TIM15, TIM15_K, _CKTIMG3, 0, GATE_TIM15), |
| |
| STM32_GATE(_RTCCK, RTC, MUX(MUX_RTC), 0, GATE_RTCCK), |
| |
| STM32_GATE(_GPIOA, GPIOA, _CKMLAHB, 0, GATE_GPIOA), |
| STM32_GATE(_GPIOB, GPIOB, _CKMLAHB, 0, GATE_GPIOB), |
| STM32_GATE(_GPIOC, GPIOC, _CKMLAHB, 0, GATE_GPIOC), |
| STM32_GATE(_GPIOD, GPIOD, _CKMLAHB, 0, GATE_GPIOD), |
| STM32_GATE(_GPIOE, GPIOE, _CKMLAHB, 0, GATE_GPIOE), |
| STM32_GATE(_GPIOF, GPIOF, _CKMLAHB, 0, GATE_GPIOF), |
| STM32_GATE(_GPIOG, GPIOG, _CKMLAHB, 0, GATE_GPIOG), |
| STM32_GATE(_GPIOH, GPIOH, _CKMLAHB, 0, GATE_GPIOH), |
| STM32_GATE(_GPIOI, GPIOI, _CKMLAHB, 0, GATE_GPIOI), |
| |
| STM32_GATE(_PKA, PKA, _CKAXI, 0, GATE_PKA), |
| STM32_GATE(_SAES_K, SAES_K, MUX(MUX_SAES), 0, GATE_SAES), |
| STM32_GATE(_CRYP1, CRYP1, _PCLK5, 0, GATE_CRYP1), |
| STM32_GATE(_HASH1, HASH1, _PCLK5, 0, GATE_HASH1), |
| |
| STM32_GATE(_RNG1_K, RNG1_K, MUX(MUX_RNG1), 0, GATE_RNG1), |
| STM32_GATE(_BKPSRAM, BKPSRAM, _PCLK5, CLK_IS_CRITICAL, GATE_BKPSRAM), |
| |
| STM32_GATE(_SDMMC1_K, SDMMC1_K, MUX(MUX_SDMMC1), 0, GATE_SDMMC1), |
| STM32_GATE(_SDMMC2_K, SDMMC2_K, MUX(MUX_SDMMC2), 0, GATE_SDMMC2), |
| STM32_GATE(_DBGCK, CK_DBG, _CKAXI, 0, GATE_DBGCK), |
| |
| STM32_GATE(_USART3_K, USART3_K, MUX(MUX_UART35), 0, GATE_USART3), |
| STM32_GATE(_UART4_K, UART4_K, MUX(MUX_UART4), 0, GATE_UART4), |
| STM32_GATE(_UART5_K, UART5_K, MUX(MUX_UART35), 0, GATE_UART5), |
| STM32_GATE(_UART7_K, UART7_K, MUX(MUX_UART78), 0, GATE_UART7), |
| STM32_GATE(_UART8_K, UART8_K, MUX(MUX_UART78), 0, GATE_UART8), |
| STM32_GATE(_USART6_K, USART6_K, MUX(MUX_UART6), 0, GATE_USART6), |
| STM32_GATE(_MCE, MCE, _CKAXI, CLK_IS_CRITICAL, GATE_MCE), |
| STM32_GATE(_FMC_K, FMC_K, MUX(MUX_FMC), 0, GATE_FMC), |
| STM32_GATE(_QSPI_K, QSPI_K, MUX(MUX_QSPI), 0, GATE_QSPI), |
| |
| STM32_COMPOSITE(_MCO1_K, CK_MCO1, MUX(MUX_MCO1), 0, GATE_MCO1, DIV_MCO1), |
| STM32_COMPOSITE(_MCO2_K, CK_MCO2, MUX(MUX_MCO2), 0, GATE_MCO2, DIV_MCO2), |
| STM32_COMPOSITE(_TRACECK, CK_TRACE, _CKAXI, 0, GATE_TRACECK, DIV_TRACE), |
| }; |
| |
| static struct stm32_pll_dt_cfg mp13_pll[_PLL_NB]; |
| |
| static struct stm32_osci_dt_cfg mp13_osci[NB_OSCILLATOR]; |
| |
| static uint32_t mp13_clksrc[MUX_MAX]; |
| |
| static uint32_t mp13_clkdiv[DIV_MAX]; |
| |
| static struct stm32_clk_platdata stm32mp13_clock_pdata = { |
| .osci = mp13_osci, |
| .nosci = NB_OSCILLATOR, |
| .pll = mp13_pll, |
| .npll = _PLL_NB, |
| .clksrc = mp13_clksrc, |
| .nclksrc = MUX_MAX, |
| .clkdiv = mp13_clkdiv, |
| .nclkdiv = DIV_MAX, |
| }; |
| |
| static struct stm32_clk_priv stm32mp13_clock_data = { |
| .base = RCC_BASE, |
| .num = ARRAY_SIZE(stm32mp13_clk), |
| .clks = stm32mp13_clk, |
| .parents = parent_mp13, |
| .nb_parents = ARRAY_SIZE(parent_mp13), |
| .gates = gates_mp13, |
| .nb_gates = ARRAY_SIZE(gates_mp13), |
| .div = dividers_mp13, |
| .nb_div = ARRAY_SIZE(dividers_mp13), |
| .osci_data = stm32mp13_osc_data, |
| .nb_osci_data = ARRAY_SIZE(stm32mp13_osc_data), |
| .gate_refcounts = refcounts_mp13, |
| .pdata = &stm32mp13_clock_pdata, |
| .ops_array = ops_array_mp13, |
| }; |
| |
| static int stm32mp1_init_clock_tree(void) |
| { |
| struct stm32_clk_priv *priv = clk_stm32_get_priv(); |
| int ret; |
| |
| #if STM32MP_USB_PROGRAMMER |
| int usbphy_p = _clk_stm32_get_parent(priv, _USBPHY_K); |
| int usbo_p = _clk_stm32_get_parent(priv, _USBO_K); |
| |
| /* Don't initialize PLL4, when used by BOOTROM */ |
| pll4_bootrom = stm32mp1_clk_is_pll4_used_by_bootrom(priv, usbphy_p); |
| #endif |
| |
| /* |
| * Switch ON oscillators found in device-tree. |
| * Note: HSI already ON after BootROM stage. |
| */ |
| stm32_clk_oscillators_enable(priv); |
| |
| /* Come back to HSI */ |
| ret = stm32mp1_come_back_to_hsi(); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = stm32_clk_hsidiv_configure(priv); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = stm32_clk_stgen_configure(priv, _STGENC); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| ret = stm32_clk_dividers_configure(priv); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| ret = stm32_clk_pll_configure(priv); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| /* Wait LSE ready before to use it */ |
| ret = stm32_clk_oscillators_wait_lse_ready(priv); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| /* Configure with expected clock source */ |
| ret = stm32_clk_source_configure(priv); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| /* Configure LSE css after RTC source configuration */ |
| ret = stm32_clk_oscillators_lse_set_css(priv); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| #if STM32MP_USB_PROGRAMMER |
| ret = stm32mp1_clk_check_usb_conflict(priv, usbphy_p, usbo_p); |
| if (ret != 0) { |
| return ret; |
| } |
| #endif |
| /* reconfigure STGEN with DT config */ |
| ret = stm32_clk_stgen_configure(priv, _STGENC); |
| if (ret != 0) { |
| panic(); |
| } |
| |
| /* 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 int clk_stm32_parse_oscillator_fdt(void *fdt, int node, const char *name, |
| struct stm32_osci_dt_cfg *osci) |
| { |
| int subnode = 0; |
| |
| /* default value oscillator not found, freq=0 */ |
| osci->freq = 0; |
| |
| fdt_for_each_subnode(subnode, fdt, node) { |
| const char *cchar = NULL; |
| const fdt32_t *cuint = NULL; |
| int ret = 0; |
| |
| cchar = fdt_get_name(fdt, subnode, &ret); |
| if (cchar == NULL) { |
| return ret; |
| } |
| |
| if (strncmp(cchar, name, (size_t)ret) || |
| fdt_get_status(subnode) == DT_DISABLED) { |
| continue; |
| } |
| |
| cuint = fdt_getprop(fdt, subnode, "clock-frequency", &ret); |
| if (cuint == NULL) { |
| return ret; |
| } |
| |
| osci->freq = fdt32_to_cpu(*cuint); |
| |
| if (fdt_getprop(fdt, subnode, "st,bypass", NULL) != NULL) { |
| osci->bypass = true; |
| } |
| |
| if (fdt_getprop(fdt, subnode, "st,digbypass", NULL) != NULL) { |
| osci->digbyp = true; |
| } |
| |
| if (fdt_getprop(fdt, subnode, "st,css", NULL) != NULL) { |
| osci->css = true; |
| } |
| |
| osci->drive = fdt_read_uint32_default(fdt, subnode, "st,drive", LSEDRV_MEDIUM_HIGH); |
| |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_parse_fdt_all_oscillator(void *fdt, struct stm32_clk_platdata *pdata) |
| { |
| int fdt_err = 0; |
| uint32_t i = 0; |
| int node = 0; |
| |
| node = fdt_path_offset(fdt, "/clocks"); |
| if (node < 0) { |
| return -FDT_ERR_NOTFOUND; |
| } |
| |
| for (i = 0; i < pdata->nosci; i++) { |
| const char *name = NULL; |
| |
| name = clk_stm32_get_oscillator_name((enum stm32_osc)i); |
| if (name == NULL) { |
| continue; |
| } |
| |
| fdt_err = clk_stm32_parse_oscillator_fdt(fdt, node, name, &pdata->osci[i]); |
| if (fdt_err < 0) { |
| panic(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| #define RCC_PLL_NAME_SIZE 12 |
| |
| static int clk_stm32_load_vco_config(void *fdt, int subnode, struct stm32_pll_vco *vco) |
| { |
| int err = 0; |
| |
| err = fdt_read_uint32_array(fdt, subnode, "divmn", (int)PLL_DIV_MN_NB, vco->div_mn); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = fdt_read_uint32_array(fdt, subnode, "csg", (int)PLL_CSG_NB, vco->csg); |
| |
| vco->csg_enabled = (err == 0); |
| |
| if (err == -FDT_ERR_NOTFOUND) { |
| err = 0; |
| } |
| |
| if (err != 0) { |
| return err; |
| } |
| |
| vco->status = RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN | RCC_PLLNCR_PLLON; |
| |
| vco->frac = fdt_read_uint32_default(fdt, subnode, "frac", 0); |
| |
| vco->src = fdt_read_uint32_default(fdt, subnode, "src", UINT32_MAX); |
| |
| return 0; |
| } |
| |
| static int clk_stm32_load_output_config(void *fdt, int subnode, struct stm32_pll_output *output) |
| { |
| int err = 0; |
| |
| err = fdt_read_uint32_array(fdt, subnode, "st,pll_div_pqr", (int)PLL_DIV_PQR_NB, |
| output->output); |
| if (err != 0) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int clk_stm32_parse_pll_fdt(void *fdt, int subnode, struct stm32_pll_dt_cfg *pll) |
| { |
| const fdt32_t *cuint = NULL; |
| int subnode_pll = 0; |
| int subnode_vco = 0; |
| int err = 0; |
| |
| cuint = fdt_getprop(fdt, subnode, "st,pll", NULL); |
| if (!cuint) { |
| return -FDT_ERR_NOTFOUND; |
| } |
| |
| subnode_pll = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); |
| if (subnode_pll < 0) { |
| return -FDT_ERR_NOTFOUND; |
| } |
| |
| cuint = fdt_getprop(fdt, subnode_pll, "st,pll_vco", NULL); |
| if (!cuint) { |
| return -FDT_ERR_NOTFOUND; |
| } |
| |
| subnode_vco = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); |
| if (subnode_vco < 0) { |
| return -FDT_ERR_NOTFOUND; |
| } |
| |
| err = clk_stm32_load_vco_config(fdt, subnode_vco, &pll->vco); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = clk_stm32_load_output_config(fdt, subnode_pll, &pll->output); |
| if (err != 0) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_parse_fdt_all_pll(void *fdt, int node, struct stm32_clk_platdata *pdata) |
| { |
| size_t i = 0U; |
| |
| /* PLL1 is not configurable with device tree */ |
| for (i = _PLL2; i < pdata->npll; i++) { |
| struct stm32_pll_dt_cfg *pll = &pdata->pll[i]; |
| char name[RCC_PLL_NAME_SIZE]; |
| int subnode = 0; |
| int err = 0; |
| |
| snprintf(name, sizeof(name), "st,pll@%u", i); |
| |
| subnode = fdt_subnode_offset(fdt, node, name); |
| if (!fdt_check_node(subnode)) { |
| continue; |
| } |
| |
| err = clk_stm32_parse_pll_fdt(fdt, subnode, pll); |
| if (err != 0) { |
| panic(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int stm32_clk_parse_fdt(struct stm32_clk_platdata *pdata) |
| { |
| void *fdt = NULL; |
| int node; |
| uint32_t err; |
| |
| if (fdt_get_address(&fdt) == 0) { |
| return -ENOENT; |
| } |
| |
| node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); |
| if (node < 0) { |
| panic(); |
| } |
| |
| err = stm32_clk_parse_fdt_all_oscillator(fdt, pdata); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = stm32_clk_parse_fdt_all_pll(fdt, node, pdata); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = stm32_clk_parse_fdt_by_name(fdt, node, "st,clkdiv", pdata->clkdiv, &pdata->nclkdiv); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = stm32_clk_parse_fdt_by_name(fdt, node, "st,clksrc", pdata->clksrc, &pdata->nclksrc); |
| if (err != 0) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| void stm32mp1_clk_rcc_regs_lock(void) |
| { |
| clk_stm32_rcc_regs_lock(); |
| } |
| |
| void stm32mp1_clk_rcc_regs_unlock(void) |
| { |
| clk_stm32_rcc_regs_unlock(); |
| } |
| |
| int stm32mp1_clk_init(void) |
| { |
| int ret; |
| |
| /* compute the PLL1 settings, not read in device tree */ |
| ret = clk_compute_pll1_settings(PLL1_NOMINAL_FREQ_IN_KHZ); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = stm32mp1_init_clock_tree(); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| clk_stm32_enable_critical_clocks(); |
| |
| return 0; |
| } |
| |
| int stm32mp1_clk_probe(void) |
| { |
| uintptr_t base = RCC_BASE; |
| int ret; |
| |
| ret = stm32_clk_parse_fdt(&stm32mp13_clock_pdata); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = clk_stm32_init(&stm32mp13_clock_data, base); |
| |
| return ret; |
| } |