1 /*-
2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29 /*
30 * Allwinner CPUS clock
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <machine/bus.h>
43
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/ofw/ofw_subr.h>
47
48 #include <dev/extres/clk/clk.h>
49
50 #include "clkdev_if.h"
51
52 #define A80_CPUS_CLK_SRC_SEL (0x3 << 16)
53 #define A80_CPUS_CLK_SRC_SEL_SHIFT 16
54 #define A80_CPUS_CLK_SRC_SEL_X32KI 0
55 #define A80_CPUS_CLK_SRC_SEL_OSC24M 1
56 #define A80_CPUS_CLK_SRC_SEL_PLL_PERIPH 2
57 #define A80_CPUS_CLK_SRC_SEL_PLL_AUDIO 3
58 #define A80_CPUS_POST_DIV (0x1f << 8)
59 #define A80_CPUS_POST_DIV_SHIFT 8
60 #define A80_CPUS_CLK_RATIO (0x3 << 4)
61 #define A80_CPUS_CLK_RATIO_SHIFT 4
62
63 #define A83T_CPUS_CLK_SRC_SEL (0x3 << 16)
64 #define A83T_CPUS_CLK_SRC_SEL_SHIFT 16
65 #define A83T_CPUS_CLK_SRC_SEL_X32KI 0
66 #define A83T_CPUS_CLK_SRC_SEL_OSC24M 1
67 #define A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH 2
68 #define A83T_CPUS_CLK_SRC_SEL_INTERNAL_OSC 3
69 #define A83T_CPUS_POST_DIV (0x1f << 8)
70 #define A83T_CPUS_POST_DIV_SHIFT 8
71 #define A83T_CPUS_CLK_RATIO (0x3 << 4)
72 #define A83T_CPUS_CLK_RATIO_SHIFT 4
73
74 enum aw_cpusclk_type {
75 AW_A80_CPUS = 1,
76 AW_A83T_CPUS,
77 };
78
79 static struct ofw_compat_data compat_data[] = {
80 { "allwinner,sun9i-a80-cpus-clk", AW_A80_CPUS },
81 { "allwinner,sun8i-a83t-cpus-clk", AW_A83T_CPUS },
82 { NULL, 0 }
83 };
84
85 struct aw_cpusclk_sc {
86 device_t clkdev;
87 bus_addr_t reg;
88 enum aw_cpusclk_type type;
89 };
90
91 #define CPUSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
92 #define CPUSCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
93 #define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
94 #define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
95
96 static int
aw_cpusclk_init(struct clknode * clk,device_t dev)97 aw_cpusclk_init(struct clknode *clk, device_t dev)
98 {
99 struct aw_cpusclk_sc *sc;
100 uint32_t val, mask, shift, index;
101
102 sc = clknode_get_softc(clk);
103
104 switch (sc->type) {
105 case AW_A80_CPUS:
106 mask = A80_CPUS_CLK_SRC_SEL;
107 shift = A80_CPUS_CLK_SRC_SEL_SHIFT;
108 break;
109 case AW_A83T_CPUS:
110 mask = A83T_CPUS_CLK_SRC_SEL;
111 shift = A83T_CPUS_CLK_SRC_SEL_SHIFT;
112 break;
113 default:
114 return (ENXIO);
115 }
116
117 DEVICE_LOCK(sc);
118 CPUSCLK_READ(sc, &val);
119 DEVICE_UNLOCK(sc);
120 index = (val & mask) >> shift;
121
122 clknode_init_parent_idx(clk, index);
123 return (0);
124 }
125
126 static int
aw_cpusclk_recalc_freq(struct clknode * clk,uint64_t * freq)127 aw_cpusclk_recalc_freq(struct clknode *clk, uint64_t *freq)
128 {
129 struct aw_cpusclk_sc *sc;
130 uint32_t val, src_sel, post_div, clk_ratio;
131
132 sc = clknode_get_softc(clk);
133
134 DEVICE_LOCK(sc);
135 CPUSCLK_READ(sc, &val);
136 DEVICE_UNLOCK(sc);
137
138 switch (sc->type) {
139 case AW_A80_CPUS:
140 src_sel = (val & A80_CPUS_CLK_SRC_SEL) >>
141 A80_CPUS_CLK_SRC_SEL_SHIFT;
142 post_div = ((val & A80_CPUS_POST_DIV) >>
143 A80_CPUS_POST_DIV_SHIFT) + 1;
144 clk_ratio = ((val & A80_CPUS_CLK_RATIO) >>
145 A80_CPUS_CLK_RATIO_SHIFT) + 1;
146 if (src_sel == A80_CPUS_CLK_SRC_SEL_PLL_PERIPH)
147 *freq = *freq / post_div / clk_ratio;
148 else
149 *freq = *freq / clk_ratio;
150 break;
151 case AW_A83T_CPUS:
152 src_sel = (val & A83T_CPUS_CLK_SRC_SEL) >>
153 A83T_CPUS_CLK_SRC_SEL_SHIFT;
154 post_div = ((val & A83T_CPUS_POST_DIV) >>
155 A83T_CPUS_POST_DIV_SHIFT) + 1;
156 clk_ratio = 1 << ((val & A83T_CPUS_CLK_RATIO) >>
157 A83T_CPUS_CLK_RATIO_SHIFT);
158 if (src_sel == A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH)
159 *freq = *freq / post_div / clk_ratio;
160 else
161 *freq = *freq / clk_ratio;
162 break;
163 default:
164 return (EINVAL);
165 }
166
167 return (0);
168 }
169
170 static int
aw_cpusclk_set_mux(struct clknode * clk,int index)171 aw_cpusclk_set_mux(struct clknode *clk, int index)
172 {
173 struct aw_cpusclk_sc *sc;
174 uint32_t mask, shift, val;
175
176 sc = clknode_get_softc(clk);
177
178 switch (sc->type) {
179 case AW_A80_CPUS:
180 mask = A80_CPUS_CLK_SRC_SEL;
181 shift = A80_CPUS_CLK_SRC_SEL_SHIFT;
182 break;
183 case AW_A83T_CPUS:
184 mask = A83T_CPUS_CLK_SRC_SEL;
185 shift = A83T_CPUS_CLK_SRC_SEL_SHIFT;
186 break;
187 default:
188 return (ENXIO);
189 }
190
191 DEVICE_LOCK(sc);
192 CPUSCLK_READ(sc, &val);
193 val &= ~mask;
194 val |= (index << shift);
195 CPUSCLK_WRITE(sc, val);
196 DEVICE_UNLOCK(sc);
197
198 return (0);
199 }
200
201 static clknode_method_t aw_cpusclk_clknode_methods[] = {
202 /* Device interface */
203 CLKNODEMETHOD(clknode_init, aw_cpusclk_init),
204 CLKNODEMETHOD(clknode_recalc_freq, aw_cpusclk_recalc_freq),
205 CLKNODEMETHOD(clknode_set_mux, aw_cpusclk_set_mux),
206 CLKNODEMETHOD_END
207 };
208 DEFINE_CLASS_1(aw_cpusclk_clknode, aw_cpusclk_clknode_class,
209 aw_cpusclk_clknode_methods, sizeof(struct aw_cpusclk_sc), clknode_class);
210
211 static int
aw_cpusclk_probe(device_t dev)212 aw_cpusclk_probe(device_t dev)
213 {
214 if (!ofw_bus_status_okay(dev))
215 return (ENXIO);
216
217 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
218 return (ENXIO);
219
220 device_set_desc(dev, "Allwinner CPUS Clock");
221 return (BUS_PROBE_DEFAULT);
222 }
223
224 static int
aw_cpusclk_attach(device_t dev)225 aw_cpusclk_attach(device_t dev)
226 {
227 struct clknode_init_def def;
228 struct aw_cpusclk_sc *sc;
229 struct clkdom *clkdom;
230 struct clknode *clk;
231 clk_t clk_parent;
232 bus_addr_t paddr;
233 bus_size_t psize;
234 phandle_t node;
235 int error, ncells, i;
236
237 node = ofw_bus_get_node(dev);
238
239 if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
240 device_printf(dev, "cannot parse 'reg' property\n");
241 return (ENXIO);
242 }
243
244 error = ofw_bus_parse_xref_list_get_length(node, "clocks",
245 "#clock-cells", &ncells);
246 if (error != 0) {
247 device_printf(dev, "cannot get clock count\n");
248 return (error);
249 }
250
251 clkdom = clkdom_create(dev);
252
253 memset(&def, 0, sizeof(def));
254 def.id = 1;
255 def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
256 M_WAITOK);
257 for (i = 0; i < ncells; i++) {
258 error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
259 if (error != 0) {
260 device_printf(dev, "cannot get clock %d\n", i);
261 goto fail;
262 }
263 def.parent_names[i] = clk_get_name(clk_parent);
264 clk_release(clk_parent);
265 }
266 def.parent_cnt = ncells;
267
268 error = clk_parse_ofw_clk_name(dev, node, &def.name);
269 if (error != 0) {
270 device_printf(dev, "cannot parse clock name\n");
271 error = ENXIO;
272 goto fail;
273 }
274
275 clk = clknode_create(clkdom, &aw_cpusclk_clknode_class, &def);
276 if (clk == NULL) {
277 device_printf(dev, "cannot create clknode\n");
278 error = ENXIO;
279 goto fail;
280 }
281 sc = clknode_get_softc(clk);
282 sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
283 sc->reg = paddr;
284 sc->clkdev = device_get_parent(dev);
285
286 clknode_register(clkdom, clk);
287
288 if (clkdom_finit(clkdom) != 0) {
289 device_printf(dev, "cannot finalize clkdom initialization\n");
290 error = ENXIO;
291 goto fail;
292 }
293
294 if (bootverbose)
295 clkdom_dump(clkdom);
296
297 return (0);
298
299 fail:
300 return (error);
301 }
302
303 static device_method_t aw_cpusclk_methods[] = {
304 /* Device interface */
305 DEVMETHOD(device_probe, aw_cpusclk_probe),
306 DEVMETHOD(device_attach, aw_cpusclk_attach),
307
308 DEVMETHOD_END
309 };
310
311 static driver_t aw_cpusclk_driver = {
312 "aw_cpusclk",
313 aw_cpusclk_methods,
314 0
315 };
316
317 static devclass_t aw_cpusclk_devclass;
318
319 EARLY_DRIVER_MODULE(aw_cpusclk, simplebus, aw_cpusclk_driver,
320 aw_cpusclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
321