From 5d1aac358f3c4cbdfb0eb025cd88cfef654f51f4 Mon Sep 17 00:00:00 2001 From: Manorit Chawdhry Date: Thu, 21 Nov 2024 17:32:51 +0530 Subject: [PATCH 1/3] arm: dts: k3-*-r5: Remove clocks from mcu_timer0 Updated PLL driver sequencing requires us to use udelay in the PLL driver as there is no poll bit to get the status of operations. tick-timer(mcu_timer0/main_timer0) setting up the clocks for itself is something that won't work as the PLL driver will be using udelay and PLLs are configured during clock probe which would end up in a recursive probe. tick-timer being used by K3 devices are configured by ROM and we really don't need to configure any of the clocks. Remove the clock dependency from R5 stage as we don't need to setup clocks for it. Signed-off-by: Manorit Chawdhry --- arch/arm/dts/k3-am62-lp-sk-u-boot.dtsi | 10 ---------- arch/arm/dts/k3-am62-r5-lp-sk.dts | 9 +++++++++ arch/arm/dts/k3-am625-r5-sk.dts | 9 +++++++++ arch/arm/dts/k3-am625-sk-u-boot.dtsi | 10 ---------- arch/arm/dts/k3-am62a7-r5-sk.dts | 10 ++++++++++ arch/arm/dts/k3-am62a7-sk-u-boot.dtsi | 5 ----- arch/arm/dts/k3-am62p5-r5-sk.dts | 8 ++++++++ arch/arm/dts/k3-j7200-r5-common-proc-board.dts | 4 ++++ arch/arm/dts/k3-j721e-r5.dtsi | 6 +++++- arch/arm/dts/k3-j721s2-r5.dtsi | 4 ++++ arch/arm/dts/k3-j722s-r5-evm.dts | 8 ++++++++ arch/arm/dts/k3-j784s4-r5.dtsi | 5 ++++- 12 files changed, 61 insertions(+), 27 deletions(-) diff --git a/arch/arm/dts/k3-am62-lp-sk-u-boot.dtsi b/arch/arm/dts/k3-am62-lp-sk-u-boot.dtsi index cbcc7f3bb45..848bc350698 100644 --- a/arch/arm/dts/k3-am62-lp-sk-u-boot.dtsi +++ b/arch/arm/dts/k3-am62-lp-sk-u-boot.dtsi @@ -5,13 +5,3 @@ */ #include "k3-am62-lp-sk-binman.dtsi" - -/ { - chosen { - tick-timer = &main_timer0; - }; -}; - -&main_timer0 { - clock-frequency = <25000000>; -}; diff --git a/arch/arm/dts/k3-am62-r5-lp-sk.dts b/arch/arm/dts/k3-am62-r5-lp-sk.dts index b8e5f49a1fc..135e8d49b91 100644 --- a/arch/arm/dts/k3-am62-r5-lp-sk.dts +++ b/arch/arm/dts/k3-am62-r5-lp-sk.dts @@ -12,6 +12,7 @@ / { aliases { + tick-timer = &main_timer0; remoteproc0 = &sysctrler; remoteproc1 = &a53_0; serial0 = &wkup_uart0; @@ -72,6 +73,14 @@ }; }; +&main_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + clock-frequency = <25000000>; +}; + /* WKUP UART0 is used for DM firmware logs */ &wkup_uart0 { status = "okay"; diff --git a/arch/arm/dts/k3-am625-r5-sk.dts b/arch/arm/dts/k3-am625-r5-sk.dts index d2dd75469c1..34c501dd51b 100644 --- a/arch/arm/dts/k3-am625-r5-sk.dts +++ b/arch/arm/dts/k3-am625-r5-sk.dts @@ -12,6 +12,7 @@ / { aliases { + tick-timer = &main_timer0; remoteproc0 = &sysctrler; remoteproc1 = &a53_0; serial0 = &wkup_uart0; @@ -70,6 +71,14 @@ }; }; +&main_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + clock-frequency = <25000000>; +}; + /* WKUP UART0 is used for DM firmware logs */ &wkup_uart0 { status = "okay"; diff --git a/arch/arm/dts/k3-am625-sk-u-boot.dtsi b/arch/arm/dts/k3-am625-sk-u-boot.dtsi index 1fc0d407cbf..487ccf04b55 100644 --- a/arch/arm/dts/k3-am625-sk-u-boot.dtsi +++ b/arch/arm/dts/k3-am625-sk-u-boot.dtsi @@ -6,16 +6,6 @@ #include "k3-am625-sk-binman.dtsi" -/ { - chosen { - tick-timer = &main_timer0; - }; -}; - -&main_timer0 { - clock-frequency = <25000000>; -}; - &main_bcdma { reg = <0x00 0x485c0100 0x00 0x100>, <0x00 0x4c000000 0x00 0x20000>, diff --git a/arch/arm/dts/k3-am62a7-r5-sk.dts b/arch/arm/dts/k3-am62a7-r5-sk.dts index 464227b3b25..49e62533a95 100644 --- a/arch/arm/dts/k3-am62a7-r5-sk.dts +++ b/arch/arm/dts/k3-am62a7-r5-sk.dts @@ -12,6 +12,7 @@ / { aliases { + tick-timer = &main_timer0; remoteproc0 = &sysctrler; remoteproc1 = &a53_0; }; @@ -71,6 +72,15 @@ }; }; +&main_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + clock-frequency = <25000000>; + bootph-pre-ram; +}; + &wkup_uart0_pins_default { bootph-pre-ram; }; diff --git a/arch/arm/dts/k3-am62a7-sk-u-boot.dtsi b/arch/arm/dts/k3-am62a7-sk-u-boot.dtsi index c42dec16194..7dfbeb10c32 100644 --- a/arch/arm/dts/k3-am62a7-sk-u-boot.dtsi +++ b/arch/arm/dts/k3-am62a7-sk-u-boot.dtsi @@ -9,7 +9,6 @@ / { chosen { stdout-path = "serial2:115200n8"; - tick-timer = &main_timer0; }; memory@80000000 { @@ -17,10 +16,6 @@ }; }; -&main_timer0 { - bootph-all; -}; - &cbass_main { bootph-all; }; diff --git a/arch/arm/dts/k3-am62p5-r5-sk.dts b/arch/arm/dts/k3-am62p5-r5-sk.dts index baf1a83dc12..b18b4ce1272 100644 --- a/arch/arm/dts/k3-am62p5-r5-sk.dts +++ b/arch/arm/dts/k3-am62p5-r5-sk.dts @@ -78,6 +78,14 @@ }; }; +&main_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + clock-frequency = <25000000>; +}; + /* WKUP UART0 is used for DM firmware logs */ &wkup_uart0 { status = "okay"; diff --git a/arch/arm/dts/k3-j7200-r5-common-proc-board.dts b/arch/arm/dts/k3-j7200-r5-common-proc-board.dts index f096b102793..69bf9993077 100644 --- a/arch/arm/dts/k3-j7200-r5-common-proc-board.dts +++ b/arch/arm/dts/k3-j7200-r5-common-proc-board.dts @@ -53,6 +53,10 @@ }; &mcu_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; clock-frequency = <250000000>; bootph-pre-ram; }; diff --git a/arch/arm/dts/k3-j721e-r5.dtsi b/arch/arm/dts/k3-j721e-r5.dtsi index 688a6cf4089..786a41c5e90 100644 --- a/arch/arm/dts/k3-j721e-r5.dtsi +++ b/arch/arm/dts/k3-j721e-r5.dtsi @@ -42,7 +42,11 @@ }; &mcu_timer0 { - status = "okay"; + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + clock-frequency = <166666666>; bootph-pre-ram; }; diff --git a/arch/arm/dts/k3-j721s2-r5.dtsi b/arch/arm/dts/k3-j721s2-r5.dtsi index 634676c8491..a820f516015 100644 --- a/arch/arm/dts/k3-j721s2-r5.dtsi +++ b/arch/arm/dts/k3-j721s2-r5.dtsi @@ -43,6 +43,10 @@ }; &mcu_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; clock-frequency = <250000000>; bootph-pre-ram; }; diff --git a/arch/arm/dts/k3-j722s-r5-evm.dts b/arch/arm/dts/k3-j722s-r5-evm.dts index 5e5c2e3111e..08286ed792d 100644 --- a/arch/arm/dts/k3-j722s-r5-evm.dts +++ b/arch/arm/dts/k3-j722s-r5-evm.dts @@ -77,6 +77,14 @@ }; }; +&main_timer0 { + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + clock-frequency = <25000000>; +}; + /* WKUP UART0 is used for DM firmware logs */ &wkup_uart0 { status = "okay"; diff --git a/arch/arm/dts/k3-j784s4-r5.dtsi b/arch/arm/dts/k3-j784s4-r5.dtsi index 0cd0ccc2dea..b377a47ed22 100644 --- a/arch/arm/dts/k3-j784s4-r5.dtsi +++ b/arch/arm/dts/k3-j784s4-r5.dtsi @@ -41,7 +41,10 @@ }; &mcu_timer0 { - status = "okay"; + /delete-property/ clocks; + /delete-property/ clocks-names; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; clock-frequency = <250000000>; bootph-pre-ram; }; From d6cd643c4e6182f1bf3ae6c3db8a22913c752bc5 Mon Sep 17 00:00:00 2001 From: Manorit Chawdhry Date: Thu, 21 Nov 2024 17:32:52 +0530 Subject: [PATCH 2/3] clk: ti: clk-k3-pll: Change variable name reg to base base is more appropriate for the usage as the variable stores the base address and seems more accurate w.r.t reg. Change reg to base. Signed-off-by: Manorit Chawdhry --- drivers/clk/ti/clk-k3-pll.c | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/clk/ti/clk-k3-pll.c b/drivers/clk/ti/clk-k3-pll.c index b3a1b4cedb7..7c18767236c 100644 --- a/drivers/clk/ti/clk-k3-pll.c +++ b/drivers/clk/ti/clk-k3-pll.c @@ -75,7 +75,7 @@ */ struct ti_pll_clk { struct clk clk; - void __iomem *reg; + void __iomem *base; }; #define to_clk_pll(_clk) container_of(_clk, struct ti_pll_clk, clk) @@ -93,7 +93,7 @@ static int ti_pll_wait_for_lock(struct clk *clk) int success; for (i = 0; i < 100000; i++) { - stat = readl(pll->reg + PLL_16FFT_STAT); + stat = readl(pll->base + PLL_16FFT_STAT); if (stat & PLL_16FFT_STAT_LOCK) { success = 1; break; @@ -101,14 +101,14 @@ static int ti_pll_wait_for_lock(struct clk *clk) } /* Enable calibration if not in fractional mode of the FRACF PLL */ - freq_ctrl1 = readl(pll->reg + PLL_16FFT_FREQ_CTRL1); + freq_ctrl1 = readl(pll->base + PLL_16FFT_FREQ_CTRL1); pllfm = freq_ctrl1 & PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK; pllfm >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT; - cfg = readl(pll->reg + PLL_16FFT_CFG); + cfg = readl(pll->base + PLL_16FFT_CFG); pll_type = (cfg & PLL_16FFT_CFG_PLL_TYPE_MASK) >> PLL_16FFT_CFG_PLL_TYPE_SHIFT; if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF && pllfm == 0) { - cal = readl(pll->reg + PLL_16FFT_CAL_CTRL); + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); /* Enable calibration for FRACF */ cal |= PLL_16FFT_CAL_CTRL_CAL_EN; @@ -124,11 +124,11 @@ static int ti_pll_wait_for_lock(struct clk *clk) cal |= 2 << PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT; /* Note this register does not readback the written value. */ - writel(cal, pll->reg + PLL_16FFT_CAL_CTRL); + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); success = 0; for (i = 0; i < 100000; i++) { - stat = readl(pll->reg + PLL_16FFT_CAL_STAT); + stat = readl(pll->base + PLL_16FFT_CAL_STAT); if (stat & PLL_16FFT_CAL_STAT_CAL_LOCK) { success = 1; break; @@ -156,14 +156,14 @@ static ulong ti_pll_clk_get_rate(struct clk *clk) u32 ctrl; /* Check if we are in bypass */ - ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl = readl(pll->base + PLL_16FFT_CTRL); if (ctrl & PLL_16FFT_CTRL_BYPASS_EN) return parent_freq; - pllm = readl(pll->reg + PLL_16FFT_FREQ_CTRL0); - pllfm = readl(pll->reg + PLL_16FFT_FREQ_CTRL1); + pllm = readl(pll->base + PLL_16FFT_FREQ_CTRL0); + pllfm = readl(pll->base + PLL_16FFT_FREQ_CTRL1); - plld = readl(pll->reg + PLL_16FFT_DIV_CTRL) & + plld = readl(pll->base + PLL_16FFT_DIV_CTRL) & PLL_16FFT_DIV_CTRL_REF_DIV_MASK; current_freq = parent_freq * pllm / plld; @@ -213,9 +213,9 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) } /* Put PLL to bypass mode */ - ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl = readl(pll->base + PLL_16FFT_CTRL); ctrl |= PLL_16FFT_CTRL_BYPASS_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); + writel(ctrl, pll->base + PLL_16FFT_CTRL); if (rate == parent_freq) { debug("%s: put %s to bypass\n", __func__, clk->dev->name); @@ -242,21 +242,21 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) else ctrl &= ~PLL_16FFT_CTRL_DSM_EN; - writel(pllm, pll->reg + PLL_16FFT_FREQ_CTRL0); - writel(pllfm, pll->reg + PLL_16FFT_FREQ_CTRL1); + writel(pllm, pll->base + PLL_16FFT_FREQ_CTRL0); + writel(pllfm, pll->base + PLL_16FFT_FREQ_CTRL1); /* * div_ctrl register contains other divider values, so rmw * only plld and leave existing values alone */ - div_ctrl = readl(pll->reg + PLL_16FFT_DIV_CTRL); + div_ctrl = readl(pll->base + PLL_16FFT_DIV_CTRL); div_ctrl &= ~PLL_16FFT_DIV_CTRL_REF_DIV_MASK; div_ctrl |= plld; - writel(div_ctrl, pll->reg + PLL_16FFT_DIV_CTRL); + writel(div_ctrl, pll->base + PLL_16FFT_DIV_CTRL); ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; ctrl |= PLL_16FFT_CTRL_PLL_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); + writel(ctrl, pll->base + PLL_16FFT_CTRL); ret = ti_pll_wait_for_lock(clk); if (ret) @@ -284,10 +284,10 @@ static int ti_pll_clk_enable(struct clk *clk) struct ti_pll_clk *pll = to_clk_pll(clk); u32 ctrl; - ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl = readl(pll->base + PLL_16FFT_CTRL); ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; ctrl |= PLL_16FFT_CTRL_PLL_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); + writel(ctrl, pll->base + PLL_16FFT_CTRL); return ti_pll_wait_for_lock(clk); } @@ -297,9 +297,9 @@ static int ti_pll_clk_disable(struct clk *clk) struct ti_pll_clk *pll = to_clk_pll(clk); u32 ctrl; - ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl = readl(pll->base + PLL_16FFT_CTRL); ctrl |= PLL_16FFT_CTRL_BYPASS_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); + writel(ctrl, pll->base + PLL_16FFT_CTRL); return 0; } @@ -323,7 +323,7 @@ struct clk *clk_register_ti_pll(const char *name, const char *parent_name, if (!pll) return ERR_PTR(-ENOMEM); - pll->reg = reg; + pll->base = reg; ret = clk_register(&pll->clk, "ti-pll-clk", name, parent_name); if (ret) { @@ -333,19 +333,19 @@ struct clk *clk_register_ti_pll(const char *name, const char *parent_name, } /* Unlock the PLL registers */ - writel(PLL_KICK0_VALUE, pll->reg + PLL_KICK0); - writel(PLL_KICK1_VALUE, pll->reg + PLL_KICK1); + writel(PLL_KICK0_VALUE, pll->base + PLL_KICK0); + writel(PLL_KICK1_VALUE, pll->base + PLL_KICK1); /* Enable all HSDIV outputs */ - cfg = readl(pll->reg + PLL_16FFT_CFG); + cfg = readl(pll->base + PLL_16FFT_CFG); for (i = 0; i < 16; i++) { hsdiv_presence_bit = BIT(16 + i); hsdiv_ctrl_offs = 0x80 + (i * 4); /* Enable HSDIV output if present */ if ((hsdiv_presence_bit & cfg) != 0UL) { - ctrl = readl(pll->reg + hsdiv_ctrl_offs); + ctrl = readl(pll->base + hsdiv_ctrl_offs); ctrl |= PLL_16FFT_HSDIV_CTRL_CLKOUT_EN; - writel(ctrl, pll->reg + hsdiv_ctrl_offs); + writel(ctrl, pll->base + hsdiv_ctrl_offs); } } From 79d91e77f4c23c052f086e920311f5a3c703cfc0 Mon Sep 17 00:00:00 2001 From: Manorit Chawdhry Date: Thu, 21 Nov 2024 17:32:53 +0530 Subject: [PATCH 3/3] clk: ti: clk-k3-pll: Add additional robustness steps to the PLL sequence Based on the recommendation from HW team make modifications to the sequence for more robustness. - Unlock the PLL registers - Enable external bypass - Disable the PLL - Program pllm and pllf - Program Ref divider - Enable other PLL controls like DSM_EN, DAC_EN,etc - Enable calibration if available - Enable PLL - Wait for PLL lock and Calibration lock - Remove external bypass Re-write the full sequence from scratch as the previous sequence was way off and keep it in a single commit for bisectability. Signed-off-by: Manorit Chawdhry --- drivers/clk/ti/clk-k3-pll.c | 335 ++++++++++++++++++++++++++++-------- 1 file changed, 265 insertions(+), 70 deletions(-) diff --git a/drivers/clk/ti/clk-k3-pll.c b/drivers/clk/ti/clk-k3-pll.c index 7c18767236c..b775bd55faa 100644 --- a/drivers/clk/ti/clk-k3-pll.c +++ b/drivers/clk/ti/clk-k3-pll.c @@ -14,6 +14,7 @@ #include #include "k3-clk.h" #include +#include /* 16FFT register offsets */ #define PLL_16FFT_CFG 0x08 @@ -29,10 +30,12 @@ /* CAL STAT register bits */ #define PLL_16FFT_CAL_STAT_CAL_LOCK BIT(31) +#define PLL_16FFT_CAL_STAT_CAL_LOCK_TIMEOUT (4350U * 100U) /* CFG register bits */ #define PLL_16FFT_CFG_PLL_TYPE_SHIFT (0) #define PLL_16FFT_CFG_PLL_TYPE_MASK (0x3 << 0) +#define PLL_16FFT_CFG_PLL_TYPE_FRAC2 0 #define PLL_16FFT_CFG_PLL_TYPE_FRACF 1 /* CAL CTRL register bits */ @@ -41,14 +44,21 @@ #define PLL_16FFT_CAL_CTRL_CAL_BYP BIT(15) #define PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT 16 #define PLL_16FFT_CAL_CTRL_CAL_CNT_MASK (0x7 << 16) +#define PLL_16FFT_CAL_CTRL_CAL_IN_MASK (0xFFFU) /* CTRL register bits */ #define PLL_16FFT_CTRL_BYPASS_EN BIT(31) +#define PLL_16FFT_CTRL_BYP_ON_LOCKLOSS BIT(16) #define PLL_16FFT_CTRL_PLL_EN BIT(15) +#define PLL_16FFT_CTRL_INTL_BYP_EN BIT(8) +#define PLL_16FFT_CTRL_CLK_4PH_EN BIT(5) +#define PLL_16FFT_CTRL_CLK_POSTDIV_EN BIT(4) #define PLL_16FFT_CTRL_DSM_EN BIT(1) +#define PLL_16FFT_CTRL_DAC_EN BIT(0) /* STAT register bits */ #define PLL_16FFT_STAT_LOCK BIT(0) +#define PLL_16FFT_STAT_LOCK_TIMEOUT (150U * 100U) /* FREQ_CTRL0 bits */ #define PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK 0xfff @@ -62,7 +72,6 @@ /* FREQ_CTRL1 bits */ #define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS 24 #define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK 0xffffff -#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT 0 /* KICK register magic values */ #define PLL_KICK0_VALUE 0x68ef3490 @@ -80,63 +89,194 @@ struct ti_pll_clk { #define to_clk_pll(_clk) container_of(_clk, struct ti_pll_clk, clk) +static int ti_pll_clk_disable(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u32 ctrl; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + + if ((ctrl & PLL_16FFT_CTRL_PLL_EN)) { + ctrl &= ~PLL_16FFT_CTRL_PLL_EN; + writel(ctrl, pll->base + PLL_16FFT_CTRL); + + /* wait 1us */ + udelay(1); + } + + return 0; +} + +static int ti_pll_clk_enable(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u32 ctrl; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + ctrl |= PLL_16FFT_CTRL_PLL_EN; + writel(ctrl, pll->base + PLL_16FFT_CTRL); + + /* Wait 1us */ + udelay(1); + + return 0; +} + +static bool clk_pll_16fft_check_lock(const struct ti_pll_clk *pll) +{ + u32 stat; + + stat = readl(pll->base + PLL_16FFT_STAT); + return (stat & PLL_16FFT_STAT_LOCK); +} + +static bool clk_pll_16fft_check_cal_lock(const struct ti_pll_clk *pll) +{ + u32 stat; + + stat = readl(pll->base + PLL_16FFT_CAL_STAT); + return (stat & PLL_16FFT_CAL_STAT_CAL_LOCK); +} + +static void clk_pll_16fft_cal_int(const struct ti_pll_clk *pll) +{ + u32 cal; + + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + + /* Enable fast cal mode */ + cal |= PLL_16FFT_CAL_CTRL_FAST_CAL; + + /* Disable calibration bypass */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_BYP; + + /* Set CALCNT to 2 */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_CNT_MASK; + cal |= 2 << PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT; + + /* Set CAL_IN to 0 */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_IN_MASK; + + /* Note this register does not readback the written value. */ + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); + + /* Wait 1us before enabling the CAL_EN field */ + udelay(1); + + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + + /* Enable calibration for FRACF */ + cal |= PLL_16FFT_CAL_CTRL_CAL_EN; + + /* Note this register does not readback the written value. */ + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); +} + +static void clk_pll_16fft_disable_cal(const struct ti_pll_clk *pll) +{ + u32 cal, stat; + + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + cal &= ~PLL_16FFT_CAL_CTRL_CAL_EN; + /* Note this register does not readback the written value. */ + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); + do { + stat = readl(pll->base + PLL_16FFT_CAL_STAT); + } while (stat & PLL_16FFT_CAL_STAT_CAL_LOCK); +} + static int ti_pll_wait_for_lock(struct clk *clk) { struct ti_pll_clk *pll = to_clk_pll(clk); - u32 stat; u32 cfg; u32 cal; u32 freq_ctrl1; - int i; + unsigned int i; u32 pllfm; u32 pll_type; - int success; + u32 cal_en = 0; + bool success; - for (i = 0; i < 100000; i++) { - stat = readl(pll->base + PLL_16FFT_STAT); - if (stat & PLL_16FFT_STAT_LOCK) { - success = 1; + /* + * Minimum VCO input freq is 5MHz, and the longest a lock should + * be consider to be timed out after 750 cycles. Be conservative + * and assume each loop takes 10 cycles and we run at a + * max of 1GHz. That gives 15000 loop cycles. We may end up waiting + * longer than necessary for timeout, but that should be ok. + */ + success = false; + for (i = 0; i < PLL_16FFT_STAT_LOCK_TIMEOUT; i++) { + if (clk_pll_16fft_check_lock(pll)) { + success = true; break; } } - /* Enable calibration if not in fractional mode of the FRACF PLL */ + /* Disable calibration in the fractional mode of the FRACF PLL based on data + * from silicon and simulation data. + */ freq_ctrl1 = readl(pll->base + PLL_16FFT_FREQ_CTRL1); pllfm = freq_ctrl1 & PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK; - pllfm >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT; + cfg = readl(pll->base + PLL_16FFT_CFG); pll_type = (cfg & PLL_16FFT_CFG_PLL_TYPE_MASK) >> PLL_16FFT_CFG_PLL_TYPE_SHIFT; - if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF && pllfm == 0) { + if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF) { cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + cal_en = (cal & PLL_16FFT_CAL_CTRL_CAL_EN); + } - /* Enable calibration for FRACF */ - cal |= PLL_16FFT_CAL_CTRL_CAL_EN; - - /* Enable fast cal mode */ - cal |= PLL_16FFT_CAL_CTRL_FAST_CAL; - - /* Disable calibration bypass */ - cal &= ~PLL_16FFT_CAL_CTRL_CAL_BYP; - - /* Set CALCNT to 2 */ - cal &= ~PLL_16FFT_CAL_CTRL_CAL_CNT_MASK; - cal |= 2 << PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT; - - /* Note this register does not readback the written value. */ - writel(cal, pll->base + PLL_16FFT_CAL_CTRL); - - success = 0; - for (i = 0; i < 100000; i++) { - stat = readl(pll->base + PLL_16FFT_CAL_STAT); - if (stat & PLL_16FFT_CAL_STAT_CAL_LOCK) { - success = 1; + if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF && + pllfm == 0 && cal_en == 1) { + /* + * Wait for calibration lock. + * + * Lock should occur within: + * + * 170 * 2^(5+CALCNT) / PFD + * 21760 / PFD + * + * CALCNT = 2, PFD = 5-50MHz. This gives a range of 0.435mS to + * 4.35mS depending on PFD frequency. + * + * Be conservative and assume each loop takes 10 cycles and we run at a + * max of 1GHz. That gives 435000 loop cycles. We may end up waiting + * longer than necessary for timeout, but that should be ok. + * + * The recommend timeout for CALLOCK to go high is 4.35 ms + */ + success = false; + for (i = 0; i < PLL_16FFT_CAL_STAT_CAL_LOCK_TIMEOUT; i++) { + if (clk_pll_16fft_check_cal_lock(pll)) { + success = true; break; } } + + /* In case of cal lock failure, operate without calibration */ + if (!success) { + debug("Failure for calibration, falling back without calibration\n"); + + /* Disable PLL */ + ti_pll_clk_disable(clk); + + /* Disable Calibration */ + clk_pll_16fft_disable_cal(pll); + + /* Enable PLL */ + ti_pll_clk_enable(clk); + + /* Wait for PLL Lock */ + for (i = 0; i < PLL_16FFT_STAT_LOCK_TIMEOUT; i++) { + if (clk_pll_16fft_check_lock(pll)) { + success = true; + break; + } + } + } } - if (success == 0) { + if (!success) { printf("%s: pll (%s) failed to lock\n", __func__, clk->dev->name); return -EBUSY; @@ -180,6 +320,30 @@ static ulong ti_pll_clk_get_rate(struct clk *clk) return current_freq; } +static bool ti_pll_clk_is_bypass(struct ti_pll_clk *pll) +{ + u32 ctrl; + bool ret; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + ret = (ctrl & PLL_16FFT_CTRL_BYPASS_EN) != 0; + + return ret; +} + +static void ti_pll_clk_bypass(struct ti_pll_clk *pll, bool bypass) +{ + u32 ctrl; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + if (bypass) + ctrl |= PLL_16FFT_CTRL_BYPASS_EN; + else + ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; + + writel(ctrl, pll->base + PLL_16FFT_CTRL); +} + static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) { struct ti_pll_clk *pll = to_clk_pll(clk); @@ -187,9 +351,13 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) u64 parent_freq = clk_get_parent_rate(clk); int ret; u32 ctrl; + u32 cfg; + u32 pll_type; unsigned long pllm; u32 pllfm = 0; unsigned long plld; + u32 freq_ctrl0; + u32 freq_ctrl1; u32 div_ctrl; u32 rem; int shift; @@ -212,16 +380,22 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) break; } - /* Put PLL to bypass mode */ - ctrl = readl(pll->base + PLL_16FFT_CTRL); - ctrl |= PLL_16FFT_CTRL_BYPASS_EN; - writel(ctrl, pll->base + PLL_16FFT_CTRL); + if (!ti_pll_clk_is_bypass(pll)) { + /* Put the PLL into bypass */ + ti_pll_clk_bypass(pll, true); + } + + /* Disable the PLL */ + ti_pll_clk_disable(clk); if (rate == parent_freq) { debug("%s: put %s to bypass\n", __func__, clk->dev->name); return rate; } + cfg = readl(pll->base + PLL_16FFT_CFG); + pll_type = (cfg & PLL_16FFT_CFG_PLL_TYPE_MASK) >> PLL_16FFT_CFG_PLL_TYPE_SHIFT; + debug("%s: pre-frac-calc: rate=%u, parent_freq=%u, plld=%u, pllm=%u\n", __func__, (u32)rate, (u32)parent_freq, (u32)plld, (u32)pllm); @@ -237,31 +411,75 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) plld = 1; } - if (pllfm) - ctrl |= PLL_16FFT_CTRL_DSM_EN; - else - ctrl &= ~PLL_16FFT_CTRL_DSM_EN; + /* Program the new rate */ + freq_ctrl0 = readl(pll->base + PLL_16FFT_FREQ_CTRL0); + freq_ctrl1 = readl(pll->base + PLL_16FFT_FREQ_CTRL1); + div_ctrl = readl(pll->base + PLL_16FFT_DIV_CTRL); - writel(pllm, pll->base + PLL_16FFT_FREQ_CTRL0); - writel(pllfm, pll->base + PLL_16FFT_FREQ_CTRL1); + freq_ctrl0 &= ~PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK; + freq_ctrl0 |= pllm; + + freq_ctrl1 &= ~PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK; + freq_ctrl1 |= pllfm; /* * div_ctrl register contains other divider values, so rmw * only plld and leave existing values alone */ - div_ctrl = readl(pll->base + PLL_16FFT_DIV_CTRL); div_ctrl &= ~PLL_16FFT_DIV_CTRL_REF_DIV_MASK; div_ctrl |= plld; - writel(div_ctrl, pll->base + PLL_16FFT_DIV_CTRL); - ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; - ctrl |= PLL_16FFT_CTRL_PLL_EN; + /* Make sure we have fractional support if required */ + ctrl = readl(pll->base + PLL_16FFT_CTRL); + + /* Don't use internal bypass,it is not glitch free. Always prefer glitchless bypass */ + ctrl &= ~(PLL_16FFT_CTRL_INTL_BYP_EN | PLL_16FFT_CTRL_CLK_4PH_EN); + + /* Always enable output if PLL, Always bypass if we lose lock */ + ctrl |= (PLL_16FFT_CTRL_CLK_POSTDIV_EN | PLL_16FFT_CTRL_BYP_ON_LOCKLOSS); + + /* Enable fractional support if required */ + if (pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF) { + if (pllfm != 0) + ctrl |= (PLL_16FFT_CTRL_DSM_EN | PLL_16FFT_CTRL_DAC_EN); + else + ctrl &= ~(PLL_16FFT_CTRL_DSM_EN | PLL_16FFT_CTRL_DAC_EN); + } + + /* Enable Fractional by default for PLL_16FFT_CFG_PLL_TYPE_FRAC2 */ + if (pll_type == PLL_16FFT_CFG_PLL_TYPE_FRAC2) + ctrl |= (PLL_16FFT_CTRL_DSM_EN | PLL_16FFT_CTRL_DAC_EN); + + writel(freq_ctrl0, pll->base + PLL_16FFT_FREQ_CTRL0); + writel(freq_ctrl1, pll->base + PLL_16FFT_FREQ_CTRL1); + writel(div_ctrl, pll->base + PLL_16FFT_DIV_CTRL); writel(ctrl, pll->base + PLL_16FFT_CTRL); + /* Configure PLL calibration*/ + if (pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF) { + if (pllfm != 0) { + /* Disable Calibration in Fractional mode */ + clk_pll_16fft_disable_cal(pll); + } else { + /* Enable Calibration in Integer mode */ + clk_pll_16fft_cal_int(pll); + } + } + + /* + * Wait at least 1 ref cycle before enabling PLL. + * Minimum VCO input frequency is 5MHz, therefore maximum + * wait time for 1 ref clock is 0.2us. + */ + udelay(1); + ti_pll_clk_enable(clk); + ret = ti_pll_wait_for_lock(clk); if (ret) return ret; + ti_pll_clk_bypass(pll, false); + debug("%s: pllm=%u, plld=%u, pllfm=%u, parent_freq=%u\n", __func__, (u32)pllm, (u32)plld, (u32)pllfm, (u32)parent_freq); @@ -279,30 +497,7 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) return current_freq; } -static int ti_pll_clk_enable(struct clk *clk) -{ - struct ti_pll_clk *pll = to_clk_pll(clk); - u32 ctrl; - ctrl = readl(pll->base + PLL_16FFT_CTRL); - ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; - ctrl |= PLL_16FFT_CTRL_PLL_EN; - writel(ctrl, pll->base + PLL_16FFT_CTRL); - - return ti_pll_wait_for_lock(clk); -} - -static int ti_pll_clk_disable(struct clk *clk) -{ - struct ti_pll_clk *pll = to_clk_pll(clk); - u32 ctrl; - - ctrl = readl(pll->base + PLL_16FFT_CTRL); - ctrl |= PLL_16FFT_CTRL_BYPASS_EN; - writel(ctrl, pll->base + PLL_16FFT_CTRL); - - return 0; -} static const struct clk_ops ti_pll_clk_ops = { .get_rate = ti_pll_clk_get_rate,