/* * Copyright (C) 2022-2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ #include #include #include #include #include #include #include #include "clk-stm32-core.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; }