aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@codeaurora.org>2017-12-28 13:46:56 -0500
committerStephen Boyd <sboyd@codeaurora.org>2017-12-28 13:46:56 -0500
commitd96f2cf93755188e3b5ffada73a7cbb7b062d7a4 (patch)
tree63c022815ebc73f810ae2b1a0edbab8ef1fd711d
parent4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff)
parente952ca3c6b2ffdfbf9618e4bd3e9aad1ff3f5eb4 (diff)
Merge tag 'sunxi-clk-for-4.16' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into clk-allwinner
Pull Allwinner clock changes from Chen-Yu Tsai: "Fixed-post-divider support is added for two types of our clocks. This in turn is used to support odd clocks on some of our SoCs. On the A64, the MMC module clocks have a hidden post-divider. On the A83T, a post-divider is needed to support programming sigma-delta modulation with known fixed parameters from the BSP kernel for the audio PLL. There's also a small cleanup to use the shorter PTR_ERR_OR_ZERO macro in various functions." * tag 'sunxi-clk-for-4.16' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux: clk: sunxi-ng: sun8i: a83t: Use sigma-delta modulation for audio PLL clk: sunxi-ng: sun8i: a83t: Add /2 fixed post divider to audio PLL clk: sunxi-ng: Support fixed post-dividers on NM style clocks clk: sunxi-ng: sun50i: a64: Add 2x fixed post-divider to MMC module clocks clk: sunxi-ng: Support fixed post-dividers on MP style clocks clk: sunxi: Use PTR_ERR_OR_ZERO()
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-a64.c57
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a83t.c18
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.c20
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.h24
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.c50
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.h2
-rw-r--r--drivers/clk/sunxi/clk-sun8i-apb0.c5
7 files changed, 134 insertions, 42 deletions
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
index 2bb4cabf802f..ee9c12cf3f08 100644
--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
@@ -400,28 +400,45 @@ static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080,
400 BIT(31), /* gate */ 400 BIT(31), /* gate */
401 0); 401 0);
402 402
403/*
404 * MMC clocks are the new timing mode (see A83T & H3) variety, but without
405 * the mode switch. This means they have a 2x post divider between the clock
406 * and the MMC module. This is not documented in the manual, but is taken
407 * into consideration when setting the mmc module clocks in the BSP kernel.
408 * Without it, MMC performance is degraded.
409 *
410 * We model it here to be consistent with other SoCs supporting this mode.
411 * The alternative would be to add the 2x multiplier when setting the MMC
412 * module clock in the MMC driver, just for the A64.
413 */
403static const char * const mmc_default_parents[] = { "osc24M", "pll-periph0-2x", 414static const char * const mmc_default_parents[] = { "osc24M", "pll-periph0-2x",
404 "pll-periph1-2x" }; 415 "pll-periph1-2x" };
405static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc_default_parents, 0x088, 416static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc0_clk, "mmc0",
406 0, 4, /* M */ 417 mmc_default_parents, 0x088,
407 16, 2, /* P */ 418 0, 4, /* M */
408 24, 2, /* mux */ 419 16, 2, /* P */
409 BIT(31), /* gate */ 420 24, 2, /* mux */
410 0); 421 BIT(31), /* gate */
411 422 2, /* post-div */
412static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc_default_parents, 0x08c, 423 0);
413 0, 4, /* M */ 424
414 16, 2, /* P */ 425static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc1_clk, "mmc1",
415 24, 2, /* mux */ 426 mmc_default_parents, 0x08c,
416 BIT(31), /* gate */ 427 0, 4, /* M */
417 0); 428 16, 2, /* P */
418 429 24, 2, /* mux */
419static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc_default_parents, 0x090, 430 BIT(31), /* gate */
420 0, 4, /* M */ 431 2, /* post-div */
421 16, 2, /* P */ 432 0);
422 24, 2, /* mux */ 433
423 BIT(31), /* gate */ 434static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc2_clk, "mmc2",
424 0); 435 mmc_default_parents, 0x090,
436 0, 4, /* M */
437 16, 2, /* P */
438 24, 2, /* mux */
439 BIT(31), /* gate */
440 2, /* post-div */
441 0);
425 442
426static const char * const ts_parents[] = { "osc24M", "pll-periph0", }; 443static const char * const ts_parents[] = { "osc24M", "pll-periph0", };
427static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098, 444static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098,
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
index 5cedcd0d8be8..04a9c33f53f0 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
@@ -76,15 +76,26 @@ static struct ccu_mult pll_c1cpux_clk = {
76 */ 76 */
77#define SUN8I_A83T_PLL_AUDIO_REG 0x008 77#define SUN8I_A83T_PLL_AUDIO_REG 0x008
78 78
79/* clock rates doubled for post divider */
80static struct ccu_sdm_setting pll_audio_sdm_table[] = {
81 { .rate = 45158400, .pattern = 0xc00121ff, .m = 29, .n = 54 },
82 { .rate = 49152000, .pattern = 0xc000e147, .m = 30, .n = 61 },
83};
84
79static struct ccu_nm pll_audio_clk = { 85static struct ccu_nm pll_audio_clk = {
80 .enable = BIT(31), 86 .enable = BIT(31),
81 .lock = BIT(2), 87 .lock = BIT(2),
82 .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), 88 .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
83 .m = _SUNXI_CCU_DIV(0, 6), 89 .m = _SUNXI_CCU_DIV(0, 6),
90 .fixed_post_div = 2,
91 .sdm = _SUNXI_CCU_SDM(pll_audio_sdm_table, BIT(24),
92 0x284, BIT(31)),
84 .common = { 93 .common = {
85 .reg = SUN8I_A83T_PLL_AUDIO_REG, 94 .reg = SUN8I_A83T_PLL_AUDIO_REG,
86 .lock_reg = CCU_SUN8I_A83T_LOCK_REG, 95 .lock_reg = CCU_SUN8I_A83T_LOCK_REG,
87 .features = CCU_FEATURE_LOCK_REG, 96 .features = CCU_FEATURE_LOCK_REG |
97 CCU_FEATURE_FIXED_POSTDIV |
98 CCU_FEATURE_SIGMA_DELTA_MOD,
88 .hw.init = CLK_HW_INIT("pll-audio", "osc24M", 99 .hw.init = CLK_HW_INIT("pll-audio", "osc24M",
89 &ccu_nm_ops, CLK_SET_RATE_UNGATE), 100 &ccu_nm_ops, CLK_SET_RATE_UNGATE),
90 }, 101 },
@@ -889,9 +900,10 @@ static int sun8i_a83t_ccu_probe(struct platform_device *pdev)
889 if (IS_ERR(reg)) 900 if (IS_ERR(reg))
890 return PTR_ERR(reg); 901 return PTR_ERR(reg);
891 902
892 /* Enforce d1 = 0, d2 = 0 for Audio PLL */ 903 /* Enforce d1 = 0, d2 = 1 for Audio PLL */
893 val = readl(reg + SUN8I_A83T_PLL_AUDIO_REG); 904 val = readl(reg + SUN8I_A83T_PLL_AUDIO_REG);
894 val &= ~(BIT(16) | BIT(18)); 905 val &= ~BIT(16);
906 val |= BIT(18);
895 writel(val, reg + SUN8I_A83T_PLL_AUDIO_REG); 907 writel(val, reg + SUN8I_A83T_PLL_AUDIO_REG);
896 908
897 /* Enforce P = 1 for both CPU cluster PLLs */ 909 /* Enforce P = 1 for both CPU cluster PLLs */
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
index 688855e7dc8c..5d0af4051737 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.c
+++ b/drivers/clk/sunxi-ng/ccu_mp.c
@@ -50,12 +50,19 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
50 unsigned int max_m, max_p; 50 unsigned int max_m, max_p;
51 unsigned int m, p; 51 unsigned int m, p;
52 52
53 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
54 rate *= cmp->fixed_post_div;
55
53 max_m = cmp->m.max ?: 1 << cmp->m.width; 56 max_m = cmp->m.max ?: 1 << cmp->m.width;
54 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); 57 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
55 58
56 ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); 59 ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p);
60 rate = *parent_rate / p / m;
61
62 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
63 rate /= cmp->fixed_post_div;
57 64
58 return *parent_rate / p / m; 65 return rate;
59} 66}
60 67
61static void ccu_mp_disable(struct clk_hw *hw) 68static void ccu_mp_disable(struct clk_hw *hw)
@@ -83,6 +90,7 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw,
83 unsigned long parent_rate) 90 unsigned long parent_rate)
84{ 91{
85 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 92 struct ccu_mp *cmp = hw_to_ccu_mp(hw);
93 unsigned long rate;
86 unsigned int m, p; 94 unsigned int m, p;
87 u32 reg; 95 u32 reg;
88 96
@@ -101,7 +109,11 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw,
101 p = reg >> cmp->p.shift; 109 p = reg >> cmp->p.shift;
102 p &= (1 << cmp->p.width) - 1; 110 p &= (1 << cmp->p.width) - 1;
103 111
104 return (parent_rate >> p) / m; 112 rate = (parent_rate >> p) / m;
113 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
114 rate /= cmp->fixed_post_div;
115
116 return rate;
105} 117}
106 118
107static int ccu_mp_determine_rate(struct clk_hw *hw, 119static int ccu_mp_determine_rate(struct clk_hw *hw,
@@ -129,6 +141,10 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
129 max_m = cmp->m.max ?: 1 << cmp->m.width; 141 max_m = cmp->m.max ?: 1 << cmp->m.width;
130 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); 142 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
131 143
144 /* Adjust target rate according to post-dividers */
145 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
146 rate = rate * cmp->fixed_post_div;
147
132 ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p); 148 ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
133 149
134 spin_lock_irqsave(cmp->common.lock, flags); 150 spin_lock_irqsave(cmp->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h
index aaef11d747ea..5107635e61de 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.h
+++ b/drivers/clk/sunxi-ng/ccu_mp.h
@@ -33,9 +33,33 @@ struct ccu_mp {
33 struct ccu_div_internal m; 33 struct ccu_div_internal m;
34 struct ccu_div_internal p; 34 struct ccu_div_internal p;
35 struct ccu_mux_internal mux; 35 struct ccu_mux_internal mux;
36
37 unsigned int fixed_post_div;
38
36 struct ccu_common common; 39 struct ccu_common common;
37}; 40};
38 41
42#define SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(_struct, _name, _parents, _reg, \
43 _mshift, _mwidth, \
44 _pshift, _pwidth, \
45 _muxshift, _muxwidth, \
46 _gate, _postdiv, _flags) \
47 struct ccu_mp _struct = { \
48 .enable = _gate, \
49 .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
50 .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \
51 .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
52 .fixed_post_div = _postdiv, \
53 .common = { \
54 .reg = _reg, \
55 .features = CCU_FEATURE_FIXED_POSTDIV, \
56 .hw.init = CLK_HW_INIT_PARENTS(_name, \
57 _parents, \
58 &ccu_mp_ops, \
59 _flags), \
60 } \
61 }
62
39#define SUNXI_CCU_MP_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ 63#define SUNXI_CCU_MP_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
40 _mshift, _mwidth, \ 64 _mshift, _mwidth, \
41 _pshift, _pwidth, \ 65 _pshift, _pwidth, \
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index 7620aa973a6e..a16de092bf94 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -70,11 +70,18 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw,
70 unsigned long parent_rate) 70 unsigned long parent_rate)
71{ 71{
72 struct ccu_nm *nm = hw_to_ccu_nm(hw); 72 struct ccu_nm *nm = hw_to_ccu_nm(hw);
73 unsigned long rate;
73 unsigned long n, m; 74 unsigned long n, m;
74 u32 reg; 75 u32 reg;
75 76
76 if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) 77 if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) {
77 return ccu_frac_helper_read_rate(&nm->common, &nm->frac); 78 rate = ccu_frac_helper_read_rate(&nm->common, &nm->frac);
79
80 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
81 rate /= nm->fixed_post_div;
82
83 return rate;
84 }
78 85
79 reg = readl(nm->common.base + nm->common.reg); 86 reg = readl(nm->common.base + nm->common.reg);
80 87
@@ -90,15 +97,15 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw,
90 if (!m) 97 if (!m)
91 m++; 98 m++;
92 99
93 if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm)) { 100 if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm))
94 unsigned long rate = 101 rate = ccu_sdm_helper_read_rate(&nm->common, &nm->sdm, m, n);
95 ccu_sdm_helper_read_rate(&nm->common, &nm->sdm, 102 else
96 m, n); 103 rate = parent_rate * n / m;
97 if (rate) 104
98 return rate; 105 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
99 } 106 rate /= nm->fixed_post_div;
100 107
101 return parent_rate * n / m; 108 return rate;
102} 109}
103 110
104static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, 111static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -107,11 +114,20 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
107 struct ccu_nm *nm = hw_to_ccu_nm(hw); 114 struct ccu_nm *nm = hw_to_ccu_nm(hw);
108 struct _ccu_nm _nm; 115 struct _ccu_nm _nm;
109 116
110 if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) 117 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
118 rate *= nm->fixed_post_div;
119
120 if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
121 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
122 rate /= nm->fixed_post_div;
111 return rate; 123 return rate;
124 }
112 125
113 if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) 126 if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) {
127 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
128 rate /= nm->fixed_post_div;
114 return rate; 129 return rate;
130 }
115 131
116 _nm.min_n = nm->n.min ?: 1; 132 _nm.min_n = nm->n.min ?: 1;
117 _nm.max_n = nm->n.max ?: 1 << nm->n.width; 133 _nm.max_n = nm->n.max ?: 1 << nm->n.width;
@@ -119,8 +135,12 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
119 _nm.max_m = nm->m.max ?: 1 << nm->m.width; 135 _nm.max_m = nm->m.max ?: 1 << nm->m.width;
120 136
121 ccu_nm_find_best(*parent_rate, rate, &_nm); 137 ccu_nm_find_best(*parent_rate, rate, &_nm);
138 rate = *parent_rate * _nm.n / _nm.m;
139
140 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
141 rate /= nm->fixed_post_div;
122 142
123 return *parent_rate * _nm.n / _nm.m; 143 return rate;
124} 144}
125 145
126static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, 146static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -131,6 +151,10 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
131 unsigned long flags; 151 unsigned long flags;
132 u32 reg; 152 u32 reg;
133 153
154 /* Adjust target rate according to post-dividers */
155 if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
156 rate = rate * nm->fixed_post_div;
157
134 if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) { 158 if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
135 spin_lock_irqsave(nm->common.lock, flags); 159 spin_lock_irqsave(nm->common.lock, flags);
136 160
diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h
index c623b0c7a23c..eba586b4c7d0 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.h
+++ b/drivers/clk/sunxi-ng/ccu_nm.h
@@ -36,6 +36,8 @@ struct ccu_nm {
36 struct ccu_frac_internal frac; 36 struct ccu_frac_internal frac;
37 struct ccu_sdm_internal sdm; 37 struct ccu_sdm_internal sdm;
38 38
39 unsigned int fixed_post_div;
40
39 struct ccu_common common; 41 struct ccu_common common;
40}; 42};
41 43
diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c
index ea1eed24778c..d5c31804ee54 100644
--- a/drivers/clk/sunxi/clk-sun8i-apb0.c
+++ b/drivers/clk/sunxi/clk-sun8i-apb0.c
@@ -98,10 +98,7 @@ static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev)
98 return PTR_ERR(reg); 98 return PTR_ERR(reg);
99 99
100 clk = sun8i_a23_apb0_register(np, reg); 100 clk = sun8i_a23_apb0_register(np, reg);
101 if (IS_ERR(clk)) 101 return PTR_ERR_OR_ZERO(clk);
102 return PTR_ERR(clk);
103
104 return 0;
105} 102}
106 103
107static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { 104static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = {