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