aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2016-09-06 06:29:04 -0400
committerMaxime Ripard <maxime.ripard@free-electrons.com>2016-09-10 05:41:18 -0400
commit87ba9e5962f3f6e9a9a44cc332d1ad222d1c0302 (patch)
treec56ffb140f8b9f7604e431369852c5732fa72f4c
parente9c959a6d17cd4fddc766bc182dd98478101c00c (diff)
clk: sunxi-ng: div: Allow to set a maximum
Some dividers might have a maximum value that is lower than the width of the register. Add a field to _ccu_div to handle those case properly. If the field is set to 0, the code will assume that the maximum value is the maximum one that can be used with the field register width. Otherwise, we'll use whatever value has been set. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Chen-Yu Tsai <wens@csie.org>
-rw-r--r--drivers/clk/sunxi-ng/ccu_div.h24
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.c23
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkm.c4
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkmp.c21
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.c16
5 files changed, 55 insertions, 33 deletions
diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
index e1b1326e68a1..34c338832c0d 100644
--- a/drivers/clk/sunxi-ng/ccu_div.h
+++ b/drivers/clk/sunxi-ng/ccu_div.h
@@ -23,6 +23,9 @@
23 * struct _ccu_div - Internal divider description 23 * struct _ccu_div - Internal divider description
24 * @shift: Bit offset of the divider in its register 24 * @shift: Bit offset of the divider in its register
25 * @width: Width of the divider field in its register 25 * @width: Width of the divider field in its register
26 * @max: Maximum value allowed for that divider. This is the
27 * arithmetic value, not the maximum value to be set in the
28 * register.
26 * @flags: clk_divider flags to apply on this divider 29 * @flags: clk_divider flags to apply on this divider
27 * @table: Divider table pointer (if applicable) 30 * @table: Divider table pointer (if applicable)
28 * 31 *
@@ -37,6 +40,8 @@ struct _ccu_div {
37 u8 shift; 40 u8 shift;
38 u8 width; 41 u8 width;
39 42
43 u32 max;
44
40 u32 flags; 45 u32 flags;
41 46
42 struct clk_div_table *table; 47 struct clk_div_table *table;
@@ -50,14 +55,25 @@ struct _ccu_div {
50 .table = _table, \ 55 .table = _table, \
51 } 56 }
52 57
53#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \
54 _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, _flags)
55
56#define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \ 58#define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \
57 _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0) 59 _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0)
58 60
61#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \
62 { \
63 .shift = _shift, \
64 .width = _width, \
65 .flags = _flags, \
66 .max = _max, \
67 }
68
69#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \
70 _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, 0, _flags)
71
72#define _SUNXI_CCU_DIV_MAX(_shift, _width, _max) \
73 _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, 0)
74
59#define _SUNXI_CCU_DIV(_shift, _width) \ 75#define _SUNXI_CCU_DIV(_shift, _width) \
60 _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, 0) 76 _SUNXI_CCU_DIV_FLAGS(_shift, _width, 0)
61 77
62struct ccu_div { 78struct ccu_div {
63 u32 enable; 79 u32 enable;
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
index cbf33ef5faa9..ebb1b31568a5 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.c
+++ b/drivers/clk/sunxi-ng/ccu_mp.c
@@ -21,9 +21,9 @@ static void ccu_mp_find_best(unsigned long parent, unsigned long rate,
21 unsigned int best_m = 0, best_p = 0; 21 unsigned int best_m = 0, best_p = 0;
22 unsigned int _m, _p; 22 unsigned int _m, _p;
23 23
24 for (_p = 0; _p <= max_p; _p++) { 24 for (_p = 1; _p <= max_p; _p <<= 1) {
25 for (_m = 1; _m <= max_m; _m++) { 25 for (_m = 1; _m <= max_m; _m++) {
26 unsigned long tmp_rate = (parent >> _p) / _m; 26 unsigned long tmp_rate = parent / _p / _m;
27 27
28 if (tmp_rate > rate) 28 if (tmp_rate > rate)
29 continue; 29 continue;
@@ -46,13 +46,15 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
46 void *data) 46 void *data)
47{ 47{
48 struct ccu_mp *cmp = data; 48 struct ccu_mp *cmp = data;
49 unsigned int max_m, max_p;
49 unsigned int m, p; 50 unsigned int m, p;
50 51
51 ccu_mp_find_best(parent_rate, rate, 52 max_m = cmp->m.max ?: 1 << cmp->m.width;
52 1 << cmp->m.width, (1 << cmp->p.width) - 1, 53 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
53 &m, &p);
54 54
55 return (parent_rate >> p) / m; 55 ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
56
57 return parent_rate / p / m;
56} 58}
57 59
58static void ccu_mp_disable(struct clk_hw *hw) 60static void ccu_mp_disable(struct clk_hw *hw)
@@ -108,13 +110,14 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
108{ 110{
109 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 111 struct ccu_mp *cmp = hw_to_ccu_mp(hw);
110 unsigned long flags; 112 unsigned long flags;
113 unsigned int max_m, max_p;
111 unsigned int m, p; 114 unsigned int m, p;
112 u32 reg; 115 u32 reg;
113 116
114 ccu_mp_find_best(parent_rate, rate, 117 max_m = cmp->m.max ?: 1 << cmp->m.width;
115 1 << cmp->m.width, (1 << cmp->p.width) - 1, 118 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
116 &m, &p);
117 119
120 ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
118 121
119 spin_lock_irqsave(cmp->common.lock, flags); 122 spin_lock_irqsave(cmp->common.lock, flags);
120 123
@@ -122,7 +125,7 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
122 reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift); 125 reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
123 reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift); 126 reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
124 127
125 writel(reg | (p << cmp->p.shift) | ((m - 1) << cmp->m.shift), 128 writel(reg | (ilog2(p) << cmp->p.shift) | ((m - 1) << cmp->m.shift),
126 cmp->common.base + cmp->common.reg); 129 cmp->common.base + cmp->common.reg);
127 130
128 spin_unlock_irqrestore(cmp->common.lock, flags); 131 spin_unlock_irqrestore(cmp->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index 182452919ede..059fdc3b4f96 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -103,7 +103,7 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
103 103
104 _nkm.max_n = 1 << nkm->n.width; 104 _nkm.max_n = 1 << nkm->n.width;
105 _nkm.max_k = 1 << nkm->k.width; 105 _nkm.max_k = 1 << nkm->k.width;
106 _nkm.max_m = 1 << nkm->m.width; 106 _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
107 107
108 ccu_nkm_find_best(parent_rate, rate, &_nkm); 108 ccu_nkm_find_best(parent_rate, rate, &_nkm);
109 109
@@ -129,7 +129,7 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
129 129
130 _nkm.max_n = 1 << nkm->n.width; 130 _nkm.max_n = 1 << nkm->n.width;
131 _nkm.max_k = 1 << nkm->k.width; 131 _nkm.max_k = 1 << nkm->k.width;
132 _nkm.max_m = 1 << nkm->m.width; 132 _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
133 133
134 ccu_nkm_find_best(parent_rate, rate, &_nkm); 134 ccu_nkm_find_best(parent_rate, rate, &_nkm);
135 135
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index 9f2b98e19dc9..9769dee99511 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -29,14 +29,14 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
29 unsigned long _n, _k, _m, _p; 29 unsigned long _n, _k, _m, _p;
30 30
31 for (_k = 1; _k <= nkmp->max_k; _k++) { 31 for (_k = 1; _k <= nkmp->max_k; _k++) {
32 for (_p = 0; _p <= nkmp->max_p; _p++) { 32 for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
33 unsigned long tmp_rate; 33 unsigned long tmp_rate;
34 34
35 rational_best_approximation(rate / _k, parent >> _p, 35 rational_best_approximation(rate / _k, parent / _p,
36 nkmp->max_n, nkmp->max_m, 36 nkmp->max_n, nkmp->max_m,
37 &_n, &_m); 37 &_n, &_m);
38 38
39 tmp_rate = (parent * _n * _k >> _p) / _m; 39 tmp_rate = parent * _n * _k / (_m * _p);
40 40
41 if (tmp_rate > rate) 41 if (tmp_rate > rate)
42 continue; 42 continue;
@@ -110,13 +110,12 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
110 110
111 _nkmp.max_n = 1 << nkmp->n.width; 111 _nkmp.max_n = 1 << nkmp->n.width;
112 _nkmp.max_k = 1 << nkmp->k.width; 112 _nkmp.max_k = 1 << nkmp->k.width;
113 _nkmp.max_m = 1 << nkmp->m.width; 113 _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
114 _nkmp.max_p = (1 << nkmp->p.width) - 1; 114 _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
115 115
116 ccu_nkmp_find_best(*parent_rate, rate, 116 ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
117 &_nkmp);
118 117
119 return (*parent_rate * _nkmp.n * _nkmp.k >> _nkmp.p) / _nkmp.m; 118 return *parent_rate * _nkmp.n * _nkmp.k / (_nkmp.m * _nkmp.p);
120} 119}
121 120
122static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, 121static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -129,8 +128,8 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
129 128
130 _nkmp.max_n = 1 << nkmp->n.width; 129 _nkmp.max_n = 1 << nkmp->n.width;
131 _nkmp.max_k = 1 << nkmp->k.width; 130 _nkmp.max_k = 1 << nkmp->k.width;
132 _nkmp.max_m = 1 << nkmp->m.width; 131 _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
133 _nkmp.max_p = (1 << nkmp->p.width) - 1; 132 _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
134 133
135 ccu_nkmp_find_best(parent_rate, rate, &_nkmp); 134 ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
136 135
@@ -145,7 +144,7 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
145 reg |= (_nkmp.n - 1) << nkmp->n.shift; 144 reg |= (_nkmp.n - 1) << nkmp->n.shift;
146 reg |= (_nkmp.k - 1) << nkmp->k.shift; 145 reg |= (_nkmp.k - 1) << nkmp->k.shift;
147 reg |= (_nkmp.m - 1) << nkmp->m.shift; 146 reg |= (_nkmp.m - 1) << nkmp->m.shift;
148 reg |= _nkmp.p << nkmp->p.shift; 147 reg |= ilog2(_nkmp.p) << nkmp->p.shift;
149 148
150 writel(reg, nkmp->common.base + nkmp->common.reg); 149 writel(reg, nkmp->common.base + nkmp->common.reg);
151 150
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index e35ddd8eec8b..b61bdd8c7a7f 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -61,11 +61,13 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
61 unsigned long *parent_rate) 61 unsigned long *parent_rate)
62{ 62{
63 struct ccu_nm *nm = hw_to_ccu_nm(hw); 63 struct ccu_nm *nm = hw_to_ccu_nm(hw);
64 unsigned long max_n, max_m;
64 unsigned long n, m; 65 unsigned long n, m;
65 66
66 rational_best_approximation(rate, *parent_rate, 67 max_n = 1 << nm->n.width;
67 1 << nm->n.width, 1 << nm->m.width, 68 max_m = nm->m.max ?: 1 << nm->m.width;
68 &n, &m); 69
70 rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m);
69 71
70 return *parent_rate * n / m; 72 return *parent_rate * n / m;
71} 73}
@@ -75,6 +77,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
75{ 77{
76 struct ccu_nm *nm = hw_to_ccu_nm(hw); 78 struct ccu_nm *nm = hw_to_ccu_nm(hw);
77 unsigned long flags; 79 unsigned long flags;
80 unsigned long max_n, max_m;
78 unsigned long n, m; 81 unsigned long n, m;
79 u32 reg; 82 u32 reg;
80 83
@@ -83,9 +86,10 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
83 else 86 else
84 ccu_frac_helper_disable(&nm->common, &nm->frac); 87 ccu_frac_helper_disable(&nm->common, &nm->frac);
85 88
86 rational_best_approximation(rate, parent_rate, 89 max_n = 1 << nm->n.width;
87 1 << nm->n.width, 1 << nm->m.width, 90 max_m = nm->m.max ?: 1 << nm->m.width;
88 &n, &m); 91
92 rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m);
89 93
90 spin_lock_irqsave(nm->common.lock, flags); 94 spin_lock_irqsave(nm->common.lock, flags);
91 95