xref: /openbsd-src/sys/dev/fdt/sxiccmu.c (revision 03adc85b7600a1f8f04886b8321c1c1c0c4933d4)
1 /*	$OpenBSD: sxiccmu.c,v 1.1 2017/01/21 08:26:49 patrick 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/types.h>
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/kernel.h>
24 #include <sys/malloc.h>
25 #include <sys/time.h>
26 #include <sys/device.h>
27 
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30 #include <machine/intr.h>
31 
32 #include <dev/fdt/sunxireg.h>
33 
34 #include <dev/ofw/openfirm.h>
35 #include <dev/ofw/ofw_clock.h>
36 #include <dev/ofw/fdt.h>
37 
38 #ifdef DEBUG_CCMU
39 #define DPRINTF(x)	do { printf x; } while (0)
40 #else
41 #define DPRINTF(x)
42 #endif
43 
44 struct sxiccmu_ccu_bit {
45 	uint16_t reg;
46 	uint8_t bit;
47 	uint8_t parent;
48 };
49 
50 #include "sxiccmu_clocks.h"
51 
52 struct sxiccmu_softc {
53 	struct device		sc_dev;
54 	bus_space_tag_t		sc_iot;
55 	bus_space_handle_t	sc_ioh;
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,sun50i-a64") ||
106 		    OF_is_compatible(node, "allwinner,sun7i-a20") ||
107 		    OF_is_compatible(node, "allwinner,sun8i-h3") ||
108 		    OF_is_compatible(node, "allwinner,sun9i-a80"));
109 	}
110 
111 	return (OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ccu") ||
112 	    OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ccu"));
113 }
114 
115 void
116 sxiccmu_attach(struct device *parent, struct device *self, void *aux)
117 {
118 	struct sxiccmu_softc *sc = (struct sxiccmu_softc *)self;
119 	struct fdt_attach_args *faa = aux;
120 	int node = faa->fa_node;
121 
122 	sc->sc_iot = faa->fa_iot;
123 	if (faa->fa_nreg > 0 && bus_space_map(sc->sc_iot,
124 	    faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh))
125 		panic("%s: bus_space_map failed!", __func__);
126 
127 	printf("\n");
128 
129 	if (OF_is_compatible(node, "allwinner,sun50i-a64-ccu")) {
130 		KASSERT(faa->fa_nreg > 0);
131 		sc->sc_gates = sun50i_a64_gates;
132 		sc->sc_ngates = nitems(sun50i_a64_gates);
133 		sc->sc_resets = sun50i_a64_resets;
134 		sc->sc_nresets = nitems(sun50i_a64_resets);
135 		sc->sc_get_frequency = sxiccmu_a64_get_frequency;
136 		sc->sc_set_frequency = sxiccmu_a64_set_frequency;
137 	} else if (OF_is_compatible(node, "allwinner,sun8i-h3-ccu")) {
138 		KASSERT(faa->fa_nreg > 0);
139 		sc->sc_gates = sun8i_h3_gates;
140 		sc->sc_ngates = nitems(sun8i_h3_gates);
141 		sc->sc_resets = sun8i_h3_resets;
142 		sc->sc_nresets = nitems(sun8i_h3_resets);
143 		sc->sc_get_frequency = sxiccmu_h3_get_frequency;
144 		sc->sc_set_frequency = sxiccmu_h3_set_frequency;
145 	} else {
146 		for (node = OF_child(node); node; node = OF_peer(node))
147 			sxiccmu_attach_clock(sc, node);
148 	}
149 
150 	if (sc->sc_gates) {
151 		sc->sc_cd.cd_node = node;
152 		sc->sc_cd.cd_cookie = sc;
153 		sc->sc_cd.cd_get_frequency = sxiccmu_ccu_get_frequency;
154 		sc->sc_cd.cd_set_frequency = sxiccmu_ccu_set_frequency;
155 		sc->sc_cd.cd_enable = sxiccmu_ccu_enable;
156 		clock_register(&sc->sc_cd);
157 	}
158 
159 	if (sc->sc_resets) {
160 		sc->sc_rd.rd_node = node;
161 		sc->sc_rd.rd_cookie = sc;
162 		sc->sc_rd.rd_reset = sxiccmu_ccu_reset;
163 		reset_register(&sc->sc_rd);
164 	}
165 }
166 
167 /*
168  * Device trees for the Allwinner SoCs have basically a clock node per
169  * register of the clock control unit.  Attaching a separate driver to
170  * each of them would be crazy, so we handle them here.
171  */
172 
173 struct sxiccmu_clock {
174 	int sc_node;
175 	bus_space_tag_t sc_iot;
176 	bus_space_handle_t sc_ioh;
177 
178 	struct clock_device sc_cd;
179 	struct reset_device sc_rd;
180 };
181 
182 struct sxiccmu_device {
183 	const char *compat;
184 	uint32_t (*get_frequency)(void *, uint32_t *);
185 	int	(*set_frequency)(void *, uint32_t *, uint32_t);
186 	void	(*enable)(void *, uint32_t *, int);
187 	void	(*reset)(void *, uint32_t *, int);
188 };
189 
190 uint32_t sxiccmu_gen_get_frequency(void *, uint32_t *);
191 uint32_t sxiccmu_osc_get_frequency(void *, uint32_t *);
192 uint32_t sxiccmu_pll6_get_frequency(void *, uint32_t *);
193 void	sxiccmu_pll6_enable(void *, uint32_t *, int);
194 uint32_t sxiccmu_apb1_get_frequency(void *, uint32_t *);
195 int	sxiccmu_gmac_set_frequency(void *, uint32_t *, uint32_t);
196 int	sxiccmu_mmc_set_frequency(void *, uint32_t *, uint32_t);
197 void	sxiccmu_mmc_enable(void *, uint32_t *, int);
198 void	sxiccmu_gate_enable(void *, uint32_t *, int);
199 void	sxiccmu_reset(void *, uint32_t *, int);
200 
201 struct sxiccmu_device sxiccmu_devices[] = {
202 	{
203 		.compat = "allwinner,sun4i-a10-osc-clk",
204 		.get_frequency = sxiccmu_osc_get_frequency,
205 	},
206 	{
207 		.compat = "allwinner,sun4i-a10-pll6-clk",
208 		.get_frequency = sxiccmu_pll6_get_frequency,
209 		.enable = sxiccmu_pll6_enable
210 	},
211 	{
212 		.compat = "allwinner,sun4i-a10-apb1-clk",
213 		.get_frequency = sxiccmu_apb1_get_frequency,
214 	},
215 	{
216 		.compat = "allwinner,sun4i-a10-ahb-gates-clk",
217 		.get_frequency = sxiccmu_gen_get_frequency,
218 		.enable = sxiccmu_gate_enable
219 	},
220 	{
221 		.compat = "allwinner,sun4i-a10-apb0-gates-clk",
222 		.get_frequency = sxiccmu_gen_get_frequency,
223 		.enable = sxiccmu_gate_enable
224 	},
225 	{
226 		.compat = "allwinner,sun4i-a10-apb1-gates-clk",
227 		.get_frequency = sxiccmu_gen_get_frequency,
228 		.enable = sxiccmu_gate_enable
229 	},
230 	{
231 		.compat = "allwinner,sun4i-a10-mmc-clk",
232 		.set_frequency = sxiccmu_mmc_set_frequency,
233 		.enable = sxiccmu_mmc_enable
234 	},
235 	{
236 		.compat = "allwinner,sun4i-a10-usb-clk",
237 		.get_frequency = sxiccmu_gen_get_frequency,
238 		.enable = sxiccmu_gate_enable,
239 		.reset = sxiccmu_reset
240 	},
241 	{
242 		.compat = "allwinner,sun5i-a10s-ahb-gates-clk",
243 		.get_frequency = sxiccmu_gen_get_frequency,
244 		.enable = sxiccmu_gate_enable
245 	},
246 	{
247 		.compat = "allwinner,sun5i-a10s-apb0-gates-clk",
248 		.get_frequency = sxiccmu_gen_get_frequency,
249 		.enable = sxiccmu_gate_enable
250 	},
251 	{
252 		.compat = "allwinner,sun5i-a10s-apb1-gates-clk",
253 		.get_frequency = sxiccmu_gen_get_frequency,
254 		.enable = sxiccmu_gate_enable
255 	},
256 	{
257 		.compat = "allwinner,sun5i-a13-ahb-gates-clk",
258 		.get_frequency = sxiccmu_gen_get_frequency,
259 		.enable = sxiccmu_gate_enable
260 	},
261 	{
262 		.compat = "allwinner,sun5i-a13-apb0-gates-clk",
263 		.get_frequency = sxiccmu_gen_get_frequency,
264 		.enable = sxiccmu_gate_enable
265 	},
266 	{
267 		.compat = "allwinner,sun5i-a13-apb1-gates-clk",
268 		.get_frequency = sxiccmu_gen_get_frequency,
269 		.enable = sxiccmu_gate_enable
270 	},
271 	{
272 		.compat = "allwinner,sun5i-a13-usb-clk",
273 		.get_frequency = sxiccmu_gen_get_frequency,
274 		.enable = sxiccmu_gate_enable,
275 		.reset = sxiccmu_reset
276 	},
277 	{
278 		.compat = "allwinner,sun6i-a31-ahb1-reset",
279 		.reset = sxiccmu_reset
280 	},
281 	{
282 		.compat = "allwinner,sun6i-a31-clock-reset",
283 		.reset = sxiccmu_reset
284 	},
285 	{
286 		.compat = "allwinner,sun7i-a20-ahb-gates-clk",
287 		.get_frequency = sxiccmu_gen_get_frequency,
288 		.enable = sxiccmu_gate_enable
289 	},
290 	{
291 		.compat = "allwinner,sun7i-a20-apb0-gates-clk",
292 		.get_frequency = sxiccmu_gen_get_frequency,
293 		.enable = sxiccmu_gate_enable
294 	},
295 	{
296 		.compat = "allwinner,sun7i-a20-apb1-gates-clk",
297 		.get_frequency = sxiccmu_gen_get_frequency,
298 		.enable = sxiccmu_gate_enable
299 	},
300 	{
301 		.compat = "allwinner,sun7i-a20-gmac-clk",
302 		.set_frequency = sxiccmu_gmac_set_frequency
303 	},
304 	{
305 		.compat = "allwinner,sun8i-h3-apb0-gates-clk",
306 		.get_frequency = sxiccmu_gen_get_frequency,
307 		.enable = sxiccmu_gate_enable
308 	},
309 	{
310 		.compat = "allwinner,sun9i-a80-apb1-clk",
311 		.get_frequency = sxiccmu_apb1_get_frequency,
312 	},
313 	{
314 		.compat = "allwinner,sun9i-a80-ahb0-gates-clk",
315 		.get_frequency = sxiccmu_gen_get_frequency,
316 		.enable = sxiccmu_gate_enable
317 	},
318 	{
319 		.compat = "allwinner,sun9i-a80-ahb1-gates-clk",
320 		.get_frequency = sxiccmu_gen_get_frequency,
321 		.enable = sxiccmu_gate_enable
322 	},
323 	{
324 		.compat = "allwinner,sun9i-a80-ahb2-gates-clk",
325 		.get_frequency = sxiccmu_gen_get_frequency,
326 		.enable = sxiccmu_gate_enable
327 	},
328 	{
329 		.compat = "allwinner,sun9i-a80-apb0-gates-clk",
330 		.get_frequency = sxiccmu_gen_get_frequency,
331 		.enable = sxiccmu_gate_enable
332 	},
333 	{
334 		.compat = "allwinner,sun9i-a80-apb1-gates-clk",
335 		.get_frequency = sxiccmu_gen_get_frequency,
336 		.enable = sxiccmu_gate_enable
337 	},
338 	{
339 		.compat = "allwinner,sun9i-a80-apbs-gates-clk",
340 		.get_frequency = sxiccmu_gen_get_frequency,
341 		.enable = sxiccmu_gate_enable
342 	},
343 	{
344 		.compat = "allwinner,sun9i-a80-mmc-clk",
345 		.set_frequency = sxiccmu_mmc_set_frequency,
346 		.enable = sxiccmu_mmc_enable
347 	},
348 	{
349 		.compat = "allwinner,sun9i-a80-usb-mod-clk",
350 		.get_frequency = sxiccmu_gen_get_frequency,
351 		.enable = sxiccmu_gate_enable,
352 		.reset = sxiccmu_reset
353 	},
354 	{
355 		.compat = "allwinner,sun9i-a80-usb-phy-clk",
356 		.get_frequency = sxiccmu_gen_get_frequency,
357 		.enable = sxiccmu_gate_enable,
358 		.reset = sxiccmu_reset
359 	},
360 };
361 
362 void
363 sxiccmu_attach_clock(struct sxiccmu_softc *sc, int node)
364 {
365 	struct sxiccmu_clock *clock;
366 	uint32_t reg[2];
367 	int i;
368 
369 	for (i = 0; i < nitems(sxiccmu_devices); i++)
370 		if (OF_is_compatible(node, sxiccmu_devices[i].compat))
371 			break;
372 	if (i == nitems(sxiccmu_devices))
373 		return;
374 
375 	clock = malloc(sizeof(*clock), M_DEVBUF, M_WAITOK);
376 	clock->sc_node = node;
377 
378 	if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) == sizeof(reg)) {
379 		clock->sc_iot = sc->sc_iot;
380 		if (bus_space_map(clock->sc_iot, reg[0], reg[1], 0,
381 		    &clock->sc_ioh)) {
382 			printf("%s: can't map registers", sc->sc_dev.dv_xname);
383 			free(clock, M_DEVBUF, sizeof(*clock));
384 			return;
385 		}
386 	}
387 
388 	clock->sc_cd.cd_node = node;
389 	clock->sc_cd.cd_cookie = clock;
390 	clock->sc_cd.cd_get_frequency = sxiccmu_devices[i].get_frequency;
391 	clock->sc_cd.cd_set_frequency = sxiccmu_devices[i].set_frequency;
392 	clock->sc_cd.cd_enable = sxiccmu_devices[i].enable;
393 	clock_register(&clock->sc_cd);
394 
395 	if (sxiccmu_devices[i].reset) {
396 		clock->sc_rd.rd_node = node;
397 		clock->sc_rd.rd_cookie = clock;
398 		clock->sc_rd.rd_reset = sxiccmu_devices[i].reset;
399 		reset_register(&clock->sc_rd);
400 	}
401 }
402 
403 /*
404  * A "generic" function that simply gets the clock frequency from the
405  * parent clock.  Useful for clock gating devices that don't scale
406  * their clocks.
407  */
408 uint32_t
409 sxiccmu_gen_get_frequency(void *cookie, uint32_t *cells)
410 {
411 	struct sxiccmu_clock *sc = cookie;
412 
413 	return clock_get_frequency(sc->sc_node, NULL);
414 }
415 
416 uint32_t
417 sxiccmu_osc_get_frequency(void *cookie, uint32_t *cells)
418 {
419 	struct sxiccmu_clock *sc = cookie;
420 
421 	return OF_getpropint(sc->sc_node, "clock-frequency", 24000000);
422 }
423 
424 #define CCU_PLL6_ENABLE			(1U << 31)
425 #define CCU_PLL6_BYPASS_EN		(1U << 30)
426 #define CCU_PLL6_SATA_CLK_EN		(1U << 14)
427 #define CCU_PLL6_FACTOR_N(x)		(((x) >> 8) & 0x1f)
428 #define CCU_PLL6_FACTOR_N_MASK		(0x1f << 8)
429 #define CCU_PLL6_FACTOR_N_SHIFT		8
430 #define CCU_PLL6_FACTOR_K(x)		(((x) >> 4) & 0x3)
431 #define CCU_PLL6_FACTOR_K_MASK		(0x3 << 4)
432 #define CCU_PLL6_FACTOR_K_SHIFT		4
433 #define CCU_PLL6_FACTOR_M(x)		(((x) >> 0) & 0x3)
434 #define CCU_PLL6_FACTOR_M_MASK		(0x3 << 0)
435 #define CCU_PLL6_FACTOR_M_SHIFT		0
436 
437 uint32_t
438 sxiccmu_pll6_get_frequency(void *cookie, uint32_t *cells)
439 {
440 	struct sxiccmu_clock *sc = cookie;
441 	uint32_t reg, k, m, n, freq;
442 	uint32_t idx = cells[0];
443 
444 	/* XXX Assume bypass is disabled. */
445 	reg = SXIREAD4(sc, 0);
446 	k = CCU_PLL6_FACTOR_K(reg) + 1;
447 	m = CCU_PLL6_FACTOR_M(reg) + 1;
448 	n = CCU_PLL6_FACTOR_N(reg);
449 
450 	freq = clock_get_frequency_idx(sc->sc_node, 0);
451 	switch (idx) {
452 	case 0:
453 		return (freq * n * k) / m / 6;		/* pll6_sata */
454 	case 1:
455 		return (freq * n * k) / 2;		/* pll6_other */
456 	case 2:
457 		return (freq * n * k);			/* pll6 */
458 	case 3:
459 		return (freq * n * k) / 4;		/* pll6_div_4 */
460 	}
461 
462 	return 0;
463 }
464 
465 void
466 sxiccmu_pll6_enable(void *cookie, uint32_t *cells, int on)
467 {
468 	struct sxiccmu_clock *sc = cookie;
469 	uint32_t idx = cells[0];
470 	uint32_t reg;
471 
472 	/*
473 	 * Since this clock has several outputs, we never turn it off.
474 	 */
475 
476 	reg = SXIREAD4(sc, 0);
477 	switch (idx) {
478 	case 0:			/* pll6_sata */
479 		if (on)
480 			reg |= CCU_PLL6_SATA_CLK_EN;
481 		else
482 			reg &= ~CCU_PLL6_SATA_CLK_EN;
483 		/* FALLTHROUGH */
484 	case 1:			/* pll6_other */
485 	case 2:			/* pll6 */
486 	case 3:			/* pll6_div_4 */
487 		if (on)
488 			reg |= CCU_PLL6_ENABLE;
489 	}
490 	SXIWRITE4(sc, 0, reg);
491 }
492 
493 #define CCU_APB1_CLK_RAT_N(x)		(((x) >> 16) & 0x3)
494 #define CCU_APB1_CLK_RAT_M(x)		(((x) >> 0) & 0x1f)
495 #define CCU_APB1_CLK_SRC_SEL(x)		(((x) >> 24) & 0x3)
496 
497 uint32_t
498 sxiccmu_apb1_get_frequency(void *cookie, uint32_t *cells)
499 {
500 	struct sxiccmu_clock *sc = cookie;
501 	uint32_t reg, m, n, freq;
502 	int idx;
503 
504 	reg = SXIREAD4(sc, 0);
505 	m = CCU_APB1_CLK_RAT_M(reg);
506 	n = CCU_APB1_CLK_RAT_N(reg);
507 	idx = CCU_APB1_CLK_SRC_SEL(reg);
508 
509 	freq = clock_get_frequency_idx(sc->sc_node, idx);
510 	return freq / (1 << n) / (m + 1);
511 }
512 
513 #define	CCU_GMAC_CLK_PIT		(1 << 2)
514 #define	CCU_GMAC_CLK_TCS		(3 << 0)
515 #define	CCU_GMAC_CLK_TCS_MII		0
516 #define	CCU_GMAC_CLK_TCS_EXT_125	1
517 #define	CCU_GMAC_CLK_TCS_INT_RGMII	2
518 
519 int
520 sxiccmu_gmac_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
521 {
522 	struct sxiccmu_clock *sc = cookie;
523 
524 	switch (freq) {
525 	case 25000000:		/* MMI, 25 MHz */
526 		SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS,
527 		    CCU_GMAC_CLK_TCS_MII);
528 		break;
529 	case 125000000:		/* RGMII, 125 MHz */
530 		SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS,
531 		    CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS_INT_RGMII);
532 		break;
533 	default:
534 		return -1;
535 	}
536 
537 	return 0;
538 }
539 
540 #define CCU_SDx_SCLK_GATING		(1U << 31)
541 #define CCU_SDx_CLK_SRC_SEL_OSC24M	(0 << 24)
542 #define CCU_SDx_CLK_SRC_SEL_PLL6	(1 << 24)
543 #define CCU_SDx_CLK_SRC_SEL_PLL5	(2 << 24)
544 #define CCU_SDx_CLK_SRC_SEL_MASK	(3 << 24)
545 #define CCU_SDx_CLK_DIV_RATIO_N_MASK	(3 << 16)
546 #define CCU_SDx_CLK_DIV_RATIO_N_SHIFT	16
547 #define CCU_SDx_CLK_DIV_RATIO_M_MASK	(7 << 0)
548 #define CCU_SDx_CLK_DIV_RATIO_M_SHIFT	0
549 
550 int
551 sxiccmu_mmc_do_set_frequency(struct sxiccmu_clock *sc, uint32_t freq,
552     uint32_t parent_freq)
553 {
554 	uint32_t reg, m, n;
555 	uint32_t clk_src;
556 
557 	switch (freq) {
558 	case 400000:
559 		n = 2, m = 15;
560 		clk_src = CCU_SDx_CLK_SRC_SEL_OSC24M;
561 		break;
562 	case 25000000:
563 	case 26000000:
564 	case 50000000:
565 	case 52000000:
566 		n = 0, m = 0;
567 		clk_src = CCU_SDx_CLK_SRC_SEL_PLL6;
568 		while ((parent_freq / (1 << n) / 16) > freq)
569 			n++;
570 		while ((parent_freq / (1 << n) / (m + 1)) > freq)
571 			m++;
572 		break;
573 	default:
574 		return -1;
575 	}
576 
577 	reg = SXIREAD4(sc, 0);
578 	reg &= ~CCU_SDx_CLK_SRC_SEL_MASK;
579 	reg |= clk_src;
580 	reg &= ~CCU_SDx_CLK_DIV_RATIO_N_MASK;
581 	reg |= n << CCU_SDx_CLK_DIV_RATIO_N_SHIFT;
582 	reg &= ~CCU_SDx_CLK_DIV_RATIO_M_MASK;
583 	reg |= m << CCU_SDx_CLK_DIV_RATIO_M_SHIFT;
584 	SXIWRITE4(sc, 0, reg);
585 
586 	return 0;
587 }
588 
589 int
590 sxiccmu_mmc_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
591 {
592 	struct sxiccmu_clock *sc = cookie;
593 	uint32_t parent_freq;
594 
595 	if (cells[0] != 0)
596 		return -1;
597 
598 	parent_freq = clock_get_frequency_idx(sc->sc_node, 1);
599 	return sxiccmu_mmc_do_set_frequency(sc, freq, parent_freq);
600 }
601 
602 void
603 sxiccmu_mmc_enable(void *cookie, uint32_t *cells, int on)
604 {
605 	struct sxiccmu_clock *sc = cookie;
606 
607 	if (cells[0] != 0)
608 		return;
609 
610 	if (on)
611 		SXISET4(sc, 0, CCU_SDx_SCLK_GATING);
612 	else
613 		SXICLR4(sc, 0, CCU_SDx_SCLK_GATING);
614 }
615 
616 void
617 sxiccmu_gate_enable(void *cookie, uint32_t *cells, int on)
618 {
619 	struct sxiccmu_clock *sc = cookie;
620 	int reg = cells[0] / 32;
621 	int bit = cells[0] % 32;
622 
623 	if (on) {
624 		clock_enable(sc->sc_node, NULL);
625 		SXISET4(sc, reg * 4, (1U << bit));
626 	} else {
627 		SXICLR4(sc, reg * 4, (1U << bit));
628 		clock_disable(sc->sc_node, NULL);
629 	}
630 }
631 
632 void
633 sxiccmu_reset(void *cookie, uint32_t *cells, int assert)
634 {
635 	struct sxiccmu_clock *sc = cookie;
636 	int reg = cells[0] / 32;
637 	int bit = cells[0] % 32;
638 
639 	if (assert)
640 		SXICLR4(sc, reg * 4, (1U << bit));
641 	else
642 		SXISET4(sc, reg * 4, (1U << bit));
643 }
644 
645 /*
646  * Device trees for the Allwinner A80 have most of the clock nodes
647  * replaced with a single clock control unit node.
648  */
649 
650 uint32_t
651 sxiccmu_ccu_get_frequency(void *cookie, uint32_t *cells)
652 {
653 	struct sxiccmu_softc *sc = cookie;
654 	uint32_t idx = cells[0];
655 	uint32_t parent;
656 
657 	if (idx < sc->sc_ngates && sc->sc_gates[idx].parent) {
658 		parent = sc->sc_gates[idx].parent;
659 		return sxiccmu_ccu_get_frequency(sc, &parent);
660 	}
661 
662 	return sc->sc_get_frequency(sc, idx);
663 }
664 
665 uint32_t
666 sxiccmu_a64_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
667 {
668 	switch (idx) {
669 	case A64_CLK_PLL_PERIPH0:
670 		/* XXX default value. */
671 		return 600000000;
672 	case A64_CLK_PLL_PERIPH0_2X:
673 		return sxiccmu_a64_get_frequency(sc, A64_CLK_PLL_PERIPH0) * 2;
674 	case A64_CLK_APB2:
675 		/* XXX Controlled by a MUX. */
676 		return 24000000;
677 	}
678 
679 	printf("%s: 0x%08x\n", __func__, idx);
680 	return 0;
681 }
682 
683 uint32_t
684 sxiccmu_h3_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
685 {
686 	switch (idx) {
687 	case H3_CLK_PLL_PERIPH0:
688 		/* XXX default value. */
689 		return 600000000;
690 	case H3_CLK_APB2:
691 		/* XXX Controlled by a MUX. */
692 		return 24000000;
693 	}
694 
695 	printf("%s: 0x%08x\n", __func__, idx);
696 	return 0;
697 }
698 
699 int
700 sxiccmu_ccu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
701 {
702 	struct sxiccmu_softc *sc = cookie;
703 	uint32_t idx = cells[0];
704 
705 	return sc->sc_set_frequency(sc, idx, freq);
706 }
707 
708 int
709 sxiccmu_a64_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
710 {
711 	struct sxiccmu_clock clock;
712 	uint32_t parent, parent_freq;
713 
714 	switch (idx) {
715 	case A64_CLK_MMC0:
716 	case A64_CLK_MMC1:
717 	case A64_CLK_MMC2:
718 		clock.sc_iot = sc->sc_iot;
719 		bus_space_subregion(sc->sc_iot, sc->sc_ioh,
720 		    sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
721 		parent = A64_CLK_PLL_PERIPH0_2X;
722 		parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
723 		return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
724 	}
725 
726 	printf("%s: 0x%08x\n", __func__, idx);
727 	return -1;
728 }
729 
730 int
731 sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
732 {
733 	struct sxiccmu_clock clock;
734 	uint32_t parent, parent_freq;
735 
736 	switch (idx) {
737 	case H3_CLK_MMC0:
738 	case H3_CLK_MMC1:
739 	case H3_CLK_MMC2:
740 		clock.sc_iot = sc->sc_iot;
741 		bus_space_subregion(sc->sc_iot, sc->sc_ioh,
742 		    sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
743 		parent = H3_CLK_PLL_PERIPH0;
744 		parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
745 		return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
746 	}
747 
748 	printf("%s: 0x%08x\n", __func__, idx);
749 	return -1;
750 }
751 
752 void
753 sxiccmu_ccu_enable(void *cookie, uint32_t *cells, int on)
754 {
755 	struct sxiccmu_softc *sc = cookie;
756 	uint32_t idx = cells[0];
757 	int reg, bit;
758 
759 	if (idx >= sc->sc_ngates || sc->sc_gates[idx].reg == 0) {
760 		printf("%s: 0x%08x\n", __func__, cells[0]);
761 		return;
762 	}
763 
764 	reg = sc->sc_gates[idx].reg;
765 	bit = sc->sc_gates[idx].bit;
766 
767 	if (on)
768 		SXISET4(sc, reg, (1U << bit));
769 	else
770 		SXICLR4(sc, reg, (1U << bit));
771 }
772 
773 void
774 sxiccmu_ccu_reset(void *cookie, uint32_t *cells, int assert)
775 {
776 	struct sxiccmu_softc *sc = cookie;
777 	uint32_t idx = cells[0];
778 	int reg, bit;
779 
780 	if (idx >= sc->sc_nresets || sc->sc_resets[idx].reg == 0) {
781 		printf("%s: 0x%08x\n", __func__, cells[0]);
782 		return;
783 	}
784 
785 	reg = sc->sc_resets[idx].reg;
786 	bit = sc->sc_resets[idx].bit;
787 
788 	if (assert)
789 		SXICLR4(sc, reg, (1U << bit));
790 	else
791 		SXISET4(sc, reg, (1U << bit));
792 }
793