diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/baikal-t1/clk-ccu-pll.c | 14 | ||||
-rw-r--r-- | drivers/clk/clk-axi-clkgen.c | 187 | ||||
-rw-r--r-- | drivers/clk/clk-qoriq.c | 2 | ||||
-rw-r--r-- | drivers/clk/meson/meson-aoclk.c | 2 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-of-mmp2.c | 4 | ||||
-rw-r--r-- | drivers/clk/mvebu/ap-cpu-clk.c | 2 | ||||
-rw-r--r-- | drivers/clk/pxa/clk-pxa.h | 8 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-cpugear.c | 2 | ||||
-rw-r--r-- | drivers/clk/uniphier/clk-uniphier-mux.c | 2 |
9 files changed, 155 insertions, 68 deletions
diff --git a/drivers/clk/baikal-t1/clk-ccu-pll.c b/drivers/clk/baikal-t1/clk-ccu-pll.c index 1eec8c0b8f50..2445d4b12baf 100644 --- a/drivers/clk/baikal-t1/clk-ccu-pll.c +++ b/drivers/clk/baikal-t1/clk-ccu-pll.c @@ -51,11 +51,13 @@ struct ccu_pll_info { }; /* - * Mark as critical all PLLs except Ethernet one. CPU and DDR PLLs are sources - * of CPU cores and DDR controller reference clocks, due to which they - * obviously shouldn't be ever gated. SATA and PCIe PLLs are the parents of - * APB-bus and DDR controller AXI-bus clocks. If they are gated the system will - * be unusable. + * Alas we have to mark all PLLs as critical. CPU and DDR PLLs are sources of + * CPU cores and DDR controller reference clocks, due to which they obviously + * shouldn't be ever gated. SATA and PCIe PLLs are the parents of APB-bus and + * DDR controller AXI-bus clocks. If they are gated the system will be + * unusable. Moreover disabling SATA and Ethernet PLLs causes automatic reset + * of the corresponding subsystems. So until we aren't ready to re-initialize + * all the devices consuming those PLLs, they will be marked as critical too. */ static const struct ccu_pll_info pll_info[] = { CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE, @@ -67,7 +69,7 @@ static const struct ccu_pll_info pll_info[] = { CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE, CLK_IS_CRITICAL), CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE, - CLK_SET_RATE_GATE) + CLK_IS_CRITICAL | CLK_SET_RATE_GATE) }; struct ccu_pll_data { diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 96f351785b41..14d803e6af62 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -27,19 +27,23 @@ #define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16) +#define MMCM_REG_CLKOUT5_2 0x07 #define MMCM_REG_CLKOUT0_1 0x08 #define MMCM_REG_CLKOUT0_2 0x09 +#define MMCM_REG_CLKOUT6_2 0x13 #define MMCM_REG_CLK_FB1 0x14 #define MMCM_REG_CLK_FB2 0x15 #define MMCM_REG_CLK_DIV 0x16 #define MMCM_REG_LOCK1 0x18 #define MMCM_REG_LOCK2 0x19 #define MMCM_REG_LOCK3 0x1a +#define MMCM_REG_POWER 0x28 #define MMCM_REG_FILTER1 0x4e #define MMCM_REG_FILTER2 0x4f #define MMCM_CLKOUT_NOCOUNT BIT(6) +#define MMCM_CLK_DIV_DIVIDE BIT(11) #define MMCM_CLK_DIV_NOCOUNT BIT(12) struct axi_clkgen { @@ -107,6 +111,8 @@ static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, unsigned long d, d_min, d_max, _d_min, _d_max; unsigned long m, m_min, m_max; unsigned long f, dout, best_f, fvco; + unsigned long fract_shift = 0; + unsigned long fvco_min_fract, fvco_max_fract; fin /= 1000; fout /= 1000; @@ -119,42 +125,89 @@ static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); d_max = min_t(unsigned long, fin / fpfd_min, 80); - m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); - m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); +again: + fvco_min_fract = fvco_min << fract_shift; + fvco_max_fract = fvco_max << fract_shift; + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << fract_shift); for (m = m_min; m <= m_max; m++) { - _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); - _d_max = min(d_max, fin * m / fvco_min); + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract)); + _d_max = min(d_max, fin * m / fvco_min_fract); for (d = _d_min; d <= _d_max; d++) { fvco = fin * m / d; dout = DIV_ROUND_CLOSEST(fvco, fout); - dout = clamp_t(unsigned long, dout, 1, 128); + dout = clamp_t(unsigned long, dout, 1, 128 << fract_shift); f = fvco / dout; if (abs(f - fout) < abs(best_f - fout)) { best_f = f; *best_d = d; - *best_m = m; - *best_dout = dout; + *best_m = m << (3 - fract_shift); + *best_dout = dout << (3 - fract_shift); if (best_f == fout) return; } } } + + /* Lets see if we find a better setting in fractional mode */ + if (fract_shift == 0) { + fract_shift = 3; + goto again; + } } -static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, - unsigned int *high, unsigned int *edge, unsigned int *nocount) +struct axi_clkgen_div_params { + unsigned int low; + unsigned int high; + unsigned int edge; + unsigned int nocount; + unsigned int frac_en; + unsigned int frac; + unsigned int frac_wf_f; + unsigned int frac_wf_r; + unsigned int frac_phase; +}; + +static void axi_clkgen_calc_clk_params(unsigned int divider, + unsigned int frac_divider, struct axi_clkgen_div_params *params) { - if (divider == 1) - *nocount = 1; - else - *nocount = 0; - *high = divider / 2; - *edge = divider % 2; - *low = divider - *high; + memset(params, 0x0, sizeof(*params)); + + if (divider == 1) { + params->nocount = 1; + return; + } + + if (frac_divider == 0) { + params->high = divider / 2; + params->edge = divider % 2; + params->low = divider - params->high; + } else { + params->frac_en = 1; + params->frac = frac_divider; + + params->high = divider / 2; + params->edge = divider % 2; + params->low = params->high; + + if (params->edge == 0) { + params->high--; + params->frac_wf_r = 1; + } + + if (params->edge == 0 || frac_divider == 1) + params->low--; + if (((params->edge == 0) ^ (frac_divider == 1)) || + (divider == 2 && frac_divider == 1)) + params->frac_wf_f = 1; + + params->frac_phase = params->edge * 4 + frac_divider / 2; + } } static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, @@ -246,15 +299,29 @@ static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) return container_of(clk_hw, struct axi_clkgen, clk_hw); } +static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen, + unsigned int reg1, unsigned int reg2, unsigned int reg3, + struct axi_clkgen_div_params *params) +{ + axi_clkgen_mmcm_write(axi_clkgen, reg1, + (params->high << 6) | params->low, 0xefff); + axi_clkgen_mmcm_write(axi_clkgen, reg2, + (params->frac << 12) | (params->frac_en << 11) | + (params->frac_wf_r << 10) | (params->edge << 7) | + (params->nocount << 6), 0x7fff); + if (reg3 != 0) { + axi_clkgen_mmcm_write(axi_clkgen, reg3, + (params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00); + } +} + static int axi_clkgen_set_rate(struct clk_hw *clk_hw, unsigned long rate, unsigned long parent_rate) { struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); unsigned int d, m, dout; - unsigned int nocount; - unsigned int high; - unsigned int edge; - unsigned int low; + struct axi_clkgen_div_params params; + uint32_t power = 0; uint32_t filter; uint32_t lock; @@ -266,24 +333,26 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw, if (d == 0 || dout == 0 || m == 0) return -EINVAL; + if ((dout & 0x7) != 0 || (m & 0x7) != 0) + power |= 0x9800; + + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_POWER, power, 0x9800); + filter = axi_clkgen_lookup_filter(m - 1); lock = axi_clkgen_lookup_lock(m - 1); - axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); - axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1, - (high << 6) | low, 0xefff); - axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2, - (edge << 7) | (nocount << 6), 0x03ff); + axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, ¶ms); + axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2, + MMCM_REG_CLKOUT5_2, ¶ms); - axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_calc_clk_params(d, 0, ¶ms); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV, - (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff); + (params.edge << 13) | (params.nocount << 12) | + (params.high << 6) | params.low, 0x3fff); - axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); - axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1, - (high << 6) | low, 0xefff); - axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2, - (edge << 7) | (nocount << 6), 0x03ff); + axi_clkgen_calc_clk_params(m >> 3, m & 0x7, ¶ms); + axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2, + MMCM_REG_CLKOUT6_2, ¶ms); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2, @@ -313,35 +382,51 @@ static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, return min_t(unsigned long long, tmp, LONG_MAX); } +static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen, + unsigned int reg1, unsigned int reg2) +{ + unsigned int val1, val2; + unsigned int div; + + axi_clkgen_mmcm_read(axi_clkgen, reg2, &val2); + if (val2 & MMCM_CLKOUT_NOCOUNT) + return 8; + + axi_clkgen_mmcm_read(axi_clkgen, reg1, &val1); + + div = (val1 & 0x3f) + ((val1 >> 6) & 0x3f); + div <<= 3; + + if (val2 & MMCM_CLK_DIV_DIVIDE) { + if ((val2 & BIT(7)) && (val2 & 0x7000) != 0x1000) + div += 8; + else + div += 16; + + div += (val2 >> 12) & 0x7; + } + + return div; +} + static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, unsigned long parent_rate) { struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); unsigned int d, m, dout; - unsigned int reg; unsigned long long tmp; + unsigned int val; - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, ®); - if (reg & MMCM_CLKOUT_NOCOUNT) { - dout = 1; - } else { - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®); - dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); - } + dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1, + MMCM_REG_CLKOUT0_2); + m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1, + MMCM_REG_CLK_FB2); - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®); - if (reg & MMCM_CLK_DIV_NOCOUNT) + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val); + if (val & MMCM_CLK_DIV_NOCOUNT) d = 1; else - d = (reg & 0x3f) + ((reg >> 6) & 0x3f); - - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, ®); - if (reg & MMCM_CLKOUT_NOCOUNT) { - m = 1; - } else { - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®); - m = (reg & 0x3f) + ((reg >> 6) & 0x3f); - } + d = (val & 0x3f) + ((val >> 6) & 0x3f); if (d == 0 || dout == 0) return 0; diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 5942e9874bc0..46101c6a20f2 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -31,7 +31,7 @@ #define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */ #define CGB_PLL1 4 #define CGB_PLL2 5 -#define MAX_PLL_DIV 16 +#define MAX_PLL_DIV 32 struct clockgen_pll_div { struct clk *clk; diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index bf8bea675d24..3a6d84cd6601 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -57,7 +57,7 @@ int meson_aoclkc_probe(struct platform_device *pdev) rstc->data = data; rstc->regmap = regmap; rstc->reset.ops = &meson_aoclk_reset_ops; - rstc->reset.nr_resets = data->num_reset, + rstc->reset.nr_resets = data->num_reset; rstc->reset.of_node = dev->of_node; ret = devm_reset_controller_register(dev, &rstc->reset); if (ret) { diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 67208aea94c5..0839fb2049e9 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -347,9 +347,9 @@ static struct mmp_param_mux_clk mmp3_apmu_mux_clks[] = { }; static struct mmp_param_div_clk apmu_div_clks[] = { - {0, "disp0_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 8, 4, 0, &disp0_lock}, + {0, "disp0_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 8, 4, CLK_DIVIDER_ONE_BASED, &disp0_lock}, {0, "disp0_sphy_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 15, 5, 0, &disp0_lock}, - {0, "disp1_div", "disp1_mux", CLK_SET_RATE_PARENT, APMU_DISP1, 8, 4, 0, &disp1_lock}, + {0, "disp1_div", "disp1_mux", CLK_SET_RATE_PARENT, APMU_DISP1, 8, 4, CLK_DIVIDER_ONE_BASED, &disp1_lock}, {0, "ccic0_sphy_div", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 10, 5, 0, &ccic0_lock}, {0, "ccic1_sphy_div", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 10, 5, 0, &ccic1_lock}, }; diff --git a/drivers/clk/mvebu/ap-cpu-clk.c b/drivers/clk/mvebu/ap-cpu-clk.c index 6b394302c76a..b4259b60dcfd 100644 --- a/drivers/clk/mvebu/ap-cpu-clk.c +++ b/drivers/clk/mvebu/ap-cpu-clk.c @@ -197,7 +197,7 @@ static int ap_cpu_clk_set_rate(struct clk_hw *hw, unsigned long rate, stable_bit = BIT(clk->pll_regs->ratio_state_offset + clk->cluster * - clk->pll_regs->ratio_state_cluster_offset), + clk->pll_regs->ratio_state_cluster_offset); ret = regmap_read_poll_timeout(clk->pll_cr_base, clk->pll_regs->ratio_state_reg, reg, reg & stable_bit, STATUS_POLL_PERIOD_US, diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h index f131d2834af4..5768e0f728ce 100644 --- a/drivers/clk/pxa/clk-pxa.h +++ b/drivers/clk/pxa/clk-pxa.h @@ -19,11 +19,11 @@ #define MUX_RO_RATE_RO_OPS(name, clk_name) \ static struct clk_hw name ## _mux_hw; \ static struct clk_hw name ## _rate_hw; \ - static struct clk_ops name ## _mux_ops = { \ + static const struct clk_ops name ## _mux_ops = { \ .get_parent = name ## _get_parent, \ .set_parent = dummy_clk_set_parent, \ }; \ - static struct clk_ops name ## _rate_ops = { \ + static const struct clk_ops name ## _rate_ops = { \ .recalc_rate = name ## _get_rate, \ }; \ static struct clk * __init clk_register_ ## name(void) \ @@ -38,7 +38,7 @@ #define RATE_RO_OPS(name, clk_name) \ static struct clk_hw name ## _rate_hw; \ - static const struct clk_ops name ## _rate_ops = { \ + static const struct clk_ops name ## _rate_ops = { \ .recalc_rate = name ## _get_rate, \ }; \ static struct clk * __init clk_register_ ## name(void) \ @@ -53,7 +53,7 @@ #define RATE_OPS(name, clk_name) \ static struct clk_hw name ## _rate_hw; \ - static struct clk_ops name ## _rate_ops = { \ + static const struct clk_ops name ## _rate_ops = { \ .recalc_rate = name ## _get_rate, \ .set_rate = name ## _set_rate, \ .determine_rate = name ## _determine_rate, \ diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c index 1a33a08abf2f..a2f01a4da127 100644 --- a/drivers/clk/uniphier/clk-uniphier-cpugear.c +++ b/drivers/clk/uniphier/clk-uniphier-cpugear.c @@ -90,7 +90,7 @@ struct clk_hw *uniphier_clk_register_cpugear(struct device *dev, init.ops = &uniphier_clk_cpugear_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = data->parent_names; - init.num_parents = data->num_parents, + init.num_parents = data->num_parents; gear->regmap = regmap; gear->regbase = data->regbase; diff --git a/drivers/clk/uniphier/clk-uniphier-mux.c b/drivers/clk/uniphier/clk-uniphier-mux.c index c0f4631601e2..462c84321b2d 100644 --- a/drivers/clk/uniphier/clk-uniphier-mux.c +++ b/drivers/clk/uniphier/clk-uniphier-mux.c @@ -70,7 +70,7 @@ struct clk_hw *uniphier_clk_register_mux(struct device *dev, init.ops = &uniphier_clk_mux_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = data->parent_names; - init.num_parents = data->num_parents, + init.num_parents = data->num_parents; mux->regmap = regmap; mux->reg = data->reg; |