blob: 5ef4de49b1a41b525ac7164492edaf7ea0f31cd8 [file] [log] [blame]
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0+)
/*
* Copyright (C) 2018-2019, STMicroelectronics
*/
#include <assert.h>
#include <drivers/stm32mp1_rcc.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
#include <initcall.h>
#include <io.h>
#include <keep.h>
#include <kernel/dt.h>
#include <kernel/generic_boot.h>
#include <kernel/panic.h>
#include <kernel/spinlock.h>
#include <platform_config.h>
#include <stm32_util.h>
#include <stdio.h>
#include <trace.h>
#include <util.h>
#ifdef CFG_DT
#include <libfdt.h>
#endif
/* Identifiers for root oscillators */
enum stm32mp_osc_id {
_HSI = 0,
_HSE,
_CSI,
_LSI,
_LSE,
_I2S_CKIN,
_USB_PHY_48,
NB_OSC,
_UNKNOWN_OSC_ID = 0xffU
};
/* Identifiers for parent clocks */
enum stm32mp1_parent_id {
/*
* Oscillators are valid IDs for parent clock and are already
* defined in enum stm32mp_osc_id, ending at NB_OSC - 1.
* This enum defines IDs are the other possible clock parents.
*/
_HSI_KER = NB_OSC,
_HSE_KER,
_HSE_KER_DIV2,
_CSI_KER,
_PLL1_P,
_PLL1_Q,
_PLL1_R,
_PLL2_P,
_PLL2_Q,
_PLL2_R,
_PLL3_P,
_PLL3_Q,
_PLL3_R,
_PLL4_P,
_PLL4_Q,
_PLL4_R,
_ACLK,
_PCLK1,
_PCLK2,
_PCLK3,
_PCLK4,
_PCLK5,
_HCLK6,
_HCLK2,
_CK_PER,
_CK_MPU,
_CK_MCU,
_PARENT_NB,
_UNKNOWN_ID = 0xff,
};
/*
* Identifiers for parent clock selectors.
* This enum lists only the parent clocks we are interested in.
*/
enum stm32mp1_parent_sel {
_STGEN_SEL,
_I2C46_SEL,
_SPI6_SEL,
_USART1_SEL,
_RNG1_SEL,
_UART6_SEL,
_UART24_SEL,
_UART35_SEL,
_UART78_SEL,
_ASS_SEL,
_MSS_SEL,
_USBPHY_SEL,
_USBO_SEL,
_PARENT_SEL_NB,
_UNKNOWN_SEL = 0xff,
};
/* Identifiers for PLLs and their configuration resources */
enum stm32mp1_pll_id {
_PLL1,
_PLL2,
_PLL3,
_PLL4,
_PLL_NB
};
enum stm32mp1_div_id {
_DIV_P,
_DIV_Q,
_DIV_R,
_DIV_NB,
};
enum stm32mp1_plltype {
PLL_800,
PLL_1600,
PLL_TYPE_NB
};
/*
* Clock generic gates clocks which state is controlled by a single RCC bit
*
* @offset: RCC register byte offset from RCC base where clock is controlled
* @bit: Bit position in the RCC 32bit register
* @clock_id: Identifier used for the clock in the clock driver API
* @set_clr: Non-null if and only-if RCC register is a CLEAR/SET register
* (CLEAR register is at offset RCC_MP_ENCLRR_OFFSET from SET register)
* @sel: _UNKNOWN_ID (fixed parent) or reference to parent clock selector
* (8bit storage of ID from enum stm32mp1_parent_sel)
* @fixed: _UNKNOWN_ID (selectable paranet) or reference to parent clock
* (8bit storage of ID from enum stm32mp1_parent_id)
*/
struct stm32mp1_clk_gate {
uint16_t offset;
uint8_t bit;
uint8_t clock_id;
uint8_t set_clr;
uint8_t sel; /* Relates to enum stm32mp1_parent_sel */
uint8_t fixed; /* Relates to enum stm32mp1_parent_id */
};
/* Parent clock selection: select register info, parent clocks references */
struct stm32mp1_clk_sel {
uint16_t offset;
uint8_t src;
uint8_t msk;
uint8_t nb_parent;
const uint8_t *parent;
};
#define REFCLK_SIZE 4
/* PLL control: type, control register offsets, up-to-4 selectable parent */
struct stm32mp1_clk_pll {
enum stm32mp1_plltype plltype;
uint16_t rckxselr;
uint16_t pllxcfgr1;
uint16_t pllxcfgr2;
uint16_t pllxfracr;
uint16_t pllxcr;
uint16_t pllxcsgr;
enum stm32mp_osc_id refclk[REFCLK_SIZE];
};
/* Clocks with selectable source and not set/clr register access */
#define _CLK_SELEC(_offset, _bit, _clock_id, _parent_sel) \
{ \
.offset = (_offset), \
.bit = (_bit), \
.clock_id = (_clock_id), \
.set_clr = 0, \
.sel = (_parent_sel), \
.fixed = _UNKNOWN_ID, \
}
/* Clocks with fixed source and not set/clr register access */
#define _CLK_FIXED(_offset, _bit, _clock_id, _parent) \
{ \
.offset = (_offset), \
.bit = (_bit), \
.clock_id = (_clock_id), \
.set_clr = 0, \
.sel = _UNKNOWN_SEL, \
.fixed = (_parent), \
}
/* Clocks with selectable source and set/clr register access */
#define _CLK_SC_SELEC(_offset, _bit, _clock_id, _parent_sel) \
{ \
.offset = (_offset), \
.bit = (_bit), \
.clock_id = (_clock_id), \
.set_clr = 1, \
.sel = (_parent_sel), \
.fixed = _UNKNOWN_ID, \
}
/* Clocks with fixed source and set/clr register access */
#define _CLK_SC_FIXED(_offset, _bit, _clock_id, _parent) \
{ \
.offset = (_offset), \
.bit = (_bit), \
.clock_id = (_clock_id), \
.set_clr = 1, \
.sel = _UNKNOWN_SEL, \
.fixed = (_parent), \
}
/*
* Clocks with selectable source and set/clr register access
* and enable bit position defined by a label (argument _bit)
*/
#define _CLK_SC2_SELEC(_offset, _bit, _clock_id, _parent_sel) \
{ \
.offset = (_offset), \
.clock_id = (_clock_id), \
.bit = _offset ## _ ## _bit ## _POS, \
.set_clr = 1, \
.sel = (_parent_sel), \
.fixed = _UNKNOWN_ID, \
}
#define _CLK_SC2_FIXED(_offset, _bit, _clock_id, _parent) \
{ \
.offset = (_offset), \
.clock_id = (_clock_id), \
.bit = _offset ## _ ## _bit ## _POS, \
.set_clr = 1, \
.sel = _UNKNOWN_SEL, \
.fixed = (_parent), \
}
#define _CLK_PARENT(idx, _offset, _src, _mask, _parent) \
[(idx)] = { \
.offset = (_offset), \
.src = (_src), \
.msk = (_mask), \
.parent = (_parent), \
.nb_parent = ARRAY_SIZE(_parent) \
}
#define _CLK_PLL(_idx, _type, _off1, _off2, _off3, _off4, \
_off5, _off6, _p1, _p2, _p3, _p4) \
[(_idx)] = { \
.plltype = (_type), \
.rckxselr = (_off1), \
.pllxcfgr1 = (_off2), \
.pllxcfgr2 = (_off3), \
.pllxfracr = (_off4), \
.pllxcr = (_off5), \
.pllxcsgr = (_off6), \
.refclk[0] = (_p1), \
.refclk[1] = (_p2), \
.refclk[2] = (_p3), \
.refclk[3] = (_p4), \
}
static const uint8_t stm32mp1_clks[][2] = {
{ CK_PER, _CK_PER },
{ CK_MPU, _CK_MPU },
{ CK_AXI, _ACLK },
{ CK_MCU, _CK_MCU },
{ CK_HSE, _HSE },
{ CK_CSI, _CSI },
{ CK_LSI, _LSI },
{ CK_LSE, _LSE },
{ CK_HSI, _HSI },
{ CK_HSE_DIV2, _HSE_KER_DIV2 },
};
#define NB_GATES ARRAY_SIZE(stm32mp1_clk_gate)
static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
_CLK_FIXED(RCC_DDRITFCR, 0, DDRC1, _ACLK),
_CLK_FIXED(RCC_DDRITFCR, 1, DDRC1LP, _ACLK),
_CLK_FIXED(RCC_DDRITFCR, 2, DDRC2, _ACLK),
_CLK_FIXED(RCC_DDRITFCR, 3, DDRC2LP, _ACLK),
_CLK_FIXED(RCC_DDRITFCR, 4, DDRPHYC, _PLL2_R),
_CLK_FIXED(RCC_DDRITFCR, 5, DDRPHYCLP, _PLL2_R),
_CLK_FIXED(RCC_DDRITFCR, 6, DDRCAPB, _PCLK4),
_CLK_FIXED(RCC_DDRITFCR, 7, DDRCAPBLP, _PCLK4),
_CLK_FIXED(RCC_DDRITFCR, 8, AXIDCG, _ACLK),
_CLK_FIXED(RCC_DDRITFCR, 9, DDRPHYCAPB, _PCLK4),
_CLK_FIXED(RCC_DDRITFCR, 10, DDRPHYCAPBLP, _PCLK4),
_CLK_SC2_SELEC(RCC_MP_APB5ENSETR, SPI6EN, SPI6_K, _SPI6_SEL),
_CLK_SC2_SELEC(RCC_MP_APB5ENSETR, I2C4EN, I2C4_K, _I2C46_SEL),
_CLK_SC2_SELEC(RCC_MP_APB5ENSETR, I2C6EN, I2C6_K, _I2C46_SEL),
_CLK_SC2_SELEC(RCC_MP_APB5ENSETR, USART1EN, USART1_K, _USART1_SEL),
_CLK_SC2_FIXED(RCC_MP_APB5ENSETR, RTCAPBEN, RTCAPB, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_APB5ENSETR, TZC1EN, TZC1, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_APB5ENSETR, TZC2EN, TZC2, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_APB5ENSETR, TZPCEN, TZPC, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_APB5ENSETR, IWDG1APBEN, IWDG1, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_APB5ENSETR, BSECEN, BSEC, _PCLK5),
_CLK_SC2_SELEC(RCC_MP_APB5ENSETR, STGENEN, STGEN_K, _STGEN_SEL),
_CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, GPIOZEN, GPIOZ, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, CRYP1EN, CRYP1, _PCLK5),
_CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, HASH1EN, HASH1, _PCLK5),
_CLK_SC2_SELEC(RCC_MP_AHB5ENSETR, RNG1EN, RNG1_K, _RNG1_SEL),
_CLK_SC2_FIXED(RCC_MP_AHB5ENSETR, BKPSRAMEN, BKPSRAM, _PCLK5),
/* Non-secure clocks */
#ifdef CFG_WITH_NSEC_GPIOS
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 3, GPIOD, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 4, GPIOE, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 5, GPIOF, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 6, GPIOG, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 7, GPIOH, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 8, GPIOI, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 9, GPIOJ, _UNKNOWN_ID),
_CLK_SC_FIXED(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_ID),
#endif
#ifdef CFG_WITH_NSEC_UARTS
_CLK_SC_SELEC(RCC_MP_APB1ENSETR, 14, USART2_K, _UART24_SEL),
_CLK_SC_SELEC(RCC_MP_APB1ENSETR, 15, USART3_K, _UART35_SEL),
_CLK_SC_SELEC(RCC_MP_APB1ENSETR, 16, UART4_K, _UART24_SEL),
_CLK_SC_SELEC(RCC_MP_APB1ENSETR, 17, UART5_K, _UART35_SEL),
_CLK_SC_SELEC(RCC_MP_APB1ENSETR, 18, UART7_K, _UART78_SEL),
_CLK_SC_SELEC(RCC_MP_APB1ENSETR, 19, UART8_K, _UART78_SEL),
_CLK_SC_SELEC(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL),
#endif
_CLK_SC_SELEC(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL),
_CLK_SC_SELEC(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL),
_CLK_SC_SELEC(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL),
_CLK_SC_SELEC(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL),
_CLK_SELEC(RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL),
_CLK_SC_FIXED(RCC_MP_APB1ENSETR, 6, TIM12_K, _PCLK1),
_CLK_SC_FIXED(RCC_MP_APB2ENSETR, 2, TIM15_K, _PCLK2),
};
KEEP_PAGER(stm32mp1_clk_gate);
/* Parents for secure aware clocks in the xxxSELR value ordering */
static const uint8_t stgen_parents[] = {
_HSI_KER, _HSE_KER
};
static const uint8_t i2c46_parents[] = {
_PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER
};
static const uint8_t spi6_parents[] = {
_PCLK5, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER, _PLL3_Q
};
static const uint8_t usart1_parents[] = {
_PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER, _PLL4_Q, _HSE_KER
};
static const uint8_t rng1_parents[] = {
_CSI, _PLL4_R, _LSE, _LSI
};
/* Parents for (some) non-secure clocks */
static const uint8_t uart6_parents[] = {
_PCLK2, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER
};
static const uint8_t uart234578_parents[] = {
_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER
};
static const uint8_t ass_parents[] = {
_HSI, _HSE, _PLL2
};
static const uint8_t mss_parents[] = {
_HSI, _HSE, _CSI, _PLL3
};
static const uint8_t usbphy_parents[] = {
_HSE_KER, _PLL4_R, _HSE_KER_DIV2
};
static const uint8_t usbo_parents[] = {
_PLL4_R, _USB_PHY_48
};
static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
/* Secure aware clocks */
_CLK_PARENT(_STGEN_SEL, RCC_STGENCKSELR, 0, 0x3, stgen_parents),
_CLK_PARENT(_I2C46_SEL, RCC_I2C46CKSELR, 0, 0x7, i2c46_parents),
_CLK_PARENT(_SPI6_SEL, RCC_SPI6CKSELR, 0, 0x7, spi6_parents),
_CLK_PARENT(_USART1_SEL, RCC_UART1CKSELR, 0, 0x7, usart1_parents),
_CLK_PARENT(_RNG1_SEL, RCC_RNG1CKSELR, 0, 0x3, rng1_parents),
/* Always non-secure clocks (maybe used in some way in secure world) */
_CLK_PARENT(_UART6_SEL, RCC_UART6CKSELR, 0, 0x7, uart6_parents),
_CLK_PARENT(_UART24_SEL, RCC_UART24CKSELR, 0, 0x7, uart234578_parents),
_CLK_PARENT(_UART35_SEL, RCC_UART35CKSELR, 0, 0x7, uart234578_parents),
_CLK_PARENT(_UART78_SEL, RCC_UART78CKSELR, 0, 0x7, uart234578_parents),
_CLK_PARENT(_ASS_SEL, RCC_ASSCKSELR, 0, 0x3, ass_parents),
_CLK_PARENT(_MSS_SEL, RCC_MSSCKSELR, 0, 0x3, mss_parents),
_CLK_PARENT(_USBPHY_SEL, RCC_USBCKSELR, 0, 0x3, usbphy_parents),
_CLK_PARENT(_USBO_SEL, RCC_USBCKSELR, 4, 0x1, usbo_parents),
};
/* PLLNCFGR2 register divider by output */
static const uint8_t pllncfgr2[_DIV_NB] = {
[_DIV_P] = RCC_PLLNCFGR2_DIVP_SHIFT,
[_DIV_Q] = RCC_PLLNCFGR2_DIVQ_SHIFT,
[_DIV_R] = RCC_PLLNCFGR2_DIVR_SHIFT,
};
static const struct stm32mp1_clk_pll stm32mp1_clk_pll[_PLL_NB] = {
_CLK_PLL(_PLL1, PLL_1600,
RCC_RCK12SELR, RCC_PLL1CFGR1, RCC_PLL1CFGR2,
RCC_PLL1FRACR, RCC_PLL1CR, RCC_PLL1CSGR,
_HSI, _HSE, _UNKNOWN_OSC_ID, _UNKNOWN_OSC_ID),
_CLK_PLL(_PLL2, PLL_1600,
RCC_RCK12SELR, RCC_PLL2CFGR1, RCC_PLL2CFGR2,
RCC_PLL2FRACR, RCC_PLL2CR, RCC_PLL2CSGR,
_HSI, _HSE, _UNKNOWN_OSC_ID, _UNKNOWN_OSC_ID),
_CLK_PLL(_PLL3, PLL_800,
RCC_RCK3SELR, RCC_PLL3CFGR1, RCC_PLL3CFGR2,
RCC_PLL3FRACR, RCC_PLL3CR, RCC_PLL3CSGR,
_HSI, _HSE, _CSI, _UNKNOWN_OSC_ID),
_CLK_PLL(_PLL4, PLL_800,
RCC_RCK4SELR, RCC_PLL4CFGR1, RCC_PLL4CFGR2,
RCC_PLL4FRACR, RCC_PLL4CR, RCC_PLL4CSGR,
_HSI, _HSE, _CSI, _I2S_CKIN),
};
/* Prescaler table lookups for clock computation */
/* div = /1 /2 /4 /8 / 16 /64 /128 /512 */
static const uint8_t stm32mp1_mcu_div[16] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9
};
/* div = /1 /2 /4 /8 /16 : same divider for PMU and APBX */
#define stm32mp1_mpu_div stm32mp1_mpu_apbx_div
#define stm32mp1_apbx_div stm32mp1_mpu_apbx_div
static const uint8_t stm32mp1_mpu_apbx_div[8] = {
0, 1, 2, 3, 4, 4, 4, 4
};
/* div = /1 /2 /3 /4 */
static const uint8_t stm32mp1_axi_div[8] = {
1, 2, 3, 4, 4, 4, 4, 4
};
static const char __maybe_unused *const stm32mp1_clk_parent_name[_PARENT_NB] = {
[_HSI] = "HSI",
[_HSE] = "HSE",
[_CSI] = "CSI",
[_LSI] = "LSI",
[_LSE] = "LSE",
[_I2S_CKIN] = "I2S_CKIN",
[_HSI_KER] = "HSI_KER",
[_HSE_KER] = "HSE_KER",
[_HSE_KER_DIV2] = "HSE_KER_DIV2",
[_CSI_KER] = "CSI_KER",
[_PLL1_P] = "PLL1_P",
[_PLL1_Q] = "PLL1_Q",
[_PLL1_R] = "PLL1_R",
[_PLL2_P] = "PLL2_P",
[_PLL2_Q] = "PLL2_Q",
[_PLL2_R] = "PLL2_R",
[_PLL3_P] = "PLL3_P",
[_PLL3_Q] = "PLL3_Q",
[_PLL3_R] = "PLL3_R",
[_PLL4_P] = "PLL4_P",
[_PLL4_Q] = "PLL4_Q",
[_PLL4_R] = "PLL4_R",
[_ACLK] = "ACLK",
[_PCLK1] = "PCLK1",
[_PCLK2] = "PCLK2",
[_PCLK3] = "PCLK3",
[_PCLK4] = "PCLK4",
[_PCLK5] = "PCLK5",
[_HCLK6] = "KCLK6",
[_HCLK2] = "HCLK2",
[_CK_PER] = "CK_PER",
[_CK_MPU] = "CK_MPU",
[_CK_MCU] = "CK_MCU",
[_USB_PHY_48] = "USB_PHY_48",
};
/*
* Oscillator frequency in Hz. This array shall be initialized
* according to platform.
*/
static unsigned long stm32mp1_osc[NB_OSC];
static unsigned long osc_frequency(enum stm32mp_osc_id idx)
{
if (idx >= ARRAY_SIZE(stm32mp1_osc)) {
DMSG("clk id %d not found", idx);
return 0;
}
return stm32mp1_osc[idx];
}
/* Reference counting for clock gating */
static unsigned int gate_refcounts[NB_GATES];
static unsigned int refcount_lock;
static const struct stm32mp1_clk_gate *gate_ref(unsigned int idx)
{
return &stm32mp1_clk_gate[idx];
}
static const struct stm32mp1_clk_sel *clk_sel_ref(unsigned int idx)
{
return &stm32mp1_clk_sel[idx];
}
static const struct stm32mp1_clk_pll *pll_ref(unsigned int idx)
{
return &stm32mp1_clk_pll[idx];
}
static int stm32mp1_clk_get_gated_id(unsigned long id)
{
unsigned int i = 0;
for (i = 0; i < NB_GATES; i++)
if (gate_ref(i)->clock_id == id)
return i;
DMSG("clk id %lu not found", id);
return -1;
}
static enum stm32mp1_parent_sel stm32mp1_clk_get_sel(int i)
{
return (enum stm32mp1_parent_sel)gate_ref(i)->sel;
}
static enum stm32mp1_parent_id stm32mp1_clk_get_fixed_parent(int i)
{
return (enum stm32mp1_parent_id)gate_ref(i)->fixed;
}
static int stm32mp1_clk_get_parent(unsigned long id)
{
const struct stm32mp1_clk_sel *sel = NULL;
unsigned int j = 0;
uint32_t p_sel = 0;
int i = 0;
enum stm32mp1_parent_id p = _UNKNOWN_ID;
enum stm32mp1_parent_sel s = _UNKNOWN_SEL;
vaddr_t rcc_base = stm32_rcc_base();
for (j = 0U; j < ARRAY_SIZE(stm32mp1_clks); j++)
if (stm32mp1_clks[j][0] == id)
return (int)stm32mp1_clks[j][1];
i = stm32mp1_clk_get_gated_id(id);
if (i < 0)
panic();
p = stm32mp1_clk_get_fixed_parent(i);
if (p < _PARENT_NB)
return (int)p;
s = stm32mp1_clk_get_sel(i);
if (s == _UNKNOWN_SEL)
return -1;
if (s >= _PARENT_SEL_NB)
panic();
sel = clk_sel_ref(s);
p_sel = (io_read32(rcc_base + sel->offset) >> sel->src) & sel->msk;
if (p_sel < sel->nb_parent)
return (int)sel->parent[p_sel];
DMSG("No parent selected for clk %lu", id);
return -1;
}
static unsigned long stm32mp1_pll_get_fref(const struct stm32mp1_clk_pll *pll)
{
uint32_t selr = io_read32(stm32_rcc_base() + pll->rckxselr);
uint32_t src = selr & RCC_SELR_REFCLK_SRC_MASK;
return osc_frequency(pll->refclk[src]);
}
/*
* pll_get_fvco() : return the VCO or (VCO / 2) frequency for the requested PLL
* - PLL1 & PLL2 => return VCO / 2 with Fpll_y_ck = FVCO / 2 * (DIVy + 1)
* - PLL3 & PLL4 => return VCO with Fpll_y_ck = FVCO / (DIVy + 1)
* => in all cases Fpll_y_ck = pll_get_fvco() / (DIVy + 1)
*/
static unsigned long stm32mp1_pll_get_fvco(const struct stm32mp1_clk_pll *pll)
{
unsigned long refclk = 0;
unsigned long fvco = 0;
uint32_t cfgr1 = 0;
uint32_t fracr = 0;
uint32_t divm = 0;
uint32_t divn = 0;
cfgr1 = io_read32(stm32_rcc_base() + pll->pllxcfgr1);
fracr = io_read32(stm32_rcc_base() + pll->pllxfracr);
divm = (cfgr1 & RCC_PLLNCFGR1_DIVM_MASK) >> RCC_PLLNCFGR1_DIVM_SHIFT;
divn = cfgr1 & RCC_PLLNCFGR1_DIVN_MASK;
refclk = stm32mp1_pll_get_fref(pll);
/*
* 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) {
unsigned long long numerator = 0;
unsigned long long denominator = 0;
uint32_t fracv = (fracr & RCC_PLLNFRACR_FRACV_MASK) >>
RCC_PLLNFRACR_FRACV_SHIFT;
numerator = (((unsigned long long)divn + 1U) << 13) + fracv;
numerator = refclk * numerator;
denominator = ((unsigned long long)divm + 1U) << 13;
fvco = (unsigned long)(numerator / denominator);
} else {
fvco = (unsigned long)(refclk * (divn + 1U) / (divm + 1U));
}
return fvco;
}
static unsigned long stm32mp1_read_pll_freq(enum stm32mp1_pll_id pll_id,
enum stm32mp1_div_id div_id)
{
const struct stm32mp1_clk_pll *pll = pll_ref(pll_id);
unsigned long dfout = 0;
uint32_t cfgr2 = 0;
uint32_t divy = 0;
if (div_id >= _DIV_NB)
return 0;
cfgr2 = io_read32(stm32_rcc_base() + pll->pllxcfgr2);
divy = (cfgr2 >> pllncfgr2[div_id]) & RCC_PLLNCFGR2_DIVX_MASK;
dfout = stm32mp1_pll_get_fvco(pll) / (divy + 1U);
return dfout;
}
static unsigned long get_clock_rate(int p)
{
uint32_t reg = 0;
uint32_t clkdiv = 0;
unsigned long clock = 0;
vaddr_t rcc_base = stm32_rcc_base();
switch (p) {
case _CK_MPU:
/* MPU sub system */
reg = io_read32(rcc_base + RCC_MPCKSELR);
switch (reg & RCC_SELR_SRC_MASK) {
case RCC_MPCKSELR_HSI:
clock = osc_frequency(_HSI);
break;
case RCC_MPCKSELR_HSE:
clock = osc_frequency(_HSE);
break;
case RCC_MPCKSELR_PLL:
clock = stm32mp1_read_pll_freq(_PLL1, _DIV_P);
break;
case RCC_MPCKSELR_PLL_MPUDIV:
clock = stm32mp1_read_pll_freq(_PLL1, _DIV_P);
reg = io_read32(rcc_base + RCC_MPCKDIVR);
clkdiv = reg & RCC_MPUDIV_MASK;
if (clkdiv)
clock /= stm32mp1_mpu_div[clkdiv];
break;
default:
break;
}
break;
/* AXI sub system */
case _ACLK:
case _HCLK2:
case _HCLK6:
case _PCLK4:
case _PCLK5:
reg = io_read32(rcc_base + RCC_ASSCKSELR);
switch (reg & RCC_SELR_SRC_MASK) {
case RCC_ASSCKSELR_HSI:
clock = osc_frequency(_HSI);
break;
case RCC_ASSCKSELR_HSE:
clock = osc_frequency(_HSE);
break;
case RCC_ASSCKSELR_PLL:
clock = stm32mp1_read_pll_freq(_PLL2, _DIV_P);
break;
default:
break;
}
/* System clock divider */
reg = io_read32(rcc_base + RCC_AXIDIVR);
clock /= stm32mp1_axi_div[reg & RCC_AXIDIV_MASK];
switch (p) {
case _PCLK4:
reg = io_read32(rcc_base + RCC_APB4DIVR);
clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
break;
case _PCLK5:
reg = io_read32(rcc_base + RCC_APB5DIVR);
clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
break;
default:
break;
}
break;
/* MCU sub system */
case _CK_MCU:
case _PCLK1:
case _PCLK2:
case _PCLK3:
reg = io_read32(rcc_base + RCC_MSSCKSELR);
switch (reg & RCC_SELR_SRC_MASK) {
case RCC_MSSCKSELR_HSI:
clock = osc_frequency(_HSI);
break;
case RCC_MSSCKSELR_HSE:
clock = osc_frequency(_HSE);
break;
case RCC_MSSCKSELR_CSI:
clock = osc_frequency(_CSI);
break;
case RCC_MSSCKSELR_PLL:
clock = stm32mp1_read_pll_freq(_PLL3, _DIV_P);
break;
default:
break;
}
/* MCU clock divider */
reg = io_read32(rcc_base + RCC_MCUDIVR);
clock >>= stm32mp1_mcu_div[reg & RCC_MCUDIV_MASK];
switch (p) {
case _PCLK1:
reg = io_read32(rcc_base + RCC_APB1DIVR);
clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
break;
case _PCLK2:
reg = io_read32(rcc_base + RCC_APB2DIVR);
clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
break;
case _PCLK3:
reg = io_read32(rcc_base + RCC_APB3DIVR);
clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK];
break;
case _CK_MCU:
default:
break;
}
break;
case _CK_PER:
reg = io_read32(rcc_base + RCC_CPERCKSELR);
switch (reg & RCC_SELR_SRC_MASK) {
case RCC_CPERCKSELR_HSI:
clock = osc_frequency(_HSI);
break;
case RCC_CPERCKSELR_HSE:
clock = osc_frequency(_HSE);
break;
case RCC_CPERCKSELR_CSI:
clock = osc_frequency(_CSI);
break;
default:
break;
}
break;
case _HSI:
case _HSI_KER:
clock = osc_frequency(_HSI);
break;
case _CSI:
case _CSI_KER:
clock = osc_frequency(_CSI);
break;
case _HSE:
case _HSE_KER:
clock = osc_frequency(_HSE);
break;
case _HSE_KER_DIV2:
clock = osc_frequency(_HSE) >> 1;
break;
case _LSI:
clock = osc_frequency(_LSI);
break;
case _LSE:
clock = osc_frequency(_LSE);
break;
/* PLL */
case _PLL1_P:
clock = stm32mp1_read_pll_freq(_PLL1, _DIV_P);
break;
case _PLL1_Q:
clock = stm32mp1_read_pll_freq(_PLL1, _DIV_Q);
break;
case _PLL1_R:
clock = stm32mp1_read_pll_freq(_PLL1, _DIV_R);
break;
case _PLL2_P:
clock = stm32mp1_read_pll_freq(_PLL2, _DIV_P);
break;
case _PLL2_Q:
clock = stm32mp1_read_pll_freq(_PLL2, _DIV_Q);
break;
case _PLL2_R:
clock = stm32mp1_read_pll_freq(_PLL2, _DIV_R);
break;
case _PLL3_P:
clock = stm32mp1_read_pll_freq(_PLL3, _DIV_P);
break;
case _PLL3_Q:
clock = stm32mp1_read_pll_freq(_PLL3, _DIV_Q);
break;
case _PLL3_R:
clock = stm32mp1_read_pll_freq(_PLL3, _DIV_R);
break;
case _PLL4_P:
clock = stm32mp1_read_pll_freq(_PLL4, _DIV_P);
break;
case _PLL4_Q:
clock = stm32mp1_read_pll_freq(_PLL4, _DIV_Q);
break;
case _PLL4_R:
clock = stm32mp1_read_pll_freq(_PLL4, _DIV_R);
break;
/* Other */
case _USB_PHY_48:
clock = osc_frequency(_USB_PHY_48);
break;
default:
break;
}
return clock;
}
static void __clk_enable(struct stm32mp1_clk_gate const *gate)
{
vaddr_t base = stm32_rcc_base();
uint32_t bit = BIT(gate->bit);
if (gate->set_clr)
io_write32(base + gate->offset, bit);
else
io_setbits32(base + gate->offset, bit);
FMSG("Clock %u has been enabled", gate->clock_id);
}
static void __clk_disable(struct stm32mp1_clk_gate const *gate)
{
vaddr_t base = stm32_rcc_base();
uint32_t bit = BIT(gate->bit);
if (gate->set_clr)
io_write32(base + gate->offset + RCC_MP_ENCLRR_OFFSET, bit);
else
io_clrbits32(base + gate->offset, bit);
FMSG("Clock %u has been disabled", gate->clock_id);
}
static bool __clk_is_enabled(struct stm32mp1_clk_gate const *gate)
{
vaddr_t base = stm32_rcc_base();
return io_read32(base + gate->offset) & BIT(gate->bit);
}
bool stm32_clock_is_enabled(unsigned long id)
{
int i = stm32mp1_clk_get_gated_id(id);
if (i < 0)
return false;
return __clk_is_enabled(gate_ref(i));
}
void stm32_clock_enable(unsigned long id)
{
int i = stm32mp1_clk_get_gated_id(id);
uint32_t exceptions = 0;
if (i < 0) {
DMSG("Invalid clock %lu: %d", id, i);
panic();
}
exceptions = may_spin_lock(&refcount_lock);
if (!gate_refcounts[i])
__clk_enable(gate_ref(i));
gate_refcounts[i]++;
may_spin_unlock(&refcount_lock, exceptions);
}
void stm32_clock_disable(unsigned long id)
{
int i = stm32mp1_clk_get_gated_id(id);
uint32_t exceptions = 0;
if (i < 0) {
DMSG("Invalid clock %lu: %d", id, i);
panic();
}
exceptions = may_spin_lock(&refcount_lock);
assert(gate_refcounts[i]);
gate_refcounts[i]--;
if (!gate_refcounts[i])
__clk_disable(gate_ref(i));
may_spin_unlock(&refcount_lock, exceptions);
}
static long get_timer_rate(long parent_rate, unsigned int apb_bus)
{
uint32_t timgxpre = 0;
uint32_t apbxdiv = 0;
vaddr_t rcc_base = stm32_rcc_base();
switch (apb_bus) {
case 1:
apbxdiv = io_read32(rcc_base + RCC_APB1DIVR) &
RCC_APBXDIV_MASK;
timgxpre = io_read32(rcc_base + RCC_TIMG1PRER) &
RCC_TIMGXPRER_TIMGXPRE;
break;
case 2:
apbxdiv = io_read32(rcc_base + RCC_APB2DIVR) &
RCC_APBXDIV_MASK;
timgxpre = io_read32(rcc_base + RCC_TIMG2PRER) &
RCC_TIMGXPRER_TIMGXPRE;
break;
default:
panic();
break;
}
if (apbxdiv == 0)
return parent_rate;
return parent_rate * (timgxpre + 1) * 2;
}
unsigned long stm32_clock_get_rate(unsigned long id)
{
int p = 0;
unsigned long rate = 0;
p = stm32mp1_clk_get_parent(id);
if (p < 0)
return 0;
rate = get_clock_rate(p);
if ((id >= TIM2_K) && (id <= TIM14_K))
rate = get_timer_rate(rate, 1);
if ((id >= TIM1_K) && (id <= TIM17_K))
rate = get_timer_rate(rate, 2);
return rate;
}
/*
* Get the parent ID of the target parent clock, or -1 if no parent found.
*/
static int get_parent_id_parent(unsigned int parent_id)
{
enum stm32mp1_parent_sel s = _UNKNOWN_SEL;
enum stm32mp1_pll_id pll_id = _PLL_NB;
uint32_t p_sel = 0;
switch (parent_id) {
case _ACLK:
case _PCLK4:
case _PCLK5:
s = _ASS_SEL;
break;
case _PLL1_P:
case _PLL1_Q:
case _PLL1_R:
pll_id = _PLL1;
break;
case _PLL2_P:
case _PLL2_Q:
case _PLL2_R:
pll_id = _PLL2;
break;
case _PLL3_P:
case _PLL3_Q:
case _PLL3_R:
pll_id = _PLL3;
break;
case _PLL4_P:
case _PLL4_Q:
case _PLL4_R:
pll_id = _PLL4;
break;
case _PCLK1:
case _PCLK2:
case _HCLK2:
case _HCLK6:
case _CK_PER:
case _CK_MPU:
case _CK_MCU:
case _USB_PHY_48:
/* We do not expected to access these */
panic();
break;
default:
/* Other parents have no parent */
return -1;
}
if (s != _UNKNOWN_SEL) {
const struct stm32mp1_clk_sel *sel = clk_sel_ref(s);
vaddr_t rcc_base = stm32_rcc_base();
p_sel = (io_read32(rcc_base + sel->offset) >> sel->src) &
sel->msk;
if (p_sel < sel->nb_parent)
return sel->parent[p_sel];
} else {
const struct stm32mp1_clk_pll *pll = pll_ref(pll_id);
p_sel = io_read32(stm32_rcc_base() + pll->rckxselr) &
RCC_SELR_REFCLK_SRC_MASK;
if (pll->refclk[p_sel] != _UNKNOWN_OSC_ID)
return pll->refclk[p_sel];
}
FMSG("No parent found for %s", stm32mp1_clk_parent_name[parent_id]);
return -1;
}
static void secure_parent_clocks(unsigned long parent_id)
{
int grandparent_id = 0;
switch (parent_id) {
/* Secure only the parents for these clocks */
case _ACLK:
case _HCLK2:
case _HCLK6:
case _PCLK4:
case _PCLK5:
break;
/* PLLs */
case _PLL1_P:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL1_P);
break;
case _PLL1_Q:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL1_Q);
break;
case _PLL1_R:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL1_R);
break;
case _PLL2_P:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL2_P);
break;
case _PLL2_Q:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL2_Q);
break;
case _PLL2_R:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL2_R);
break;
case _PLL3_P:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3_P);
break;
case _PLL3_Q:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3_Q);
break;
case _PLL3_R:
stm32mp_register_secure_periph(STM32MP1_SHRES_PLL3_R);
break;
/* Source clocks */
case _HSI:
case _HSI_KER:
stm32mp_register_secure_periph(STM32MP1_SHRES_HSI);
break;
case _LSI:
stm32mp_register_secure_periph(STM32MP1_SHRES_LSI);
break;
case _CSI:
case _CSI_KER:
stm32mp_register_secure_periph(STM32MP1_SHRES_CSI);
break;
case _HSE:
case _HSE_KER:
case _HSE_KER_DIV2:
stm32mp_register_secure_periph(STM32MP1_SHRES_HSE);
break;
case _LSE:
stm32mp_register_secure_periph(STM32MP1_SHRES_LSE);
break;
default:
panic();
}
grandparent_id = get_parent_id_parent(parent_id);
if (grandparent_id >= 0)
secure_parent_clocks(grandparent_id);
}
void stm32mp_register_clock_parents_secure(unsigned long clock_id)
{
int parent_id = 0;
switch (clock_id) {
case PLL1:
parent_id = get_parent_id_parent(_PLL1_P);
break;
case PLL2:
parent_id = get_parent_id_parent(_PLL2_P);
break;
case PLL3:
parent_id = get_parent_id_parent(_PLL3_P);
break;
case PLL4:
EMSG("PLL4 cannot be secure");
panic();
default:
/* Others are expected gateable clock */
parent_id = stm32mp1_clk_get_parent(clock_id);
break;
}
if (parent_id < 0) {
DMSG("No parent for clock %lu", clock_id);
panic();
}
secure_parent_clocks(parent_id);
}
#ifdef CFG_EMBED_DTB
#define DT_RCC_CLK_COMPAT "st,stm32mp1-rcc"
static const char *stm32mp_osc_node_label[NB_OSC] = {
[_LSI] = "clk-lsi",
[_LSE] = "clk-lse",
[_HSI] = "clk-hsi",
[_HSE] = "clk-hse",
[_CSI] = "clk-csi",
[_I2S_CKIN] = "i2s_ckin",
[_USB_PHY_48] = "ck_usbo_48m"
};
static unsigned int clk_freq_prop(void *fdt, int node)
{
int ret = 0;
const fdt32_t *cuint = fdt_getprop(fdt, node, "clock-frequency", &ret);
if (!cuint)
panic();
return fdt32_to_cpu(*cuint);
}
static void get_osc_freq_from_dt(void *fdt)
{
enum stm32mp_osc_id idx = _UNKNOWN_OSC_ID;
int clk_node = fdt_path_offset(fdt, "/clocks");
if (clk_node < 0)
panic();
COMPILE_TIME_ASSERT((int)_HSI == 0);
for (idx = _HSI; idx < NB_OSC; idx++) {
const char *name = stm32mp_osc_node_label[idx];
int subnode = 0;
fdt_for_each_subnode(subnode, fdt, clk_node) {
const char *cchar = NULL;
int ret = 0;
cchar = fdt_get_name(fdt, subnode, &ret);
if (!cchar)
panic();
if (strncmp(cchar, name, (size_t)ret) == 0) {
stm32mp1_osc[idx] = clk_freq_prop(fdt, subnode);
DMSG("Osc %s: %lu Hz", name, stm32mp1_osc[idx]);
break;
}
}
if (!stm32mp1_osc[idx])
DMSG("Osc %s: no frequency info", name);
}
}
static TEE_Result stm32mp1_clk_early_init(void)
{
void *fdt = NULL;
int node = 0;
unsigned int i = 0;
int len = 0;
int ignored = 0;
fdt = get_embedded_dt();
node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
if (node < 0 || _fdt_reg_base_address(fdt, node) != RCC_BASE)
panic();
if (_fdt_get_status(fdt, node) & DT_STATUS_OK_SEC) {
io_setbits32(stm32_rcc_base() + RCC_TZCR, RCC_TZCR_TZEN);
} else {
if (io_read32(stm32_rcc_base() + RCC_TZCR) & RCC_TZCR_TZEN)
panic("Refuse to release RCC[TZEN]");
IMSG("RCC is non-secure");
}
get_osc_freq_from_dt(fdt);
/*
* OP-TEE core is not in charge of configuring clock parenthood.
* This is expected from an earlier boot stage. Modifying the clock
* tree parenthood here may jeopardize already configured clocks.
* The sequence below ignores such DT directives with a friendly
* debug trace.
*/
if (fdt_getprop(fdt, node, "st,clksrc", &len)) {
DMSG("Ignore source clocks configuration from DT");
ignored++;
}
if (fdt_getprop(fdt, node, "st,clkdiv", &len)) {
DMSG("Ignore clock divisors configuration from DT");
ignored++;
}
if (fdt_getprop(fdt, node, "st,pkcs", &len)) {
DMSG("Ignore peripheral clocks tree configuration from DT");
ignored++;
}
for (i = (enum stm32mp1_pll_id)0; i < _PLL_NB; i++) {
char name[] = "st,pll@X";
snprintf(name, sizeof(name), "st,pll@%d", i);
node = fdt_subnode_offset(fdt, node, name);
if (node < 0)
continue;
if (fdt_getprop(fdt, node, "cfg", &len) ||
fdt_getprop(fdt, node, "frac", &len)) {
DMSG("Ignore PLL%u configurations from DT", i);
ignored++;
}
}
if (ignored != 0)
IMSG("DT clock tree configurations were ignored");
return TEE_SUCCESS;
}
service_init(stm32mp1_clk_early_init);
#endif /*CFG_EMBED_DTB*/