diff options
Diffstat (limited to 'drivers/clk/clk-vt8500.c')
| -rw-r--r-- | drivers/clk/clk-vt8500.c | 289 |
1 files changed, 169 insertions, 120 deletions
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index 82306f5fb9c2..eae5b3fbfb82 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -1,25 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Clock implementation for VIA/Wondermedia SoC's * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/slab.h> #include <linux/bitops.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> +#define LEGACY_PMC_BASE 0xD8130000 + /* All clocks share the same lock as none can be changed concurrently */ static DEFINE_SPINLOCK(_lock); @@ -53,6 +47,21 @@ struct clk_pll { static void __iomem *pmc_base; +static __init void vtwm_set_pmc_base(void) +{ + struct device_node *np = + of_find_compatible_node(NULL, NULL, "via,vt8500-pmc"); + + if (np) + pmc_base = of_iomap(np, 0); + else + pmc_base = ioremap(LEGACY_PMC_BASE, 0x1000); + of_node_put(np); + + if (!pmc_base) + pr_err("%s:of_iomap(pmc) failed\n", __func__); +} + #define to_clk_device(_hw) container_of(_hw, struct clk_device, hw) #define VT8500_PMC_BUSY_MASK 0x18 @@ -119,30 +128,31 @@ static unsigned long vt8500_dclk_recalc_rate(struct clk_hw *hw, return parent_rate / div; } -static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int vt8500_dclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_device *cdev = to_clk_device(hw); u32 divisor; - if (rate == 0) + if (req->rate == 0) return 0; - divisor = *prate / rate; + divisor = req->best_parent_rate / req->rate; /* If prate / rate would be decimal, incr the divisor */ - if (rate * divisor < *prate) + if (req->rate * divisor < req->best_parent_rate) divisor++; /* * If this is a request for SDMMC we have to adjust the divisor * when >31 to use the fixed predivisor */ - if ((cdev->div_mask == 0x3F) && (divisor > 31)) { + if ((cdev->div_mask == 0x3F) && (divisor > 31)) divisor = 64 * ((divisor / 64) + 1); - } - return *prate / divisor; + req->rate = req->best_parent_rate / divisor; + + return 0; } static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -193,7 +203,7 @@ static const struct clk_ops vt8500_gated_clk_ops = { }; static const struct clk_ops vt8500_divisor_clk_ops = { - .round_rate = vt8500_dclk_round_rate, + .determine_rate = vt8500_dclk_determine_rate, .set_rate = vt8500_dclk_set_rate, .recalc_rate = vt8500_dclk_recalc_rate, }; @@ -202,7 +212,7 @@ static const struct clk_ops vt8500_gated_divisor_clk_ops = { .enable = vt8500_dclk_enable, .disable = vt8500_dclk_disable, .is_enabled = vt8500_dclk_is_enabled, - .round_rate = vt8500_dclk_round_rate, + .determine_rate = vt8500_dclk_determine_rate, .set_rate = vt8500_dclk_set_rate, .recalc_rate = vt8500_dclk_recalc_rate, }; @@ -214,7 +224,7 @@ static const struct clk_ops vt8500_gated_divisor_clk_ops = { static __init void vtwm_device_clk_init(struct device_node *node) { u32 en_reg, div_reg; - struct clk *clk; + struct clk_hw *hw; struct clk_device *dev_clk; const char *clk_name = node->name; const char *parent_name; @@ -222,6 +232,9 @@ static __init void vtwm_device_clk_init(struct device_node *node) int rc; int clk_init_flags = 0; + if (!pmc_base) + vtwm_set_pmc_base(); + dev_clk = kzalloc(sizeof(*dev_clk), GFP_KERNEL); if (WARN_ON(!dev_clk)) return; @@ -280,13 +293,14 @@ static __init void vtwm_device_clk_init(struct device_node *node) dev_clk->hw.init = &init; - clk = clk_register(NULL, &dev_clk->hw); - if (WARN_ON(IS_ERR(clk))) { + hw = &dev_clk->hw; + rc = clk_hw_register(NULL, hw); + if (WARN_ON(rc)) { kfree(dev_clk); return; } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); + rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); + clk_hw_register_clkdev(hw, clk_name, NULL); } CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); @@ -334,7 +348,7 @@ CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); #define WM8850_BITS_TO_VAL(m, d1, d2) \ ((((m / 2) - 1) << 16) | ((d1 - 1) << 8) | d2) -static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, +static int vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *multiplier, u32 *prediv) { unsigned long tclk; @@ -344,7 +358,7 @@ static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, pr_err("%s: requested rate out of range\n", __func__); *multiplier = 0; *prediv = 1; - return; + return -EINVAL; } if (rate <= parent_rate * 31) /* use the prediv to double the resolution */ @@ -358,47 +372,55 @@ static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, if (tclk != rate) pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, tclk); + + return 0; } -static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate, - u32 *multiplier, u32 *divisor1, u32 *divisor2) +/* + * M * parent [O1] => / P [O2] => / D [O3] + * Where O1 is 900MHz...3GHz; + * O2 is 600MHz >= (M * parent) / P >= 300MHz; + * M is 36...120 [25MHz parent]; D is 1 or 2 or 4 or 8. + * Possible ranges (O3): + * D = 8: 37,5MHz...75MHz + * D = 4: 75MHz...150MHz + * D = 2: 150MHz...300MHz + * D = 1: 300MHz...600MHz + */ +static int wm8650_find_pll_bits(unsigned long rate, + unsigned long parent_rate, u32 *multiplier, u32 *divisor1, + u32 *divisor2) { - u32 mul, div1, div2; - u32 best_mul, best_div1, best_div2; - unsigned long tclk, rate_err, best_err; + unsigned long O1, min_err, rate_err; - best_err = (unsigned long)-1; + if (!parent_rate || (rate < 37500000) || (rate > 600000000)) + return -EINVAL; - /* Find the closest match (lower or equal to requested) */ - for (div1 = 5; div1 >= 3; div1--) - for (div2 = 3; div2 >= 0; div2--) - for (mul = 3; mul <= 1023; mul++) { - tclk = parent_rate * mul / (div1 * (1 << div2)); - if (tclk > rate) - continue; - /* error will always be +ve */ - rate_err = rate - tclk; - if (rate_err == 0) { - *multiplier = mul; - *divisor1 = div1; - *divisor2 = div2; - return; - } + *divisor2 = rate <= 75000000 ? 3 : rate <= 150000000 ? 2 : + rate <= 300000000 ? 1 : 0; + /* + * Divisor P cannot be calculated. Test all divisors and find where M + * will be as close as possible to the requested rate. + */ + min_err = ULONG_MAX; + for (*divisor1 = 5; *divisor1 >= 3; (*divisor1)--) { + O1 = rate * *divisor1 * (1 << (*divisor2)); + rate_err = O1 % parent_rate; + if (rate_err < min_err) { + *multiplier = O1 / parent_rate; + if (rate_err == 0) + return 0; + + min_err = rate_err; + } + } - if (rate_err < best_err) { - best_err = rate_err; - best_mul = mul; - best_div1 = div1; - best_div2 = div2; - } - } + if ((*multiplier < 3) || (*multiplier > 1023)) + return -EINVAL; - /* if we got here, it wasn't an exact match */ - pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, - rate - best_err); - *multiplier = best_mul; - *divisor1 = best_div1; - *divisor2 = best_div2; + pr_warn("%s: rate error is %lu\n", __func__, min_err); + + return 0; } static u32 wm8750_get_filter(u32 parent_rate, u32 divisor1) @@ -428,11 +450,11 @@ static u32 wm8750_get_filter(u32 parent_rate, u32 divisor1) return 0; } -static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, +static int wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *filter, u32 *multiplier, u32 *divisor1, u32 *divisor2) { - u32 mul, div1, div2; - u32 best_mul, best_div1, best_div2; + u32 mul; + int div1, div2; unsigned long tclk, rate_err, best_err; best_err = (unsigned long)-1; @@ -451,32 +473,36 @@ static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, *multiplier = mul; *divisor1 = div1; *divisor2 = div2; - return; + return 0; } if (rate_err < best_err) { best_err = rate_err; - best_mul = mul; - best_div1 = div1; - best_div2 = div2; + *multiplier = mul; + *divisor1 = div1; + *divisor2 = div2; } } + if (best_err == (unsigned long)-1) { + pr_warn("%s: impossible rate %lu\n", __func__, rate); + return -EINVAL; + } + /* if we got here, it wasn't an exact match */ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, rate - best_err); - *filter = wm8750_get_filter(parent_rate, best_div1); - *multiplier = best_mul; - *divisor1 = best_div1; - *divisor2 = best_div2; + *filter = wm8750_get_filter(parent_rate, *divisor1); + + return 0; } -static void wm8850_find_pll_bits(unsigned long rate, unsigned long parent_rate, +static int wm8850_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *multiplier, u32 *divisor1, u32 *divisor2) { - u32 mul, div1, div2; - u32 best_mul, best_div1, best_div2; + u32 mul; + int div1, div2; unsigned long tclk, rate_err, best_err; best_err = (unsigned long)-1; @@ -495,24 +521,27 @@ static void wm8850_find_pll_bits(unsigned long rate, unsigned long parent_rate, *multiplier = mul; *divisor1 = div1; *divisor2 = div2; - return; + return 0; } if (rate_err < best_err) { best_err = rate_err; - best_mul = mul; - best_div1 = div1; - best_div2 = div2; + *multiplier = mul; + *divisor1 = div1; + *divisor2 = div2; } } + if (best_err == (unsigned long)-1) { + pr_warn("%s: impossible rate %lu\n", __func__, rate); + return -EINVAL; + } + /* if we got here, it wasn't an exact match */ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, rate - best_err); - *multiplier = best_mul; - *divisor1 = best_div1; - *divisor2 = best_div2; + return 0; } static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, @@ -522,31 +551,39 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, u32 filter, mul, div1, div2; u32 pll_val; unsigned long flags = 0; + int ret; /* sanity check */ switch (pll->type) { case PLL_TYPE_VT8500: - vt8500_find_pll_bits(rate, parent_rate, &mul, &div1); - pll_val = VT8500_BITS_TO_VAL(mul, div1); + ret = vt8500_find_pll_bits(rate, parent_rate, &mul, &div1); + if (!ret) + pll_val = VT8500_BITS_TO_VAL(mul, div1); break; case PLL_TYPE_WM8650: - wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); - pll_val = WM8650_BITS_TO_VAL(mul, div1, div2); + ret = wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); + if (!ret) + pll_val = WM8650_BITS_TO_VAL(mul, div1, div2); break; case PLL_TYPE_WM8750: - wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); - pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); + ret = wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); + if (!ret) + pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); break; case PLL_TYPE_WM8850: - wm8850_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); - pll_val = WM8850_BITS_TO_VAL(mul, div1, div2); + ret = wm8850_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); + if (!ret) + pll_val = WM8850_BITS_TO_VAL(mul, div1, div2); break; default: pr_err("%s: invalid pll type\n", __func__); - return 0; + ret = -EINVAL; } + if (ret) + return ret; + spin_lock_irqsave(pll->lock, flags); vt8500_pmc_wait_busy(); @@ -558,35 +595,53 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int vtwm_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_pll *pll = to_clk_pll(hw); u32 filter, mul, div1, div2; long round_rate; + int ret; switch (pll->type) { case PLL_TYPE_VT8500: - vt8500_find_pll_bits(rate, *prate, &mul, &div1); - round_rate = VT8500_BITS_TO_FREQ(*prate, mul, div1); + ret = vt8500_find_pll_bits(req->rate, req->best_parent_rate, + &mul, &div1); + if (!ret) + round_rate = VT8500_BITS_TO_FREQ(req->best_parent_rate, + mul, div1); break; case PLL_TYPE_WM8650: - wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2); - round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2); + ret = wm8650_find_pll_bits(req->rate, req->best_parent_rate, + &mul, &div1, &div2); + if (!ret) + round_rate = WM8650_BITS_TO_FREQ(req->best_parent_rate, + mul, div1, div2); break; case PLL_TYPE_WM8750: - wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); - round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); + ret = wm8750_find_pll_bits(req->rate, req->best_parent_rate, + &filter, &mul, &div1, &div2); + if (!ret) + round_rate = WM8750_BITS_TO_FREQ(req->best_parent_rate, + mul, div1, div2); break; case PLL_TYPE_WM8850: - wm8850_find_pll_bits(rate, *prate, &mul, &div1, &div2); - round_rate = WM8850_BITS_TO_FREQ(*prate, mul, div1, div2); + ret = wm8850_find_pll_bits(req->rate, req->best_parent_rate, + &mul, &div1, &div2); + if (!ret) + round_rate = WM8850_BITS_TO_FREQ(req->best_parent_rate, + mul, div1, div2); break; default: - round_rate = 0; + return -EINVAL; } - return round_rate; + if (ret) + req->rate = ret; + else + req->rate = round_rate; + + return 0; } static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw, @@ -620,8 +675,8 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw, return pll_freq; } -const struct clk_ops vtwm_pll_ops = { - .round_rate = vtwm_pll_round_rate, +static const struct clk_ops vtwm_pll_ops = { + .determine_rate = vtwm_pll_determine_rate, .set_rate = vtwm_pll_set_rate, .recalc_rate = vtwm_pll_recalc_rate, }; @@ -629,13 +684,16 @@ const struct clk_ops vtwm_pll_ops = { static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type) { u32 reg; - struct clk *clk; + struct clk_hw *hw; struct clk_pll *pll_clk; const char *clk_name = node->name; const char *parent_name; struct clk_init_data init; int rc; + if (!pmc_base) + vtwm_set_pmc_base(); + rc = of_property_read_u32(node, "reg", ®); if (WARN_ON(rc)) return; @@ -659,13 +717,14 @@ static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type) pll_clk->hw.init = &init; - clk = clk_register(NULL, &pll_clk->hw); - if (WARN_ON(IS_ERR(clk))) { + hw = &pll_clk->hw; + rc = clk_hw_register(NULL, &pll_clk->hw); + if (WARN_ON(rc)) { kfree(pll_clk); return; } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); + rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); + clk_hw_register_clkdev(hw, clk_name, NULL); } @@ -694,13 +753,3 @@ static void __init wm8850_pll_init(struct device_node *node) vtwm_pll_clk_init(node, PLL_TYPE_WM8850); } CLK_OF_DECLARE(wm8850_pll, "wm,wm8850-pll-clock", wm8850_pll_init); - -void __init vtwm_clk_init(void __iomem *base) -{ - if (!base) - return; - - pmc_base = base; - - of_clk_init(NULL); -} |
