From bff1cef5f23afbe49f5ebd766980dc612f5e9d0a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 19 Apr 2019 14:42:26 +0300 Subject: clk: tegra: Don't enable already enabled PLLs Initially Common Clock Framework isn't aware of the clock-enable status, this results in enabling of clocks that were enabled by bootloader. This is not a big deal for a regular clock-gates, but for PLL's it may have some unpleasant consequences. Thus re-enabling PLLX (the main CPU parent clock) may result in extra long period of PLL re-locking. Acked-by: Peter De Schrijver Signed-off-by: Dmitry Osipenko Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-pll.c | 50 +++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index b50b7460014b..ebc8481a2122 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -444,6 +444,9 @@ static int clk_pll_enable(struct clk_hw *hw) unsigned long flags = 0; int ret; + if (clk_pll_is_enabled(hw)) + return 0; + if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -940,11 +943,16 @@ static int clk_plle_training(struct tegra_clk_pll *pll) static int clk_plle_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); struct tegra_clk_pll_freq_table sel; + unsigned long input_rate; u32 val; int err; + if (clk_pll_is_enabled(hw)) + return 0; + + input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); + if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) return -EINVAL; @@ -1355,6 +1363,9 @@ static int clk_pllc_enable(struct clk_hw *hw) int ret; unsigned long flags = 0; + if (clk_pll_is_enabled(hw)) + return 0; + if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -1567,7 +1578,12 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw) u32 val; int ret; unsigned long flags = 0; - unsigned long input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); + unsigned long input_rate; + + if (clk_pll_is_enabled(hw)) + return 0; + + input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) return -EINVAL; @@ -1704,6 +1720,9 @@ static int clk_pllu_tegra114_enable(struct clk_hw *hw) return -EINVAL; } + if (clk_pll_is_enabled(hw)) + return 0; + input_rate = clk_hw_get_rate(__clk_get_hw(osc)); if (pll->lock) @@ -2379,6 +2398,16 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, return clk; } +static int clk_plle_tegra210_is_enabled(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + val = pll_readl_base(pll); + + return val & PLLE_BASE_ENABLE ? 1 : 0; +} + static int clk_plle_tegra210_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); @@ -2386,7 +2415,12 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw) u32 val; int ret = 0; unsigned long flags = 0; - unsigned long input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); + unsigned long input_rate; + + if (clk_plle_tegra210_is_enabled(hw)) + return 0; + + input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) return -EINVAL; @@ -2497,16 +2531,6 @@ out: spin_unlock_irqrestore(pll->lock, flags); } -static int clk_plle_tegra210_is_enabled(struct clk_hw *hw) -{ - struct tegra_clk_pll *pll = to_clk_pll(hw); - u32 val; - - val = pll_readl_base(pll); - - return val & PLLE_BASE_ENABLE ? 1 : 0; -} - static const struct clk_ops tegra_clk_plle_tegra210_ops = { .is_enabled = clk_plle_tegra210_is_enabled, .enable = clk_plle_tegra210_enable, -- cgit v1.2.3 From 40db569d6769ffa3864fd1b89616b1a7323568a8 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 12 Apr 2019 00:48:34 +0300 Subject: clk: tegra: Fix PLLM programming on Tegra124+ when PMC overrides divider There are wrongly set parenthesis in the code that are resulting in a wrong configuration being programmed for PLLM. The original fix was made by Danny Huang in the downstream kernel. The patch was tested on Nyan Big Tegra124 chromebook, PLLM rate changing works correctly now and system doesn't lock up after changing the PLLM rate due to EMC scaling. Cc: Tested-by: Steev Klimaszewski Signed-off-by: Dmitry Osipenko Acked-By: Peter De Schrijver Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-pll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index ebc8481a2122..6b976b2514f7 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -666,8 +666,8 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, pll_override_writel(val, params->pmc_divp_reg, pll); val = pll_override_readl(params->pmc_divnm_reg, pll); - val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | - ~(divn_mask(pll) << div_nmp->override_divn_shift); + val &= ~((divm_mask(pll) << div_nmp->override_divm_shift) | + (divn_mask(pll) << div_nmp->override_divn_shift)); val |= (cfg->m << div_nmp->override_divm_shift) | (cfg->n << div_nmp->override_divn_shift); pll_override_writel(val, params->pmc_divnm_reg, pll); -- cgit v1.2.3 From 449c695d97e0842affa44245582e3b7dee272fde Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 12 Apr 2019 00:48:35 +0300 Subject: clk: tegra124: Remove lock-enable bit from PLLM According to the Tegra124 TRM documentation, PLLM_MISC2 register doesn't have the lock-enable bit as well as any other PLLM-related register. Hence PLLM re-locking can't be initiated by software. The incorrect bit setting should have been harmless since that bit is undefined according to TRM. Tested-by: Steev Klimaszewski Signed-off-by: Dmitry Osipenko Acked-By: Peter De Schrijver Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-tegra124.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index df0018f7bf7e..940592375583 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -413,7 +413,6 @@ static struct tegra_clk_pll_params pll_m_params = { .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, .lock_mask = PLL_BASE_LOCK, - .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .max_p = 5, .pdiv_tohw = pllm_p, @@ -421,7 +420,7 @@ static struct tegra_clk_pll_params pll_m_params = { .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, .freq_table = pll_m_freq_table, - .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE, + .flags = TEGRA_PLL_USE_LOCK, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { -- cgit v1.2.3 From 924ee3d551c9deb16090230b824988bd37e72aa8 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Apr 2019 22:23:17 +0300 Subject: clk: tegra: emc: Don't enable EMC clock manually The EMC clock marked as critical, hence it is already enabled at the registration time. Signed-off-by: Dmitry Osipenko Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-emc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 0621a3a82ea6..23416982e7c7 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -532,7 +532,5 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, /* Allow debugging tools to see the EMC clock */ clk_register_clkdev(clk, "emc", "tegra-clk-debug"); - clk_prepare_enable(clk); - return clk; }; -- cgit v1.2.3 From 888ca40e2843a24d2d4830780a4a5705a3fb219a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Apr 2019 22:23:18 +0300 Subject: clk: tegra: emc: Support multiple RAM codes The timings parser doesn't append timings, but instead it parses only the first timing and hence doesn't store all of the timings when device-tree has timings for multiple RAM codes. In a result EMC scaling doesn't work if timings are missing. Tested-by: Steev Klimaszewski Signed-off-by: Dmitry Osipenko Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-emc.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 23416982e7c7..28068584ff6e 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -121,18 +121,23 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) struct tegra_clk_emc *tegra; u8 ram_code = tegra_read_ram_code(); struct emc_timing *timing = NULL; - int i; + int i, k; tegra = container_of(hw, struct tegra_clk_emc, hw); - for (i = 0; i < tegra->num_timings; i++) { + for (k = 0; k < tegra->num_timings; k++) { + if (tegra->timings[k].ram_code == ram_code) + break; + } + + for (i = k; i < tegra->num_timings; i++) { if (tegra->timings[i].ram_code != ram_code) - continue; + break; timing = tegra->timings + i; if (timing->rate > req->max_rate) { - i = max(i, 1); + i = max(i, k + 1); req->rate = tegra->timings[i - 1].rate; return 0; } @@ -282,7 +287,7 @@ static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, for (i = timing_index+1; i < tegra->num_timings; i++) { timing = tegra->timings + i; if (timing->ram_code != ram_code) - continue; + break; if (emc_parent_clk_sources[timing->parent_index] != emc_parent_clk_sources[ @@ -293,7 +298,7 @@ static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, for (i = timing_index-1; i >= 0; --i) { timing = tegra->timings + i; if (timing->ram_code != ram_code) - continue; + break; if (emc_parent_clk_sources[timing->parent_index] != emc_parent_clk_sources[ @@ -433,19 +438,23 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, struct device_node *node, u32 ram_code) { + struct emc_timing *timings_ptr; struct device_node *child; int child_count = of_get_child_count(node); int i = 0, err; + size_t size; + + size = (tegra->num_timings + child_count) * sizeof(struct emc_timing); - tegra->timings = kcalloc(child_count, sizeof(struct emc_timing), - GFP_KERNEL); + tegra->timings = krealloc(tegra->timings, size, GFP_KERNEL); if (!tegra->timings) return -ENOMEM; - tegra->num_timings = child_count; + timings_ptr = tegra->timings + tegra->num_timings; + tegra->num_timings += child_count; for_each_child_of_node(node, child) { - struct emc_timing *timing = tegra->timings + (i++); + struct emc_timing *timing = timings_ptr + (i++); err = load_one_timing_from_dt(tegra, timing, child); if (err) { @@ -456,7 +465,7 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, timing->ram_code = ram_code; } - sort(tegra->timings, tegra->num_timings, sizeof(struct emc_timing), + sort(timings_ptr, child_count, sizeof(struct emc_timing), cmp_timings, NULL); return 0; @@ -499,10 +508,10 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, * fuses until the apbmisc driver is loaded. */ err = load_timings_from_dt(tegra, node, node_ram_code); - of_node_put(node); - if (err) + if (err) { + of_node_put(node); return ERR_PTR(err); - break; + } } if (tegra->num_timings == 0) -- cgit v1.2.3 From 913c3072eb58978f46851c195aa81586d0fd182b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Apr 2019 22:23:19 +0300 Subject: clk: tegra: emc: Fix EMC max-rate clamping When a clk user requests rate that is higher than the maximum possible, the rate shall be clamped to the maximum and not to the current value. Signed-off-by: Dmitry Osipenko Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-emc.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 28068584ff6e..9a0179235939 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -121,7 +121,7 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) struct tegra_clk_emc *tegra; u8 ram_code = tegra_read_ram_code(); struct emc_timing *timing = NULL; - int i, k; + int i, k, t; tegra = container_of(hw, struct tegra_clk_emc, hw); @@ -130,12 +130,17 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) break; } - for (i = k; i < tegra->num_timings; i++) { - if (tegra->timings[i].ram_code != ram_code) + for (t = k; t < tegra->num_timings; t++) { + if (tegra->timings[t].ram_code != ram_code) break; + } + for (i = k; i < t; i++) { timing = tegra->timings + i; + if (timing->rate < req->rate && i != t - 1) + continue; + if (timing->rate > req->max_rate) { i = max(i, k + 1); req->rate = tegra->timings[i - 1].rate; @@ -145,10 +150,8 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) if (timing->rate < req->min_rate) continue; - if (timing->rate >= req->rate) { - req->rate = timing->rate; - return 0; - } + req->rate = timing->rate; + return 0; } if (timing) { -- cgit v1.2.3 From f4037654a89906045a1c6a046c35e625524747ce Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Apr 2019 22:23:20 +0300 Subject: clk: tegra: emc: Replace BUG() with WARN_ONCE() There is no justification for the BUG() in this code. Signed-off-by: Dmitry Osipenko Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-emc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 9a0179235939..93ecb538e59b 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -222,7 +222,10 @@ static int emc_set_timing(struct tegra_clk_emc *tegra, if (emc_get_parent(&tegra->hw) == timing->parent_index && clk_get_rate(timing->parent) != timing->parent_rate) { - BUG(); + WARN_ONCE(1, "parent %s rate mismatch %lu %lu\n", + __clk_get_name(timing->parent), + clk_get_rate(timing->parent), + timing->parent_rate); return -EINVAL; } -- cgit v1.2.3 From e71f4d385878671991e200083c7d30eb4ca8e99a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Apr 2019 22:23:21 +0300 Subject: clk: tegra: divider: Mark Memory Controller clock as read-only The Memory Controller (MC) clock rate can't be simply changed and nothing in kernel need to change the rate, hence let's make the clock read-only. This id also needed for the EMC driver because timing configuration may require the MC clock diver to be disabled, that is handled by the EMC clock / EMC driver integration and CLK framework shall not touch the MC divider configuration on the EMC clock rate change. Signed-off-by: Dmitry Osipenko Signed-off-by: Stephen Boyd --- drivers/clk/tegra/clk-divider.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/clk/tegra') diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 205fe8ff63f0..2a1822a22740 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -175,6 +175,7 @@ struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, void __iomem *reg, spinlock_t *lock) { return clk_register_divider_table(NULL, name, parent_name, - CLK_IS_CRITICAL, reg, 16, 1, 0, + CLK_IS_CRITICAL, + reg, 16, 1, CLK_DIVIDER_READ_ONLY, mc_div_table, lock); } -- cgit v1.2.3