xref: /openbsd-src/sys/dev/fdt/sxiccmu.c (revision 8ead0783a05eee83ab02af2c7b14b10fbcdce47d)
1 /*	$OpenBSD: sxiccmu.c,v 1.7 2017/09/08 05:36:52 deraadt Exp $	*/
2 /*
3  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4  * Copyright (c) 2013 Artturi Alm
5  * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/malloc.h>
24 #include <sys/time.h>
25 #include <sys/device.h>
26 
27 #include <machine/bus.h>
28 #include <machine/fdt.h>
29 #include <machine/intr.h>
30 
31 #include <dev/fdt/sunxireg.h>
32 
33 #include <dev/ofw/openfirm.h>
34 #include <dev/ofw/ofw_clock.h>
35 #include <dev/ofw/fdt.h>
36 
37 #ifdef DEBUG_CCMU
38 #define DPRINTF(x)	do { printf x; } while (0)
39 #else
40 #define DPRINTF(x)
41 #endif
42 
43 struct sxiccmu_ccu_bit {
44 	uint16_t reg;
45 	uint8_t bit;
46 	uint8_t parent;
47 };
48 
49 #include "sxiccmu_clocks.h"
50 
51 struct sxiccmu_softc {
52 	struct device		sc_dev;
53 	bus_space_tag_t		sc_iot;
54 	bus_space_handle_t	sc_ioh;
55 	int			sc_node;
56 
57 	struct sxiccmu_ccu_bit	*sc_gates;
58 	int			sc_ngates;
59 	struct clock_device	sc_cd;
60 
61 	struct sxiccmu_ccu_bit	*sc_resets;
62 	int			sc_nresets;
63 	struct reset_device	sc_rd;
64 
65 	uint32_t		(*sc_get_frequency)(struct sxiccmu_softc *,
66 				    uint32_t);
67 	int			(*sc_set_frequency)(struct sxiccmu_softc *,
68 				    uint32_t, uint32_t);
69 };
70 
71 int	sxiccmu_match(struct device *, void *, void *);
72 void	sxiccmu_attach(struct device *, struct device *, void *);
73 
74 struct cfattach	sxiccmu_ca = {
75 	sizeof (struct sxiccmu_softc), sxiccmu_match, sxiccmu_attach
76 };
77 
78 struct cfdriver sxiccmu_cd = {
79 	NULL, "sxiccmu", DV_DULL
80 };
81 
82 void sxiccmu_attach_clock(struct sxiccmu_softc *, int);
83 
84 uint32_t sxiccmu_ccu_get_frequency(void *, uint32_t *);
85 int	sxiccmu_ccu_set_frequency(void *, uint32_t *, uint32_t);
86 void	sxiccmu_ccu_enable(void *, uint32_t *, int);
87 void	sxiccmu_ccu_reset(void *, uint32_t *, int);
88 
89 uint32_t sxiccmu_a64_get_frequency(struct sxiccmu_softc *, uint32_t);
90 int	sxiccmu_a64_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
91 uint32_t sxiccmu_h3_get_frequency(struct sxiccmu_softc *, uint32_t);
92 int	sxiccmu_h3_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
93 
94 int
95 sxiccmu_match(struct device *parent, void *match, void *aux)
96 {
97 	struct fdt_attach_args *faa = aux;
98 
99 	if (faa->fa_node == OF_finddevice("/clocks")) {
100 		int node = OF_parent(faa->fa_node);
101 
102 		return (OF_is_compatible(node, "allwinner,sun4i-a10") ||
103 		    OF_is_compatible(node, "allwinner,sun5i-a10s") ||
104 		    OF_is_compatible(node, "allwinner,sun5i-r8") ||
105 		    OF_is_compatible(node, "allwinner,sun7i-a20") ||
106 		    OF_is_compatible(node, "allwinner,sun8i-h3") ||
107 		    OF_is_compatible(node, "allwinner,sun9i-a80") ||
108 		    OF_is_compatible(node, "allwinner,sun50i-a64") ||
109 		    OF_is_compatible(node, "allwinner,sun50i-h5"));
110 	}
111 
112 	return (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ccu") ||
113 	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ccu") ||
114 	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-ccu"));
115 }
116 
117 void
118 sxiccmu_attach(struct device *parent, struct device *self, void *aux)
119 {
120 	struct sxiccmu_softc *sc = (struct sxiccmu_softc *)self;
121 	struct fdt_attach_args *faa = aux;
122 	int node;
123 
124 	sc->sc_node = faa->fa_node;
125 	sc->sc_iot = faa->fa_iot;
126 	if (faa->fa_nreg > 0 && bus_space_map(sc->sc_iot,
127 	    faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh))
128 		panic("%s: bus_space_map failed!", __func__);
129 
130 	printf("\n");
131 
132 	if (OF_is_compatible(sc->sc_node, "allwinner,sun50i-a64-ccu")) {
133 		KASSERT(faa->fa_nreg > 0);
134 		sc->sc_gates = sun50i_a64_gates;
135 		sc->sc_ngates = nitems(sun50i_a64_gates);
136 		sc->sc_resets = sun50i_a64_resets;
137 		sc->sc_nresets = nitems(sun50i_a64_resets);
138 		sc->sc_get_frequency = sxiccmu_a64_get_frequency;
139 		sc->sc_set_frequency = sxiccmu_a64_set_frequency;
140 	} else if (OF_is_compatible(sc->sc_node, "allwinner,sun8i-h3-ccu") ||
141 	    OF_is_compatible(sc->sc_node, "allwinner,sun50i-h5-ccu")) {
142 		KASSERT(faa->fa_nreg > 0);
143 		sc->sc_gates = sun8i_h3_gates;
144 		sc->sc_ngates = nitems(sun8i_h3_gates);
145 		sc->sc_resets = sun8i_h3_resets;
146 		sc->sc_nresets = nitems(sun8i_h3_resets);
147 		sc->sc_get_frequency = sxiccmu_h3_get_frequency;
148 		sc->sc_set_frequency = sxiccmu_h3_set_frequency;
149 	} else {
150 		for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
151 			sxiccmu_attach_clock(sc, node);
152 	}
153 
154 	if (sc->sc_gates) {
155 		sc->sc_cd.cd_node = sc->sc_node;
156 		sc->sc_cd.cd_cookie = sc;
157 		sc->sc_cd.cd_get_frequency = sxiccmu_ccu_get_frequency;
158 		sc->sc_cd.cd_set_frequency = sxiccmu_ccu_set_frequency;
159 		sc->sc_cd.cd_enable = sxiccmu_ccu_enable;
160 		clock_register(&sc->sc_cd);
161 	}
162 
163 	if (sc->sc_resets) {
164 		sc->sc_rd.rd_node = sc->sc_node;
165 		sc->sc_rd.rd_cookie = sc;
166 		sc->sc_rd.rd_reset = sxiccmu_ccu_reset;
167 		reset_register(&sc->sc_rd);
168 	}
169 }
170 
171 /*
172  * Classic device trees for the Allwinner SoCs have basically a clock
173  * node per register of the clock control unit.  Attaching a separate
174  * driver to each of them would be crazy, so we handle them here.
175  */
176 
177 struct sxiccmu_clock {
178 	int sc_node;
179 	bus_space_tag_t sc_iot;
180 	bus_space_handle_t sc_ioh;
181 
182 	struct clock_device sc_cd;
183 	struct reset_device sc_rd;
184 };
185 
186 struct sxiccmu_device {
187 	const char *compat;
188 	uint32_t (*get_frequency)(void *, uint32_t *);
189 	int	(*set_frequency)(void *, uint32_t *, uint32_t);
190 	void	(*enable)(void *, uint32_t *, int);
191 	void	(*reset)(void *, uint32_t *, int);
192 };
193 
194 uint32_t sxiccmu_gen_get_frequency(void *, uint32_t *);
195 uint32_t sxiccmu_osc_get_frequency(void *, uint32_t *);
196 uint32_t sxiccmu_pll6_get_frequency(void *, uint32_t *);
197 void	sxiccmu_pll6_enable(void *, uint32_t *, int);
198 uint32_t sxiccmu_apb1_get_frequency(void *, uint32_t *);
199 int	sxiccmu_gmac_set_frequency(void *, uint32_t *, uint32_t);
200 int	sxiccmu_mmc_set_frequency(void *, uint32_t *, uint32_t);
201 void	sxiccmu_mmc_enable(void *, uint32_t *, int);
202 void	sxiccmu_gate_enable(void *, uint32_t *, int);
203 void	sxiccmu_reset(void *, uint32_t *, int);
204 
205 struct sxiccmu_device sxiccmu_devices[] = {
206 	{
207 		.compat = "allwinner,sun4i-a10-osc-clk",
208 		.get_frequency = sxiccmu_osc_get_frequency,
209 	},
210 	{
211 		.compat = "allwinner,sun4i-a10-pll6-clk",
212 		.get_frequency = sxiccmu_pll6_get_frequency,
213 		.enable = sxiccmu_pll6_enable
214 	},
215 	{
216 		.compat = "allwinner,sun4i-a10-apb1-clk",
217 		.get_frequency = sxiccmu_apb1_get_frequency,
218 	},
219 	{
220 		.compat = "allwinner,sun4i-a10-ahb-gates-clk",
221 		.get_frequency = sxiccmu_gen_get_frequency,
222 		.enable = sxiccmu_gate_enable
223 	},
224 	{
225 		.compat = "allwinner,sun4i-a10-apb0-gates-clk",
226 		.get_frequency = sxiccmu_gen_get_frequency,
227 		.enable = sxiccmu_gate_enable
228 	},
229 	{
230 		.compat = "allwinner,sun4i-a10-apb1-gates-clk",
231 		.get_frequency = sxiccmu_gen_get_frequency,
232 		.enable = sxiccmu_gate_enable
233 	},
234 	{
235 		.compat = "allwinner,sun4i-a10-mmc-clk",
236 		.set_frequency = sxiccmu_mmc_set_frequency,
237 		.enable = sxiccmu_mmc_enable
238 	},
239 	{
240 		.compat = "allwinner,sun4i-a10-usb-clk",
241 		.get_frequency = sxiccmu_gen_get_frequency,
242 		.enable = sxiccmu_gate_enable,
243 		.reset = sxiccmu_reset
244 	},
245 	{
246 		.compat = "allwinner,sun5i-a10s-ahb-gates-clk",
247 		.get_frequency = sxiccmu_gen_get_frequency,
248 		.enable = sxiccmu_gate_enable
249 	},
250 	{
251 		.compat = "allwinner,sun5i-a10s-apb0-gates-clk",
252 		.get_frequency = sxiccmu_gen_get_frequency,
253 		.enable = sxiccmu_gate_enable
254 	},
255 	{
256 		.compat = "allwinner,sun5i-a10s-apb1-gates-clk",
257 		.get_frequency = sxiccmu_gen_get_frequency,
258 		.enable = sxiccmu_gate_enable
259 	},
260 	{
261 		.compat = "allwinner,sun5i-a13-ahb-gates-clk",
262 		.get_frequency = sxiccmu_gen_get_frequency,
263 		.enable = sxiccmu_gate_enable
264 	},
265 	{
266 		.compat = "allwinner,sun5i-a13-apb0-gates-clk",
267 		.get_frequency = sxiccmu_gen_get_frequency,
268 		.enable = sxiccmu_gate_enable
269 	},
270 	{
271 		.compat = "allwinner,sun5i-a13-apb1-gates-clk",
272 		.get_frequency = sxiccmu_gen_get_frequency,
273 		.enable = sxiccmu_gate_enable
274 	},
275 	{
276 		.compat = "allwinner,sun5i-a13-usb-clk",
277 		.get_frequency = sxiccmu_gen_get_frequency,
278 		.enable = sxiccmu_gate_enable,
279 		.reset = sxiccmu_reset
280 	},
281 	{
282 		.compat = "allwinner,sun6i-a31-ahb1-reset",
283 		.reset = sxiccmu_reset
284 	},
285 	{
286 		.compat = "allwinner,sun6i-a31-clock-reset",
287 		.reset = sxiccmu_reset
288 	},
289 	{
290 		.compat = "allwinner,sun7i-a20-ahb-gates-clk",
291 		.get_frequency = sxiccmu_gen_get_frequency,
292 		.enable = sxiccmu_gate_enable
293 	},
294 	{
295 		.compat = "allwinner,sun7i-a20-apb0-gates-clk",
296 		.get_frequency = sxiccmu_gen_get_frequency,
297 		.enable = sxiccmu_gate_enable
298 	},
299 	{
300 		.compat = "allwinner,sun7i-a20-apb1-gates-clk",
301 		.get_frequency = sxiccmu_gen_get_frequency,
302 		.enable = sxiccmu_gate_enable
303 	},
304 	{
305 		.compat = "allwinner,sun7i-a20-gmac-clk",
306 		.set_frequency = sxiccmu_gmac_set_frequency
307 	},
308 	{
309 		.compat = "allwinner,sun8i-h3-apb0-gates-clk",
310 		.get_frequency = sxiccmu_gen_get_frequency,
311 		.enable = sxiccmu_gate_enable
312 	},
313 	{
314 		.compat = "allwinner,sun9i-a80-apb1-clk",
315 		.get_frequency = sxiccmu_apb1_get_frequency,
316 	},
317 	{
318 		.compat = "allwinner,sun9i-a80-ahb0-gates-clk",
319 		.get_frequency = sxiccmu_gen_get_frequency,
320 		.enable = sxiccmu_gate_enable
321 	},
322 	{
323 		.compat = "allwinner,sun9i-a80-ahb1-gates-clk",
324 		.get_frequency = sxiccmu_gen_get_frequency,
325 		.enable = sxiccmu_gate_enable
326 	},
327 	{
328 		.compat = "allwinner,sun9i-a80-ahb2-gates-clk",
329 		.get_frequency = sxiccmu_gen_get_frequency,
330 		.enable = sxiccmu_gate_enable
331 	},
332 	{
333 		.compat = "allwinner,sun9i-a80-apb0-gates-clk",
334 		.get_frequency = sxiccmu_gen_get_frequency,
335 		.enable = sxiccmu_gate_enable
336 	},
337 	{
338 		.compat = "allwinner,sun9i-a80-apb1-gates-clk",
339 		.get_frequency = sxiccmu_gen_get_frequency,
340 		.enable = sxiccmu_gate_enable
341 	},
342 	{
343 		.compat = "allwinner,sun9i-a80-apbs-gates-clk",
344 		.get_frequency = sxiccmu_gen_get_frequency,
345 		.enable = sxiccmu_gate_enable
346 	},
347 	{
348 		.compat = "allwinner,sun9i-a80-mmc-clk",
349 		.set_frequency = sxiccmu_mmc_set_frequency,
350 		.enable = sxiccmu_mmc_enable
351 	},
352 	{
353 		.compat = "allwinner,sun9i-a80-usb-mod-clk",
354 		.get_frequency = sxiccmu_gen_get_frequency,
355 		.enable = sxiccmu_gate_enable,
356 		.reset = sxiccmu_reset
357 	},
358 	{
359 		.compat = "allwinner,sun9i-a80-usb-phy-clk",
360 		.get_frequency = sxiccmu_gen_get_frequency,
361 		.enable = sxiccmu_gate_enable,
362 		.reset = sxiccmu_reset
363 	},
364 };
365 
366 void
367 sxiccmu_attach_clock(struct sxiccmu_softc *sc, int node)
368 {
369 	struct sxiccmu_clock *clock;
370 	uint32_t reg[2];
371 	int i;
372 
373 	for (i = 0; i < nitems(sxiccmu_devices); i++)
374 		if (OF_is_compatible(node, sxiccmu_devices[i].compat))
375 			break;
376 	if (i == nitems(sxiccmu_devices))
377 		return;
378 
379 	clock = malloc(sizeof(*clock), M_DEVBUF, M_WAITOK);
380 	clock->sc_node = node;
381 
382 	if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) == sizeof(reg)) {
383 		clock->sc_iot = sc->sc_iot;
384 		if (bus_space_map(clock->sc_iot, reg[0], reg[1], 0,
385 		    &clock->sc_ioh)) {
386 			printf("%s: can't map registers", sc->sc_dev.dv_xname);
387 			free(clock, M_DEVBUF, sizeof(*clock));
388 			return;
389 		}
390 	}
391 
392 	clock->sc_cd.cd_node = node;
393 	clock->sc_cd.cd_cookie = clock;
394 	clock->sc_cd.cd_get_frequency = sxiccmu_devices[i].get_frequency;
395 	clock->sc_cd.cd_set_frequency = sxiccmu_devices[i].set_frequency;
396 	clock->sc_cd.cd_enable = sxiccmu_devices[i].enable;
397 	clock_register(&clock->sc_cd);
398 
399 	if (sxiccmu_devices[i].reset) {
400 		clock->sc_rd.rd_node = node;
401 		clock->sc_rd.rd_cookie = clock;
402 		clock->sc_rd.rd_reset = sxiccmu_devices[i].reset;
403 		reset_register(&clock->sc_rd);
404 	}
405 }
406 
407 /*
408  * A "generic" function that simply gets the clock frequency from the
409  * parent clock.  Useful for clock gating devices that don't scale
410  * their clocks.
411  */
412 uint32_t
413 sxiccmu_gen_get_frequency(void *cookie, uint32_t *cells)
414 {
415 	struct sxiccmu_clock *sc = cookie;
416 
417 	return clock_get_frequency(sc->sc_node, NULL);
418 }
419 
420 uint32_t
421 sxiccmu_osc_get_frequency(void *cookie, uint32_t *cells)
422 {
423 	struct sxiccmu_clock *sc = cookie;
424 
425 	return OF_getpropint(sc->sc_node, "clock-frequency", 24000000);
426 }
427 
428 #define CCU_PLL6_ENABLE			(1U << 31)
429 #define CCU_PLL6_BYPASS_EN		(1U << 30)
430 #define CCU_PLL6_SATA_CLK_EN		(1U << 14)
431 #define CCU_PLL6_FACTOR_N(x)		(((x) >> 8) & 0x1f)
432 #define CCU_PLL6_FACTOR_N_MASK		(0x1f << 8)
433 #define CCU_PLL6_FACTOR_N_SHIFT		8
434 #define CCU_PLL6_FACTOR_K(x)		(((x) >> 4) & 0x3)
435 #define CCU_PLL6_FACTOR_K_MASK		(0x3 << 4)
436 #define CCU_PLL6_FACTOR_K_SHIFT		4
437 #define CCU_PLL6_FACTOR_M(x)		(((x) >> 0) & 0x3)
438 #define CCU_PLL6_FACTOR_M_MASK		(0x3 << 0)
439 #define CCU_PLL6_FACTOR_M_SHIFT		0
440 
441 uint32_t
442 sxiccmu_pll6_get_frequency(void *cookie, uint32_t *cells)
443 {
444 	struct sxiccmu_clock *sc = cookie;
445 	uint32_t reg, k, m, n, freq;
446 	uint32_t idx = cells[0];
447 
448 	/* XXX Assume bypass is disabled. */
449 	reg = SXIREAD4(sc, 0);
450 	k = CCU_PLL6_FACTOR_K(reg) + 1;
451 	m = CCU_PLL6_FACTOR_M(reg) + 1;
452 	n = CCU_PLL6_FACTOR_N(reg);
453 
454 	freq = clock_get_frequency_idx(sc->sc_node, 0);
455 	switch (idx) {
456 	case 0:
457 		return (freq * n * k) / m / 6;		/* pll6_sata */
458 	case 1:
459 		return (freq * n * k) / 2;		/* pll6_other */
460 	case 2:
461 		return (freq * n * k);			/* pll6 */
462 	case 3:
463 		return (freq * n * k) / 4;		/* pll6_div_4 */
464 	}
465 
466 	return 0;
467 }
468 
469 void
470 sxiccmu_pll6_enable(void *cookie, uint32_t *cells, int on)
471 {
472 	struct sxiccmu_clock *sc = cookie;
473 	uint32_t idx = cells[0];
474 	uint32_t reg;
475 
476 	/*
477 	 * Since this clock has several outputs, we never turn it off.
478 	 */
479 
480 	reg = SXIREAD4(sc, 0);
481 	switch (idx) {
482 	case 0:			/* pll6_sata */
483 		if (on)
484 			reg |= CCU_PLL6_SATA_CLK_EN;
485 		else
486 			reg &= ~CCU_PLL6_SATA_CLK_EN;
487 		/* FALLTHROUGH */
488 	case 1:			/* pll6_other */
489 	case 2:			/* pll6 */
490 	case 3:			/* pll6_div_4 */
491 		if (on)
492 			reg |= CCU_PLL6_ENABLE;
493 	}
494 	SXIWRITE4(sc, 0, reg);
495 }
496 
497 #define CCU_APB1_CLK_RAT_N(x)		(((x) >> 16) & 0x3)
498 #define CCU_APB1_CLK_RAT_M(x)		(((x) >> 0) & 0x1f)
499 #define CCU_APB1_CLK_SRC_SEL(x)		(((x) >> 24) & 0x3)
500 
501 uint32_t
502 sxiccmu_apb1_get_frequency(void *cookie, uint32_t *cells)
503 {
504 	struct sxiccmu_clock *sc = cookie;
505 	uint32_t reg, m, n, freq;
506 	int idx;
507 
508 	reg = SXIREAD4(sc, 0);
509 	m = CCU_APB1_CLK_RAT_M(reg);
510 	n = CCU_APB1_CLK_RAT_N(reg);
511 	idx = CCU_APB1_CLK_SRC_SEL(reg);
512 
513 	freq = clock_get_frequency_idx(sc->sc_node, idx);
514 	return freq / (1 << n) / (m + 1);
515 }
516 
517 #define	CCU_GMAC_CLK_PIT		(1 << 2)
518 #define	CCU_GMAC_CLK_TCS		(3 << 0)
519 #define	CCU_GMAC_CLK_TCS_MII		0
520 #define	CCU_GMAC_CLK_TCS_EXT_125	1
521 #define	CCU_GMAC_CLK_TCS_INT_RGMII	2
522 
523 int
524 sxiccmu_gmac_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
525 {
526 	struct sxiccmu_clock *sc = cookie;
527 
528 	switch (freq) {
529 	case 25000000:		/* MMI, 25 MHz */
530 		SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS,
531 		    CCU_GMAC_CLK_TCS_MII);
532 		break;
533 	case 125000000:		/* RGMII, 125 MHz */
534 		SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS,
535 		    CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS_INT_RGMII);
536 		break;
537 	default:
538 		return -1;
539 	}
540 
541 	return 0;
542 }
543 
544 #define CCU_SDx_SCLK_GATING		(1U << 31)
545 #define CCU_SDx_CLK_SRC_SEL_OSC24M	(0 << 24)
546 #define CCU_SDx_CLK_SRC_SEL_PLL6	(1 << 24)
547 #define CCU_SDx_CLK_SRC_SEL_PLL5	(2 << 24)
548 #define CCU_SDx_CLK_SRC_SEL_MASK	(3 << 24)
549 #define CCU_SDx_CLK_DIV_RATIO_N_MASK	(3 << 16)
550 #define CCU_SDx_CLK_DIV_RATIO_N_SHIFT	16
551 #define CCU_SDx_CLK_DIV_RATIO_M_MASK	(7 << 0)
552 #define CCU_SDx_CLK_DIV_RATIO_M_SHIFT	0
553 
554 int
555 sxiccmu_mmc_do_set_frequency(struct sxiccmu_clock *sc, uint32_t freq,
556     uint32_t parent_freq)
557 {
558 	uint32_t reg, m, n;
559 	uint32_t clk_src;
560 
561 	switch (freq) {
562 	case 400000:
563 		n = 2, m = 15;
564 		clk_src = CCU_SDx_CLK_SRC_SEL_OSC24M;
565 		break;
566 	case 25000000:
567 	case 26000000:
568 	case 50000000:
569 	case 52000000:
570 		n = 0, m = 0;
571 		clk_src = CCU_SDx_CLK_SRC_SEL_PLL6;
572 		while ((parent_freq / (1 << n) / 16) > freq)
573 			n++;
574 		while ((parent_freq / (1 << n) / (m + 1)) > freq)
575 			m++;
576 		break;
577 	default:
578 		return -1;
579 	}
580 
581 	reg = SXIREAD4(sc, 0);
582 	reg &= ~CCU_SDx_CLK_SRC_SEL_MASK;
583 	reg |= clk_src;
584 	reg &= ~CCU_SDx_CLK_DIV_RATIO_N_MASK;
585 	reg |= n << CCU_SDx_CLK_DIV_RATIO_N_SHIFT;
586 	reg &= ~CCU_SDx_CLK_DIV_RATIO_M_MASK;
587 	reg |= m << CCU_SDx_CLK_DIV_RATIO_M_SHIFT;
588 	SXIWRITE4(sc, 0, reg);
589 
590 	return 0;
591 }
592 
593 int
594 sxiccmu_mmc_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
595 {
596 	struct sxiccmu_clock *sc = cookie;
597 	uint32_t parent_freq;
598 
599 	if (cells[0] != 0)
600 		return -1;
601 
602 	parent_freq = clock_get_frequency_idx(sc->sc_node, 1);
603 	return sxiccmu_mmc_do_set_frequency(sc, freq, parent_freq);
604 }
605 
606 void
607 sxiccmu_mmc_enable(void *cookie, uint32_t *cells, int on)
608 {
609 	struct sxiccmu_clock *sc = cookie;
610 
611 	if (cells[0] != 0)
612 		return;
613 
614 	if (on)
615 		SXISET4(sc, 0, CCU_SDx_SCLK_GATING);
616 	else
617 		SXICLR4(sc, 0, CCU_SDx_SCLK_GATING);
618 }
619 
620 void
621 sxiccmu_gate_enable(void *cookie, uint32_t *cells, int on)
622 {
623 	struct sxiccmu_clock *sc = cookie;
624 	int reg = cells[0] / 32;
625 	int bit = cells[0] % 32;
626 
627 	if (on) {
628 		clock_enable(sc->sc_node, NULL);
629 		SXISET4(sc, reg * 4, (1U << bit));
630 	} else {
631 		SXICLR4(sc, reg * 4, (1U << bit));
632 		clock_disable(sc->sc_node, NULL);
633 	}
634 }
635 
636 void
637 sxiccmu_reset(void *cookie, uint32_t *cells, int assert)
638 {
639 	struct sxiccmu_clock *sc = cookie;
640 	int reg = cells[0] / 32;
641 	int bit = cells[0] % 32;
642 
643 	if (assert)
644 		SXICLR4(sc, reg * 4, (1U << bit));
645 	else
646 		SXISET4(sc, reg * 4, (1U << bit));
647 }
648 
649 /*
650  * Newer device trees, such as those for the Allwinner H3/A64 have
651  * most of the clock nodes replaced with a single clock control unit
652  * node.
653  */
654 
655 uint32_t
656 sxiccmu_ccu_get_frequency(void *cookie, uint32_t *cells)
657 {
658 	struct sxiccmu_softc *sc = cookie;
659 	uint32_t idx = cells[0];
660 	uint32_t parent;
661 
662 	if (idx < sc->sc_ngates && sc->sc_gates[idx].parent) {
663 		parent = sc->sc_gates[idx].parent;
664 		return sxiccmu_ccu_get_frequency(sc, &parent);
665 	}
666 
667 	return sc->sc_get_frequency(sc, idx);
668 }
669 
670 /* Allwinner H3/A64 */
671 #define CCU_AHB1_APB1_CFG_REG		0x0054
672 #define CCU_AHB1_CLK_SRC_SEL		(3 << 12)
673 #define CCU_AHB1_CLK_SRC_SEL_LOSC	(0 << 12)
674 #define CCU_AHB1_CLK_SRC_SEL_OSC24M	(1 << 12)
675 #define CCU_AHB1_CLK_SRC_SEL_AXI	(2 << 12)
676 #define CCU_AHB1_CLK_SRC_SEL_PERIPH0	(3 << 12)
677 #define CCU_AHB1_PRE_DIV(x)		((((x) >> 6) & 3) + 1)
678 #define CCU_AHB1_CLK_DIV_RATIO(x)	(1 << (((x) >> 4) & 3))
679 #define CCU_AHB2_CFG_REG		0x005c
680 #define CCU_AHB2_CLK_CFG		(3 << 0)
681 
682 uint32_t
683 sxiccmu_a64_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
684 {
685 	uint32_t parent;
686 	uint32_t reg, div;
687 
688 	switch (idx) {
689 	case A64_CLK_LOSC:
690 		return clock_get_frequency(sc->sc_node, "losc");
691 	case A64_CLK_HOSC:
692 		return clock_get_frequency(sc->sc_node, "hosc");
693 	case A64_CLK_PLL_PERIPH0:
694 		/* Not hardcoded, but recommended. */
695 		return 600000000;
696 	case A64_CLK_PLL_PERIPH0_2X:
697 		return sxiccmu_a64_get_frequency(sc, A64_CLK_PLL_PERIPH0) * 2;
698 	case A64_CLK_APB2:
699 		/* XXX Controlled by a MUX. */
700 		return 24000000;
701 	case A64_CLK_AHB1:
702 		reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
703 		div = CCU_AHB1_CLK_DIV_RATIO(reg);
704 		switch (reg & CCU_AHB1_CLK_SRC_SEL) {
705 		case CCU_AHB1_CLK_SRC_SEL_LOSC:
706 			parent = A64_CLK_LOSC;
707 			break;
708 		case CCU_AHB1_CLK_SRC_SEL_OSC24M:
709 			parent = A64_CLK_HOSC;
710 			break;
711 		case CCU_AHB1_CLK_SRC_SEL_AXI:
712 			parent = A64_CLK_AXI;
713 			break;
714 		case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
715 			parent = A64_CLK_PLL_PERIPH0;
716 			div *= CCU_AHB1_PRE_DIV(reg);
717 			break;
718 		default:
719 			return 0;
720 		}
721 		return sxiccmu_ccu_get_frequency(sc, &parent) / div;
722 	case A64_CLK_AHB2:
723 		reg = SXIREAD4(sc, CCU_AHB2_CFG_REG);
724 		switch (reg & CCU_AHB2_CLK_CFG) {
725 		case 0:
726 			parent = A64_CLK_AHB1;
727 			div = 1;
728 			break;
729 		case 1:
730 			parent = A64_CLK_PLL_PERIPH0;
731 			div = 2;
732 			break;
733 		default:
734 			return 0;
735 		}
736 		return sxiccmu_ccu_get_frequency(sc, &parent) / div;
737 	}
738 
739 	printf("%s: 0x%08x\n", __func__, idx);
740 	return 0;
741 }
742 
743 uint32_t
744 sxiccmu_h3_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
745 {
746 	uint32_t parent;
747 	uint32_t reg, div;
748 
749 	switch (idx) {
750 	case H3_CLK_LOSC:
751 		return clock_get_frequency(sc->sc_node, "losc");
752 	case H3_CLK_HOSC:
753 		return clock_get_frequency(sc->sc_node, "hosc");
754 	case H3_CLK_PLL_PERIPH0:
755 		/* Not hardcoded, but recommended. */
756 		return 600000000;
757 	case H3_CLK_APB2:
758 		/* XXX Controlled by a MUX. */
759 		return 24000000;
760 	case H3_CLK_AHB1:
761 		reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
762 		div = CCU_AHB1_CLK_DIV_RATIO(reg);
763 		switch (reg & CCU_AHB1_CLK_SRC_SEL) {
764 		case CCU_AHB1_CLK_SRC_SEL_LOSC:
765 			parent = H3_CLK_LOSC;
766 			break;
767 		case CCU_AHB1_CLK_SRC_SEL_OSC24M:
768 			parent = H3_CLK_HOSC;
769 			break;
770 		case CCU_AHB1_CLK_SRC_SEL_AXI:
771 			parent = H3_CLK_AXI;
772 			break;
773 		case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
774 			parent = H3_CLK_PLL_PERIPH0;
775 			div *= CCU_AHB1_PRE_DIV(reg);
776 			break;
777 		default:
778 			return 0;
779 		}
780 		return sxiccmu_ccu_get_frequency(sc, &parent) / div;
781 	case H3_CLK_AHB2:
782 		reg = SXIREAD4(sc, CCU_AHB2_CFG_REG);
783 		switch (reg & CCU_AHB2_CLK_CFG) {
784 		case 0:
785 			parent = H3_CLK_AHB1;
786 			div = 1;
787 			break;
788 		case 1:
789 			parent = H3_CLK_PLL_PERIPH0;
790 			div = 2;
791 			break;
792 		default:
793 			return 0;
794 		}
795 		return sxiccmu_ccu_get_frequency(sc, &parent) / div;
796 	}
797 
798 	printf("%s: 0x%08x\n", __func__, idx);
799 	return 0;
800 }
801 
802 int
803 sxiccmu_ccu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
804 {
805 	struct sxiccmu_softc *sc = cookie;
806 	uint32_t idx = cells[0];
807 
808 	return sc->sc_set_frequency(sc, idx, freq);
809 }
810 
811 int
812 sxiccmu_a64_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
813 {
814 	struct sxiccmu_clock clock;
815 	uint32_t parent, parent_freq;
816 
817 	switch (idx) {
818 	case A64_CLK_MMC0:
819 	case A64_CLK_MMC1:
820 	case A64_CLK_MMC2:
821 		clock.sc_iot = sc->sc_iot;
822 		bus_space_subregion(sc->sc_iot, sc->sc_ioh,
823 		    sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
824 		parent = A64_CLK_PLL_PERIPH0_2X;
825 		parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
826 		return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
827 	}
828 
829 	printf("%s: 0x%08x\n", __func__, idx);
830 	return -1;
831 }
832 
833 int
834 sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
835 {
836 	struct sxiccmu_clock clock;
837 	uint32_t parent, parent_freq;
838 
839 	switch (idx) {
840 	case H3_CLK_MMC0:
841 	case H3_CLK_MMC1:
842 	case H3_CLK_MMC2:
843 		clock.sc_iot = sc->sc_iot;
844 		bus_space_subregion(sc->sc_iot, sc->sc_ioh,
845 		    sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
846 		parent = H3_CLK_PLL_PERIPH0;
847 		parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
848 		return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
849 	}
850 
851 	printf("%s: 0x%08x\n", __func__, idx);
852 	return -1;
853 }
854 
855 void
856 sxiccmu_ccu_enable(void *cookie, uint32_t *cells, int on)
857 {
858 	struct sxiccmu_softc *sc = cookie;
859 	uint32_t idx = cells[0];
860 	int reg, bit;
861 
862 	if (idx >= sc->sc_ngates || sc->sc_gates[idx].reg == 0) {
863 		printf("%s: 0x%08x\n", __func__, cells[0]);
864 		return;
865 	}
866 
867 	reg = sc->sc_gates[idx].reg;
868 	bit = sc->sc_gates[idx].bit;
869 
870 	if (on)
871 		SXISET4(sc, reg, (1U << bit));
872 	else
873 		SXICLR4(sc, reg, (1U << bit));
874 }
875 
876 void
877 sxiccmu_ccu_reset(void *cookie, uint32_t *cells, int assert)
878 {
879 	struct sxiccmu_softc *sc = cookie;
880 	uint32_t idx = cells[0];
881 	int reg, bit;
882 
883 	if (idx >= sc->sc_nresets || sc->sc_resets[idx].reg == 0) {
884 		printf("%s: 0x%08x\n", __func__, cells[0]);
885 		return;
886 	}
887 
888 	reg = sc->sc_resets[idx].reg;
889 	bit = sc->sc_resets[idx].bit;
890 
891 	if (assert)
892 		SXICLR4(sc, reg, (1U << bit));
893 	else
894 		SXISET4(sc, reg, (1U << bit));
895 }
896