clk: stm32mp1: add clock tree initialization

add binding and code for clock tree initialization from device tree

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
This commit is contained in:
Patrick Delaunay 2018-03-12 10:46:16 +01:00 committed by Tom Rini
parent a6151916cb
commit 266fa4df00
3 changed files with 1086 additions and 0 deletions

View File

@ -0,0 +1,226 @@
STMicroelectronics STM32MP1 clock tree initialization
=====================================================
The STM32MP clock tree initialization is based on device tree information
for RCC IP and on fixed clocks.
-------------------------------
RCC CLOCK = st,stm32mp1-rcc-clk
-------------------------------
The RCC IP is both a reset and a clock controller but this documentation only
describes the fields added for clock tree initialization which are not present
in Linux binding.
Please refer to ../mfd/st,stm32-rcc.txt for all the other properties common
with Linux.
Required properties:
- compatible: Should be "st,stm32mp1-rcc-clk"
- st,clksrc : The clock source in this order
for STM32MP15x: 9 clock sources are requested
MPU AXI MCU PLL12 PLL3 PLL4 RTC MCO1 MCO2
with value equals to RCC clock specifier as defined in
dt-bindings/clock/stm32mp1-clksrc.h: CLK_<NAME>_<SOURCE>
- st,clkdiv : The div parameters in this order
for STM32MP15x: 11 dividers value are requested
MPU AXI MCU APB1 APB2 APB3 APB4 APB5 RTC MCO1 MCO2
with DIV coding defined in RCC associated register RCC_xxxDIVR
most the case, it is:
0x0: not divided
0x1: division by 2
0x2: division by 4
0x3: division by 8
...
but for RTC MCO1 MCO2, the coding is different:
0x0: not divided
0x1: division by 2
0x2: division by 3
0x3: division by 4
...
Optional Properties:
- st,pll
PLL children node for PLL1 to PLL4 : (see ref manual for details)
with associated index 0 to 3 (st,pll@0 to st,pll@4)
PLLx is off when the associated node is absent
- Sub-nodes:
- cfg: The parameters for PLL configuration in this order:
DIVM DIVN DIVP DIVQ DIVR Output
with DIV value as defined in RCC spec:
0x0: bypass (division by 1)
0x1: division by 2
0x2: division by 3
0x3: division by 4
...
and Output = bitfield for each output value = 1:ON/0:OFF
BIT(0) => output P : DIVPEN
BIT(1) => output Q : DIVQEN
BIT(2) => output R : DIVREN
NB : macro PQR(p,q,r) can be used to build this value
with p,p,r = 0 or 1
- frac : Fractional part of the multiplication factor
(optional, PLL is in integer mode when absent)
- csg : Clock Spreading Generator (optional)
with parameters in this order:
MOD_PER INC_STEP SSCG_MODE
* MOD_PER: Modulation Period Adjustment
* INC_STEP: Modulation Depth Adjustment
* SSCG_MODE: Spread spectrum clock generator mode
you can use associated defines from stm32mp1-clksrc.h
* SSCG_MODE_CENTER_SPREAD = 0
* SSCG_MODE_DOWN_SPREAD = 1
- st,pkcs : used to configure the peripherals kernel clock selection
containing a list of peripheral kernel clock source identifier as defined
in the file dt-bindings/clock/stm32mp1-clksrc.h
Example:
rcc: rcc@50000000 {
compatible = "syscon", "simple-mfd";
reg = <0x50000000 0x1000>;
rcc_clk: rcc-clk@50000000 {
#clock-cells = <1>;
compatible = "st,stm32mp1-rcc-clk";
st,clksrc = < CLK_MPU_PLL1P
CLK_AXI_PLL2P
CLK_MCU_HSI
CLK_PLL12_HSE
CLK_PLL3_HSE
CLK_PLL4_HSE
CLK_RTC_HSE
CLK_MCO1_DISABLED
CLK_MCO2_DISABLED
>;
st,clkdiv = <
1 /*MPU*/
0 /*AXI*/
0 /*MCU*/
1 /*APB1*/
1 /*APB2*/
1 /*APB3*/
1 /*APB4*/
5 /*APB5*/
23 /*RTC*/
0 /*MCO1*/
0 /*MCO2*/
>;
st,pll@0 {
cfg = < 1 53 0 0 0 1 >;
frac = < 0x810 >;
};
st,pll@1 {
cfg = < 1 43 1 0 0 PQR(0,1,1)>;
csg = <10 20 1>;
};
st,pll@2 {
cfg = < 2 85 3 13 3 0>;
csg = <10 20 SSCG_MODE_CENTER_SPREAD>;
};
st,pll@3 {
cfg = < 2 78 4 7 9 3>;
};
st,pkcs = <
CLK_STGEN_HSE
CLK_CKPER_HSI
CLK_USBPHY_PLL2P
CLK_DSI_PLL2Q
>;
};
};
--------------------------
other clocks = fixed-clock
--------------------------
The clock tree is also based on 5 fixed-clock in clocks node
used to define the state of associated ST32MP1 oscillators:
- clk-lsi
- clk-lse
- clk-hsi
- clk-hse
- clk-csi
At boot the clock tree initialization will
- enable the oscillator present in device tree
- disable HSI oscillator if the node is absent (always activated by bootrom)
Optional properties :
a) for external oscillator: "clk-lse", "clk-hse"
3 optional fields are managed
- "st,bypass" Configure the oscillator bypass mode (HSEBYP, LSEBYP)
- "st,css" Activate the clock security system (HSECSSON, LSECSSON)
- "st,drive" (only for LSE) value of the drive for the oscillator
(see LSEDRV_ define in the file dt-bindings/clock/stm32mp1-clksrc.h)
Example board file:
/ {
clocks {
clk_hse: clk-hse {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <64000000>;
st,bypass;
};
clk_lse: clk-lse {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
st,css;
st,drive = <LSEDRV_LOWEST>;
};
};
b) for internal oscillator: "clk-hsi"
internally HSI clock is fixed to 64MHz for STM32MP157 soc
in device tree clk-hsi is the clock after HSIDIV (ck_hsi in RCC doc)
So this clock frequency is used to compute the expected HSI_DIV
for the clock tree initialisation
ex: for HSIDIV = /1
/ {
clocks {
clk_hsi: clk-hsi {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <64000000>;
};
};
ex: for HSIDIV = /2
/ {
clocks {
clk_hsi: clk-hsi {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32000000>;
};
};

View File

@ -12,10 +12,21 @@
#include <spl.h>
#include <syscon.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
#include <dt-bindings/clock/stm32mp1-clksrc.h>
#if !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD)
/* activate clock tree initialization in the driver */
#define STM32MP1_CLOCK_TREE_INIT
#endif
#define MAX_HSI_HZ 64000000
/* TIMEOUT */
#define TIMEOUT_200MS 200000
#define TIMEOUT_1S 1000000
/* RCC registers */
#define RCC_OCENSETR 0x0C
#define RCC_OCENCLRR 0x10
@ -1079,6 +1090,565 @@ static ulong stm32mp1_clk_get_rate(struct clk *clk)
return rate;
}
#ifdef STM32MP1_CLOCK_TREE_INIT
static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset,
u32 mask_on)
{
u32 address = rcc + offset;
if (enable)
setbits_le32(address, mask_on);
else
clrbits_le32(address, mask_on);
}
static void stm32mp1_hs_ocs_set(int enable, fdt_addr_t rcc, u32 mask_on)
{
if (enable)
setbits_le32(rcc + RCC_OCENSETR, mask_on);
else
setbits_le32(rcc + RCC_OCENCLRR, mask_on);
}
static int stm32mp1_osc_wait(int enable, fdt_addr_t rcc, u32 offset,
u32 mask_rdy)
{
u32 mask_test = 0;
u32 address = rcc + offset;
u32 val;
int ret;
if (enable)
mask_test = mask_rdy;
ret = readl_poll_timeout(address, val,
(val & mask_rdy) == mask_test,
TIMEOUT_1S);
if (ret)
pr_err("OSC %x @ %x timeout for enable=%d : 0x%x\n",
mask_rdy, address, enable, readl(address));
return ret;
}
static void stm32mp1_lse_enable(fdt_addr_t rcc, int bypass, int lsedrv)
{
u32 value;
if (bypass)
setbits_le32(rcc + RCC_BDCR, RCC_BDCR_LSEBYP);
/*
* warning: not recommended to switch directly from "high drive"
* to "medium low drive", and vice-versa.
*/
value = (readl(rcc + RCC_BDCR) & RCC_BDCR_LSEDRV_MASK)
>> RCC_BDCR_LSEDRV_SHIFT;
while (value != lsedrv) {
if (value > lsedrv)
value--;
else
value++;
clrsetbits_le32(rcc + RCC_BDCR,
RCC_BDCR_LSEDRV_MASK,
value << RCC_BDCR_LSEDRV_SHIFT);
}
stm32mp1_ls_osc_set(1, rcc, RCC_BDCR, RCC_BDCR_LSEON);
}
static void stm32mp1_lse_wait(fdt_addr_t rcc)
{
stm32mp1_osc_wait(1, rcc, RCC_BDCR, RCC_BDCR_LSERDY);
}
static void stm32mp1_lsi_set(fdt_addr_t rcc, int enable)
{
stm32mp1_ls_osc_set(enable, rcc, RCC_RDLSICR, RCC_RDLSICR_LSION);
stm32mp1_osc_wait(enable, rcc, RCC_RDLSICR, RCC_RDLSICR_LSIRDY);
}
static void stm32mp1_hse_enable(fdt_addr_t rcc, int bypass, int css)
{
if (bypass)
setbits_le32(rcc + RCC_OCENSETR, RCC_OCENR_HSEBYP);
stm32mp1_hs_ocs_set(1, rcc, RCC_OCENR_HSEON);
stm32mp1_osc_wait(1, rcc, RCC_OCRDYR, RCC_OCRDYR_HSERDY);
if (css)
setbits_le32(rcc + RCC_OCENSETR, RCC_OCENR_HSECSSON);
}
static void stm32mp1_csi_set(fdt_addr_t rcc, int enable)
{
stm32mp1_ls_osc_set(enable, rcc, RCC_OCENSETR, RCC_OCENR_CSION);
stm32mp1_osc_wait(enable, rcc, RCC_OCRDYR, RCC_OCRDYR_CSIRDY);
}
static void stm32mp1_hsi_set(fdt_addr_t rcc, int enable)
{
stm32mp1_hs_ocs_set(enable, rcc, RCC_OCENR_HSION);
stm32mp1_osc_wait(enable, rcc, RCC_OCRDYR, RCC_OCRDYR_HSIRDY);
}
static int stm32mp1_set_hsidiv(fdt_addr_t rcc, u8 hsidiv)
{
u32 address = rcc + RCC_OCRDYR;
u32 val;
int ret;
clrsetbits_le32(rcc + RCC_HSICFGR,
RCC_HSICFGR_HSIDIV_MASK,
RCC_HSICFGR_HSIDIV_MASK & hsidiv);
ret = readl_poll_timeout(address, val,
val & RCC_OCRDYR_HSIDIVRDY,
TIMEOUT_200MS);
if (ret)
pr_err("HSIDIV failed @ 0x%x: 0x%x\n",
address, readl(address));
return ret;
}
static int stm32mp1_hsidiv(fdt_addr_t rcc, ulong hsifreq)
{
u8 hsidiv;
u32 hsidivfreq = MAX_HSI_HZ;
for (hsidiv = 0; hsidiv < 4; hsidiv++,
hsidivfreq = hsidivfreq / 2)
if (hsidivfreq == hsifreq)
break;
if (hsidiv == 4) {
pr_err("clk-hsi frequency invalid");
return -1;
}
if (hsidiv > 0)
return stm32mp1_set_hsidiv(rcc, hsidiv);
return 0;
}
static void pll_start(struct stm32mp1_clk_priv *priv, int pll_id)
{
const struct stm32mp1_clk_pll *pll = priv->data->pll;
writel(RCC_PLLNCR_PLLON, priv->base + pll[pll_id].pllxcr);
}
static int pll_output(struct stm32mp1_clk_priv *priv, int pll_id, int output)
{
const struct stm32mp1_clk_pll *pll = priv->data->pll;
u32 pllxcr = priv->base + pll[pll_id].pllxcr;
u32 val;
int ret;
ret = readl_poll_timeout(pllxcr, val, val & RCC_PLLNCR_PLLRDY,
TIMEOUT_200MS);
if (ret) {
pr_err("PLL%d start failed @ 0x%x: 0x%x\n",
pll_id, pllxcr, readl(pllxcr));
return ret;
}
/* start the requested output */
setbits_le32(pllxcr, output << RCC_PLLNCR_DIVEN_SHIFT);
return 0;
}
static int pll_stop(struct stm32mp1_clk_priv *priv, int pll_id)
{
const struct stm32mp1_clk_pll *pll = priv->data->pll;
u32 pllxcr = priv->base + pll[pll_id].pllxcr;
u32 val;
/* stop all output */
clrbits_le32(pllxcr,
RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN);
/* stop PLL */
clrbits_le32(pllxcr, RCC_PLLNCR_PLLON);
/* wait PLL stopped */
return readl_poll_timeout(pllxcr, val, (val & RCC_PLLNCR_PLLRDY) == 0,
TIMEOUT_200MS);
}
static void pll_config_output(struct stm32mp1_clk_priv *priv,
int pll_id, u32 *pllcfg)
{
const struct stm32mp1_clk_pll *pll = priv->data->pll;
fdt_addr_t rcc = priv->base;
u32 value;
value = (pllcfg[PLLCFG_P] << RCC_PLLNCFGR2_DIVP_SHIFT)
& RCC_PLLNCFGR2_DIVP_MASK;
value |= (pllcfg[PLLCFG_Q] << RCC_PLLNCFGR2_DIVQ_SHIFT)
& RCC_PLLNCFGR2_DIVQ_MASK;
value |= (pllcfg[PLLCFG_R] << RCC_PLLNCFGR2_DIVR_SHIFT)
& RCC_PLLNCFGR2_DIVR_MASK;
writel(value, rcc + pll[pll_id].pllxcfgr2);
}
static int pll_config(struct stm32mp1_clk_priv *priv, int pll_id,
u32 *pllcfg, u32 fracv)
{
const struct stm32mp1_clk_pll *pll = priv->data->pll;
fdt_addr_t rcc = priv->base;
enum stm32mp1_plltype type = pll[pll_id].plltype;
int src;
ulong refclk;
u8 ifrge = 0;
u32 value;
src = readl(priv->base + pll[pll_id].rckxselr) & RCC_SELR_SRC_MASK;
refclk = stm32mp1_clk_get_fixed(priv, pll[pll_id].refclk[src]) /
(pllcfg[PLLCFG_M] + 1);
if (refclk < (stm32mp1_pll[type].refclk_min * 1000000) ||
refclk > (stm32mp1_pll[type].refclk_max * 1000000)) {
debug("invalid refclk = %x\n", (u32)refclk);
return -EINVAL;
}
if (type == PLL_800 && refclk >= 8000000)
ifrge = 1;
value = (pllcfg[PLLCFG_N] << RCC_PLLNCFGR1_DIVN_SHIFT)
& RCC_PLLNCFGR1_DIVN_MASK;
value |= (pllcfg[PLLCFG_M] << RCC_PLLNCFGR1_DIVM_SHIFT)
& RCC_PLLNCFGR1_DIVM_MASK;
value |= (ifrge << RCC_PLLNCFGR1_IFRGE_SHIFT)
& RCC_PLLNCFGR1_IFRGE_MASK;
writel(value, rcc + pll[pll_id].pllxcfgr1);
/* fractional configuration: load sigma-delta modulator (SDM) */
/* Write into FRACV the new fractional value , and FRACLE to 0 */
writel(fracv << RCC_PLLNFRACR_FRACV_SHIFT,
rcc + pll[pll_id].pllxfracr);
/* Write FRACLE to 1 : FRACV value is loaded into the SDM */
setbits_le32(rcc + pll[pll_id].pllxfracr,
RCC_PLLNFRACR_FRACLE);
pll_config_output(priv, pll_id, pllcfg);
return 0;
}
static void pll_csg(struct stm32mp1_clk_priv *priv, int pll_id, u32 *csg)
{
const struct stm32mp1_clk_pll *pll = priv->data->pll;
u32 pllxcsg;
pllxcsg = ((csg[PLLCSG_MOD_PER] << RCC_PLLNCSGR_MOD_PER_SHIFT) &
RCC_PLLNCSGR_MOD_PER_MASK) |
((csg[PLLCSG_INC_STEP] << RCC_PLLNCSGR_INC_STEP_SHIFT) &
RCC_PLLNCSGR_INC_STEP_MASK) |
((csg[PLLCSG_SSCG_MODE] << RCC_PLLNCSGR_SSCG_MODE_SHIFT) &
RCC_PLLNCSGR_SSCG_MODE_MASK);
writel(pllxcsg, priv->base + pll[pll_id].pllxcsgr);
}
static int set_clksrc(struct stm32mp1_clk_priv *priv, unsigned int clksrc)
{
u32 address = priv->base + (clksrc >> 4);
u32 val;
int ret;
clrsetbits_le32(address, RCC_SELR_SRC_MASK, clksrc & RCC_SELR_SRC_MASK);
ret = readl_poll_timeout(address, val, val & RCC_SELR_SRCRDY,
TIMEOUT_200MS);
if (ret)
pr_err("CLKSRC %x start failed @ 0x%x: 0x%x\n",
clksrc, address, readl(address));
return ret;
}
static int set_clkdiv(unsigned int clkdiv, u32 address)
{
u32 val;
int ret;
clrsetbits_le32(address, RCC_DIVR_DIV_MASK, clkdiv & RCC_DIVR_DIV_MASK);
ret = readl_poll_timeout(address, val, val & RCC_DIVR_DIVRDY,
TIMEOUT_200MS);
if (ret)
pr_err("CLKDIV %x start failed @ 0x%x: 0x%x\n",
clkdiv, address, readl(address));
return ret;
}
static void stm32mp1_mco_csg(struct stm32mp1_clk_priv *priv,
u32 clksrc, u32 clkdiv)
{
u32 address = priv->base + (clksrc >> 4);
/*
* binding clksrc : bit15-4 offset
* bit3: disable
* bit2-0: MCOSEL[2:0]
*/
if (clksrc & 0x8) {
clrbits_le32(address, RCC_MCOCFG_MCOON);
} else {
clrsetbits_le32(address,
RCC_MCOCFG_MCOSRC_MASK,
clksrc & RCC_MCOCFG_MCOSRC_MASK);
clrsetbits_le32(address,
RCC_MCOCFG_MCODIV_MASK,
clkdiv << RCC_MCOCFG_MCODIV_SHIFT);
setbits_le32(address, RCC_MCOCFG_MCOON);
}
}
static void set_rtcsrc(struct stm32mp1_clk_priv *priv,
unsigned int clksrc,
int lse_css)
{
u32 address = priv->base + RCC_BDCR;
if (readl(address) & RCC_BDCR_RTCCKEN)
goto skip_rtc;
if (clksrc == CLK_RTC_DISABLED)
goto skip_rtc;
clrsetbits_le32(address,
RCC_BDCR_RTCSRC_MASK,
clksrc << RCC_BDCR_RTCSRC_SHIFT);
setbits_le32(address, RCC_BDCR_RTCCKEN);
skip_rtc:
if (lse_css)
setbits_le32(address, RCC_BDCR_LSECSSON);
}
static void pkcs_config(struct stm32mp1_clk_priv *priv, u32 pkcs)
{
u32 address = priv->base + ((pkcs >> 4) & 0xFFF);
u32 value = pkcs & 0xF;
u32 mask = 0xF;
if (pkcs & BIT(31)) {
mask <<= 4;
value <<= 4;
}
clrsetbits_le32(address, mask, value);
}
static int stm32mp1_clktree(struct udevice *dev)
{
struct stm32mp1_clk_priv *priv = dev_get_priv(dev);
fdt_addr_t rcc = priv->base;
unsigned int clksrc[CLKSRC_NB];
unsigned int clkdiv[CLKDIV_NB];
unsigned int pllcfg[_PLL_NB][PLLCFG_NB];
ofnode plloff[_PLL_NB];
int ret;
int i, len;
int lse_css = 0;
const u32 *pkcs_cell;
/* check mandatory field */
ret = dev_read_u32_array(dev, "st,clksrc", clksrc, CLKSRC_NB);
if (ret < 0) {
debug("field st,clksrc invalid: error %d\n", ret);
return -FDT_ERR_NOTFOUND;
}
ret = dev_read_u32_array(dev, "st,clkdiv", clkdiv, CLKDIV_NB);
if (ret < 0) {
debug("field st,clkdiv invalid: error %d\n", ret);
return -FDT_ERR_NOTFOUND;
}
/* check mandatory field in each pll */
for (i = 0; i < _PLL_NB; i++) {
char name[12];
sprintf(name, "st,pll@%d", i);
plloff[i] = dev_read_subnode(dev, name);
if (!ofnode_valid(plloff[i]))
continue;
ret = ofnode_read_u32_array(plloff[i], "cfg",
pllcfg[i], PLLCFG_NB);
if (ret < 0) {
debug("field cfg invalid: error %d\n", ret);
return -FDT_ERR_NOTFOUND;
}
}
debug("configuration MCO\n");
stm32mp1_mco_csg(priv, clksrc[CLKSRC_MCO1], clkdiv[CLKDIV_MCO1]);
stm32mp1_mco_csg(priv, clksrc[CLKSRC_MCO2], clkdiv[CLKDIV_MCO2]);
debug("switch ON osillator\n");
/*
* switch ON oscillator found in device-tree,
* HSI already ON after bootrom
*/
if (priv->osc[_LSI])
stm32mp1_lsi_set(rcc, 1);
if (priv->osc[_LSE]) {
int bypass;
int lsedrv;
struct udevice *dev = priv->osc_dev[_LSE];
bypass = dev_read_bool(dev, "st,bypass");
lse_css = dev_read_bool(dev, "st,css");
lsedrv = dev_read_u32_default(dev, "st,drive",
LSEDRV_MEDIUM_HIGH);
stm32mp1_lse_enable(rcc, bypass, lsedrv);
}
if (priv->osc[_HSE]) {
int bypass, css;
struct udevice *dev = priv->osc_dev[_HSE];
bypass = dev_read_bool(dev, "st,bypass");
css = dev_read_bool(dev, "st,css");
stm32mp1_hse_enable(rcc, bypass, css);
}
/* CSI is mandatory for automatic I/O compensation (SYSCFG_CMPCR)
* => switch on CSI even if node is not present in device tree
*/
stm32mp1_csi_set(rcc, 1);
/* come back to HSI */
debug("come back to HSI\n");
set_clksrc(priv, CLK_MPU_HSI);
set_clksrc(priv, CLK_AXI_HSI);
set_clksrc(priv, CLK_MCU_HSI);
debug("pll stop\n");
for (i = 0; i < _PLL_NB; i++)
pll_stop(priv, i);
/* configure HSIDIV */
debug("configure HSIDIV\n");
if (priv->osc[_HSI])
stm32mp1_hsidiv(rcc, priv->osc[_HSI]);
/* select DIV */
debug("select DIV\n");
/* no ready bit when MPUSRC != CLK_MPU_PLL1P_DIV, MPUDIV is disabled */
writel(clkdiv[CLKDIV_MPU] & RCC_DIVR_DIV_MASK, rcc + RCC_MPCKDIVR);
set_clkdiv(clkdiv[CLKDIV_AXI], rcc + RCC_AXIDIVR);
set_clkdiv(clkdiv[CLKDIV_APB4], rcc + RCC_APB4DIVR);
set_clkdiv(clkdiv[CLKDIV_APB5], rcc + RCC_APB5DIVR);
set_clkdiv(clkdiv[CLKDIV_MCU], rcc + RCC_MCUDIVR);
set_clkdiv(clkdiv[CLKDIV_APB1], rcc + RCC_APB1DIVR);
set_clkdiv(clkdiv[CLKDIV_APB2], rcc + RCC_APB2DIVR);
set_clkdiv(clkdiv[CLKDIV_APB3], rcc + RCC_APB3DIVR);
/* no ready bit for RTC */
writel(clkdiv[CLKDIV_RTC] & RCC_DIVR_DIV_MASK, rcc + RCC_RTCDIVR);
/* configure PLLs source */
debug("configure PLLs source\n");
set_clksrc(priv, clksrc[CLKSRC_PLL12]);
set_clksrc(priv, clksrc[CLKSRC_PLL3]);
set_clksrc(priv, clksrc[CLKSRC_PLL4]);
/* configure and start PLLs */
debug("configure PLLs\n");
for (i = 0; i < _PLL_NB; i++) {
u32 fracv;
u32 csg[PLLCSG_NB];
debug("configure PLL %d @ %d\n", i,
ofnode_to_offset(plloff[i]));
if (!ofnode_valid(plloff[i]))
continue;
fracv = ofnode_read_u32_default(plloff[i], "frac", 0);
pll_config(priv, i, pllcfg[i], fracv);
ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB);
if (!ret) {
pll_csg(priv, i, csg);
} else if (ret != -FDT_ERR_NOTFOUND) {
debug("invalid csg node for pll@%d res=%d\n", i, ret);
return ret;
}
pll_start(priv, i);
}
/* wait and start PLLs ouptut when ready */
for (i = 0; i < _PLL_NB; i++) {
if (!ofnode_valid(plloff[i]))
continue;
debug("output PLL %d\n", i);
pll_output(priv, i, pllcfg[i][PLLCFG_O]);
}
/* wait LSE ready before to use it */
if (priv->osc[_LSE])
stm32mp1_lse_wait(rcc);
/* configure with expected clock source */
debug("CLKSRC\n");
set_clksrc(priv, clksrc[CLKSRC_MPU]);
set_clksrc(priv, clksrc[CLKSRC_AXI]);
set_clksrc(priv, clksrc[CLKSRC_MCU]);
set_rtcsrc(priv, clksrc[CLKSRC_RTC], lse_css);
/* configure PKCK */
debug("PKCK\n");
pkcs_cell = dev_read_prop(dev, "st,pkcs", &len);
if (pkcs_cell) {
bool ckper_disabled = false;
for (i = 0; i < len / sizeof(u32); i++) {
u32 pkcs = (u32)fdt32_to_cpu(pkcs_cell[i]);
if (pkcs == CLK_CKPER_DISABLED) {
ckper_disabled = true;
continue;
}
pkcs_config(priv, pkcs);
}
/* CKPER is source for some peripheral clock
* (FMC-NAND / QPSI-NOR) and switching source is allowed
* only if previous clock is still ON
* => deactivated CKPER only after switching clock
*/
if (ckper_disabled)
pkcs_config(priv, CLK_CKPER_DISABLED);
}
debug("oscillator off\n");
/* switch OFF HSI if not found in device-tree */
if (!priv->osc[_HSI])
stm32mp1_hsi_set(rcc, 0);
/* Software Self-Refresh mode (SSR) during DDR initilialization */
clrsetbits_le32(priv->base + RCC_DDRITFCR,
RCC_DDRITFCR_DDRCKMOD_MASK,
RCC_DDRITFCR_DDRCKMOD_SSR <<
RCC_DDRITFCR_DDRCKMOD_SHIFT);
return 0;
}
#endif /* STM32MP1_CLOCK_TREE_INIT */
static void stm32mp1_osc_clk_init(const char *name,
struct stm32mp1_clk_priv *priv,
int index)
@ -1133,6 +1703,12 @@ static int stm32mp1_clk_probe(struct udevice *dev)
stm32mp1_osc_init(dev);
#ifdef STM32MP1_CLOCK_TREE_INIT
/* clock tree init is done only one time, before relocation */
if (!(gd->flags & GD_FLG_RELOC))
result = stm32mp1_clktree(dev);
#endif
return result;
}

View File

@ -0,0 +1,284 @@
/*
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause
*/
#ifndef _DT_BINDINGS_CLOCK_STM32MP1_CLKSRC_H_
#define _DT_BINDINGS_CLOCK_STM32MP1_CLKSRC_H_
/* PLL output is enable when x=1, with x=p,q or r */
#define PQR(p, q, r) (((p) & 1) | (((q) & 1) << 1) | (((r) & 1) << 2))
/* st,clksrc: mandatory clock source */
#define CLK_MPU_HSI 0x00000200
#define CLK_MPU_HSE 0x00000201
#define CLK_MPU_PLL1P 0x00000202
#define CLK_MPU_PLL1P_DIV 0x00000203
#define CLK_AXI_HSI 0x00000240
#define CLK_AXI_HSE 0x00000241
#define CLK_AXI_PLL2P 0x00000242
#define CLK_MCU_HSI 0x00000480
#define CLK_MCU_HSE 0x00000481
#define CLK_MCU_CSI 0x00000482
#define CLK_MCU_PLL3P 0x00000483
#define CLK_PLL12_HSI 0x00000280
#define CLK_PLL12_HSE 0x00000281
#define CLK_PLL3_HSI 0x00008200
#define CLK_PLL3_HSE 0x00008201
#define CLK_PLL3_CSI 0x00008202
#define CLK_PLL4_HSI 0x00008240
#define CLK_PLL4_HSE 0x00008241
#define CLK_PLL4_CSI 0x00008242
#define CLK_PLL4_I2SCKIN 0x00008243
#define CLK_RTC_DISABLED 0x00001400
#define CLK_RTC_LSE 0x00001401
#define CLK_RTC_LSI 0x00001402
#define CLK_RTC_HSE 0x00001403
#define CLK_MCO1_HSI 0x00008000
#define CLK_MCO1_HSE 0x00008001
#define CLK_MCO1_CSI 0x00008002
#define CLK_MCO1_LSI 0x00008003
#define CLK_MCO1_LSE 0x00008004
#define CLK_MCO1_DISABLED 0x0000800F
#define CLK_MCO2_MPU 0x00008040
#define CLK_MCO2_AXI 0x00008041
#define CLK_MCO2_MCU 0x00008042
#define CLK_MCO2_PLL4P 0x00008043
#define CLK_MCO2_HSE 0x00008044
#define CLK_MCO2_HSI 0x00008045
#define CLK_MCO2_DISABLED 0x0000804F
/* st,pkcs: peripheral kernel clock source */
#define CLK_I2C12_PCLK1 0x00008C00
#define CLK_I2C12_PLL4R 0x00008C01
#define CLK_I2C12_HSI 0x00008C02
#define CLK_I2C12_CSI 0x00008C03
#define CLK_I2C12_DISABLED 0x00008C07
#define CLK_I2C35_PCLK1 0x00008C40
#define CLK_I2C35_PLL4R 0x00008C41
#define CLK_I2C35_HSI 0x00008C42
#define CLK_I2C35_CSI 0x00008C43
#define CLK_I2C35_DISABLED 0x00008C47
#define CLK_I2C46_PCLK5 0x00000C00
#define CLK_I2C46_PLL3Q 0x00000C01
#define CLK_I2C46_HSI 0x00000C02
#define CLK_I2C46_CSI 0x00000C03
#define CLK_I2C46_DISABLED 0x00000C07
#define CLK_SAI1_PLL4Q 0x00008C80
#define CLK_SAI1_PLL3Q 0x00008C81
#define CLK_SAI1_I2SCKIN 0x00008C82
#define CLK_SAI1_CKPER 0x00008C83
#define CLK_SAI1_PLL3R 0x00008C84
#define CLK_SAI1_DISABLED 0x00008C87
#define CLK_SAI2_PLL4Q 0x00008CC0
#define CLK_SAI2_PLL3Q 0x00008CC1
#define CLK_SAI2_I2SCKIN 0x00008CC2
#define CLK_SAI2_CKPER 0x00008CC3
#define CLK_SAI2_SPDIF 0x00008CC4
#define CLK_SAI2_PLL3R 0x00008CC5
#define CLK_SAI2_DISABLED 0x00008CC7
#define CLK_SAI3_PLL4Q 0x00008D00
#define CLK_SAI3_PLL3Q 0x00008D01
#define CLK_SAI3_I2SCKIN 0x00008D02
#define CLK_SAI3_CKPER 0x00008D03
#define CLK_SAI3_PLL3R 0x00008D04
#define CLK_SAI3_DISABLED 0x00008D07
#define CLK_SAI4_PLL4Q 0x00008D40
#define CLK_SAI4_PLL3Q 0x00008D41
#define CLK_SAI4_I2SCKIN 0x00008D42
#define CLK_SAI4_CKPER 0x00008D43
#define CLK_SAI4_PLL3R 0x00008D44
#define CLK_SAI4_DISABLED 0x00008D47
#define CLK_SPI2S1_PLL4P 0x00008D80
#define CLK_SPI2S1_PLL3Q 0x00008D81
#define CLK_SPI2S1_I2SCKIN 0x00008D82
#define CLK_SPI2S1_CKPER 0x00008D83
#define CLK_SPI2S1_PLL3R 0x00008D84
#define CLK_SPI2S1_DISABLED 0x00008D87
#define CLK_SPI2S23_PLL4P 0x00008DC0
#define CLK_SPI2S23_PLL3Q 0x00008DC1
#define CLK_SPI2S23_I2SCKIN 0x00008DC2
#define CLK_SPI2S23_CKPER 0x00008DC3
#define CLK_SPI2S23_PLL3R 0x00008DC4
#define CLK_SPI2S23_DISABLED 0x00008DC7
#define CLK_SPI45_PCLK2 0x00008E00
#define CLK_SPI45_PLL4Q 0x00008E01
#define CLK_SPI45_HSI 0x00008E02
#define CLK_SPI45_CSI 0x00008E03
#define CLK_SPI45_HSE 0x00008E04
#define CLK_SPI45_DISABLED 0x00008E07
#define CLK_SPI6_PCLK5 0x00000C40
#define CLK_SPI6_PLL4Q 0x00000C41
#define CLK_SPI6_HSI 0x00000C42
#define CLK_SPI6_CSI 0x00000C43
#define CLK_SPI6_HSE 0x00000C44
#define CLK_SPI6_PLL3Q 0x00000C45
#define CLK_SPI6_DISABLED 0x00000C47
#define CLK_UART6_PCLK2 0x00008E40
#define CLK_UART6_PLL4Q 0x00008E41
#define CLK_UART6_HSI 0x00008E42
#define CLK_UART6_CSI 0x00008E43
#define CLK_UART6_HSE 0x00008E44
#define CLK_UART6_DISABLED 0x00008E47
#define CLK_UART24_PCLK1 0x00008E80
#define CLK_UART24_PLL4Q 0x00008E81
#define CLK_UART24_HSI 0x00008E82
#define CLK_UART24_CSI 0x00008E83
#define CLK_UART24_HSE 0x00008E84
#define CLK_UART24_DISABLED 0x00008E87
#define CLK_UART35_PCLK1 0x00008EC0
#define CLK_UART35_PLL4Q 0x00008EC1
#define CLK_UART35_HSI 0x00008EC2
#define CLK_UART35_CSI 0x00008EC3
#define CLK_UART35_HSE 0x00008EC4
#define CLK_UART35_DISABLED 0x00008EC7
#define CLK_UART78_PCLK1 0x00008F00
#define CLK_UART78_PLL4Q 0x00008F01
#define CLK_UART78_HSI 0x00008F02
#define CLK_UART78_CSI 0x00008F03
#define CLK_UART78_HSE 0x00008F04
#define CLK_UART78_DISABLED 0x00008F07
#define CLK_UART1_PCLK5 0x00000C80
#define CLK_UART1_PLL3Q 0x00000C81
#define CLK_UART1_HSI 0x00000C82
#define CLK_UART1_CSI 0x00000C83
#define CLK_UART1_PLL4Q 0x00000C84
#define CLK_UART1_HSE 0x00000C85
#define CLK_UART1_DISABLED 0x00000C87
#define CLK_SDMMC12_HCLK6 0x00008F40
#define CLK_SDMMC12_PLL3R 0x00008F41
#define CLK_SDMMC12_PLL4P 0x00008F42
#define CLK_SDMMC12_HSI 0x00008F43
#define CLK_SDMMC12_DISABLED 0x00008F47
#define CLK_SDMMC3_HCLK2 0x00008F80
#define CLK_SDMMC3_PLL3R 0x00008F81
#define CLK_SDMMC3_PLL4P 0x00008F82
#define CLK_SDMMC3_HSI 0x00008F83
#define CLK_SDMMC3_DISABLED 0x00008F87
#define CLK_ETH_PLL4P 0x00008FC0
#define CLK_ETH_PLL3Q 0x00008FC1
#define CLK_ETH_DISABLED 0x00008FC3
#define CLK_QSPI_ACLK 0x00009000
#define CLK_QSPI_PLL3R 0x00009001
#define CLK_QSPI_PLL4P 0x00009002
#define CLK_QSPI_CKPER 0x00009003
#define CLK_FMC_ACLK 0x00009040
#define CLK_FMC_PLL3R 0x00009041
#define CLK_FMC_PLL4P 0x00009042
#define CLK_FMC_CKPER 0x00009043
#define CLK_FDCAN_HSE 0x000090C0
#define CLK_FDCAN_PLL3Q 0x000090C1
#define CLK_FDCAN_PLL4Q 0x000090C2
#define CLK_FDCAN_PLL4R 0x000090C3
#define CLK_SPDIF_PLL4P 0x00009140
#define CLK_SPDIF_PLL3Q 0x00009141
#define CLK_SPDIF_HSI 0x00009142
#define CLK_SPDIF_DISABLED 0x00009143
#define CLK_CEC_LSE 0x00009180
#define CLK_CEC_LSI 0x00009181
#define CLK_CEC_CSI_DIV122 0x00009182
#define CLK_CEC_DISABLED 0x00009183
#define CLK_USBPHY_HSE 0x000091C0
#define CLK_USBPHY_PLL4R 0x000091C1
#define CLK_USBPHY_HSE_DIV2 0x000091C2
#define CLK_USBPHY_DISABLED 0x000091C3
#define CLK_USBO_PLL4R 0x800091C0
#define CLK_USBO_USBPHY 0x800091C1
#define CLK_RNG1_CSI 0x00000CC0
#define CLK_RNG1_PLL4R 0x00000CC1
#define CLK_RNG1_LSE 0x00000CC2
#define CLK_RNG1_LSI 0x00000CC3
#define CLK_RNG2_CSI 0x00009200
#define CLK_RNG2_PLL4R 0x00009201
#define CLK_RNG2_LSE 0x00009202
#define CLK_RNG2_LSI 0x00009203
#define CLK_CKPER_HSI 0x00000D00
#define CLK_CKPER_CSI 0x00000D01
#define CLK_CKPER_HSE 0x00000D02
#define CLK_CKPER_DISABLED 0x00000D03
#define CLK_STGEN_HSI 0x00000D40
#define CLK_STGEN_HSE 0x00000D41
#define CLK_STGEN_DISABLED 0x00000D43
#define CLK_DSI_DSIPLL 0x00009240
#define CLK_DSI_PLL4P 0x00009241
#define CLK_ADC_PLL4R 0x00009280
#define CLK_ADC_CKPER 0x00009281
#define CLK_ADC_PLL3Q 0x00009282
#define CLK_ADC_DISABLED 0x00009283
#define CLK_LPTIM45_PCLK3 0x000092C0
#define CLK_LPTIM45_PLL4P 0x000092C1
#define CLK_LPTIM45_PLL3Q 0x000092C2
#define CLK_LPTIM45_LSE 0x000092C3
#define CLK_LPTIM45_LSI 0x000092C4
#define CLK_LPTIM45_CKPER 0x000092C5
#define CLK_LPTIM45_DISABLED 0x000092C7
#define CLK_LPTIM23_PCLK3 0x00009300
#define CLK_LPTIM23_PLL4Q 0x00009301
#define CLK_LPTIM23_CKPER 0x00009302
#define CLK_LPTIM23_LSE 0x00009303
#define CLK_LPTIM23_LSI 0x00009304
#define CLK_LPTIM23_DISABLED 0x00009307
#define CLK_LPTIM1_PCLK1 0x00009340
#define CLK_LPTIM1_PLL4P 0x00009341
#define CLK_LPTIM1_PLL3Q 0x00009342
#define CLK_LPTIM1_LSE 0x00009343
#define CLK_LPTIM1_LSI 0x00009344
#define CLK_LPTIM1_CKPER 0x00009345
#define CLK_LPTIM1_DISABLED 0x00009347
/* define for st,pll /csg */
#define SSCG_MODE_CENTER_SPREAD 0
#define SSCG_MODE_DOWN_SPREAD 1
/* define for st,drive */
#define LSEDRV_LOWEST 0
#define LSEDRV_MEDIUM_LOW 1
#define LSEDRV_MEDIUM_HIGH 2
#define LSEDRV_HIGHEST 3
#endif