Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/*
2 * Copyright 2014 Chen-Yu Tsai
3 *
4 * Chen-Yu Tsai <wens@csie.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/clk-provider.h>
18#include <linux/clkdev.h>
19#include <linux/of.h>
20#include <linux/of_address.h>
21#include <linux/log2.h>
22
23#include "clk-factors.h"
24
25
26/**
27 * sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL1
28 * PLL4 rate is calculated as follows
29 * rate = (parent_rate * n >> p) / (m + 1);
30 * parent_rate is always 24Mhz
31 *
32 * p and m are named div1 and div2 in Allwinner's SDK
33 */
34
35static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate,
36 u8 *n, u8 *k, u8 *m, u8 *p)
37{
38 int div;
39
40 /* Normalize value to a 6M multiple */
41 div = DIV_ROUND_UP(*freq, 6000000);
42
43 /* divs above 256 cannot be odd */
44 if (div > 256)
45 div = round_up(div, 2);
46
47 /* divs above 512 must be a multiple of 4 */
48 if (div > 512)
49 div = round_up(div, 4);
50
51 *freq = 6000000 * div;
52
53 /* we were called to round the frequency, we can now return */
54 if (n == NULL)
55 return;
56
57 /* p will be 1 for divs under 512 */
58 if (div < 512)
59 *p = 1;
60 else
61 *p = 0;
62
63 /* m will be 1 if div is odd */
64 if (div & 1)
65 *m = 1;
66 else
67 *m = 0;
68
69 /* calculate a suitable n based on m and p */
70 *n = div / (*p + 1) / (*m + 1);
71}
72
73static struct clk_factors_config sun9i_a80_pll4_config = {
74 .mshift = 18,
75 .mwidth = 1,
76 .nshift = 8,
77 .nwidth = 8,
78 .pshift = 16,
79 .pwidth = 1,
80};
81
82static const struct factors_data sun9i_a80_pll4_data __initconst = {
83 .enable = 31,
84 .table = &sun9i_a80_pll4_config,
85 .getter = sun9i_a80_get_pll4_factors,
86};
87
88static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
89
90static void __init sun9i_a80_pll4_setup(struct device_node *node)
91{
92 sunxi_factors_register(node, &sun9i_a80_pll4_data, &sun9i_a80_pll4_lock);
93}
94CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
95
96
97/**
98 * sun9i_a80_get_gt_factors() - calculates m factor for GT
99 * GT rate is calculated as follows
100 * rate = parent_rate / (m + 1);
101 */
102
103static void sun9i_a80_get_gt_factors(u32 *freq, u32 parent_rate,
104 u8 *n, u8 *k, u8 *m, u8 *p)
105{
106 u32 div;
107
108 if (parent_rate < *freq)
109 *freq = parent_rate;
110
111 div = DIV_ROUND_UP(parent_rate, *freq);
112
113 /* maximum divider is 4 */
114 if (div > 4)
115 div = 4;
116
117 *freq = parent_rate / div;
118
119 /* we were called to round the frequency, we can now return */
120 if (!m)
121 return;
122
123 *m = div;
124}
125
126static struct clk_factors_config sun9i_a80_gt_config = {
127 .mshift = 0,
128 .mwidth = 2,
129};
130
131static const struct factors_data sun9i_a80_gt_data __initconst = {
132 .mux = 24,
133 .muxmask = BIT(1) | BIT(0),
134 .table = &sun9i_a80_gt_config,
135 .getter = sun9i_a80_get_gt_factors,
136};
137
138static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
139
140static void __init sun9i_a80_gt_setup(struct device_node *node)
141{
142 struct clk *gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
143 &sun9i_a80_gt_lock);
144
145 /* The GT bus clock needs to be always enabled */
146 __clk_get(gt);
147 clk_prepare_enable(gt);
148}
149CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup);
150
151
152/**
153 * sun9i_a80_get_ahb_factors() - calculates p factor for AHB0/1/2
154 * AHB rate is calculated as follows
155 * rate = parent_rate >> p;
156 */
157
158static void sun9i_a80_get_ahb_factors(u32 *freq, u32 parent_rate,
159 u8 *n, u8 *k, u8 *m, u8 *p)
160{
161 u32 _p;
162
163 if (parent_rate < *freq)
164 *freq = parent_rate;
165
166 _p = order_base_2(DIV_ROUND_UP(parent_rate, *freq));
167
168 /* maximum p is 3 */
169 if (_p > 3)
170 _p = 3;
171
172 *freq = parent_rate >> _p;
173
174 /* we were called to round the frequency, we can now return */
175 if (!p)
176 return;
177
178 *p = _p;
179}
180
181static struct clk_factors_config sun9i_a80_ahb_config = {
182 .pshift = 0,
183 .pwidth = 2,
184};
185
186static const struct factors_data sun9i_a80_ahb_data __initconst = {
187 .mux = 24,
188 .muxmask = BIT(1) | BIT(0),
189 .table = &sun9i_a80_ahb_config,
190 .getter = sun9i_a80_get_ahb_factors,
191};
192
193static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
194
195static void __init sun9i_a80_ahb_setup(struct device_node *node)
196{
197 sunxi_factors_register(node, &sun9i_a80_ahb_data, &sun9i_a80_ahb_lock);
198}
199CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
200
201
202static const struct factors_data sun9i_a80_apb0_data __initconst = {
203 .mux = 24,
204 .muxmask = BIT(0),
205 .table = &sun9i_a80_ahb_config,
206 .getter = sun9i_a80_get_ahb_factors,
207};
208
209static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
210
211static void __init sun9i_a80_apb0_setup(struct device_node *node)
212{
213 sunxi_factors_register(node, &sun9i_a80_apb0_data, &sun9i_a80_apb0_lock);
214}
215CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
216
217
218/**
219 * sun9i_a80_get_apb1_factors() - calculates m, p factors for APB1
220 * APB1 rate is calculated as follows
221 * rate = (parent_rate >> p) / (m + 1);
222 */
223
224static void sun9i_a80_get_apb1_factors(u32 *freq, u32 parent_rate,
225 u8 *n, u8 *k, u8 *m, u8 *p)
226{
227 u32 div;
228 u8 calcm, calcp;
229
230 if (parent_rate < *freq)
231 *freq = parent_rate;
232
233 div = DIV_ROUND_UP(parent_rate, *freq);
234
235 /* Highest possible divider is 256 (p = 3, m = 31) */
236 if (div > 256)
237 div = 256;
238
239 calcp = order_base_2(div);
240 calcm = (parent_rate >> calcp) - 1;
241 *freq = (parent_rate >> calcp) / (calcm + 1);
242
243 /* we were called to round the frequency, we can now return */
244 if (n == NULL)
245 return;
246
247 *m = calcm;
248 *p = calcp;
249}
250
251static struct clk_factors_config sun9i_a80_apb1_config = {
252 .mshift = 0,
253 .mwidth = 5,
254 .pshift = 16,
255 .pwidth = 2,
256};
257
258static const struct factors_data sun9i_a80_apb1_data __initconst = {
259 .mux = 24,
260 .muxmask = BIT(0),
261 .table = &sun9i_a80_apb1_config,
262 .getter = sun9i_a80_get_apb1_factors,
263};
264
265static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
266
267static void __init sun9i_a80_apb1_setup(struct device_node *node)
268{
269 sunxi_factors_register(node, &sun9i_a80_apb1_data, &sun9i_a80_apb1_lock);
270}
271CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);