#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
+#include <stm32_rcc.h>
#include <asm/io.h>
#include <asm/arch/stm32.h>
-#include <asm/arch/stm32_periph.h>
#include <asm/arch/stm32_pwr.h>
#include <dt-bindings/mfd/stm32f7-rcc.h>
#define RCC_CR_CSSON BIT(19)
#define RCC_CR_PLLON BIT(24)
#define RCC_CR_PLLRDY BIT(25)
+#define RCC_CR_PLLSAION BIT(28)
+#define RCC_CR_PLLSAIRDY BIT(29)
#define RCC_PLLCFGR_PLLM_MASK GENMASK(5, 0)
#define RCC_PLLCFGR_PLLN_MASK GENMASK(14, 6)
#define RCC_CFGR_PPRE1_SHIFT 10
#define RCC_CFGR_PPRE2_SHIFT 13
+#define RCC_PLLCFGR_PLLSAIN_MASK GENMASK(14, 6)
+#define RCC_PLLCFGR_PLLSAIP_MASK GENMASK(17, 16)
+#define RCC_PLLSAICFGR_PLLSAIN_SHIFT 6
+#define RCC_PLLSAICFGR_PLLSAIP_SHIFT 16
+#define RCC_PLLSAICFGR_PLLSAIP_4 BIT(16)
+#define RCC_PLLSAICFGR_PLLSAIQ_4 BIT(26)
+#define RCC_PLLSAICFGR_PLLSAIR_2 BIT(29)
+
+#define RCC_DCKCFGRX_CK48MSEL BIT(27)
+#define RCC_DCKCFGRX_SDMMC1SEL BIT(28)
+#define RCC_DCKCFGR2_SDMMC2SEL BIT(29)
+
/*
* RCC AHB1ENR specific definitions
*/
* RCC APB2ENR specific definitions
*/
#define RCC_APB2ENR_SYSCFGEN BIT(14)
+#define RCC_APB2ENR_SAI1EN BIT(22)
-
-struct pll_psc {
- u8 pll_m;
- u16 pll_n;
- u8 pll_p;
- u8 pll_q;
- u8 ahb_psc;
- u8 apb1_psc;
- u8 apb2_psc;
+enum periph_clock {
+ TIMER2_CLOCK_CFG,
};
-#define AHB_PSC_1 0
-#define AHB_PSC_2 0x8
-#define AHB_PSC_4 0x9
-#define AHB_PSC_8 0xA
-#define AHB_PSC_16 0xB
-#define AHB_PSC_64 0xC
-#define AHB_PSC_128 0xD
-#define AHB_PSC_256 0xE
-#define AHB_PSC_512 0xF
-
-#define APB_PSC_1 0
-#define APB_PSC_2 0x4
-#define APB_PSC_4 0x5
-#define APB_PSC_8 0x6
-#define APB_PSC_16 0x7
-
-struct stm32_clk_info {
- struct pll_psc sys_pll_psc;
- bool has_overdrive;
-};
-
-struct stm32_clk_info stm32f4_clk_info = {
+static const struct stm32_clk_info stm32f4_clk_info = {
/* 180 MHz */
.sys_pll_psc = {
- .pll_m = 8,
.pll_n = 360,
.pll_p = 2,
.pll_q = 8,
.apb2_psc = APB_PSC_2,
},
.has_overdrive = false,
+ .v2 = false,
};
-struct stm32_clk_info stm32f7_clk_info = {
+static const struct stm32_clk_info stm32f7_clk_info = {
/* 200 MHz */
.sys_pll_psc = {
- .pll_m = 25,
.pll_n = 400,
.pll_p = 2,
.pll_q = 8,
.apb2_psc = APB_PSC_2,
},
.has_overdrive = true,
+ .v2 = true,
};
struct stm32_clk {
struct stm32_rcc_regs *base;
struct stm32_pwr_regs *pwr_regs;
- struct stm32_clk_info *info;
+ struct stm32_clk_info info;
+ unsigned long hse_rate;
};
static int configure_clocks(struct udevice *dev)
struct stm32_clk *priv = dev_get_priv(dev);
struct stm32_rcc_regs *regs = priv->base;
struct stm32_pwr_regs *pwr = priv->pwr_regs;
- struct pll_psc sys_pll_psc = priv->info->sys_pll_psc;
+ struct pll_psc *sys_pll_psc = &priv->info.sys_pll_psc;
+ u32 pllsaicfgr = 0;
/* Reset RCC configuration */
setbits_le32(®s->cr, RCC_CR_HSION);
writel(0, ®s->cfgr); /* Reset CFGR */
clrbits_le32(®s->cr, (RCC_CR_HSEON | RCC_CR_CSSON
- | RCC_CR_PLLON));
+ | RCC_CR_PLLON | RCC_CR_PLLSAION));
writel(0x24003010, ®s->pllcfgr); /* Reset value from RM */
clrbits_le32(®s->cr, RCC_CR_HSEBYP);
writel(0, ®s->cir); /* Disable all interrupts */
;
setbits_le32(®s->cfgr, ((
- sys_pll_psc.ahb_psc << RCC_CFGR_HPRE_SHIFT)
- | (sys_pll_psc.apb1_psc << RCC_CFGR_PPRE1_SHIFT)
- | (sys_pll_psc.apb2_psc << RCC_CFGR_PPRE2_SHIFT)));
+ sys_pll_psc->ahb_psc << RCC_CFGR_HPRE_SHIFT)
+ | (sys_pll_psc->apb1_psc << RCC_CFGR_PPRE1_SHIFT)
+ | (sys_pll_psc->apb2_psc << RCC_CFGR_PPRE2_SHIFT)));
/* Configure the main PLL */
setbits_le32(®s->pllcfgr, RCC_PLLCFGR_PLLSRC); /* pll source HSE */
clrsetbits_le32(®s->pllcfgr, RCC_PLLCFGR_PLLM_MASK,
- sys_pll_psc.pll_m << RCC_PLLCFGR_PLLM_SHIFT);
+ sys_pll_psc->pll_m << RCC_PLLCFGR_PLLM_SHIFT);
clrsetbits_le32(®s->pllcfgr, RCC_PLLCFGR_PLLN_MASK,
- sys_pll_psc.pll_n << RCC_PLLCFGR_PLLN_SHIFT);
+ sys_pll_psc->pll_n << RCC_PLLCFGR_PLLN_SHIFT);
clrsetbits_le32(®s->pllcfgr, RCC_PLLCFGR_PLLP_MASK,
- ((sys_pll_psc.pll_p >> 1) - 1) << RCC_PLLCFGR_PLLP_SHIFT);
+ ((sys_pll_psc->pll_p >> 1) - 1) << RCC_PLLCFGR_PLLP_SHIFT);
clrsetbits_le32(®s->pllcfgr, RCC_PLLCFGR_PLLQ_MASK,
- sys_pll_psc.pll_q << RCC_PLLCFGR_PLLQ_SHIFT);
+ sys_pll_psc->pll_q << RCC_PLLCFGR_PLLQ_SHIFT);
+
+ /* Configure the SAI PLL to get a 48 MHz source */
+ pllsaicfgr = RCC_PLLSAICFGR_PLLSAIR_2 | RCC_PLLSAICFGR_PLLSAIQ_4 |
+ RCC_PLLSAICFGR_PLLSAIP_4;
+ pllsaicfgr |= 192 << RCC_PLLSAICFGR_PLLSAIN_SHIFT;
+ writel(pllsaicfgr, ®s->pllsaicfgr);
/* Enable the main PLL */
setbits_le32(®s->cr, RCC_CR_PLLON);
while (!(readl(®s->cr) & RCC_CR_PLLRDY))
;
+ if (priv->info.v2) { /*stm32f7 case */
+ /* select PLLSAI as 48MHz clock source */
+ setbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_CK48MSEL);
+
+ /* select 48MHz as SDMMC1 clock source */
+ clrbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_SDMMC1SEL);
+
+ /* select 48MHz as SDMMC2 clock source */
+ clrbits_le32(®s->dckcfgr2, RCC_DCKCFGR2_SDMMC2SEL);
+ } else { /* stm32f4 case */
+ /* select PLLSAI as 48MHz clock source */
+ setbits_le32(®s->dckcfgr, RCC_DCKCFGRX_CK48MSEL);
+
+ /* select 48MHz as SDMMC1 clock source */
+ clrbits_le32(®s->dckcfgr, RCC_DCKCFGRX_SDMMC1SEL);
+ }
+
+ /* Enable the SAI PLL */
+ setbits_le32(®s->cr, RCC_CR_PLLSAION);
+ while (!(readl(®s->cr) & RCC_CR_PLLSAIRDY))
+ ;
+
setbits_le32(®s->apb1enr, RCC_APB1ENR_PWREN);
- if (priv->info->has_overdrive) {
+ if (priv->info.has_overdrive) {
/*
* Enable high performance mode
* System frequency up to 200 MHz
while ((readl(®s->cfgr) & RCC_CFGR_SWS_MASK) !=
RCC_CFGR_SWS_PLL)
;
+ /* gate the SAI clock, needed for MMC 1&2 clocks */
+ setbits_le32(®s->apb2enr, RCC_APB2ENR_SAI1EN);
+
+#ifdef CONFIG_ETH_DESIGNWARE
+ /* gate the SYSCFG clock, needed to set RMII ethernet interface */
+ setbits_le32(®s->apb2enr, RCC_APB2ENR_SYSCFGEN);
+#endif
return 0;
}
+static unsigned long stm32_clk_pll48clk_rate(struct stm32_clk *priv,
+ u32 sysclk)
+{
+ struct stm32_rcc_regs *regs = priv->base;
+ u16 pllq, pllm, pllsain, pllsaip;
+ bool pllsai;
+
+ pllq = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLQ_MASK)
+ >> RCC_PLLCFGR_PLLQ_SHIFT;
+
+ if (priv->info.v2) /*stm32f7 case */
+ pllsai = readl(®s->dckcfgr2) & RCC_DCKCFGRX_CK48MSEL;
+ else
+ pllsai = readl(®s->dckcfgr) & RCC_DCKCFGRX_CK48MSEL;
+
+ if (pllsai) {
+ /* PLL48CLK is selected from PLLSAI, get PLLSAI value */
+ pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK);
+ pllsain = ((readl(®s->pllsaicfgr) & RCC_PLLCFGR_PLLSAIN_MASK)
+ >> RCC_PLLSAICFGR_PLLSAIN_SHIFT);
+ pllsaip = ((((readl(®s->pllsaicfgr) & RCC_PLLCFGR_PLLSAIP_MASK)
+ >> RCC_PLLSAICFGR_PLLSAIP_SHIFT) + 1) << 1);
+ return ((priv->hse_rate / pllm) * pllsain) / pllsaip;
+ }
+ /* PLL48CLK is selected from PLLQ */
+ return sysclk / pllq;
+}
+
static unsigned long stm32_clk_get_rate(struct clk *clk)
{
struct stm32_clk *priv = dev_get_priv(clk->dev);
>> RCC_PLLCFGR_PLLN_SHIFT);
pllp = ((((readl(®s->pllcfgr) & RCC_PLLCFGR_PLLP_MASK)
>> RCC_PLLCFGR_PLLP_SHIFT) + 1) << 1);
- sysclk = ((CONFIG_STM32_HSE_HZ / pllm) * plln) / pllp;
+ sysclk = ((priv->hse_rate / pllm) * plln) / pllp;
} else {
return -EINVAL;
}
return sysclk >>= shift;
/* APB2 CLOCK */
case STM32F7_APB2_CLOCK(TIM1) ... STM32F7_APB2_CLOCK(LTDC):
+ /*
+ * particular case for SDMMC1 and SDMMC2 :
+ * 48Mhz source clock can be from main PLL or from
+ * SAI PLL
+ */
+ switch (clk->id) {
+ case STM32F7_APB2_CLOCK(SDMMC1):
+ if (readl(®s->dckcfgr2) & RCC_DCKCFGRX_SDMMC1SEL)
+ /* System clock is selected as SDMMC1 clock */
+ return sysclk;
+ else
+ return stm32_clk_pll48clk_rate(priv, sysclk);
+ break;
+ case STM32F7_APB2_CLOCK(SDMMC2):
+ if (readl(®s->dckcfgr2) & RCC_DCKCFGR2_SDMMC2SEL)
+ /* System clock is selected as SDMMC2 clock */
+ return sysclk;
+ else
+ return stm32_clk_pll48clk_rate(priv, sysclk);
+ break;
+ }
+
shift = apb_psc_table[(
(readl(®s->cfgr) & RCC_CFGR_APB2_PSC_MASK)
>> RCC_CFGR_PPRE2_SHIFT)];
void clock_setup(int peripheral)
{
switch (peripheral) {
- case SYSCFG_CLOCK_CFG:
- setbits_le32(&STM32_RCC->apb2enr, RCC_APB2ENR_SYSCFGEN);
- break;
case TIMER2_CLOCK_CFG:
setbits_le32(&STM32_RCC->apb1enr, RCC_APB1ENR_TIM2EN);
break;
- case STMMAC_CLOCK_CFG:
- setbits_le32(&STM32_RCC->ahb1enr, RCC_AHB1ENR_ETHMAC_EN);
- setbits_le32(&STM32_RCC->ahb1enr, RCC_AHB1ENR_ETHMAC_RX_EN);
- setbits_le32(&STM32_RCC->ahb1enr, RCC_AHB1ENR_ETHMAC_TX_EN);
- break;
default:
break;
}
static int stm32_clk_probe(struct udevice *dev)
{
struct ofnode_phandle_args args;
+ struct udevice *fixed_clock_dev = NULL;
+ struct clk clk;
int err;
debug("%s\n", __func__);
return -EINVAL;
priv->base = (struct stm32_rcc_regs *)addr;
- priv->info = (struct stm32_clk_info *)dev_get_driver_data(dev);
- if (priv->info->has_overdrive) {
+ switch (dev_get_driver_data(dev)) {
+ case STM32F4:
+ memcpy(&priv->info, &stm32f4_clk_info,
+ sizeof(struct stm32_clk_info));
+ break;
+ case STM32F7:
+ memcpy(&priv->info, &stm32f7_clk_info,
+ sizeof(struct stm32_clk_info));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* retrieve HSE frequency (external oscillator) */
+ err = uclass_get_device_by_name(UCLASS_CLK, "clk-hse",
+ &fixed_clock_dev);
+
+ if (err) {
+ pr_err("Can't find fixed clock (%d)", err);
+ return err;
+ }
+
+ err = clk_request(fixed_clock_dev, &clk);
+ if (err) {
+ pr_err("Can't request %s clk (%d)", fixed_clock_dev->name,
+ err);
+ return err;
+ }
+
+ /*
+ * set pllm factor accordingly to the external oscillator
+ * frequency (HSE). For STM32F4 and STM32F7, we want VCO
+ * freq at 1MHz
+ * if input PLL frequency is 25Mhz, divide it by 25
+ */
+ clk.id = 0;
+ priv->hse_rate = clk_get_rate(&clk);
+
+ if (priv->hse_rate < 1000000) {
+ pr_err("%s: unexpected HSE clock rate = %ld \"n", __func__,
+ priv->hse_rate);
+ return -EINVAL;
+ }
+
+ priv->info.sys_pll_psc.pll_m = priv->hse_rate / 1000000;
+
+ if (priv->info.has_overdrive) {
err = dev_read_phandle_with_args(dev, "st,syscfg", NULL, 0, 0,
&args);
if (err) {
.get_rate = stm32_clk_get_rate,
};
-static const struct udevice_id stm32_clk_ids[] = {
- { .compatible = "st,stm32f42xx-rcc", .data = (ulong)&stm32f4_clk_info},
- { .compatible = "st,stm32f746-rcc", .data = (ulong)&stm32f7_clk_info},
- {}
-};
-
U_BOOT_DRIVER(stm32fx_clk) = {
- .name = "stm32fx_clk",
+ .name = "stm32fx_rcc_clock",
.id = UCLASS_CLK,
- .of_match = stm32_clk_ids,
.ops = &stm32_clk_ops,
.probe = stm32_clk_probe,
.priv_auto_alloc_size = sizeof(struct stm32_clk),