]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/clk/sunxi-ng/ccu_nkmp.c
Merge tag 'powerpc-5.2-2' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[mirror_ubuntu-hirsute-kernel.git] / drivers / clk / sunxi-ng / ccu_nkmp.c
CommitLineData
4f728b5d
MR
1/*
2 * Copyright (C) 2016 Maxime Ripard
3 * Maxime Ripard <maxime.ripard@free-electrons.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 */
10
11#include <linux/clk-provider.h>
62e59c4e 12#include <linux/io.h>
4f728b5d
MR
13
14#include "ccu_gate.h"
15#include "ccu_nkmp.h"
16
17struct _ccu_nkmp {
6e0d50da
MR
18 unsigned long n, min_n, max_n;
19 unsigned long k, min_k, max_k;
20 unsigned long m, min_m, max_m;
21 unsigned long p, min_p, max_p;
4f728b5d
MR
22};
23
a5ebc336
JS
24static unsigned long ccu_nkmp_calc_rate(unsigned long parent,
25 unsigned long n, unsigned long k,
26 unsigned long m, unsigned long p)
27{
28 u64 rate = parent;
29
30 rate *= n * k;
31 do_div(rate, m * p);
32
33 return rate;
34}
35
4f728b5d
MR
36static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
37 struct _ccu_nkmp *nkmp)
38{
39 unsigned long best_rate = 0;
40 unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0;
41 unsigned long _n, _k, _m, _p;
42
6e0d50da
MR
43 for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) {
44 for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) {
45 for (_m = nkmp->min_m; _m <= nkmp->max_m; _m++) {
46 for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) {
ee28648c
MR
47 unsigned long tmp_rate;
48
a5ebc336
JS
49 tmp_rate = ccu_nkmp_calc_rate(parent,
50 _n, _k,
51 _m, _p);
ee28648c
MR
52
53 if (tmp_rate > rate)
54 continue;
55
56 if ((rate - tmp_rate) < (rate - best_rate)) {
57 best_rate = tmp_rate;
58 best_n = _n;
59 best_k = _k;
60 best_m = _m;
61 best_p = _p;
62 }
63 }
4f728b5d
MR
64 }
65 }
66 }
67
68 nkmp->n = best_n;
69 nkmp->k = best_k;
70 nkmp->m = best_m;
71 nkmp->p = best_p;
72}
73
74static void ccu_nkmp_disable(struct clk_hw *hw)
75{
76 struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
77
78 return ccu_gate_helper_disable(&nkmp->common, nkmp->enable);
79}
80
81static int ccu_nkmp_enable(struct clk_hw *hw)
82{
83 struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
84
85 return ccu_gate_helper_enable(&nkmp->common, nkmp->enable);
86}
87
88static int ccu_nkmp_is_enabled(struct clk_hw *hw)
89{
90 struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
91
92 return ccu_gate_helper_is_enabled(&nkmp->common, nkmp->enable);
93}
94
95static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw,
96 unsigned long parent_rate)
97{
98 struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
a910f251 99 unsigned long n, m, k, p, rate;
4f728b5d
MR
100 u32 reg;
101
102 reg = readl(nkmp->common.base + nkmp->common.reg);
103
104 n = reg >> nkmp->n.shift;
105 n &= (1 << nkmp->n.width) - 1;
e66f81bb
MR
106 n += nkmp->n.offset;
107 if (!n)
108 n++;
4f728b5d
MR
109
110 k = reg >> nkmp->k.shift;
111 k &= (1 << nkmp->k.width) - 1;
e66f81bb
MR
112 k += nkmp->k.offset;
113 if (!k)
114 k++;
4f728b5d
MR
115
116 m = reg >> nkmp->m.shift;
117 m &= (1 << nkmp->m.width) - 1;
e66f81bb
MR
118 m += nkmp->m.offset;
119 if (!m)
120 m++;
4f728b5d
MR
121
122 p = reg >> nkmp->p.shift;
123 p &= (1 << nkmp->p.width) - 1;
124
a910f251
IZ
125 rate = ccu_nkmp_calc_rate(parent_rate, n, k, m, 1 << p);
126 if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
127 rate /= nkmp->fixed_post_div;
128
129 return rate;
4f728b5d
MR
130}
131
132static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
133 unsigned long *parent_rate)
134{
135 struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
136 struct _ccu_nkmp _nkmp;
137
a910f251
IZ
138 if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
139 rate *= nkmp->fixed_post_div;
140
a8e5433c
JS
141 if (nkmp->max_rate && rate > nkmp->max_rate) {
142 rate = nkmp->max_rate;
143 if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
144 rate /= nkmp->fixed_post_div;
145 return rate;
146 }
147
4162c5ce 148 _nkmp.min_n = nkmp->n.min ?: 1;
0c3c8e13 149 _nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width;
4162c5ce 150 _nkmp.min_k = nkmp->k.min ?: 1;
0c3c8e13 151 _nkmp.max_k = nkmp->k.max ?: 1 << nkmp->k.width;
6e0d50da 152 _nkmp.min_m = 1;
87ba9e59 153 _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
6e0d50da 154 _nkmp.min_p = 1;
87ba9e59 155 _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
4f728b5d 156
87ba9e59 157 ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
4f728b5d 158
a910f251 159 rate = ccu_nkmp_calc_rate(*parent_rate, _nkmp.n, _nkmp.k,
a5ebc336 160 _nkmp.m, _nkmp.p);
a910f251
IZ
161 if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
162 rate = rate / nkmp->fixed_post_div;
163
164 return rate;
4f728b5d
MR
165}
166
167static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
168 unsigned long parent_rate)
169{
170 struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
2abc330c 171 u32 n_mask = 0, k_mask = 0, m_mask = 0, p_mask = 0;
4f728b5d
MR
172 struct _ccu_nkmp _nkmp;
173 unsigned long flags;
174 u32 reg;
175
a910f251
IZ
176 if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
177 rate = rate * nkmp->fixed_post_div;
178
95ad8ed9 179 _nkmp.min_n = nkmp->n.min ?: 1;
0c3c8e13 180 _nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width;
95ad8ed9 181 _nkmp.min_k = nkmp->k.min ?: 1;
0c3c8e13 182 _nkmp.max_k = nkmp->k.max ?: 1 << nkmp->k.width;
6e0d50da 183 _nkmp.min_m = 1;
87ba9e59 184 _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
6e0d50da 185 _nkmp.min_p = 1;
87ba9e59 186 _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
4f728b5d
MR
187
188 ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
189
1054e4dd
JS
190 /*
191 * If width is 0, GENMASK() macro may not generate expected mask (0)
192 * as it falls under undefined behaviour by C standard due to shifts
193 * which are equal or greater than width of left operand. This can
194 * be easily avoided by explicitly checking if width is 0.
195 */
2abc330c
JS
196 if (nkmp->n.width)
197 n_mask = GENMASK(nkmp->n.width + nkmp->n.shift - 1,
198 nkmp->n.shift);
199 if (nkmp->k.width)
200 k_mask = GENMASK(nkmp->k.width + nkmp->k.shift - 1,
201 nkmp->k.shift);
202 if (nkmp->m.width)
203 m_mask = GENMASK(nkmp->m.width + nkmp->m.shift - 1,
204 nkmp->m.shift);
205 if (nkmp->p.width)
206 p_mask = GENMASK(nkmp->p.width + nkmp->p.shift - 1,
207 nkmp->p.shift);
d897ef56 208
4f728b5d
MR
209 spin_lock_irqsave(nkmp->common.lock, flags);
210
211 reg = readl(nkmp->common.base + nkmp->common.reg);
d897ef56
JS
212 reg &= ~(n_mask | k_mask | m_mask | p_mask);
213
214 reg |= ((_nkmp.n - nkmp->n.offset) << nkmp->n.shift) & n_mask;
215 reg |= ((_nkmp.k - nkmp->k.offset) << nkmp->k.shift) & k_mask;
216 reg |= ((_nkmp.m - nkmp->m.offset) << nkmp->m.shift) & m_mask;
217 reg |= (ilog2(_nkmp.p) << nkmp->p.shift) & p_mask;
4f728b5d
MR
218
219 writel(reg, nkmp->common.base + nkmp->common.reg);
220
221 spin_unlock_irqrestore(nkmp->common.lock, flags);
222
223 ccu_helper_wait_for_lock(&nkmp->common, nkmp->lock);
224
225 return 0;
226}
227
228const struct clk_ops ccu_nkmp_ops = {
229 .disable = ccu_nkmp_disable,
230 .enable = ccu_nkmp_enable,
231 .is_enabled = ccu_nkmp_is_enabled,
232
233 .recalc_rate = ccu_nkmp_recalc_rate,
234 .round_rate = ccu_nkmp_round_rate,
235 .set_rate = ccu_nkmp_set_rate,
236};