1*95c4b0b5Skevlo /* $OpenBSD: sxiccmu.c,v 1.38 2024/03/07 01:04:16 kevlo Exp $ */
20ea1c705Spatrick /*
30ea1c705Spatrick * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
40ea1c705Spatrick * Copyright (c) 2013 Artturi Alm
5d526c0edSkettenis * Copyright (c) 2016,2017 Mark Kettenis <kettenis@openbsd.org>
60ea1c705Spatrick *
70ea1c705Spatrick * Permission to use, copy, modify, and distribute this software for any
80ea1c705Spatrick * purpose with or without fee is hereby granted, provided that the above
90ea1c705Spatrick * copyright notice and this permission notice appear in all copies.
100ea1c705Spatrick *
110ea1c705Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
120ea1c705Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
130ea1c705Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
140ea1c705Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
150ea1c705Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
160ea1c705Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
170ea1c705Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180ea1c705Spatrick */
190ea1c705Spatrick
200ea1c705Spatrick #include <sys/param.h>
210ea1c705Spatrick #include <sys/systm.h>
220ea1c705Spatrick #include <sys/kernel.h>
230ea1c705Spatrick #include <sys/malloc.h>
240ea1c705Spatrick #include <sys/time.h>
250ea1c705Spatrick #include <sys/device.h>
260ea1c705Spatrick
270ea1c705Spatrick #include <machine/bus.h>
280ea1c705Spatrick #include <machine/fdt.h>
290ea1c705Spatrick #include <machine/intr.h>
300ea1c705Spatrick
310ea1c705Spatrick #include <dev/fdt/sunxireg.h>
320ea1c705Spatrick
330ea1c705Spatrick #include <dev/ofw/openfirm.h>
340ea1c705Spatrick #include <dev/ofw/ofw_clock.h>
3529e54e85Skettenis #include <dev/ofw/ofw_misc.h>
360ea1c705Spatrick #include <dev/ofw/fdt.h>
370ea1c705Spatrick
3829e54e85Skettenis /* R40 */
3929e54e85Skettenis #define R40_GMAC_CLK_REG 0x0164
4029e54e85Skettenis
410ea1c705Spatrick #ifdef DEBUG_CCMU
420ea1c705Spatrick #define DPRINTF(x) do { printf x; } while (0)
430ea1c705Spatrick #else
440ea1c705Spatrick #define DPRINTF(x)
450ea1c705Spatrick #endif
460ea1c705Spatrick
470ea1c705Spatrick struct sxiccmu_ccu_bit {
480ea1c705Spatrick uint16_t reg;
490ea1c705Spatrick uint8_t bit;
500ea1c705Spatrick uint8_t parent;
510ea1c705Spatrick };
520ea1c705Spatrick
530ea1c705Spatrick #include "sxiccmu_clocks.h"
540ea1c705Spatrick
550ea1c705Spatrick struct sxiccmu_softc {
560ea1c705Spatrick struct device sc_dev;
570ea1c705Spatrick bus_space_tag_t sc_iot;
580ea1c705Spatrick bus_space_handle_t sc_ioh;
59b5fbf381Skettenis int sc_node;
600ea1c705Spatrick
6137c734d3Snaddy const struct sxiccmu_ccu_bit *sc_gates;
620ea1c705Spatrick int sc_ngates;
630ea1c705Spatrick struct clock_device sc_cd;
640ea1c705Spatrick
6537c734d3Snaddy const struct sxiccmu_ccu_bit *sc_resets;
660ea1c705Spatrick int sc_nresets;
670ea1c705Spatrick struct reset_device sc_rd;
680ea1c705Spatrick
690ea1c705Spatrick uint32_t (*sc_get_frequency)(struct sxiccmu_softc *,
700ea1c705Spatrick uint32_t);
710ea1c705Spatrick int (*sc_set_frequency)(struct sxiccmu_softc *,
720ea1c705Spatrick uint32_t, uint32_t);
730ea1c705Spatrick };
740ea1c705Spatrick
750ea1c705Spatrick int sxiccmu_match(struct device *, void *, void *);
760ea1c705Spatrick void sxiccmu_attach(struct device *, struct device *, void *);
770ea1c705Spatrick
789fdf0c62Smpi const struct cfattach sxiccmu_ca = {
790ea1c705Spatrick sizeof (struct sxiccmu_softc), sxiccmu_match, sxiccmu_attach
800ea1c705Spatrick };
810ea1c705Spatrick
820ea1c705Spatrick struct cfdriver sxiccmu_cd = {
830ea1c705Spatrick NULL, "sxiccmu", DV_DULL
840ea1c705Spatrick };
850ea1c705Spatrick
864e7b86a5Skettenis void sxiccmu_attach_clock(struct sxiccmu_softc *, int, int);
870ea1c705Spatrick
880ea1c705Spatrick uint32_t sxiccmu_ccu_get_frequency(void *, uint32_t *);
890ea1c705Spatrick int sxiccmu_ccu_set_frequency(void *, uint32_t *, uint32_t);
900ea1c705Spatrick void sxiccmu_ccu_enable(void *, uint32_t *, int);
910ea1c705Spatrick void sxiccmu_ccu_reset(void *, uint32_t *, int);
920ea1c705Spatrick
93e6e86968Skettenis uint32_t sxiccmu_a10_get_frequency(struct sxiccmu_softc *, uint32_t);
94e6e86968Skettenis int sxiccmu_a10_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
95199786d9Skettenis uint32_t sxiccmu_a10s_get_frequency(struct sxiccmu_softc *, uint32_t);
96199786d9Skettenis int sxiccmu_a10s_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
97f0a8b882Skevlo uint32_t sxiccmu_a23_get_frequency(struct sxiccmu_softc *, uint32_t);
98f0a8b882Skevlo int sxiccmu_a23_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
990ea1c705Spatrick uint32_t sxiccmu_a64_get_frequency(struct sxiccmu_softc *, uint32_t);
1000ea1c705Spatrick int sxiccmu_a64_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
101d526c0edSkettenis uint32_t sxiccmu_a80_get_frequency(struct sxiccmu_softc *, uint32_t);
102d526c0edSkettenis int sxiccmu_a80_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
1034e48f70dSkettenis uint32_t sxiccmu_d1_get_frequency(struct sxiccmu_softc *, uint32_t);
1044e48f70dSkettenis int sxiccmu_d1_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
1050ea1c705Spatrick uint32_t sxiccmu_h3_get_frequency(struct sxiccmu_softc *, uint32_t);
1060ea1c705Spatrick int sxiccmu_h3_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
107322e71d8Skettenis uint32_t sxiccmu_h3_r_get_frequency(struct sxiccmu_softc *, uint32_t);
1088b22851bSkettenis uint32_t sxiccmu_h6_get_frequency(struct sxiccmu_softc *, uint32_t);
1098b22851bSkettenis int sxiccmu_h6_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
1108b22851bSkettenis uint32_t sxiccmu_h6_r_get_frequency(struct sxiccmu_softc *, uint32_t);
11122652d99Suaa uint32_t sxiccmu_h616_get_frequency(struct sxiccmu_softc *, uint32_t);
11222652d99Suaa int sxiccmu_h616_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
11322652d99Suaa uint32_t sxiccmu_h616_r_get_frequency(struct sxiccmu_softc *, uint32_t);
114a6d148daSkettenis uint32_t sxiccmu_r40_get_frequency(struct sxiccmu_softc *, uint32_t);
115a6d148daSkettenis int sxiccmu_r40_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
1163fb3ba8fSkettenis uint32_t sxiccmu_v3s_get_frequency(struct sxiccmu_softc *, uint32_t);
1173fb3ba8fSkettenis int sxiccmu_v3s_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
1182c064ff8Skettenis uint32_t sxiccmu_nop_get_frequency(struct sxiccmu_softc *, uint32_t);
1192c064ff8Skettenis int sxiccmu_nop_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
1200ea1c705Spatrick
1210ea1c705Spatrick int
sxiccmu_match(struct device * parent,void * match,void * aux)1220ea1c705Spatrick sxiccmu_match(struct device *parent, void *match, void *aux)
1230ea1c705Spatrick {
1240ea1c705Spatrick struct fdt_attach_args *faa = aux;
1252c064ff8Skettenis int node = faa->fa_node;
1260ea1c705Spatrick
1272c064ff8Skettenis if (node == OF_finddevice("/clocks")) {
1282c064ff8Skettenis node = OF_parent(node);
1290ea1c705Spatrick
1300ea1c705Spatrick return (OF_is_compatible(node, "allwinner,sun4i-a10") ||
1310ea1c705Spatrick OF_is_compatible(node, "allwinner,sun5i-a10s") ||
1320ea1c705Spatrick OF_is_compatible(node, "allwinner,sun5i-r8") ||
1330ea1c705Spatrick OF_is_compatible(node, "allwinner,sun7i-a20") ||
134f0a8b882Skevlo OF_is_compatible(node, "allwinner,sun8i-a23") ||
135f0a8b882Skevlo OF_is_compatible(node, "allwinner,sun8i-a33") ||
1360ea1c705Spatrick OF_is_compatible(node, "allwinner,sun8i-h3") ||
1373fb3ba8fSkettenis OF_is_compatible(node, "allwinner,sun8i-v3s") ||
1385c793bcfSkettenis OF_is_compatible(node, "allwinner,sun9i-a80") ||
1395c793bcfSkettenis OF_is_compatible(node, "allwinner,sun50i-a64") ||
1405c793bcfSkettenis OF_is_compatible(node, "allwinner,sun50i-h5"));
1410ea1c705Spatrick }
1420ea1c705Spatrick
1432c064ff8Skettenis return (OF_is_compatible(node, "allwinner,sun4i-a10-ccu") ||
144199786d9Skettenis OF_is_compatible(node, "allwinner,sun5i-a10s-ccu") ||
145199786d9Skettenis OF_is_compatible(node, "allwinner,sun5i-a13-ccu") ||
1462c064ff8Skettenis OF_is_compatible(node, "allwinner,sun7i-a20-ccu") ||
147f0a8b882Skevlo OF_is_compatible(node, "allwinner,sun8i-a23-ccu") ||
1484e7b86a5Skettenis OF_is_compatible(node, "allwinner,sun8i-a23-prcm") ||
149f0a8b882Skevlo OF_is_compatible(node, "allwinner,sun8i-a33-ccu") ||
1502c064ff8Skettenis OF_is_compatible(node, "allwinner,sun8i-h3-ccu") ||
151322e71d8Skettenis OF_is_compatible(node, "allwinner,sun8i-h3-r-ccu") ||
152a6d148daSkettenis OF_is_compatible(node, "allwinner,sun8i-r40-ccu") ||
1533fb3ba8fSkettenis OF_is_compatible(node, "allwinner,sun8i-v3s-ccu") ||
1542c064ff8Skettenis OF_is_compatible(node, "allwinner,sun9i-a80-ccu") ||
1552c064ff8Skettenis OF_is_compatible(node, "allwinner,sun9i-a80-usb-clks") ||
1562c064ff8Skettenis OF_is_compatible(node, "allwinner,sun9i-a80-mmc-config-clk") ||
1574e48f70dSkettenis OF_is_compatible(node, "allwinner,sun20i-d1-ccu") ||
1582c064ff8Skettenis OF_is_compatible(node, "allwinner,sun50i-a64-ccu") ||
15945fbe8fbSkettenis OF_is_compatible(node, "allwinner,sun50i-a64-r-ccu") ||
1608b22851bSkettenis OF_is_compatible(node, "allwinner,sun50i-h5-ccu") ||
1618b22851bSkettenis OF_is_compatible(node, "allwinner,sun50i-h6-ccu") ||
16222652d99Suaa OF_is_compatible(node, "allwinner,sun50i-h6-r-ccu") ||
16322652d99Suaa OF_is_compatible(node, "allwinner,sun50i-h616-ccu") ||
16422652d99Suaa OF_is_compatible(node, "allwinner,sun50i-h616-r-ccu"));
1650ea1c705Spatrick }
1660ea1c705Spatrick
1670ea1c705Spatrick void
sxiccmu_attach(struct device * parent,struct device * self,void * aux)1680ea1c705Spatrick sxiccmu_attach(struct device *parent, struct device *self, void *aux)
1690ea1c705Spatrick {
1700ea1c705Spatrick struct sxiccmu_softc *sc = (struct sxiccmu_softc *)self;
1710ea1c705Spatrick struct fdt_attach_args *faa = aux;
1722c064ff8Skettenis int node = faa->fa_node;
1730ea1c705Spatrick
174b5fbf381Skettenis sc->sc_node = faa->fa_node;
1750ea1c705Spatrick sc->sc_iot = faa->fa_iot;
1760ea1c705Spatrick if (faa->fa_nreg > 0 && bus_space_map(sc->sc_iot,
1770ea1c705Spatrick faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh))
1780ea1c705Spatrick panic("%s: bus_space_map failed!", __func__);
1790ea1c705Spatrick
18029e54e85Skettenis /* On the R40, the GMAC needs to poke at one of our registers. */
18129e54e85Skettenis if (OF_is_compatible(node, "allwinner,sun8i-r40-ccu")) {
18229e54e85Skettenis bus_space_handle_t ioh;
18329e54e85Skettenis
18429e54e85Skettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
18529e54e85Skettenis R40_GMAC_CLK_REG, 4, &ioh);
18629e54e85Skettenis regmap_register(faa->fa_node, sc->sc_iot, ioh, 4);
18729e54e85Skettenis }
18829e54e85Skettenis
1890ea1c705Spatrick printf("\n");
1900ea1c705Spatrick
1912c064ff8Skettenis if (OF_is_compatible(node, "allwinner,sun4i-a10-ccu") ||
1922c064ff8Skettenis OF_is_compatible(node, "allwinner,sun7i-a20-ccu")) {
193e6e86968Skettenis KASSERT(faa->fa_nreg > 0);
194e6e86968Skettenis sc->sc_gates = sun4i_a10_gates;
195e6e86968Skettenis sc->sc_ngates = nitems(sun4i_a10_gates);
196e6e86968Skettenis sc->sc_resets = sun4i_a10_resets;
197e6e86968Skettenis sc->sc_nresets = nitems(sun4i_a10_resets);
198e6e86968Skettenis sc->sc_get_frequency = sxiccmu_a10_get_frequency;
199e6e86968Skettenis sc->sc_set_frequency = sxiccmu_a10_set_frequency;
200199786d9Skettenis } else if (OF_is_compatible(node, "allwinner,sun5i-a10s-ccu")) {
201199786d9Skettenis KASSERT(faa->fa_nreg > 0);
202199786d9Skettenis sc->sc_gates = sun5i_a10s_gates;
203199786d9Skettenis sc->sc_ngates = nitems(sun5i_a10s_gates);
204199786d9Skettenis sc->sc_resets = sun5i_a10s_resets;
205199786d9Skettenis sc->sc_nresets = nitems(sun5i_a10s_resets);
206199786d9Skettenis sc->sc_get_frequency = sxiccmu_a10s_get_frequency;
207199786d9Skettenis sc->sc_set_frequency = sxiccmu_a10s_set_frequency;
208f0a8b882Skevlo } else if (OF_is_compatible(node, "allwinner,sun8i-a23-ccu") ||
209f0a8b882Skevlo OF_is_compatible(node, "allwinner,sun8i-a33-ccu")) {
210f0a8b882Skevlo KASSERT(faa->fa_nreg > 0);
211f0a8b882Skevlo sc->sc_gates = sun8i_a23_gates;
212f0a8b882Skevlo sc->sc_ngates = nitems(sun8i_a23_gates);
213f0a8b882Skevlo sc->sc_resets = sun8i_a23_resets;
214f0a8b882Skevlo sc->sc_nresets = nitems(sun8i_a23_resets);
215f0a8b882Skevlo sc->sc_get_frequency = sxiccmu_a23_get_frequency;
216f0a8b882Skevlo sc->sc_set_frequency = sxiccmu_a23_set_frequency;
2172c064ff8Skettenis } else if (OF_is_compatible(node, "allwinner,sun8i-h3-ccu") ||
2182c064ff8Skettenis OF_is_compatible(node, "allwinner,sun50i-h5-ccu")) {
2190ea1c705Spatrick KASSERT(faa->fa_nreg > 0);
2200ea1c705Spatrick sc->sc_gates = sun8i_h3_gates;
2210ea1c705Spatrick sc->sc_ngates = nitems(sun8i_h3_gates);
2220ea1c705Spatrick sc->sc_resets = sun8i_h3_resets;
2230ea1c705Spatrick sc->sc_nresets = nitems(sun8i_h3_resets);
2240ea1c705Spatrick sc->sc_get_frequency = sxiccmu_h3_get_frequency;
2250ea1c705Spatrick sc->sc_set_frequency = sxiccmu_h3_set_frequency;
22645fbe8fbSkettenis } else if (OF_is_compatible(node, "allwinner,sun8i-h3-r-ccu") ||
22745fbe8fbSkettenis OF_is_compatible(node, "allwinner,sun50i-a64-r-ccu")) {
228322e71d8Skettenis KASSERT(faa->fa_nreg > 0);
229322e71d8Skettenis sc->sc_gates = sun8i_h3_r_gates;
230322e71d8Skettenis sc->sc_ngates = nitems(sun8i_h3_r_gates);
231322e71d8Skettenis sc->sc_resets = sun8i_h3_r_resets;
232322e71d8Skettenis sc->sc_nresets = nitems(sun8i_h3_r_resets);
233322e71d8Skettenis sc->sc_get_frequency = sxiccmu_h3_r_get_frequency;
234322e71d8Skettenis sc->sc_set_frequency = sxiccmu_nop_set_frequency;
235a6d148daSkettenis } else if (OF_is_compatible(node, "allwinner,sun8i-r40-ccu")) {
236a6d148daSkettenis KASSERT(faa->fa_nreg > 0);
237a6d148daSkettenis sc->sc_gates = sun8i_r40_gates;
238a6d148daSkettenis sc->sc_ngates = nitems(sun8i_r40_gates);
239a6d148daSkettenis sc->sc_resets = sun8i_r40_resets;
240a6d148daSkettenis sc->sc_nresets = nitems(sun8i_r40_resets);
241a6d148daSkettenis sc->sc_get_frequency = sxiccmu_r40_get_frequency;
242a6d148daSkettenis sc->sc_set_frequency = sxiccmu_r40_set_frequency;
2433fb3ba8fSkettenis } else if (OF_is_compatible(node, "allwinner,sun8i-v3s-ccu")) {
2443fb3ba8fSkettenis KASSERT(faa->fa_nreg > 0);
2453fb3ba8fSkettenis sc->sc_gates = sun8i_v3s_gates;
2463fb3ba8fSkettenis sc->sc_ngates = nitems(sun8i_v3s_gates);
2473fb3ba8fSkettenis sc->sc_resets = sun8i_v3s_resets;
2483fb3ba8fSkettenis sc->sc_nresets = nitems(sun8i_v3s_resets);
2493fb3ba8fSkettenis sc->sc_get_frequency = sxiccmu_v3s_get_frequency;
2503fb3ba8fSkettenis sc->sc_set_frequency = sxiccmu_v3s_set_frequency;
2512c064ff8Skettenis } else if (OF_is_compatible(node, "allwinner,sun9i-a80-ccu")) {
252d526c0edSkettenis KASSERT(faa->fa_nreg > 0);
253d526c0edSkettenis sc->sc_gates = sun9i_a80_gates;
254d526c0edSkettenis sc->sc_ngates = nitems(sun9i_a80_gates);
255d526c0edSkettenis sc->sc_resets = sun9i_a80_resets;
256d526c0edSkettenis sc->sc_nresets = nitems(sun9i_a80_resets);
257d526c0edSkettenis sc->sc_get_frequency = sxiccmu_a80_get_frequency;
258d526c0edSkettenis sc->sc_set_frequency = sxiccmu_a80_set_frequency;
2592c064ff8Skettenis } else if (OF_is_compatible(node, "allwinner,sun9i-a80-usb-clks")) {
2602c064ff8Skettenis KASSERT(faa->fa_nreg > 0);
2612c064ff8Skettenis sc->sc_gates = sun9i_a80_usb_gates;
2622c064ff8Skettenis sc->sc_ngates = nitems(sun9i_a80_usb_gates);
2632c064ff8Skettenis sc->sc_resets = sun9i_a80_usb_resets;
2642c064ff8Skettenis sc->sc_nresets = nitems(sun9i_a80_usb_resets);
2652c064ff8Skettenis sc->sc_get_frequency = sxiccmu_nop_get_frequency;
2662c064ff8Skettenis sc->sc_set_frequency = sxiccmu_nop_set_frequency;
2672c064ff8Skettenis } else if (OF_is_compatible(node, "allwinner,sun9i-a80-mmc-config-clk")) {
2682c064ff8Skettenis KASSERT(faa->fa_nreg > 0);
2692c064ff8Skettenis sc->sc_gates = sun9i_a80_mmc_gates;
2702c064ff8Skettenis sc->sc_ngates = nitems(sun9i_a80_mmc_gates);
2712c064ff8Skettenis sc->sc_resets = sun9i_a80_mmc_resets;
2722c064ff8Skettenis sc->sc_nresets = nitems(sun9i_a80_mmc_resets);
2732c064ff8Skettenis sc->sc_get_frequency = sxiccmu_nop_get_frequency;
2742c064ff8Skettenis sc->sc_set_frequency = sxiccmu_nop_set_frequency;
2754e48f70dSkettenis } else if (OF_is_compatible(node, "allwinner,sun20i-d1-ccu")) {
2764e48f70dSkettenis KASSERT(faa->fa_nreg > 0);
2774e48f70dSkettenis sc->sc_gates = sun20i_d1_gates;
2784e48f70dSkettenis sc->sc_ngates = nitems(sun20i_d1_gates);
2794e48f70dSkettenis sc->sc_resets = sun20i_d1_resets;
2804e48f70dSkettenis sc->sc_nresets = nitems(sun20i_d1_resets);
2814e48f70dSkettenis sc->sc_get_frequency = sxiccmu_d1_get_frequency;
2824e48f70dSkettenis sc->sc_set_frequency = sxiccmu_d1_set_frequency;
2832c064ff8Skettenis } else if (OF_is_compatible(node, "allwinner,sun50i-a64-ccu")) {
284d526c0edSkettenis KASSERT(faa->fa_nreg > 0);
285d526c0edSkettenis sc->sc_gates = sun50i_a64_gates;
286d526c0edSkettenis sc->sc_ngates = nitems(sun50i_a64_gates);
287d526c0edSkettenis sc->sc_resets = sun50i_a64_resets;
288d526c0edSkettenis sc->sc_nresets = nitems(sun50i_a64_resets);
289d526c0edSkettenis sc->sc_get_frequency = sxiccmu_a64_get_frequency;
290d526c0edSkettenis sc->sc_set_frequency = sxiccmu_a64_set_frequency;
2918b22851bSkettenis } else if (OF_is_compatible(node, "allwinner,sun50i-h6-ccu")) {
2928b22851bSkettenis KASSERT(faa->fa_nreg > 0);
2938b22851bSkettenis sc->sc_gates = sun50i_h6_gates;
2948b22851bSkettenis sc->sc_ngates = nitems(sun50i_h6_gates);
2958b22851bSkettenis sc->sc_resets = sun50i_h6_resets;
2968b22851bSkettenis sc->sc_nresets = nitems(sun50i_h6_resets);
2978b22851bSkettenis sc->sc_get_frequency = sxiccmu_h6_get_frequency;
2988b22851bSkettenis sc->sc_set_frequency = sxiccmu_h6_set_frequency;
2998b22851bSkettenis } else if (OF_is_compatible(node, "allwinner,sun50i-h6-r-ccu")) {
3008b22851bSkettenis KASSERT(faa->fa_nreg > 0);
3018b22851bSkettenis sc->sc_gates = sun50i_h6_r_gates;
3028b22851bSkettenis sc->sc_ngates = nitems(sun50i_h6_r_gates);
3038b22851bSkettenis sc->sc_resets = sun50i_h6_r_resets;
3048b22851bSkettenis sc->sc_nresets = nitems(sun50i_h6_r_resets);
3058b22851bSkettenis sc->sc_get_frequency = sxiccmu_h6_r_get_frequency;
3068b22851bSkettenis sc->sc_set_frequency = sxiccmu_nop_set_frequency;
30722652d99Suaa } else if (OF_is_compatible(node, "allwinner,sun50i-h616-ccu")) {
30822652d99Suaa KASSERT(faa->fa_nreg > 0);
30922652d99Suaa sc->sc_gates = sun50i_h616_gates;
31022652d99Suaa sc->sc_ngates = nitems(sun50i_h616_gates);
31122652d99Suaa sc->sc_resets = sun50i_h616_resets;
31222652d99Suaa sc->sc_nresets = nitems(sun50i_h616_resets);
31322652d99Suaa sc->sc_get_frequency = sxiccmu_h616_get_frequency;
31422652d99Suaa sc->sc_set_frequency = sxiccmu_h616_set_frequency;
31522652d99Suaa } else if (OF_is_compatible(node, "allwinner,sun50i-h616-r-ccu")) {
31622652d99Suaa KASSERT(faa->fa_nreg > 0);
31722652d99Suaa sc->sc_gates = sun50i_h616_r_gates;
31822652d99Suaa sc->sc_ngates = nitems(sun50i_h616_r_gates);
31922652d99Suaa sc->sc_resets = sun50i_h616_r_resets;
32022652d99Suaa sc->sc_nresets = nitems(sun50i_h616_r_resets);
32122652d99Suaa sc->sc_get_frequency = sxiccmu_h616_r_get_frequency;
32222652d99Suaa sc->sc_set_frequency = sxiccmu_nop_set_frequency;
3230ea1c705Spatrick } else {
3242c064ff8Skettenis for (node = OF_child(node); node; node = OF_peer(node))
3254e7b86a5Skettenis sxiccmu_attach_clock(sc, node, faa->fa_nreg);
3260ea1c705Spatrick }
3270ea1c705Spatrick
3280ea1c705Spatrick if (sc->sc_gates) {
329b5fbf381Skettenis sc->sc_cd.cd_node = sc->sc_node;
3300ea1c705Spatrick sc->sc_cd.cd_cookie = sc;
3310ea1c705Spatrick sc->sc_cd.cd_get_frequency = sxiccmu_ccu_get_frequency;
3320ea1c705Spatrick sc->sc_cd.cd_set_frequency = sxiccmu_ccu_set_frequency;
3330ea1c705Spatrick sc->sc_cd.cd_enable = sxiccmu_ccu_enable;
3340ea1c705Spatrick clock_register(&sc->sc_cd);
3350ea1c705Spatrick }
3360ea1c705Spatrick
3370ea1c705Spatrick if (sc->sc_resets) {
338b5fbf381Skettenis sc->sc_rd.rd_node = sc->sc_node;
3390ea1c705Spatrick sc->sc_rd.rd_cookie = sc;
3400ea1c705Spatrick sc->sc_rd.rd_reset = sxiccmu_ccu_reset;
3410ea1c705Spatrick reset_register(&sc->sc_rd);
3420ea1c705Spatrick }
3430ea1c705Spatrick }
3440ea1c705Spatrick
3450ea1c705Spatrick /*
346b5fbf381Skettenis * Classic device trees for the Allwinner SoCs have basically a clock
347b5fbf381Skettenis * node per register of the clock control unit. Attaching a separate
348b5fbf381Skettenis * driver to each of them would be crazy, so we handle them here.
3490ea1c705Spatrick */
3500ea1c705Spatrick
3510ea1c705Spatrick struct sxiccmu_clock {
3520ea1c705Spatrick int sc_node;
3530ea1c705Spatrick bus_space_tag_t sc_iot;
3540ea1c705Spatrick bus_space_handle_t sc_ioh;
3550ea1c705Spatrick
3560ea1c705Spatrick struct clock_device sc_cd;
3570ea1c705Spatrick struct reset_device sc_rd;
3580ea1c705Spatrick };
3590ea1c705Spatrick
3600ea1c705Spatrick struct sxiccmu_device {
3610ea1c705Spatrick const char *compat;
3620ea1c705Spatrick uint32_t (*get_frequency)(void *, uint32_t *);
3630ea1c705Spatrick int (*set_frequency)(void *, uint32_t *, uint32_t);
3640ea1c705Spatrick void (*enable)(void *, uint32_t *, int);
3650ea1c705Spatrick void (*reset)(void *, uint32_t *, int);
3664e7b86a5Skettenis bus_size_t offset;
3670ea1c705Spatrick };
3680ea1c705Spatrick
3690ea1c705Spatrick uint32_t sxiccmu_gen_get_frequency(void *, uint32_t *);
3700ea1c705Spatrick uint32_t sxiccmu_osc_get_frequency(void *, uint32_t *);
3710ea1c705Spatrick uint32_t sxiccmu_pll6_get_frequency(void *, uint32_t *);
3720ea1c705Spatrick void sxiccmu_pll6_enable(void *, uint32_t *, int);
3730ea1c705Spatrick uint32_t sxiccmu_apb1_get_frequency(void *, uint32_t *);
3745e83a44fSkettenis uint32_t sxiccmu_cpus_get_frequency(void *, uint32_t *);
3755e83a44fSkettenis uint32_t sxiccmu_apbs_get_frequency(void *, uint32_t *);
3760ea1c705Spatrick int sxiccmu_gmac_set_frequency(void *, uint32_t *, uint32_t);
3770ea1c705Spatrick int sxiccmu_mmc_set_frequency(void *, uint32_t *, uint32_t);
3780ea1c705Spatrick void sxiccmu_mmc_enable(void *, uint32_t *, int);
3790ea1c705Spatrick void sxiccmu_gate_enable(void *, uint32_t *, int);
3800ea1c705Spatrick void sxiccmu_reset(void *, uint32_t *, int);
3810ea1c705Spatrick
38237c734d3Snaddy const struct sxiccmu_device sxiccmu_devices[] = {
3830ea1c705Spatrick {
3840ea1c705Spatrick .compat = "allwinner,sun4i-a10-osc-clk",
3850ea1c705Spatrick .get_frequency = sxiccmu_osc_get_frequency,
3860ea1c705Spatrick },
3870ea1c705Spatrick {
3880ea1c705Spatrick .compat = "allwinner,sun4i-a10-pll6-clk",
3890ea1c705Spatrick .get_frequency = sxiccmu_pll6_get_frequency,
3900ea1c705Spatrick .enable = sxiccmu_pll6_enable
3910ea1c705Spatrick },
3920ea1c705Spatrick {
3930ea1c705Spatrick .compat = "allwinner,sun4i-a10-apb1-clk",
3940ea1c705Spatrick .get_frequency = sxiccmu_apb1_get_frequency,
3950ea1c705Spatrick },
3960ea1c705Spatrick {
3970ea1c705Spatrick .compat = "allwinner,sun4i-a10-ahb-gates-clk",
3980ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
3990ea1c705Spatrick .enable = sxiccmu_gate_enable
4000ea1c705Spatrick },
4010ea1c705Spatrick {
4020ea1c705Spatrick .compat = "allwinner,sun4i-a10-apb0-gates-clk",
4030ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4040ea1c705Spatrick .enable = sxiccmu_gate_enable
4050ea1c705Spatrick },
4060ea1c705Spatrick {
4070ea1c705Spatrick .compat = "allwinner,sun4i-a10-apb1-gates-clk",
4080ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4090ea1c705Spatrick .enable = sxiccmu_gate_enable
4100ea1c705Spatrick },
4110ea1c705Spatrick {
4120ea1c705Spatrick .compat = "allwinner,sun4i-a10-mmc-clk",
4130ea1c705Spatrick .set_frequency = sxiccmu_mmc_set_frequency,
4140ea1c705Spatrick .enable = sxiccmu_mmc_enable
4150ea1c705Spatrick },
4160ea1c705Spatrick {
4170ea1c705Spatrick .compat = "allwinner,sun4i-a10-usb-clk",
4180ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4190ea1c705Spatrick .enable = sxiccmu_gate_enable,
4200ea1c705Spatrick .reset = sxiccmu_reset
4210ea1c705Spatrick },
4220ea1c705Spatrick {
4230ea1c705Spatrick .compat = "allwinner,sun5i-a10s-ahb-gates-clk",
4240ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4250ea1c705Spatrick .enable = sxiccmu_gate_enable
4260ea1c705Spatrick },
4270ea1c705Spatrick {
4280ea1c705Spatrick .compat = "allwinner,sun5i-a10s-apb0-gates-clk",
4290ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4300ea1c705Spatrick .enable = sxiccmu_gate_enable
4310ea1c705Spatrick },
4320ea1c705Spatrick {
4330ea1c705Spatrick .compat = "allwinner,sun5i-a10s-apb1-gates-clk",
4340ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4350ea1c705Spatrick .enable = sxiccmu_gate_enable
4360ea1c705Spatrick },
4370ea1c705Spatrick {
4380ea1c705Spatrick .compat = "allwinner,sun5i-a13-ahb-gates-clk",
4390ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4400ea1c705Spatrick .enable = sxiccmu_gate_enable
4410ea1c705Spatrick },
4420ea1c705Spatrick {
4430ea1c705Spatrick .compat = "allwinner,sun5i-a13-apb0-gates-clk",
4440ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4450ea1c705Spatrick .enable = sxiccmu_gate_enable
4460ea1c705Spatrick },
4470ea1c705Spatrick {
4480ea1c705Spatrick .compat = "allwinner,sun5i-a13-apb1-gates-clk",
4490ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4500ea1c705Spatrick .enable = sxiccmu_gate_enable
4510ea1c705Spatrick },
4520ea1c705Spatrick {
4530ea1c705Spatrick .compat = "allwinner,sun5i-a13-usb-clk",
4540ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4550ea1c705Spatrick .enable = sxiccmu_gate_enable,
4560ea1c705Spatrick .reset = sxiccmu_reset
4570ea1c705Spatrick },
4580ea1c705Spatrick {
4590ea1c705Spatrick .compat = "allwinner,sun6i-a31-ahb1-reset",
4600ea1c705Spatrick .reset = sxiccmu_reset
4610ea1c705Spatrick },
4620ea1c705Spatrick {
4630ea1c705Spatrick .compat = "allwinner,sun6i-a31-clock-reset",
4644e7b86a5Skettenis .reset = sxiccmu_reset,
4654e7b86a5Skettenis .offset = 0x00b0
4660ea1c705Spatrick },
4670ea1c705Spatrick {
4680ea1c705Spatrick .compat = "allwinner,sun7i-a20-ahb-gates-clk",
4690ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4700ea1c705Spatrick .enable = sxiccmu_gate_enable
4710ea1c705Spatrick },
4720ea1c705Spatrick {
4730ea1c705Spatrick .compat = "allwinner,sun7i-a20-apb0-gates-clk",
4740ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4750ea1c705Spatrick .enable = sxiccmu_gate_enable
4760ea1c705Spatrick },
4770ea1c705Spatrick {
4780ea1c705Spatrick .compat = "allwinner,sun7i-a20-apb1-gates-clk",
4790ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
4800ea1c705Spatrick .enable = sxiccmu_gate_enable
4810ea1c705Spatrick },
4820ea1c705Spatrick {
4830ea1c705Spatrick .compat = "allwinner,sun7i-a20-gmac-clk",
4840ea1c705Spatrick .set_frequency = sxiccmu_gmac_set_frequency
4850ea1c705Spatrick },
4860ea1c705Spatrick {
4875e83a44fSkettenis .compat = "allwinner,sun8i-a23-apb0-clk",
4884e7b86a5Skettenis .get_frequency = sxiccmu_apbs_get_frequency,
4894e7b86a5Skettenis .offset = 0x000c
4905e83a44fSkettenis },
4915e83a44fSkettenis {
492f0a8b882Skevlo .compat = "allwinner,sun8i-a23-ahb1-gates-clk",
493f0a8b882Skevlo .get_frequency = sxiccmu_gen_get_frequency,
494f0a8b882Skevlo .enable = sxiccmu_gate_enable
495f0a8b882Skevlo },
496f0a8b882Skevlo {
497f0a8b882Skevlo .compat = "allwinner,sun8i-a23-apb0-gates-clk",
498f0a8b882Skevlo .get_frequency = sxiccmu_gen_get_frequency,
4994e7b86a5Skettenis .enable = sxiccmu_gate_enable,
5004e7b86a5Skettenis .offset = 0x0028
501f0a8b882Skevlo },
502f0a8b882Skevlo {
503f0a8b882Skevlo .compat = "allwinner,sun8i-a23-apb1-gates-clk",
504f0a8b882Skevlo .get_frequency = sxiccmu_gen_get_frequency,
505f0a8b882Skevlo .enable = sxiccmu_gate_enable
506f0a8b882Skevlo },
507f0a8b882Skevlo {
508f0a8b882Skevlo .compat = "allwinner,sun8i-a23-apb2-gates-clk",
509f0a8b882Skevlo .get_frequency = sxiccmu_gen_get_frequency,
510f0a8b882Skevlo .enable = sxiccmu_gate_enable
511f0a8b882Skevlo },
512f0a8b882Skevlo {
513f0a8b882Skevlo .compat = "allwinner,sun8i-a23-usb-clk",
514f0a8b882Skevlo .get_frequency = sxiccmu_gen_get_frequency,
515f0a8b882Skevlo .enable = sxiccmu_gate_enable,
516f0a8b882Skevlo .reset = sxiccmu_reset
517f0a8b882Skevlo },
518f0a8b882Skevlo {
5190ea1c705Spatrick .compat = "allwinner,sun8i-h3-apb0-gates-clk",
5200ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5210ea1c705Spatrick .enable = sxiccmu_gate_enable
5220ea1c705Spatrick },
5230ea1c705Spatrick {
5240ea1c705Spatrick .compat = "allwinner,sun9i-a80-apb1-clk",
5250ea1c705Spatrick .get_frequency = sxiccmu_apb1_get_frequency,
5260ea1c705Spatrick },
5270ea1c705Spatrick {
5280ea1c705Spatrick .compat = "allwinner,sun9i-a80-ahb0-gates-clk",
5290ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5300ea1c705Spatrick .enable = sxiccmu_gate_enable
5310ea1c705Spatrick },
5320ea1c705Spatrick {
5330ea1c705Spatrick .compat = "allwinner,sun9i-a80-ahb1-gates-clk",
5340ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5350ea1c705Spatrick .enable = sxiccmu_gate_enable
5360ea1c705Spatrick },
5370ea1c705Spatrick {
5380ea1c705Spatrick .compat = "allwinner,sun9i-a80-ahb2-gates-clk",
5390ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5400ea1c705Spatrick .enable = sxiccmu_gate_enable
5410ea1c705Spatrick },
5420ea1c705Spatrick {
5430ea1c705Spatrick .compat = "allwinner,sun9i-a80-apb0-gates-clk",
5440ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5450ea1c705Spatrick .enable = sxiccmu_gate_enable
5460ea1c705Spatrick },
5470ea1c705Spatrick {
5480ea1c705Spatrick .compat = "allwinner,sun9i-a80-apb1-gates-clk",
5490ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5500ea1c705Spatrick .enable = sxiccmu_gate_enable
5510ea1c705Spatrick },
5520ea1c705Spatrick {
5530ea1c705Spatrick .compat = "allwinner,sun9i-a80-apbs-gates-clk",
5540ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5550ea1c705Spatrick .enable = sxiccmu_gate_enable
5560ea1c705Spatrick },
5570ea1c705Spatrick {
5585e83a44fSkettenis .compat = "allwinner,sun9i-a80-cpus-clk",
5595e83a44fSkettenis .get_frequency = sxiccmu_cpus_get_frequency
5605e83a44fSkettenis },
5615e83a44fSkettenis {
5620ea1c705Spatrick .compat = "allwinner,sun9i-a80-mmc-clk",
5630ea1c705Spatrick .set_frequency = sxiccmu_mmc_set_frequency,
5640ea1c705Spatrick .enable = sxiccmu_mmc_enable
5650ea1c705Spatrick },
5660ea1c705Spatrick {
5670ea1c705Spatrick .compat = "allwinner,sun9i-a80-usb-mod-clk",
5680ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5690ea1c705Spatrick .enable = sxiccmu_gate_enable,
5700ea1c705Spatrick .reset = sxiccmu_reset
5710ea1c705Spatrick },
5720ea1c705Spatrick {
5730ea1c705Spatrick .compat = "allwinner,sun9i-a80-usb-phy-clk",
5740ea1c705Spatrick .get_frequency = sxiccmu_gen_get_frequency,
5750ea1c705Spatrick .enable = sxiccmu_gate_enable,
5760ea1c705Spatrick .reset = sxiccmu_reset
5770ea1c705Spatrick },
5780ea1c705Spatrick };
5790ea1c705Spatrick
5800ea1c705Spatrick void
sxiccmu_attach_clock(struct sxiccmu_softc * sc,int node,int nreg)5814e7b86a5Skettenis sxiccmu_attach_clock(struct sxiccmu_softc *sc, int node, int nreg)
5820ea1c705Spatrick {
5830ea1c705Spatrick struct sxiccmu_clock *clock;
5840ea1c705Spatrick uint32_t reg[2];
5854e7b86a5Skettenis int i, error = ENODEV;
5860ea1c705Spatrick
5870ea1c705Spatrick for (i = 0; i < nitems(sxiccmu_devices); i++)
5880ea1c705Spatrick if (OF_is_compatible(node, sxiccmu_devices[i].compat))
5890ea1c705Spatrick break;
5900ea1c705Spatrick if (i == nitems(sxiccmu_devices))
5910ea1c705Spatrick return;
5920ea1c705Spatrick
5930ea1c705Spatrick clock = malloc(sizeof(*clock), M_DEVBUF, M_WAITOK);
5940ea1c705Spatrick clock->sc_node = node;
5950ea1c705Spatrick
5960ea1c705Spatrick clock->sc_iot = sc->sc_iot;
5974e7b86a5Skettenis if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) == sizeof(reg)) {
5984e7b86a5Skettenis error = bus_space_map(clock->sc_iot, reg[0], reg[1], 0,
5994e7b86a5Skettenis &clock->sc_ioh);
6004e7b86a5Skettenis } else if (nreg > 0) {
6014e7b86a5Skettenis error = bus_space_subregion(clock->sc_iot, sc->sc_ioh,
6024e7b86a5Skettenis sxiccmu_devices[i].offset, 4, &clock->sc_ioh);
6034e7b86a5Skettenis }
6044e7b86a5Skettenis if (error) {
6050ea1c705Spatrick printf("%s: can't map registers", sc->sc_dev.dv_xname);
6060ea1c705Spatrick free(clock, M_DEVBUF, sizeof(*clock));
6070ea1c705Spatrick return;
6080ea1c705Spatrick }
6090ea1c705Spatrick
6100ea1c705Spatrick clock->sc_cd.cd_node = node;
6110ea1c705Spatrick clock->sc_cd.cd_cookie = clock;
6120ea1c705Spatrick clock->sc_cd.cd_get_frequency = sxiccmu_devices[i].get_frequency;
6130ea1c705Spatrick clock->sc_cd.cd_set_frequency = sxiccmu_devices[i].set_frequency;
6140ea1c705Spatrick clock->sc_cd.cd_enable = sxiccmu_devices[i].enable;
6150ea1c705Spatrick clock_register(&clock->sc_cd);
6160ea1c705Spatrick
6170ea1c705Spatrick if (sxiccmu_devices[i].reset) {
6180ea1c705Spatrick clock->sc_rd.rd_node = node;
6190ea1c705Spatrick clock->sc_rd.rd_cookie = clock;
6200ea1c705Spatrick clock->sc_rd.rd_reset = sxiccmu_devices[i].reset;
6210ea1c705Spatrick reset_register(&clock->sc_rd);
6220ea1c705Spatrick }
6230ea1c705Spatrick }
6240ea1c705Spatrick
6250ea1c705Spatrick /*
6260ea1c705Spatrick * A "generic" function that simply gets the clock frequency from the
6270ea1c705Spatrick * parent clock. Useful for clock gating devices that don't scale
6280ea1c705Spatrick * their clocks.
6290ea1c705Spatrick */
6300ea1c705Spatrick uint32_t
sxiccmu_gen_get_frequency(void * cookie,uint32_t * cells)6310ea1c705Spatrick sxiccmu_gen_get_frequency(void *cookie, uint32_t *cells)
6320ea1c705Spatrick {
6330ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
6340ea1c705Spatrick
6350ea1c705Spatrick return clock_get_frequency(sc->sc_node, NULL);
6360ea1c705Spatrick }
6370ea1c705Spatrick
6380ea1c705Spatrick uint32_t
sxiccmu_osc_get_frequency(void * cookie,uint32_t * cells)6390ea1c705Spatrick sxiccmu_osc_get_frequency(void *cookie, uint32_t *cells)
6400ea1c705Spatrick {
6410ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
6420ea1c705Spatrick
6430ea1c705Spatrick return OF_getpropint(sc->sc_node, "clock-frequency", 24000000);
6440ea1c705Spatrick }
6450ea1c705Spatrick
6460ea1c705Spatrick #define CCU_PLL6_ENABLE (1U << 31)
6470ea1c705Spatrick #define CCU_PLL6_BYPASS_EN (1U << 30)
6480ea1c705Spatrick #define CCU_PLL6_SATA_CLK_EN (1U << 14)
6490ea1c705Spatrick #define CCU_PLL6_FACTOR_N(x) (((x) >> 8) & 0x1f)
6500ea1c705Spatrick #define CCU_PLL6_FACTOR_N_MASK (0x1f << 8)
6510ea1c705Spatrick #define CCU_PLL6_FACTOR_N_SHIFT 8
6520ea1c705Spatrick #define CCU_PLL6_FACTOR_K(x) (((x) >> 4) & 0x3)
6530ea1c705Spatrick #define CCU_PLL6_FACTOR_K_MASK (0x3 << 4)
6540ea1c705Spatrick #define CCU_PLL6_FACTOR_K_SHIFT 4
6550ea1c705Spatrick #define CCU_PLL6_FACTOR_M(x) (((x) >> 0) & 0x3)
6560ea1c705Spatrick #define CCU_PLL6_FACTOR_M_MASK (0x3 << 0)
6570ea1c705Spatrick #define CCU_PLL6_FACTOR_M_SHIFT 0
6580ea1c705Spatrick
6590ea1c705Spatrick uint32_t
sxiccmu_pll6_get_frequency(void * cookie,uint32_t * cells)6600ea1c705Spatrick sxiccmu_pll6_get_frequency(void *cookie, uint32_t *cells)
6610ea1c705Spatrick {
6620ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
6630ea1c705Spatrick uint32_t reg, k, m, n, freq;
6640ea1c705Spatrick uint32_t idx = cells[0];
6650ea1c705Spatrick
6660ea1c705Spatrick /* XXX Assume bypass is disabled. */
6670ea1c705Spatrick reg = SXIREAD4(sc, 0);
6680ea1c705Spatrick k = CCU_PLL6_FACTOR_K(reg) + 1;
6690ea1c705Spatrick m = CCU_PLL6_FACTOR_M(reg) + 1;
6700ea1c705Spatrick n = CCU_PLL6_FACTOR_N(reg);
6710ea1c705Spatrick
6720ea1c705Spatrick freq = clock_get_frequency_idx(sc->sc_node, 0);
6730ea1c705Spatrick switch (idx) {
6740ea1c705Spatrick case 0:
6750ea1c705Spatrick return (freq * n * k) / m / 6; /* pll6_sata */
6760ea1c705Spatrick case 1:
6770ea1c705Spatrick return (freq * n * k) / 2; /* pll6_other */
6780ea1c705Spatrick case 2:
6790ea1c705Spatrick return (freq * n * k); /* pll6 */
6800ea1c705Spatrick case 3:
6810ea1c705Spatrick return (freq * n * k) / 4; /* pll6_div_4 */
6820ea1c705Spatrick }
6830ea1c705Spatrick
6840ea1c705Spatrick return 0;
6850ea1c705Spatrick }
6860ea1c705Spatrick
6870ea1c705Spatrick void
sxiccmu_pll6_enable(void * cookie,uint32_t * cells,int on)6880ea1c705Spatrick sxiccmu_pll6_enable(void *cookie, uint32_t *cells, int on)
6890ea1c705Spatrick {
6900ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
6910ea1c705Spatrick uint32_t idx = cells[0];
6920ea1c705Spatrick uint32_t reg;
6930ea1c705Spatrick
6940ea1c705Spatrick /*
6950ea1c705Spatrick * Since this clock has several outputs, we never turn it off.
6960ea1c705Spatrick */
6970ea1c705Spatrick
6980ea1c705Spatrick reg = SXIREAD4(sc, 0);
6990ea1c705Spatrick switch (idx) {
7000ea1c705Spatrick case 0: /* pll6_sata */
7010ea1c705Spatrick if (on)
7020ea1c705Spatrick reg |= CCU_PLL6_SATA_CLK_EN;
7030ea1c705Spatrick else
7040ea1c705Spatrick reg &= ~CCU_PLL6_SATA_CLK_EN;
7050ea1c705Spatrick /* FALLTHROUGH */
7060ea1c705Spatrick case 1: /* pll6_other */
7070ea1c705Spatrick case 2: /* pll6 */
7080ea1c705Spatrick case 3: /* pll6_div_4 */
7090ea1c705Spatrick if (on)
7100ea1c705Spatrick reg |= CCU_PLL6_ENABLE;
7110ea1c705Spatrick }
7120ea1c705Spatrick SXIWRITE4(sc, 0, reg);
7130ea1c705Spatrick }
7140ea1c705Spatrick
7150ea1c705Spatrick #define CCU_APB1_CLK_RAT_N(x) (((x) >> 16) & 0x3)
7160ea1c705Spatrick #define CCU_APB1_CLK_RAT_M(x) (((x) >> 0) & 0x1f)
7170ea1c705Spatrick #define CCU_APB1_CLK_SRC_SEL(x) (((x) >> 24) & 0x3)
7180ea1c705Spatrick
7190ea1c705Spatrick uint32_t
sxiccmu_apb1_get_frequency(void * cookie,uint32_t * cells)7200ea1c705Spatrick sxiccmu_apb1_get_frequency(void *cookie, uint32_t *cells)
7210ea1c705Spatrick {
7220ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
7230ea1c705Spatrick uint32_t reg, m, n, freq;
7240ea1c705Spatrick int idx;
7250ea1c705Spatrick
7260ea1c705Spatrick reg = SXIREAD4(sc, 0);
7270ea1c705Spatrick m = CCU_APB1_CLK_RAT_M(reg);
7280ea1c705Spatrick n = CCU_APB1_CLK_RAT_N(reg);
7290ea1c705Spatrick idx = CCU_APB1_CLK_SRC_SEL(reg);
7300ea1c705Spatrick
7310ea1c705Spatrick freq = clock_get_frequency_idx(sc->sc_node, idx);
7320ea1c705Spatrick return freq / (1 << n) / (m + 1);
7330ea1c705Spatrick }
7340ea1c705Spatrick
7355e83a44fSkettenis #define CCU_CPUS_CLK_SRC_SEL(x) (((x) >> 16) & 0x3)
7365e83a44fSkettenis #define CCU_CPUS_POST_DIV(x) (((x) >> 8) & 0x1f)
7375e83a44fSkettenis #define CCU_CPUS_CLK_RATIO(x) (((x) >> 0) & 0x3)
7385e83a44fSkettenis
7395e83a44fSkettenis uint32_t
sxiccmu_cpus_get_frequency(void * cookie,uint32_t * cells)7405e83a44fSkettenis sxiccmu_cpus_get_frequency(void *cookie, uint32_t *cells)
7415e83a44fSkettenis {
7425e83a44fSkettenis struct sxiccmu_clock *sc = cookie;
7435e83a44fSkettenis uint32_t reg, post_div, clk_ratio, freq;
7445e83a44fSkettenis int idx;
7455e83a44fSkettenis
7465e83a44fSkettenis reg = SXIREAD4(sc, 0);
7475e83a44fSkettenis idx = CCU_CPUS_CLK_SRC_SEL(reg);
7485e83a44fSkettenis post_div = (idx == 2 ? CCU_CPUS_POST_DIV(reg): 0);
7495e83a44fSkettenis clk_ratio = CCU_CPUS_CLK_RATIO(reg);
7505e83a44fSkettenis
7515e83a44fSkettenis freq = clock_get_frequency_idx(sc->sc_node, idx);
7525e83a44fSkettenis return freq / (clk_ratio + 1) / (post_div + 1);
7535e83a44fSkettenis }
7545e83a44fSkettenis
7555e83a44fSkettenis #define CCU_APBS_CLK_RATIO(x) (((x) >> 0) & 0x3)
7565e83a44fSkettenis
7575e83a44fSkettenis uint32_t
sxiccmu_apbs_get_frequency(void * cookie,uint32_t * cells)7585e83a44fSkettenis sxiccmu_apbs_get_frequency(void *cookie, uint32_t *cells)
7595e83a44fSkettenis {
7605e83a44fSkettenis struct sxiccmu_clock *sc = cookie;
7615e83a44fSkettenis uint32_t reg, freq;
7625e83a44fSkettenis
7635e83a44fSkettenis reg = SXIREAD4(sc, 0);
7645e83a44fSkettenis freq = clock_get_frequency(sc->sc_node, NULL);
7655e83a44fSkettenis return freq / (CCU_APBS_CLK_RATIO(reg) + 1);
7665e83a44fSkettenis }
7675e83a44fSkettenis
7680ea1c705Spatrick #define CCU_GMAC_CLK_PIT (1 << 2)
7690ea1c705Spatrick #define CCU_GMAC_CLK_TCS (3 << 0)
7700ea1c705Spatrick #define CCU_GMAC_CLK_TCS_MII 0
7710ea1c705Spatrick #define CCU_GMAC_CLK_TCS_EXT_125 1
7720ea1c705Spatrick #define CCU_GMAC_CLK_TCS_INT_RGMII 2
7730ea1c705Spatrick
7740ea1c705Spatrick int
sxiccmu_gmac_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)7750ea1c705Spatrick sxiccmu_gmac_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
7760ea1c705Spatrick {
7770ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
7780ea1c705Spatrick
7790ea1c705Spatrick switch (freq) {
7800ea1c705Spatrick case 25000000: /* MMI, 25 MHz */
7810ea1c705Spatrick SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS,
7820ea1c705Spatrick CCU_GMAC_CLK_TCS_MII);
7830ea1c705Spatrick break;
7840ea1c705Spatrick case 125000000: /* RGMII, 125 MHz */
7850ea1c705Spatrick SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS,
7860ea1c705Spatrick CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS_INT_RGMII);
7870ea1c705Spatrick break;
7880ea1c705Spatrick default:
7890ea1c705Spatrick return -1;
7900ea1c705Spatrick }
7910ea1c705Spatrick
7920ea1c705Spatrick return 0;
7930ea1c705Spatrick }
7940ea1c705Spatrick
7950ea1c705Spatrick #define CCU_SDx_SCLK_GATING (1U << 31)
7960ea1c705Spatrick #define CCU_SDx_CLK_SRC_SEL_OSC24M (0 << 24)
7970ea1c705Spatrick #define CCU_SDx_CLK_SRC_SEL_PLL6 (1 << 24)
7980ea1c705Spatrick #define CCU_SDx_CLK_SRC_SEL_PLL5 (2 << 24)
7990ea1c705Spatrick #define CCU_SDx_CLK_SRC_SEL_MASK (3 << 24)
8000ea1c705Spatrick #define CCU_SDx_CLK_DIV_RATIO_N_MASK (3 << 16)
8010ea1c705Spatrick #define CCU_SDx_CLK_DIV_RATIO_N_SHIFT 16
8020ea1c705Spatrick #define CCU_SDx_CLK_DIV_RATIO_M_MASK (7 << 0)
8030ea1c705Spatrick #define CCU_SDx_CLK_DIV_RATIO_M_SHIFT 0
8040ea1c705Spatrick
8050ea1c705Spatrick int
sxiccmu_mmc_do_set_frequency(struct sxiccmu_clock * sc,uint32_t freq,uint32_t parent_freq)8060ea1c705Spatrick sxiccmu_mmc_do_set_frequency(struct sxiccmu_clock *sc, uint32_t freq,
8070ea1c705Spatrick uint32_t parent_freq)
8080ea1c705Spatrick {
8090ea1c705Spatrick uint32_t reg, m, n;
8100ea1c705Spatrick uint32_t clk_src;
8110ea1c705Spatrick
8120ea1c705Spatrick switch (freq) {
8130ea1c705Spatrick case 400000:
8140ea1c705Spatrick n = 2, m = 15;
8150ea1c705Spatrick clk_src = CCU_SDx_CLK_SRC_SEL_OSC24M;
8160ea1c705Spatrick break;
8171ed198b7Skettenis case 20000000:
8180ea1c705Spatrick case 25000000:
8190ea1c705Spatrick case 26000000:
8200ea1c705Spatrick case 50000000:
8210ea1c705Spatrick case 52000000:
8220ea1c705Spatrick n = 0, m = 0;
8230ea1c705Spatrick clk_src = CCU_SDx_CLK_SRC_SEL_PLL6;
8240ea1c705Spatrick while ((parent_freq / (1 << n) / 16) > freq)
8250ea1c705Spatrick n++;
8260ea1c705Spatrick while ((parent_freq / (1 << n) / (m + 1)) > freq)
8270ea1c705Spatrick m++;
8280ea1c705Spatrick break;
8290ea1c705Spatrick default:
8300ea1c705Spatrick return -1;
8310ea1c705Spatrick }
8320ea1c705Spatrick
8330ea1c705Spatrick reg = SXIREAD4(sc, 0);
8340ea1c705Spatrick reg &= ~CCU_SDx_CLK_SRC_SEL_MASK;
8350ea1c705Spatrick reg |= clk_src;
8360ea1c705Spatrick reg &= ~CCU_SDx_CLK_DIV_RATIO_N_MASK;
8370ea1c705Spatrick reg |= n << CCU_SDx_CLK_DIV_RATIO_N_SHIFT;
8380ea1c705Spatrick reg &= ~CCU_SDx_CLK_DIV_RATIO_M_MASK;
8390ea1c705Spatrick reg |= m << CCU_SDx_CLK_DIV_RATIO_M_SHIFT;
8400ea1c705Spatrick SXIWRITE4(sc, 0, reg);
8410ea1c705Spatrick
8420ea1c705Spatrick return 0;
8430ea1c705Spatrick }
8440ea1c705Spatrick
8450ea1c705Spatrick int
sxiccmu_mmc_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)8460ea1c705Spatrick sxiccmu_mmc_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
8470ea1c705Spatrick {
8480ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
8490ea1c705Spatrick uint32_t parent_freq;
8500ea1c705Spatrick
8510ea1c705Spatrick if (cells[0] != 0)
8520ea1c705Spatrick return -1;
8530ea1c705Spatrick
8540ea1c705Spatrick parent_freq = clock_get_frequency_idx(sc->sc_node, 1);
8550ea1c705Spatrick return sxiccmu_mmc_do_set_frequency(sc, freq, parent_freq);
8560ea1c705Spatrick }
8570ea1c705Spatrick
8580ea1c705Spatrick void
sxiccmu_mmc_enable(void * cookie,uint32_t * cells,int on)8590ea1c705Spatrick sxiccmu_mmc_enable(void *cookie, uint32_t *cells, int on)
8600ea1c705Spatrick {
8610ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
8620ea1c705Spatrick
8630ea1c705Spatrick if (cells[0] != 0)
8640ea1c705Spatrick return;
8650ea1c705Spatrick
8660ea1c705Spatrick if (on)
8670ea1c705Spatrick SXISET4(sc, 0, CCU_SDx_SCLK_GATING);
8680ea1c705Spatrick else
8690ea1c705Spatrick SXICLR4(sc, 0, CCU_SDx_SCLK_GATING);
8700ea1c705Spatrick }
8710ea1c705Spatrick
8720ea1c705Spatrick void
sxiccmu_gate_enable(void * cookie,uint32_t * cells,int on)8730ea1c705Spatrick sxiccmu_gate_enable(void *cookie, uint32_t *cells, int on)
8740ea1c705Spatrick {
8750ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
8760ea1c705Spatrick int reg = cells[0] / 32;
8770ea1c705Spatrick int bit = cells[0] % 32;
8780ea1c705Spatrick
8790ea1c705Spatrick if (on) {
8800ea1c705Spatrick clock_enable(sc->sc_node, NULL);
8810ea1c705Spatrick SXISET4(sc, reg * 4, (1U << bit));
8820ea1c705Spatrick } else {
8830ea1c705Spatrick SXICLR4(sc, reg * 4, (1U << bit));
8840ea1c705Spatrick clock_disable(sc->sc_node, NULL);
8850ea1c705Spatrick }
8860ea1c705Spatrick }
8870ea1c705Spatrick
8880ea1c705Spatrick void
sxiccmu_reset(void * cookie,uint32_t * cells,int assert)8890ea1c705Spatrick sxiccmu_reset(void *cookie, uint32_t *cells, int assert)
8900ea1c705Spatrick {
8910ea1c705Spatrick struct sxiccmu_clock *sc = cookie;
8920ea1c705Spatrick int reg = cells[0] / 32;
8930ea1c705Spatrick int bit = cells[0] % 32;
8940ea1c705Spatrick
8950ea1c705Spatrick if (assert)
8960ea1c705Spatrick SXICLR4(sc, reg * 4, (1U << bit));
8970ea1c705Spatrick else
8980ea1c705Spatrick SXISET4(sc, reg * 4, (1U << bit));
8990ea1c705Spatrick }
9000ea1c705Spatrick
9010ea1c705Spatrick /*
902b5fbf381Skettenis * Newer device trees, such as those for the Allwinner H3/A64 have
903b5fbf381Skettenis * most of the clock nodes replaced with a single clock control unit
904b5fbf381Skettenis * node.
9050ea1c705Spatrick */
9060ea1c705Spatrick
9070ea1c705Spatrick uint32_t
sxiccmu_ccu_get_frequency(void * cookie,uint32_t * cells)9080ea1c705Spatrick sxiccmu_ccu_get_frequency(void *cookie, uint32_t *cells)
9090ea1c705Spatrick {
9100ea1c705Spatrick struct sxiccmu_softc *sc = cookie;
9110ea1c705Spatrick uint32_t idx = cells[0];
9120ea1c705Spatrick uint32_t parent;
9130ea1c705Spatrick
9140ea1c705Spatrick if (idx < sc->sc_ngates && sc->sc_gates[idx].parent) {
9150ea1c705Spatrick parent = sc->sc_gates[idx].parent;
9160ea1c705Spatrick return sxiccmu_ccu_get_frequency(sc, &parent);
9170ea1c705Spatrick }
9180ea1c705Spatrick
9190ea1c705Spatrick return sc->sc_get_frequency(sc, idx);
9200ea1c705Spatrick }
9210ea1c705Spatrick
922199786d9Skettenis /* Allwinner A10/A10s/A13/A20 */
923d524c0faSkettenis #define A10_PLL1_CFG_REG 0x0000
924193b1cd6Skettenis #define A10_PLL1_OUT_EXT_DIVP_MASK (0x3 << 16)
925193b1cd6Skettenis #define A10_PLL1_OUT_EXT_DIVP_SHIFT 16
926d524c0faSkettenis #define A10_PLL1_OUT_EXT_DIVP(x) (((x) >> 16) & 0x3)
927d524c0faSkettenis #define A10_PLL1_FACTOR_N(x) (((x) >> 8) & 0x1f)
928193b1cd6Skettenis #define A10_PLL1_FACTOR_N_MASK (0x1f << 8)
929193b1cd6Skettenis #define A10_PLL1_FACTOR_N_SHIFT 8
930d524c0faSkettenis #define A10_PLL1_FACTOR_K(x) (((x) >> 4) & 0x3)
931193b1cd6Skettenis #define A10_PLL1_FACTOR_K_MASK (0x3 << 4)
932193b1cd6Skettenis #define A10_PLL1_FACTOR_K_SHIFT 4
933d524c0faSkettenis #define A10_PLL1_FACTOR_M(x) (((x) >> 0) & 0x3)
934193b1cd6Skettenis #define A10_PLL1_FACTOR_M_MASK (0x3 << 0)
935193b1cd6Skettenis #define A10_PLL1_FACTOR_M_SHIFT 0
936d524c0faSkettenis #define A10_CPU_AHB_APB0_CFG_REG 0x0054
937d524c0faSkettenis #define A10_CPU_CLK_SRC_SEL (0x3 << 16)
938d524c0faSkettenis #define A10_CPU_CLK_SRC_SEL_LOSC (0x0 << 16)
939d524c0faSkettenis #define A10_CPU_CLK_SRC_SEL_OSC24M (0x1 << 16)
940d524c0faSkettenis #define A10_CPU_CLK_SRC_SEL_PLL1 (0x2 << 16)
941d524c0faSkettenis #define A10_CPU_CLK_SRC_SEL_200MHZ (0x3 << 16)
942ea5e035fSkettenis #define A10_AHB_CLK_DIV_RATIO(x) (((x) >> 8) & 0x3)
943ea5e035fSkettenis #define A10_AXI_CLK_DIV_RATIO(x) (((x) >> 0) & 0x3)
944d524c0faSkettenis
945d524c0faSkettenis uint32_t
sxiccmu_a10_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)946d524c0faSkettenis sxiccmu_a10_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
947d524c0faSkettenis {
948d524c0faSkettenis uint32_t parent;
949ea5e035fSkettenis uint32_t reg, div;
950ea5e035fSkettenis uint32_t k, m, n, p;
951d524c0faSkettenis
952d524c0faSkettenis switch (idx) {
953d524c0faSkettenis case A10_CLK_LOSC:
954d524c0faSkettenis return clock_get_frequency(sc->sc_node, "losc");
955d524c0faSkettenis case A10_CLK_HOSC:
956d524c0faSkettenis return clock_get_frequency(sc->sc_node, "hosc");
957d524c0faSkettenis case A10_CLK_PLL_CORE:
958d524c0faSkettenis reg = SXIREAD4(sc, A10_PLL1_CFG_REG);
959d524c0faSkettenis k = A10_PLL1_FACTOR_K(reg) + 1;
960d524c0faSkettenis m = A10_PLL1_FACTOR_M(reg) + 1;
961d524c0faSkettenis n = A10_PLL1_FACTOR_N(reg);
962d524c0faSkettenis p = 1 << A10_PLL1_OUT_EXT_DIVP(reg);
963d524c0faSkettenis return (24000000 * n * k) / (m * p);
964d524c0faSkettenis case A10_CLK_PLL_PERIPH_BASE:
965d524c0faSkettenis /* Not hardcoded, but recommended. */
966d524c0faSkettenis return 600000000;
967d524c0faSkettenis case A10_CLK_PLL_PERIPH:
968d524c0faSkettenis return sxiccmu_a10_get_frequency(sc, A10_CLK_PLL_PERIPH_BASE) * 2;
969d524c0faSkettenis case A10_CLK_CPU:
970d524c0faSkettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
971d524c0faSkettenis switch (reg & A10_CPU_CLK_SRC_SEL) {
972d524c0faSkettenis case A10_CPU_CLK_SRC_SEL_LOSC:
973d524c0faSkettenis parent = A10_CLK_LOSC;
974d524c0faSkettenis break;
975d524c0faSkettenis case A10_CPU_CLK_SRC_SEL_OSC24M:
976d524c0faSkettenis parent = A10_CLK_HOSC;
977d524c0faSkettenis break;
978d524c0faSkettenis case A10_CPU_CLK_SRC_SEL_PLL1:
979d524c0faSkettenis parent = A10_CLK_PLL_CORE;
980d524c0faSkettenis break;
981d524c0faSkettenis case A10_CPU_CLK_SRC_SEL_200MHZ:
982d524c0faSkettenis return 200000000;
983d524c0faSkettenis }
984d524c0faSkettenis return sxiccmu_ccu_get_frequency(sc, &parent);
985ea5e035fSkettenis case A10_CLK_AXI:
986ea5e035fSkettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
987ea5e035fSkettenis div = 1 << A10_AXI_CLK_DIV_RATIO(reg);
988ea5e035fSkettenis parent = A10_CLK_CPU;
989ea5e035fSkettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
990ea5e035fSkettenis case A10_CLK_AHB:
991ea5e035fSkettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
992ea5e035fSkettenis div = 1 << A10_AHB_CLK_DIV_RATIO(reg);
993ea5e035fSkettenis parent = A10_CLK_AXI;
994ea5e035fSkettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
995d524c0faSkettenis case A10_CLK_APB1:
996d524c0faSkettenis /* XXX Controlled by a MUX. */
997d524c0faSkettenis return 24000000;
998d524c0faSkettenis }
999d524c0faSkettenis
1000d524c0faSkettenis printf("%s: 0x%08x\n", __func__, idx);
1001d524c0faSkettenis return 0;
1002d524c0faSkettenis }
1003d524c0faSkettenis
1004199786d9Skettenis uint32_t
sxiccmu_a10s_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)1005199786d9Skettenis sxiccmu_a10s_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
1006199786d9Skettenis {
1007199786d9Skettenis uint32_t parent;
1008199786d9Skettenis uint32_t reg, div;
1009199786d9Skettenis uint32_t k, m, n, p;
1010199786d9Skettenis
1011199786d9Skettenis switch (idx) {
1012199786d9Skettenis case A10S_CLK_LOSC:
1013199786d9Skettenis return clock_get_frequency(sc->sc_node, "losc");
1014199786d9Skettenis case A10S_CLK_HOSC:
1015199786d9Skettenis return clock_get_frequency(sc->sc_node, "hosc");
1016199786d9Skettenis case A10S_CLK_PLL_CORE:
1017199786d9Skettenis reg = SXIREAD4(sc, A10_PLL1_CFG_REG);
1018199786d9Skettenis k = A10_PLL1_FACTOR_K(reg) + 1;
1019199786d9Skettenis m = A10_PLL1_FACTOR_M(reg) + 1;
1020199786d9Skettenis n = A10_PLL1_FACTOR_N(reg);
1021199786d9Skettenis p = 1 << A10_PLL1_OUT_EXT_DIVP(reg);
1022199786d9Skettenis return (24000000 * n * k) / (m * p);
1023199786d9Skettenis case A10S_CLK_PLL_PERIPH:
1024199786d9Skettenis return 1200000000;
1025199786d9Skettenis case A10S_CLK_CPU:
1026199786d9Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1027199786d9Skettenis switch (reg & A10_CPU_CLK_SRC_SEL) {
1028199786d9Skettenis case A10_CPU_CLK_SRC_SEL_LOSC:
1029199786d9Skettenis parent = A10S_CLK_LOSC;
1030199786d9Skettenis break;
1031199786d9Skettenis case A10_CPU_CLK_SRC_SEL_OSC24M:
1032199786d9Skettenis parent = A10S_CLK_HOSC;
1033199786d9Skettenis break;
1034199786d9Skettenis case A10_CPU_CLK_SRC_SEL_PLL1:
1035199786d9Skettenis parent = A10S_CLK_PLL_CORE;
1036199786d9Skettenis break;
1037199786d9Skettenis case A10_CPU_CLK_SRC_SEL_200MHZ:
1038199786d9Skettenis return 200000000;
1039199786d9Skettenis }
1040199786d9Skettenis return sxiccmu_ccu_get_frequency(sc, &parent);
1041199786d9Skettenis case A10S_CLK_AXI:
1042199786d9Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1043199786d9Skettenis div = 1 << A10_AXI_CLK_DIV_RATIO(reg);
1044199786d9Skettenis parent = A10S_CLK_CPU;
1045199786d9Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1046199786d9Skettenis case A10S_CLK_AHB:
1047199786d9Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1048199786d9Skettenis div = 1 << A10_AHB_CLK_DIV_RATIO(reg);
1049199786d9Skettenis parent = A10S_CLK_AXI;
1050199786d9Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1051199786d9Skettenis case A10S_CLK_APB1:
1052199786d9Skettenis /* XXX Controlled by a MUX. */
1053199786d9Skettenis return 24000000;
1054199786d9Skettenis }
1055199786d9Skettenis
1056199786d9Skettenis printf("%s: 0x%08x\n", __func__, idx);
1057199786d9Skettenis return 0;
1058199786d9Skettenis }
1059199786d9Skettenis
106029e54e85Skettenis /* Allwinner A23/A64/H3/H5/R40 */
1061b5fbf381Skettenis #define CCU_AHB1_APB1_CFG_REG 0x0054
1062b5fbf381Skettenis #define CCU_AHB1_CLK_SRC_SEL (3 << 12)
1063b5fbf381Skettenis #define CCU_AHB1_CLK_SRC_SEL_LOSC (0 << 12)
1064b5fbf381Skettenis #define CCU_AHB1_CLK_SRC_SEL_OSC24M (1 << 12)
1065b5fbf381Skettenis #define CCU_AHB1_CLK_SRC_SEL_AXI (2 << 12)
1066b5fbf381Skettenis #define CCU_AHB1_CLK_SRC_SEL_PERIPH0 (3 << 12)
1067b5fbf381Skettenis #define CCU_AHB1_PRE_DIV(x) ((((x) >> 6) & 3) + 1)
1068b5fbf381Skettenis #define CCU_AHB1_CLK_DIV_RATIO(x) (1 << (((x) >> 4) & 3))
1069b5fbf381Skettenis #define CCU_AHB2_CFG_REG 0x005c
1070b5fbf381Skettenis #define CCU_AHB2_CLK_CFG (3 << 0)
1071b5fbf381Skettenis
10720ea1c705Spatrick uint32_t
sxiccmu_a23_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)1073f0a8b882Skevlo sxiccmu_a23_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
1074f0a8b882Skevlo {
1075f0a8b882Skevlo uint32_t parent;
1076f0a8b882Skevlo uint32_t reg, div;
1077f0a8b882Skevlo
1078f0a8b882Skevlo switch (idx) {
1079f0a8b882Skevlo case A23_CLK_LOSC:
1080f0a8b882Skevlo return clock_get_frequency(sc->sc_node, "losc");
1081f0a8b882Skevlo case A23_CLK_HOSC:
1082f0a8b882Skevlo return clock_get_frequency(sc->sc_node, "hosc");
1083f0a8b882Skevlo case A23_CLK_PLL_PERIPH:
1084f0a8b882Skevlo /* Not hardcoded, but recommended. */
1085f0a8b882Skevlo return 600000000;
1086f0a8b882Skevlo case A23_CLK_APB2:
1087f0a8b882Skevlo /* XXX Controlled by a MUX. */
1088f0a8b882Skevlo return 24000000;
1089f0a8b882Skevlo case A23_CLK_AHB1:
1090f0a8b882Skevlo reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
1091f0a8b882Skevlo div = CCU_AHB1_CLK_DIV_RATIO(reg);
1092f0a8b882Skevlo switch (reg & CCU_AHB1_CLK_SRC_SEL) {
1093f0a8b882Skevlo case CCU_AHB1_CLK_SRC_SEL_LOSC:
1094f0a8b882Skevlo parent = A23_CLK_LOSC;
1095f0a8b882Skevlo break;
1096f0a8b882Skevlo case CCU_AHB1_CLK_SRC_SEL_OSC24M:
1097f0a8b882Skevlo parent = A23_CLK_HOSC;
1098f0a8b882Skevlo break;
1099f0a8b882Skevlo case CCU_AHB1_CLK_SRC_SEL_AXI:
1100f0a8b882Skevlo parent = A23_CLK_AXI;
1101f0a8b882Skevlo break;
1102f0a8b882Skevlo case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
1103f0a8b882Skevlo parent = A23_CLK_PLL_PERIPH;
1104f0a8b882Skevlo div *= CCU_AHB1_PRE_DIV(reg);
1105f0a8b882Skevlo break;
1106f0a8b882Skevlo default:
1107f0a8b882Skevlo return 0;
1108f0a8b882Skevlo }
1109f0a8b882Skevlo return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1110f0a8b882Skevlo }
1111f0a8b882Skevlo
1112f0a8b882Skevlo printf("%s: 0x%08x\n", __func__, idx);
1113f0a8b882Skevlo return 0;
1114f0a8b882Skevlo }
1115f0a8b882Skevlo
1116bced15f9Skettenis #define A64_PLL_CPUX_CTRL_REG 0x0000
1117bced15f9Skettenis #define A64_PLL_CPUX_LOCK (1 << 28)
1118bced15f9Skettenis #define A64_PLL_CPUX_OUT_EXT_DIVP(x) (((x) >> 16) & 0x3)
1119bced15f9Skettenis #define A64_PLL_CPUX_OUT_EXT_DIVP_MASK (0x3 << 16)
1120bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_N(x) (((x) >> 8) & 0x1f)
1121bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_N_MASK (0x1f << 8)
1122bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_N_SHIFT 8
1123bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_K(x) (((x) >> 4) & 0x3)
1124bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_K_MASK (0x3 << 4)
1125bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_K_SHIFT 4
1126bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_M(x) (((x) >> 0) & 0x3)
1127bced15f9Skettenis #define A64_PLL_CPUX_FACTOR_M_MASK (0x3 << 0)
1128bced15f9Skettenis #define A64_CPUX_AXI_CFG_REG 0x0050
1129bced15f9Skettenis #define A64_CPUX_CLK_SRC_SEL (0x3 << 16)
1130bced15f9Skettenis #define A64_CPUX_CLK_SRC_SEL_LOSC (0x0 << 16)
1131bced15f9Skettenis #define A64_CPUX_CLK_SRC_SEL_OSC24M (0x1 << 16)
1132bced15f9Skettenis #define A64_CPUX_CLK_SRC_SEL_PLL_CPUX (0x2 << 16)
1133bced15f9Skettenis
1134f0a8b882Skevlo uint32_t
sxiccmu_a64_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)11350ea1c705Spatrick sxiccmu_a64_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
11360ea1c705Spatrick {
1137fcb049cbSpatrick uint32_t parent;
1138fcb049cbSpatrick uint32_t reg, div;
1139bced15f9Skettenis uint32_t k, m, n, p;
1140fcb049cbSpatrick
11410ea1c705Spatrick switch (idx) {
1142bced15f9Skettenis case A64_CLK_PLL_CPUX:
1143bced15f9Skettenis reg = SXIREAD4(sc, A64_PLL_CPUX_CTRL_REG);
1144bced15f9Skettenis k = A64_PLL_CPUX_FACTOR_K(reg) + 1;
1145bced15f9Skettenis m = A64_PLL_CPUX_FACTOR_M(reg) + 1;
1146bced15f9Skettenis n = A64_PLL_CPUX_FACTOR_N(reg) + 1;
1147bced15f9Skettenis p = 1 << A64_PLL_CPUX_OUT_EXT_DIVP(reg);
1148bced15f9Skettenis return (24000000 * n * k) / (m * p);
1149bced15f9Skettenis case A64_CLK_CPUX:
1150bced15f9Skettenis reg = SXIREAD4(sc, A64_CPUX_AXI_CFG_REG);
1151bced15f9Skettenis switch (reg & A64_CPUX_CLK_SRC_SEL) {
1152bced15f9Skettenis case A64_CPUX_CLK_SRC_SEL_LOSC:
1153bced15f9Skettenis parent = A64_CLK_LOSC;
1154bced15f9Skettenis break;
1155bced15f9Skettenis case A64_CPUX_CLK_SRC_SEL_OSC24M:
1156bced15f9Skettenis parent = A64_CLK_HOSC;
1157bced15f9Skettenis break;
1158bced15f9Skettenis case A64_CPUX_CLK_SRC_SEL_PLL_CPUX:
1159bced15f9Skettenis parent = A64_CLK_PLL_CPUX;
1160bced15f9Skettenis break;
1161bced15f9Skettenis default:
1162bced15f9Skettenis return 0;
1163bced15f9Skettenis }
1164bced15f9Skettenis return sxiccmu_ccu_get_frequency(sc, &parent);
1165fcb049cbSpatrick case A64_CLK_LOSC:
1166fcb049cbSpatrick return clock_get_frequency(sc->sc_node, "losc");
1167fcb049cbSpatrick case A64_CLK_HOSC:
1168fcb049cbSpatrick return clock_get_frequency(sc->sc_node, "hosc");
11690ea1c705Spatrick case A64_CLK_PLL_PERIPH0:
1170b5fbf381Skettenis /* Not hardcoded, but recommended. */
11710ea1c705Spatrick return 600000000;
11720ea1c705Spatrick case A64_CLK_PLL_PERIPH0_2X:
11730ea1c705Spatrick return sxiccmu_a64_get_frequency(sc, A64_CLK_PLL_PERIPH0) * 2;
11740ea1c705Spatrick case A64_CLK_APB2:
11750ea1c705Spatrick /* XXX Controlled by a MUX. */
11760ea1c705Spatrick return 24000000;
1177fcb049cbSpatrick case A64_CLK_AHB1:
1178fcb049cbSpatrick reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
1179fcb049cbSpatrick div = CCU_AHB1_CLK_DIV_RATIO(reg);
1180fcb049cbSpatrick switch (reg & CCU_AHB1_CLK_SRC_SEL) {
1181fcb049cbSpatrick case CCU_AHB1_CLK_SRC_SEL_LOSC:
1182fcb049cbSpatrick parent = A64_CLK_LOSC;
1183fcb049cbSpatrick break;
1184fcb049cbSpatrick case CCU_AHB1_CLK_SRC_SEL_OSC24M:
1185fcb049cbSpatrick parent = A64_CLK_HOSC;
1186fcb049cbSpatrick break;
1187fcb049cbSpatrick case CCU_AHB1_CLK_SRC_SEL_AXI:
1188fcb049cbSpatrick parent = A64_CLK_AXI;
1189fcb049cbSpatrick break;
1190fcb049cbSpatrick case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
1191fcb049cbSpatrick parent = A64_CLK_PLL_PERIPH0;
1192fcb049cbSpatrick div *= CCU_AHB1_PRE_DIV(reg);
1193fcb049cbSpatrick break;
1194fcb049cbSpatrick default:
1195fcb049cbSpatrick return 0;
1196fcb049cbSpatrick }
1197fcb049cbSpatrick return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1198fcb049cbSpatrick case A64_CLK_AHB2:
1199fcb049cbSpatrick reg = SXIREAD4(sc, CCU_AHB2_CFG_REG);
1200fcb049cbSpatrick switch (reg & CCU_AHB2_CLK_CFG) {
1201fcb049cbSpatrick case 0:
1202fcb049cbSpatrick parent = A64_CLK_AHB1;
1203fcb049cbSpatrick div = 1;
1204fcb049cbSpatrick break;
1205fcb049cbSpatrick case 1:
1206fcb049cbSpatrick parent = A64_CLK_PLL_PERIPH0;
1207fcb049cbSpatrick div = 2;
1208fcb049cbSpatrick break;
1209fcb049cbSpatrick default:
1210fcb049cbSpatrick return 0;
1211fcb049cbSpatrick }
1212fcb049cbSpatrick return sxiccmu_ccu_get_frequency(sc, &parent) / div;
12130ea1c705Spatrick }
12140ea1c705Spatrick
12150ea1c705Spatrick printf("%s: 0x%08x\n", __func__, idx);
12160ea1c705Spatrick return 0;
12170ea1c705Spatrick }
12180ea1c705Spatrick
1219cf192b2fSkettenis #define A80_AHB1_CLK_CFG_REG 0x0064
1220cf192b2fSkettenis #define A80_AHB1_SRC_CLK_SELECT (3 << 24)
1221cf192b2fSkettenis #define A80_AHB1_SRC_CLK_SELECT_GTBUS (0 << 24)
1222cf192b2fSkettenis #define A80_AHB1_SRC_CLK_SELECT_PERIPH0 (1 << 24)
1223cf192b2fSkettenis #define A80_AHB1_CLK_DIV_RATIO(x) (1 << ((x) & 0x3))
1224cf192b2fSkettenis
12250ea1c705Spatrick uint32_t
sxiccmu_a80_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)1226d526c0edSkettenis sxiccmu_a80_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
1227d526c0edSkettenis {
1228cf192b2fSkettenis uint32_t parent;
1229cf192b2fSkettenis uint32_t reg, div;
1230cf192b2fSkettenis
1231d526c0edSkettenis switch (idx) {
1232d526c0edSkettenis case A80_CLK_PLL_PERIPH0:
1233d526c0edSkettenis /* Not hardcoded, but recommended. */
1234d526c0edSkettenis return 960000000;
1235cf192b2fSkettenis case A80_CLK_AHB1:
1236cf192b2fSkettenis reg = SXIREAD4(sc, A80_AHB1_CLK_CFG_REG);
1237cf192b2fSkettenis div = A80_AHB1_CLK_DIV_RATIO(reg);
1238cf192b2fSkettenis switch (reg & A80_AHB1_SRC_CLK_SELECT) {
1239cf192b2fSkettenis case A80_AHB1_SRC_CLK_SELECT_GTBUS:
1240cf192b2fSkettenis parent = A80_CLK_GTBUS;
1241cf192b2fSkettenis break;
1242cf192b2fSkettenis case A80_AHB1_SRC_CLK_SELECT_PERIPH0:
1243cf192b2fSkettenis parent = A80_CLK_PLL_PERIPH0;
1244cf192b2fSkettenis break;
1245cf192b2fSkettenis default:
1246cf192b2fSkettenis parent = A80_CLK_PLL_PERIPH1;
1247cf192b2fSkettenis break;
1248cf192b2fSkettenis }
1249cf192b2fSkettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1250d526c0edSkettenis case A80_CLK_APB1:
1251d526c0edSkettenis /* XXX Controlled by a MUX. */
1252d526c0edSkettenis return 24000000;
1253d526c0edSkettenis }
1254d526c0edSkettenis
1255d526c0edSkettenis printf("%s: 0x%08x\n", __func__, idx);
1256d526c0edSkettenis return 0;
1257d526c0edSkettenis }
1258d526c0edSkettenis
12594e48f70dSkettenis /* Allwinner D1 */
12604718ccb1Skettenis #define D1_PLL_CPU_CTRL_REG 0x0000
12614718ccb1Skettenis #define D1_PLL_CPU_FACTOR_M(x) (((x) >> 0) & 0x3)
12624718ccb1Skettenis #define D1_PLL_CPU_FACTOR_N(x) (((x) >> 8) & 0xff)
12634718ccb1Skettenis #define D1_RISCV_CLK_REG 0x0d00
12644718ccb1Skettenis #define D1_RISCV_CLK_SEL (7 << 24)
12654718ccb1Skettenis #define D1_RISCV_CLK_SEL_HOSC (0 << 24)
12664718ccb1Skettenis #define D1_RISCV_CLK_SEL_PLL_CPU (5 << 24)
12674718ccb1Skettenis #define D1_RISCV_DIV_CFG_FACTOR_M(x) (((x) >> 0) & 0x1f)
1268*95c4b0b5Skevlo #define D1_PSI_CLK_REG 0x0510
1269*95c4b0b5Skevlo #define D1_PSI_CLK_FACTOR_N(x) (((x) >> 8) & 0x3)
1270*95c4b0b5Skevlo #define D1_PSI_CLK_FACTOR_M(x) (((x) >> 0) & 0x3)
12714e48f70dSkettenis
12724e48f70dSkettenis uint32_t
sxiccmu_d1_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)12734e48f70dSkettenis sxiccmu_d1_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
12744e48f70dSkettenis {
12754718ccb1Skettenis uint32_t parent;
1276*95c4b0b5Skevlo uint32_t reg, freq;
12774718ccb1Skettenis uint32_t m, n;
12784718ccb1Skettenis
12794e48f70dSkettenis switch (idx) {
12804718ccb1Skettenis case D1_CLK_HOSC:
12814718ccb1Skettenis return clock_get_frequency(sc->sc_node, "hosc");
12824718ccb1Skettenis case D1_CLK_PLL_CPU:
12834718ccb1Skettenis reg = SXIREAD4(sc, D1_PLL_CPU_CTRL_REG);
12844718ccb1Skettenis m = D1_PLL_CPU_FACTOR_M(reg) + 1;
12854718ccb1Skettenis n = D1_PLL_CPU_FACTOR_N(reg) + 1;
12864718ccb1Skettenis return (24000000 * n) / m;
12874718ccb1Skettenis case D1_CLK_PLL_PERIPH0:
12884718ccb1Skettenis /* Not hardcoded, but recommended. */
12894718ccb1Skettenis return 600000000;
12904e48f70dSkettenis case D1_CLK_APB1:
12914e48f70dSkettenis /* XXX Controlled by a MUX. */
12924e48f70dSkettenis return 24000000;
12934718ccb1Skettenis case D1_CLK_RISCV:
12944718ccb1Skettenis reg = SXIREAD4(sc, D1_RISCV_CLK_REG);
12954718ccb1Skettenis switch (reg & D1_RISCV_CLK_SEL) {
12964718ccb1Skettenis case D1_RISCV_CLK_SEL_HOSC:
12974718ccb1Skettenis parent = D1_CLK_HOSC;
12984718ccb1Skettenis break;
12994718ccb1Skettenis case D1_RISCV_CLK_SEL_PLL_CPU:
13004718ccb1Skettenis parent = D1_CLK_PLL_CPU;
13014718ccb1Skettenis break;
13024718ccb1Skettenis default:
13034718ccb1Skettenis return 0;
13044718ccb1Skettenis }
13054718ccb1Skettenis m = D1_RISCV_DIV_CFG_FACTOR_M(reg) + 1;
13064718ccb1Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / m;
1307*95c4b0b5Skevlo case D1_CLK_PSI_AHB:
1308*95c4b0b5Skevlo reg = SXIREAD4(sc, D1_PSI_CLK_REG);
1309*95c4b0b5Skevlo /* assume PLL_PERIPH0 source */
1310*95c4b0b5Skevlo freq = sxiccmu_d1_get_frequency(sc, D1_CLK_PLL_PERIPH0);
1311*95c4b0b5Skevlo m = D1_PSI_CLK_FACTOR_M(reg) + 1;
1312*95c4b0b5Skevlo n = 1 << D1_PSI_CLK_FACTOR_N(reg);
1313*95c4b0b5Skevlo return freq / (m * n);
13144e48f70dSkettenis }
13154e48f70dSkettenis
13164e48f70dSkettenis printf("%s: 0x%08x\n", __func__, idx);
13174e48f70dSkettenis return 0;
13184e48f70dSkettenis }
13194e48f70dSkettenis
13203c38ffe8Skettenis /* Allwinner H3/H5 */
13213c38ffe8Skettenis #define H3_PLL_CPUX_CTRL_REG 0x0000
1322dcec61dcSmiod #define H3_PLL_CPUX_ENABLE (1U << 31)
1323f1089a02Skettenis #define H3_PLL_CPUX_LOCK (1 << 28)
13243c38ffe8Skettenis #define H3_PLL_CPUX_OUT_EXT_DIVP(x) (((x) >> 16) & 0x3)
1325f1089a02Skettenis #define H3_PLL_CPUX_OUT_EXT_DIVP_MASK (0x3 << 16)
1326f1089a02Skettenis #define H3_PLL_CPUX_OUT_EXT_DIVP_SHIFT 16
13273c38ffe8Skettenis #define H3_PLL_CPUX_FACTOR_N(x) (((x) >> 8) & 0x1f)
1328f1089a02Skettenis #define H3_PLL_CPUX_FACTOR_N_MASK (0x1f << 8)
1329f1089a02Skettenis #define H3_PLL_CPUX_FACTOR_N_SHIFT 8
13303c38ffe8Skettenis #define H3_PLL_CPUX_FACTOR_K(x) (((x) >> 4) & 0x3)
1331f1089a02Skettenis #define H3_PLL_CPUX_FACTOR_K_MASK (0x3 << 4)
1332f1089a02Skettenis #define H3_PLL_CPUX_FACTOR_K_SHIFT 4
13333c38ffe8Skettenis #define H3_PLL_CPUX_FACTOR_M(x) (((x) >> 0) & 0x3)
1334f1089a02Skettenis #define H3_PLL_CPUX_FACTOR_M_MASK (0x3 << 0)
1335f1089a02Skettenis #define H3_PLL_CPUX_FACTOR_M_SHIFT 0
13363c38ffe8Skettenis #define H3_CPUX_AXI_CFG_REG 0x0050
13373c38ffe8Skettenis #define H3_CPUX_CLK_SRC_SEL (0x3 << 16)
13383c38ffe8Skettenis #define H3_CPUX_CLK_SRC_SEL_LOSC (0x0 << 16)
13393c38ffe8Skettenis #define H3_CPUX_CLK_SRC_SEL_OSC24M (0x1 << 16)
13403c38ffe8Skettenis #define H3_CPUX_CLK_SRC_SEL_PLL_CPUX (0x2 << 16)
13419424095dSuaa #define H3_PLL_STABLE_TIME_REG1 0x0204
13429424095dSuaa #define H3_PLL_STABLE_TIME_REG1_TIME(x) (((x) >> 0) & 0xffff)
13433c38ffe8Skettenis
1344d526c0edSkettenis uint32_t
sxiccmu_h3_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)13450ea1c705Spatrick sxiccmu_h3_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
13460ea1c705Spatrick {
1347b5fbf381Skettenis uint32_t parent;
1348b5fbf381Skettenis uint32_t reg, div;
13493c38ffe8Skettenis uint32_t k, m, n, p;
1350b5fbf381Skettenis
13510ea1c705Spatrick switch (idx) {
1352b5fbf381Skettenis case H3_CLK_LOSC:
1353b5fbf381Skettenis return clock_get_frequency(sc->sc_node, "losc");
1354b5fbf381Skettenis case H3_CLK_HOSC:
1355b5fbf381Skettenis return clock_get_frequency(sc->sc_node, "hosc");
13563c38ffe8Skettenis case H3_CLK_PLL_CPUX:
13573c38ffe8Skettenis reg = SXIREAD4(sc, H3_PLL_CPUX_CTRL_REG);
13583c38ffe8Skettenis k = H3_PLL_CPUX_FACTOR_K(reg) + 1;
13593c38ffe8Skettenis m = H3_PLL_CPUX_FACTOR_M(reg) + 1;
13603c38ffe8Skettenis n = H3_PLL_CPUX_FACTOR_N(reg) + 1;
13613c38ffe8Skettenis p = 1 << H3_PLL_CPUX_OUT_EXT_DIVP(reg);
13623c38ffe8Skettenis return (24000000 * n * k) / (m * p);
13630ea1c705Spatrick case H3_CLK_PLL_PERIPH0:
1364b5fbf381Skettenis /* Not hardcoded, but recommended. */
13650ea1c705Spatrick return 600000000;
13663c38ffe8Skettenis case H3_CLK_CPUX:
13673c38ffe8Skettenis reg = SXIREAD4(sc, H3_CPUX_AXI_CFG_REG);
13683c38ffe8Skettenis switch (reg & H3_CPUX_CLK_SRC_SEL) {
13693c38ffe8Skettenis case H3_CPUX_CLK_SRC_SEL_LOSC:
13703c38ffe8Skettenis parent = H3_CLK_LOSC;
13713c38ffe8Skettenis break;
13723c38ffe8Skettenis case H3_CPUX_CLK_SRC_SEL_OSC24M:
13733c38ffe8Skettenis parent = H3_CLK_HOSC;
13743c38ffe8Skettenis break;
13753c38ffe8Skettenis case H3_CPUX_CLK_SRC_SEL_PLL_CPUX:
13763c38ffe8Skettenis parent = H3_CLK_PLL_CPUX;
13773c38ffe8Skettenis break;
13783c38ffe8Skettenis default:
13793c38ffe8Skettenis return 0;
13803c38ffe8Skettenis }
13813c38ffe8Skettenis return sxiccmu_ccu_get_frequency(sc, &parent);
13820ea1c705Spatrick case H3_CLK_APB2:
13830ea1c705Spatrick /* XXX Controlled by a MUX. */
13840ea1c705Spatrick return 24000000;
1385b5fbf381Skettenis case H3_CLK_AHB1:
1386b5fbf381Skettenis reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
1387b5fbf381Skettenis div = CCU_AHB1_CLK_DIV_RATIO(reg);
1388b5fbf381Skettenis switch (reg & CCU_AHB1_CLK_SRC_SEL) {
1389b5fbf381Skettenis case CCU_AHB1_CLK_SRC_SEL_LOSC:
1390b5fbf381Skettenis parent = H3_CLK_LOSC;
1391b5fbf381Skettenis break;
1392b5fbf381Skettenis case CCU_AHB1_CLK_SRC_SEL_OSC24M:
1393b5fbf381Skettenis parent = H3_CLK_HOSC;
1394b5fbf381Skettenis break;
1395b5fbf381Skettenis case CCU_AHB1_CLK_SRC_SEL_AXI:
1396b5fbf381Skettenis parent = H3_CLK_AXI;
1397b5fbf381Skettenis break;
1398b5fbf381Skettenis case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
1399b5fbf381Skettenis parent = H3_CLK_PLL_PERIPH0;
1400b5fbf381Skettenis div *= CCU_AHB1_PRE_DIV(reg);
1401b5fbf381Skettenis break;
1402b5fbf381Skettenis default:
1403b5fbf381Skettenis return 0;
1404b5fbf381Skettenis }
1405b5fbf381Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1406b5fbf381Skettenis case H3_CLK_AHB2:
1407b5fbf381Skettenis reg = SXIREAD4(sc, CCU_AHB2_CFG_REG);
1408b5fbf381Skettenis switch (reg & CCU_AHB2_CLK_CFG) {
1409b5fbf381Skettenis case 0:
1410b5fbf381Skettenis parent = H3_CLK_AHB1;
1411b5fbf381Skettenis div = 1;
1412b5fbf381Skettenis break;
1413b5fbf381Skettenis case 1:
1414b5fbf381Skettenis parent = H3_CLK_PLL_PERIPH0;
1415b5fbf381Skettenis div = 2;
1416b5fbf381Skettenis break;
1417b5fbf381Skettenis default:
1418b5fbf381Skettenis return 0;
1419b5fbf381Skettenis }
1420b5fbf381Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
14210ea1c705Spatrick }
14220ea1c705Spatrick
14230ea1c705Spatrick printf("%s: 0x%08x\n", __func__, idx);
14240ea1c705Spatrick return 0;
14250ea1c705Spatrick }
14260ea1c705Spatrick
1427322e71d8Skettenis #define H3_AHB0_CLK_REG 0x0000
1428322e71d8Skettenis #define H3_AHB0_CLK_SRC_SEL (0x3 << 16)
1429322e71d8Skettenis #define H3_AHB0_CLK_SRC_SEL_OSC32K (0x0 << 16)
1430322e71d8Skettenis #define H3_AHB0_CLK_SRC_SEL_OSC24M (0x1 << 16)
1431322e71d8Skettenis #define H3_AHB0_CLK_SRC_SEL_PLL_PERIPH0 (0x2 << 16)
1432322e71d8Skettenis #define H3_AHB0_CLK_SRC_SEL_IOSC (0x3 << 16)
1433322e71d8Skettenis #define H3_AHB0_CLK_PRE_DIV(x) ((((x) >> 8) & 0x1f) + 1)
1434322e71d8Skettenis #define H3_AHB0_CLK_RATIO(x) (1 << (((x) >> 4) & 3))
1435322e71d8Skettenis #define H3_APB0_CFG_REG 0x000c
1436322e71d8Skettenis #define H3_APB0_CLK_RATIO(x) (1 << ((x) & 1))
1437322e71d8Skettenis
1438322e71d8Skettenis uint32_t
sxiccmu_h3_r_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)1439322e71d8Skettenis sxiccmu_h3_r_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
1440322e71d8Skettenis {
1441322e71d8Skettenis uint32_t parent;
1442322e71d8Skettenis uint32_t reg, div;
1443322e71d8Skettenis uint32_t freq;
1444322e71d8Skettenis
1445322e71d8Skettenis switch (idx) {
1446322e71d8Skettenis case H3_R_CLK_AHB0:
1447322e71d8Skettenis reg = SXIREAD4(sc, H3_AHB0_CLK_REG);
1448322e71d8Skettenis switch (reg & H3_AHB0_CLK_SRC_SEL) {
1449322e71d8Skettenis case H3_AHB0_CLK_SRC_SEL_OSC32K:
1450322e71d8Skettenis freq = clock_get_frequency(sc->sc_node, "losc");
1451322e71d8Skettenis break;
1452322e71d8Skettenis case H3_AHB0_CLK_SRC_SEL_OSC24M:
1453322e71d8Skettenis freq = clock_get_frequency(sc->sc_node, "hosc");
1454322e71d8Skettenis break;
1455322e71d8Skettenis case H3_AHB0_CLK_SRC_SEL_PLL_PERIPH0:
1456322e71d8Skettenis freq = clock_get_frequency(sc->sc_node, "pll-periph");
1457322e71d8Skettenis break;
1458322e71d8Skettenis case H3_AHB0_CLK_SRC_SEL_IOSC:
1459322e71d8Skettenis freq = clock_get_frequency(sc->sc_node, "iosc");
1460322e71d8Skettenis break;
1461322e71d8Skettenis }
1462322e71d8Skettenis div = H3_AHB0_CLK_PRE_DIV(reg) * H3_AHB0_CLK_RATIO(reg);
1463322e71d8Skettenis return freq / div;
1464322e71d8Skettenis case H3_R_CLK_APB0:
1465322e71d8Skettenis reg = SXIREAD4(sc, H3_APB0_CFG_REG);
1466322e71d8Skettenis div = H3_APB0_CLK_RATIO(reg);
1467322e71d8Skettenis parent = H3_R_CLK_AHB0;
1468322e71d8Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1469322e71d8Skettenis }
1470322e71d8Skettenis
1471322e71d8Skettenis printf("%s: 0x%08x\n", __func__, idx);
1472322e71d8Skettenis return 0;
1473322e71d8Skettenis }
1474322e71d8Skettenis
1475e67d8589Suaa /* Allwinner H6 */
1476e67d8589Suaa #define H6_AHB3_CFG_REG 0x051c
1477e67d8589Suaa #define H6_AHB3_CLK_FACTOR_N(x) (((x) >> 8) & 0x3)
1478e67d8589Suaa #define H6_AHB3_CLK_FACTOR_M(x) (((x) >> 0) & 0x3)
1479e67d8589Suaa
14802c064ff8Skettenis uint32_t
sxiccmu_h6_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)14818b22851bSkettenis sxiccmu_h6_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
14828b22851bSkettenis {
1483e67d8589Suaa uint32_t reg, m, n;
1484e67d8589Suaa uint32_t freq;
1485e67d8589Suaa
14868b22851bSkettenis switch (idx) {
14878b22851bSkettenis case H6_CLK_PLL_PERIPH0:
14888b22851bSkettenis /* Not hardcoded, but recommended. */
14898b22851bSkettenis return 600000000;
14908b22851bSkettenis case H6_CLK_PLL_PERIPH0_2X:
14918b22851bSkettenis return sxiccmu_h6_get_frequency(sc, H6_CLK_PLL_PERIPH0) * 2;
1492e67d8589Suaa case H6_CLK_AHB3:
1493e67d8589Suaa reg = SXIREAD4(sc, H6_AHB3_CFG_REG);
1494e67d8589Suaa /* assume PLL_PERIPH0 source */
1495e67d8589Suaa freq = sxiccmu_h6_get_frequency(sc, H6_CLK_PLL_PERIPH0);
1496e67d8589Suaa m = H6_AHB3_CLK_FACTOR_M(reg) + 1;
1497e67d8589Suaa n = 1 << H6_AHB3_CLK_FACTOR_N(reg);
1498e67d8589Suaa return freq / (m * n);
14998b22851bSkettenis case H6_CLK_APB2:
15008b22851bSkettenis /* XXX Controlled by a MUX. */
15018b22851bSkettenis return 24000000;
15028b22851bSkettenis }
15038b22851bSkettenis
15048b22851bSkettenis printf("%s: 0x%08x\n", __func__, idx);
15058b22851bSkettenis return 0;
15068b22851bSkettenis }
15078b22851bSkettenis
15088b22851bSkettenis uint32_t
sxiccmu_h6_r_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)15098b22851bSkettenis sxiccmu_h6_r_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
15108b22851bSkettenis {
15118b22851bSkettenis switch (idx) {
15128b22851bSkettenis case H6_R_CLK_APB2:
15138b22851bSkettenis /* XXX Controlled by a MUX. */
15148b22851bSkettenis return 24000000;
151522652d99Suaa }
151622652d99Suaa
151722652d99Suaa printf("%s: 0x%08x\n", __func__, idx);
151822652d99Suaa return 0;
151922652d99Suaa }
152022652d99Suaa
152122652d99Suaa /* Allwinner H616 */
152222652d99Suaa #define H616_AHB3_CFG_REG 0x051c
152322652d99Suaa #define H616_AHB3_CLK_FACTOR_N(x) (((x) >> 8) & 0x3)
152422652d99Suaa #define H616_AHB3_CLK_FACTOR_M(x) (((x) >> 0) & 0x3)
152522652d99Suaa
152622652d99Suaa uint32_t
sxiccmu_h616_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)152722652d99Suaa sxiccmu_h616_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
152822652d99Suaa {
152922652d99Suaa uint32_t reg, m, n;
153022652d99Suaa uint32_t freq;
153122652d99Suaa
153222652d99Suaa switch (idx) {
153322652d99Suaa case H616_CLK_PLL_PERIPH0:
153422652d99Suaa /* Not hardcoded, but recommended. */
153522652d99Suaa return 600000000;
153622652d99Suaa case H616_CLK_PLL_PERIPH0_2X:
153722652d99Suaa return sxiccmu_h616_get_frequency(sc, H616_CLK_PLL_PERIPH0) * 2;
153822652d99Suaa case H616_CLK_AHB3:
153922652d99Suaa reg = SXIREAD4(sc, H616_AHB3_CFG_REG);
154022652d99Suaa /* assume PLL_PERIPH0 source */
154122652d99Suaa freq = sxiccmu_h616_get_frequency(sc, H616_CLK_PLL_PERIPH0);
154222652d99Suaa m = H616_AHB3_CLK_FACTOR_M(reg) + 1;
154322652d99Suaa n = 1 << H616_AHB3_CLK_FACTOR_N(reg);
154422652d99Suaa return freq / (m * n);
154522652d99Suaa case H616_CLK_APB2:
154622652d99Suaa /* XXX Controlled by a MUX. */
154722652d99Suaa return 24000000;
154822652d99Suaa }
154922652d99Suaa
155022652d99Suaa printf("%s: 0x%08x\n", __func__, idx);
155122652d99Suaa return 0;
155222652d99Suaa }
155322652d99Suaa
155422652d99Suaa uint32_t
sxiccmu_h616_r_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)155522652d99Suaa sxiccmu_h616_r_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
155622652d99Suaa {
155722652d99Suaa switch (idx) {
155822652d99Suaa case H616_R_CLK_APB2:
155922652d99Suaa /* XXX Controlled by a MUX. */
156022652d99Suaa return 24000000;
15618b22851bSkettenis }
15628b22851bSkettenis
15638b22851bSkettenis printf("%s: 0x%08x\n", __func__, idx);
15648b22851bSkettenis return 0;
15658b22851bSkettenis }
15668b22851bSkettenis
15678b22851bSkettenis uint32_t
sxiccmu_r40_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)1568a6d148daSkettenis sxiccmu_r40_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
1569a6d148daSkettenis {
157029e54e85Skettenis uint32_t parent;
157129e54e85Skettenis uint32_t reg, div;
157229e54e85Skettenis
1573a6d148daSkettenis switch (idx) {
157429e54e85Skettenis case R40_CLK_LOSC:
157529e54e85Skettenis return clock_get_frequency(sc->sc_node, "losc");
157629e54e85Skettenis case R40_CLK_HOSC:
157729e54e85Skettenis return clock_get_frequency(sc->sc_node, "hosc");
1578a6d148daSkettenis case R40_CLK_PLL_PERIPH0:
1579a6d148daSkettenis /* Not hardcoded, but recommended. */
1580a6d148daSkettenis return 600000000;
1581a6d148daSkettenis case R40_CLK_PLL_PERIPH0_2X:
158229e54e85Skettenis return sxiccmu_r40_get_frequency(sc, R40_CLK_PLL_PERIPH0) * 2;
158329e54e85Skettenis case R40_CLK_AHB1:
158429e54e85Skettenis reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
158529e54e85Skettenis div = CCU_AHB1_CLK_DIV_RATIO(reg);
158629e54e85Skettenis switch (reg & CCU_AHB1_CLK_SRC_SEL) {
158729e54e85Skettenis case CCU_AHB1_CLK_SRC_SEL_LOSC:
158829e54e85Skettenis parent = R40_CLK_LOSC;
158929e54e85Skettenis break;
159029e54e85Skettenis case CCU_AHB1_CLK_SRC_SEL_OSC24M:
159129e54e85Skettenis parent = R40_CLK_HOSC;
159229e54e85Skettenis break;
159329e54e85Skettenis case CCU_AHB1_CLK_SRC_SEL_AXI:
159429e54e85Skettenis parent = R40_CLK_AXI;
159529e54e85Skettenis break;
159629e54e85Skettenis case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
159729e54e85Skettenis parent = R40_CLK_PLL_PERIPH0;
159829e54e85Skettenis div *= CCU_AHB1_PRE_DIV(reg);
159929e54e85Skettenis break;
160029e54e85Skettenis }
160129e54e85Skettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
1602a6d148daSkettenis case R40_CLK_APB2:
1603a6d148daSkettenis /* XXX Controlled by a MUX. */
1604a6d148daSkettenis return 24000000;
1605a6d148daSkettenis }
1606a6d148daSkettenis
1607a6d148daSkettenis printf("%s: 0x%08x\n", __func__, idx);
1608a6d148daSkettenis return 0;
1609a6d148daSkettenis }
1610a6d148daSkettenis
1611a6d148daSkettenis uint32_t
sxiccmu_v3s_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)16123fb3ba8fSkettenis sxiccmu_v3s_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
16133fb3ba8fSkettenis {
16143fb3ba8fSkettenis uint32_t parent;
16153fb3ba8fSkettenis uint32_t reg, div;
16163fb3ba8fSkettenis
16173fb3ba8fSkettenis switch (idx) {
16183fb3ba8fSkettenis case V3S_CLK_LOSC:
16193fb3ba8fSkettenis return clock_get_frequency(sc->sc_node, "losc");
16203fb3ba8fSkettenis case V3S_CLK_HOSC:
16213fb3ba8fSkettenis return clock_get_frequency(sc->sc_node, "hosc");
16223fb3ba8fSkettenis case V3S_CLK_PLL_PERIPH0:
16233fb3ba8fSkettenis /* Not hardcoded, but recommended. */
16243fb3ba8fSkettenis return 600000000;
16253fb3ba8fSkettenis case V3S_CLK_APB2:
16263fb3ba8fSkettenis /* XXX Controlled by a MUX. */
16273fb3ba8fSkettenis return 24000000;
16283fb3ba8fSkettenis case V3S_CLK_AHB1:
16293fb3ba8fSkettenis reg = SXIREAD4(sc, CCU_AHB1_APB1_CFG_REG);
16303fb3ba8fSkettenis div = CCU_AHB1_CLK_DIV_RATIO(reg);
16313fb3ba8fSkettenis switch (reg & CCU_AHB1_CLK_SRC_SEL) {
16323fb3ba8fSkettenis case CCU_AHB1_CLK_SRC_SEL_LOSC:
16333fb3ba8fSkettenis parent = V3S_CLK_LOSC;
16343fb3ba8fSkettenis break;
16353fb3ba8fSkettenis case CCU_AHB1_CLK_SRC_SEL_OSC24M:
16363fb3ba8fSkettenis parent = V3S_CLK_HOSC;
16373fb3ba8fSkettenis break;
16383fb3ba8fSkettenis case CCU_AHB1_CLK_SRC_SEL_AXI:
16393fb3ba8fSkettenis parent = V3S_CLK_AXI;
16403fb3ba8fSkettenis break;
16413fb3ba8fSkettenis case CCU_AHB1_CLK_SRC_SEL_PERIPH0:
16423fb3ba8fSkettenis parent = V3S_CLK_PLL_PERIPH0;
16433fb3ba8fSkettenis div *= CCU_AHB1_PRE_DIV(reg);
16443fb3ba8fSkettenis break;
16453fb3ba8fSkettenis default:
16463fb3ba8fSkettenis return 0;
16473fb3ba8fSkettenis }
16483fb3ba8fSkettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
16493fb3ba8fSkettenis case V3S_CLK_AHB2:
16503fb3ba8fSkettenis reg = SXIREAD4(sc, CCU_AHB2_CFG_REG);
16513fb3ba8fSkettenis switch (reg & CCU_AHB2_CLK_CFG) {
16523fb3ba8fSkettenis case 0:
16533fb3ba8fSkettenis parent = V3S_CLK_AHB1;
16543fb3ba8fSkettenis div = 1;
16553fb3ba8fSkettenis break;
16563fb3ba8fSkettenis case 1:
16573fb3ba8fSkettenis parent = V3S_CLK_PLL_PERIPH0;
16583fb3ba8fSkettenis div = 2;
16593fb3ba8fSkettenis break;
16603fb3ba8fSkettenis default:
16613fb3ba8fSkettenis return 0;
16623fb3ba8fSkettenis }
16633fb3ba8fSkettenis return sxiccmu_ccu_get_frequency(sc, &parent) / div;
16643fb3ba8fSkettenis }
16653fb3ba8fSkettenis
16663fb3ba8fSkettenis printf("%s: 0x%08x\n", __func__, idx);
16673fb3ba8fSkettenis return 0;
16683fb3ba8fSkettenis }
16693fb3ba8fSkettenis
16703fb3ba8fSkettenis uint32_t
sxiccmu_nop_get_frequency(struct sxiccmu_softc * sc,uint32_t idx)16712c064ff8Skettenis sxiccmu_nop_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
16722c064ff8Skettenis {
16732c064ff8Skettenis printf("%s: 0x%08x\n", __func__, idx);
16742c064ff8Skettenis return 0;
16752c064ff8Skettenis }
16762c064ff8Skettenis
16770ea1c705Spatrick int
sxiccmu_ccu_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)16780ea1c705Spatrick sxiccmu_ccu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
16790ea1c705Spatrick {
16800ea1c705Spatrick struct sxiccmu_softc *sc = cookie;
16810ea1c705Spatrick uint32_t idx = cells[0];
16820ea1c705Spatrick
16830ea1c705Spatrick return sc->sc_set_frequency(sc, idx, freq);
16840ea1c705Spatrick }
16850ea1c705Spatrick
16860ea1c705Spatrick int
sxiccmu_a10_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)1687e6e86968Skettenis sxiccmu_a10_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
1688e6e86968Skettenis {
1689e6e86968Skettenis struct sxiccmu_clock clock;
1690e6e86968Skettenis uint32_t parent, parent_freq;
1691193b1cd6Skettenis uint32_t reg;
1692193b1cd6Skettenis int k, n;
1693193b1cd6Skettenis int error;
1694e6e86968Skettenis
1695e6e86968Skettenis switch (idx) {
1696193b1cd6Skettenis case A10_CLK_PLL_CORE:
1697193b1cd6Skettenis k = 1; n = 32;
1698193b1cd6Skettenis while (k <= 4 && (24000000 * n * k) < freq)
1699193b1cd6Skettenis k++;
1700193b1cd6Skettenis while (n >= 1 && (24000000 * n * k) > freq)
1701193b1cd6Skettenis n--;
1702193b1cd6Skettenis
1703193b1cd6Skettenis reg = SXIREAD4(sc, A10_PLL1_CFG_REG);
1704193b1cd6Skettenis reg &= ~A10_PLL1_OUT_EXT_DIVP_MASK;
1705193b1cd6Skettenis reg &= ~A10_PLL1_FACTOR_N_MASK;
1706193b1cd6Skettenis reg &= ~A10_PLL1_FACTOR_K_MASK;
1707193b1cd6Skettenis reg &= ~A10_PLL1_FACTOR_M_MASK;
1708193b1cd6Skettenis reg |= (n << A10_PLL1_FACTOR_N_SHIFT);
1709193b1cd6Skettenis reg |= ((k - 1) << A10_PLL1_FACTOR_K_SHIFT);
1710193b1cd6Skettenis SXIWRITE4(sc, A10_PLL1_CFG_REG, reg);
1711193b1cd6Skettenis
1712193b1cd6Skettenis /* No need to wait PLL to lock? */
1713193b1cd6Skettenis
1714193b1cd6Skettenis return 0;
1715193b1cd6Skettenis case A10_CLK_CPU:
1716193b1cd6Skettenis /* Switch to 24 MHz clock. */
1717193b1cd6Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1718193b1cd6Skettenis reg &= ~A10_CPU_CLK_SRC_SEL;
1719193b1cd6Skettenis reg |= A10_CPU_CLK_SRC_SEL_OSC24M;
1720193b1cd6Skettenis SXIWRITE4(sc, A10_CPU_AHB_APB0_CFG_REG, reg);
1721193b1cd6Skettenis
1722193b1cd6Skettenis error = sxiccmu_a10_set_frequency(sc, A10_CLK_PLL_CORE, freq);
1723193b1cd6Skettenis
1724193b1cd6Skettenis /* Switch back to PLL. */
1725193b1cd6Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1726193b1cd6Skettenis reg &= ~A10_CPU_CLK_SRC_SEL;
1727193b1cd6Skettenis reg |= A10_CPU_CLK_SRC_SEL_PLL1;
1728193b1cd6Skettenis SXIWRITE4(sc, A10_CPU_AHB_APB0_CFG_REG, reg);
1729193b1cd6Skettenis return error;
1730e6e86968Skettenis case A10_CLK_MMC0:
1731e6e86968Skettenis case A10_CLK_MMC1:
1732e6e86968Skettenis case A10_CLK_MMC2:
1733e6e86968Skettenis case A10_CLK_MMC3:
1734e6e86968Skettenis clock.sc_iot = sc->sc_iot;
1735e6e86968Skettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
1736e6e86968Skettenis sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
1737e6e86968Skettenis parent = A10_CLK_PLL_PERIPH;
1738e6e86968Skettenis parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
1739e6e86968Skettenis return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
1740e6e86968Skettenis }
1741e6e86968Skettenis
1742e6e86968Skettenis printf("%s: 0x%08x\n", __func__, idx);
1743e6e86968Skettenis return -1;
1744e6e86968Skettenis }
1745e6e86968Skettenis
1746e6e86968Skettenis int
sxiccmu_a10s_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)1747199786d9Skettenis sxiccmu_a10s_set_frequency(struct sxiccmu_softc *sc, uint32_t idx,
17484e623b4aSderaadt uint32_t freq)
1749199786d9Skettenis {
1750199786d9Skettenis struct sxiccmu_clock clock;
1751199786d9Skettenis uint32_t parent, parent_freq;
1752199786d9Skettenis uint32_t reg;
1753199786d9Skettenis int k, n;
1754199786d9Skettenis int error;
1755199786d9Skettenis
1756199786d9Skettenis switch (idx) {
1757199786d9Skettenis case A10S_CLK_PLL_CORE:
1758199786d9Skettenis k = 1; n = 32;
1759199786d9Skettenis while (k <= 4 && (24000000 * n * k) < freq)
1760199786d9Skettenis k++;
1761199786d9Skettenis while (n >= 1 && (24000000 * n * k) > freq)
1762199786d9Skettenis n--;
1763199786d9Skettenis
1764199786d9Skettenis reg = SXIREAD4(sc, A10_PLL1_CFG_REG);
1765199786d9Skettenis reg &= ~A10_PLL1_OUT_EXT_DIVP_MASK;
1766199786d9Skettenis reg &= ~A10_PLL1_FACTOR_N_MASK;
1767199786d9Skettenis reg &= ~A10_PLL1_FACTOR_K_MASK;
1768199786d9Skettenis reg &= ~A10_PLL1_FACTOR_M_MASK;
1769199786d9Skettenis reg |= (n << A10_PLL1_FACTOR_N_SHIFT);
1770199786d9Skettenis reg |= ((k - 1) << A10_PLL1_FACTOR_K_SHIFT);
1771199786d9Skettenis SXIWRITE4(sc, A10_PLL1_CFG_REG, reg);
1772199786d9Skettenis
1773199786d9Skettenis /* No need to wait PLL to lock? */
1774199786d9Skettenis
1775199786d9Skettenis return 0;
1776199786d9Skettenis case A10S_CLK_CPU:
1777199786d9Skettenis /* Switch to 24 MHz clock. */
1778199786d9Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1779199786d9Skettenis reg &= ~A10_CPU_CLK_SRC_SEL;
1780199786d9Skettenis reg |= A10_CPU_CLK_SRC_SEL_OSC24M;
1781199786d9Skettenis SXIWRITE4(sc, A10_CPU_AHB_APB0_CFG_REG, reg);
1782199786d9Skettenis
1783199786d9Skettenis error = sxiccmu_a10s_set_frequency(sc, A10S_CLK_PLL_CORE, freq);
1784199786d9Skettenis
1785199786d9Skettenis /* Switch back to PLL. */
1786199786d9Skettenis reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
1787199786d9Skettenis reg &= ~A10_CPU_CLK_SRC_SEL;
1788199786d9Skettenis reg |= A10_CPU_CLK_SRC_SEL_PLL1;
1789199786d9Skettenis SXIWRITE4(sc, A10_CPU_AHB_APB0_CFG_REG, reg);
1790199786d9Skettenis return error;
1791199786d9Skettenis case A10S_CLK_MMC0:
1792199786d9Skettenis case A10S_CLK_MMC1:
1793199786d9Skettenis case A10S_CLK_MMC2:
1794199786d9Skettenis clock.sc_iot = sc->sc_iot;
1795199786d9Skettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
1796199786d9Skettenis sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
1797199786d9Skettenis parent = A10S_CLK_PLL_PERIPH;
1798199786d9Skettenis parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
1799199786d9Skettenis return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
1800199786d9Skettenis }
1801199786d9Skettenis
1802199786d9Skettenis printf("%s: 0x%08x\n", __func__, idx);
1803199786d9Skettenis return -1;
1804199786d9Skettenis }
1805199786d9Skettenis
1806199786d9Skettenis int
sxiccmu_a23_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)1807f0a8b882Skevlo sxiccmu_a23_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
1808f0a8b882Skevlo {
1809f0a8b882Skevlo struct sxiccmu_clock clock;
1810f0a8b882Skevlo uint32_t parent, parent_freq;
1811f0a8b882Skevlo
1812f0a8b882Skevlo switch (idx) {
1813f0a8b882Skevlo case A23_CLK_MMC0:
1814f0a8b882Skevlo case A23_CLK_MMC1:
1815f0a8b882Skevlo case A23_CLK_MMC2:
1816f0a8b882Skevlo clock.sc_iot = sc->sc_iot;
1817f0a8b882Skevlo bus_space_subregion(sc->sc_iot, sc->sc_ioh,
1818f0a8b882Skevlo sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
1819f0a8b882Skevlo parent = A23_CLK_PLL_PERIPH;
1820f0a8b882Skevlo parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
1821f0a8b882Skevlo return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
1822f0a8b882Skevlo }
1823f0a8b882Skevlo
1824f0a8b882Skevlo printf("%s: 0x%08x\n", __func__, idx);
1825f0a8b882Skevlo return -1;
1826f0a8b882Skevlo }
1827f0a8b882Skevlo
1828f0a8b882Skevlo int
sxiccmu_a64_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)18290ea1c705Spatrick sxiccmu_a64_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
18300ea1c705Spatrick {
18310ea1c705Spatrick struct sxiccmu_clock clock;
18320ea1c705Spatrick uint32_t parent, parent_freq;
1833bced15f9Skettenis uint32_t reg;
1834bced15f9Skettenis int k, n;
1835bced15f9Skettenis int error;
18360ea1c705Spatrick
18370ea1c705Spatrick switch (idx) {
1838bced15f9Skettenis case A64_CLK_PLL_CPUX:
1839bced15f9Skettenis k = 1; n = 32;
1840bced15f9Skettenis while (k <= 4 && (24000000 * n * k) < freq)
1841bced15f9Skettenis k++;
1842bced15f9Skettenis while (n >= 1 && (24000000 * n * k) > freq)
1843bced15f9Skettenis n--;
1844bced15f9Skettenis
1845bced15f9Skettenis reg = SXIREAD4(sc, A64_PLL_CPUX_CTRL_REG);
1846bced15f9Skettenis reg &= ~A64_PLL_CPUX_OUT_EXT_DIVP_MASK;
1847bced15f9Skettenis reg &= ~A64_PLL_CPUX_FACTOR_N_MASK;
1848bced15f9Skettenis reg &= ~A64_PLL_CPUX_FACTOR_K_MASK;
1849bced15f9Skettenis reg &= ~A64_PLL_CPUX_FACTOR_M_MASK;
1850bced15f9Skettenis reg |= ((n - 1) << A64_PLL_CPUX_FACTOR_N_SHIFT);
1851bced15f9Skettenis reg |= ((k - 1) << A64_PLL_CPUX_FACTOR_K_SHIFT);
1852bced15f9Skettenis SXIWRITE4(sc, A64_PLL_CPUX_CTRL_REG, reg);
1853bced15f9Skettenis
1854bced15f9Skettenis /* Wait for PLL to lock. */
1855bced15f9Skettenis while ((SXIREAD4(sc, A64_PLL_CPUX_CTRL_REG) &
1856bced15f9Skettenis A64_PLL_CPUX_LOCK) == 0) {
1857bced15f9Skettenis delay(200);
1858bced15f9Skettenis }
1859bced15f9Skettenis
1860bced15f9Skettenis return 0;
1861bced15f9Skettenis case A64_CLK_CPUX:
1862bced15f9Skettenis /* Switch to 24 MHz clock. */
1863bced15f9Skettenis reg = SXIREAD4(sc, A64_CPUX_AXI_CFG_REG);
1864bced15f9Skettenis reg &= ~A64_CPUX_CLK_SRC_SEL;
1865bced15f9Skettenis reg |= A64_CPUX_CLK_SRC_SEL_OSC24M;
1866bced15f9Skettenis SXIWRITE4(sc, A64_CPUX_AXI_CFG_REG, reg);
1867bced15f9Skettenis
1868bced15f9Skettenis error = sxiccmu_a64_set_frequency(sc, A64_CLK_PLL_CPUX, freq);
1869bced15f9Skettenis
1870bced15f9Skettenis /* Switch back to PLL. */
1871bced15f9Skettenis reg = SXIREAD4(sc, A64_CPUX_AXI_CFG_REG);
1872bced15f9Skettenis reg &= ~A64_CPUX_CLK_SRC_SEL;
1873bced15f9Skettenis reg |= A64_CPUX_CLK_SRC_SEL_PLL_CPUX;
1874bced15f9Skettenis SXIWRITE4(sc, A64_CPUX_AXI_CFG_REG, reg);
1875bced15f9Skettenis return error;
18760ea1c705Spatrick case A64_CLK_MMC0:
18770ea1c705Spatrick case A64_CLK_MMC1:
18780ea1c705Spatrick case A64_CLK_MMC2:
18790ea1c705Spatrick clock.sc_iot = sc->sc_iot;
18800ea1c705Spatrick bus_space_subregion(sc->sc_iot, sc->sc_ioh,
18810ea1c705Spatrick sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
18820ea1c705Spatrick parent = A64_CLK_PLL_PERIPH0_2X;
18830ea1c705Spatrick parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
18840ea1c705Spatrick return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
18850ea1c705Spatrick }
18860ea1c705Spatrick
18870ea1c705Spatrick printf("%s: 0x%08x\n", __func__, idx);
18880ea1c705Spatrick return -1;
18890ea1c705Spatrick }
18900ea1c705Spatrick
18910ea1c705Spatrick int
sxiccmu_a80_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)1892d526c0edSkettenis sxiccmu_a80_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
1893d526c0edSkettenis {
1894d526c0edSkettenis struct sxiccmu_clock clock;
1895d526c0edSkettenis uint32_t parent, parent_freq;
1896d526c0edSkettenis
1897d526c0edSkettenis switch (idx) {
1898d526c0edSkettenis case A80_CLK_MMC0:
1899d526c0edSkettenis case A80_CLK_MMC1:
1900d526c0edSkettenis case A80_CLK_MMC2:
1901d526c0edSkettenis clock.sc_iot = sc->sc_iot;
1902d526c0edSkettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
1903d526c0edSkettenis sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
1904d526c0edSkettenis parent = A80_CLK_PLL_PERIPH0;
1905d526c0edSkettenis parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
1906d526c0edSkettenis return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
1907d526c0edSkettenis }
1908d526c0edSkettenis
1909d526c0edSkettenis printf("%s: 0x%08x\n", __func__, idx);
1910d526c0edSkettenis return -1;
1911d526c0edSkettenis }
1912d526c0edSkettenis
19134718ccb1Skettenis #define D1_SMHC0_CLK_REG 0x0830
19144718ccb1Skettenis #define D1_SMHC1_CLK_REG 0x0834
19154718ccb1Skettenis #define D1_SMHC2_CLK_REG 0x0838
19164718ccb1Skettenis #define D1_SMHC_CLK_SRC_SEL (0x3 << 24)
19174718ccb1Skettenis #define D1_SMHC_CLK_SRC_SEL_HOSC (0x0 << 24)
19184718ccb1Skettenis #define D1_SMHC_CLK_SRC_SEL_PLL_PERIPH0 (0x1 << 24)
19194718ccb1Skettenis #define D1_SMHC_FACTOR_N_MASK (0x3 << 8)
19204718ccb1Skettenis #define D1_SMHC_FACTOR_N_SHIFT 8
19214718ccb1Skettenis #define D1_SMHC_FACTOR_M_MASK (0xf << 0)
19224718ccb1Skettenis #define D1_SMHC_FACTOR_M_SHIFT 0
19234718ccb1Skettenis
19244718ccb1Skettenis int
sxiccmu_d1_mmc_set_frequency(struct sxiccmu_softc * sc,bus_size_t offset,uint32_t freq)19254718ccb1Skettenis sxiccmu_d1_mmc_set_frequency(struct sxiccmu_softc *sc, bus_size_t offset,
19264718ccb1Skettenis uint32_t freq)
19274718ccb1Skettenis {
19284718ccb1Skettenis uint32_t parent_freq;
19294718ccb1Skettenis uint32_t reg, m, n;
19304718ccb1Skettenis uint32_t clk_src;
19314718ccb1Skettenis
19324718ccb1Skettenis switch (freq) {
19334718ccb1Skettenis case 400000:
19344718ccb1Skettenis n = 2, m = 15;
19354718ccb1Skettenis clk_src = D1_SMHC_CLK_SRC_SEL_HOSC;
19364718ccb1Skettenis break;
19374718ccb1Skettenis case 20000000:
19384718ccb1Skettenis case 25000000:
19394718ccb1Skettenis case 26000000:
19404718ccb1Skettenis case 50000000:
19414718ccb1Skettenis case 52000000:
19424718ccb1Skettenis n = 0, m = 0;
19434718ccb1Skettenis clk_src = D1_SMHC_CLK_SRC_SEL_PLL_PERIPH0;
19444718ccb1Skettenis parent_freq =
19454718ccb1Skettenis sxiccmu_d1_get_frequency(sc, D1_CLK_PLL_PERIPH0);
19464718ccb1Skettenis while ((parent_freq / (1 << n) / 16) > freq)
19474718ccb1Skettenis n++;
19484718ccb1Skettenis while ((parent_freq / (1 << n) / (m + 1)) > freq)
19494718ccb1Skettenis m++;
19504718ccb1Skettenis break;
19514718ccb1Skettenis default:
19524718ccb1Skettenis return -1;
19534718ccb1Skettenis }
19544718ccb1Skettenis
19554718ccb1Skettenis reg = SXIREAD4(sc, offset);
19564718ccb1Skettenis reg &= ~D1_SMHC_CLK_SRC_SEL;
19574718ccb1Skettenis reg |= clk_src;
19584718ccb1Skettenis reg &= ~D1_SMHC_FACTOR_N_MASK;
19594718ccb1Skettenis reg |= n << D1_SMHC_FACTOR_N_SHIFT;
19604718ccb1Skettenis reg &= ~D1_SMHC_FACTOR_M_MASK;
19614718ccb1Skettenis reg |= m << D1_SMHC_FACTOR_M_SHIFT;
19624718ccb1Skettenis SXIWRITE4(sc, offset, reg);
19634718ccb1Skettenis
19644718ccb1Skettenis return 0;
19654718ccb1Skettenis }
19664718ccb1Skettenis
1967d526c0edSkettenis int
sxiccmu_d1_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)19684e48f70dSkettenis sxiccmu_d1_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
19694e48f70dSkettenis {
19704718ccb1Skettenis switch (idx) {
19714718ccb1Skettenis case D1_CLK_MMC0:
19724718ccb1Skettenis return sxiccmu_d1_mmc_set_frequency(sc, D1_SMHC0_CLK_REG, freq);
19734718ccb1Skettenis case D1_CLK_MMC1:
19744718ccb1Skettenis return sxiccmu_d1_mmc_set_frequency(sc, D1_SMHC1_CLK_REG, freq);
19754718ccb1Skettenis case D1_CLK_MMC2:
19764718ccb1Skettenis return sxiccmu_d1_mmc_set_frequency(sc, D1_SMHC2_CLK_REG, freq);
19774718ccb1Skettenis }
19784718ccb1Skettenis
19794e48f70dSkettenis printf("%s: 0x%08x\n", __func__, idx);
19804e48f70dSkettenis return -1;
19814e48f70dSkettenis }
19824e48f70dSkettenis
19834e48f70dSkettenis int
sxiccmu_h3_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)19840ea1c705Spatrick sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
19850ea1c705Spatrick {
19860ea1c705Spatrick struct sxiccmu_clock clock;
19870ea1c705Spatrick uint32_t parent, parent_freq;
19889424095dSuaa uint32_t reg, lock_time;
1989f1089a02Skettenis int k, n;
1990f1089a02Skettenis int error;
19910ea1c705Spatrick
19920ea1c705Spatrick switch (idx) {
1993f1089a02Skettenis case H3_CLK_PLL_CPUX:
1994f1089a02Skettenis k = 1; n = 32;
1995f1089a02Skettenis while (k <= 4 && (24000000 * n * k) < freq)
1996f1089a02Skettenis k++;
1997f1089a02Skettenis while (n >= 1 && (24000000 * n * k) > freq)
1998f1089a02Skettenis n--;
1999f1089a02Skettenis
20009424095dSuaa /* Gate the PLL first */
2001f1089a02Skettenis reg = SXIREAD4(sc, H3_PLL_CPUX_CTRL_REG);
20029424095dSuaa reg &= ~H3_PLL_CPUX_ENABLE;
20039424095dSuaa SXIWRITE4(sc, H3_PLL_CPUX_CTRL_REG, reg);
20049424095dSuaa
20059424095dSuaa /* Set factors and external divider. */
2006f1089a02Skettenis reg &= ~H3_PLL_CPUX_OUT_EXT_DIVP_MASK;
2007f1089a02Skettenis reg &= ~H3_PLL_CPUX_FACTOR_N_MASK;
2008f1089a02Skettenis reg &= ~H3_PLL_CPUX_FACTOR_K_MASK;
2009f1089a02Skettenis reg &= ~H3_PLL_CPUX_FACTOR_M_MASK;
2010f1089a02Skettenis reg |= ((n - 1) << H3_PLL_CPUX_FACTOR_N_SHIFT);
2011f1089a02Skettenis reg |= ((k - 1) << H3_PLL_CPUX_FACTOR_K_SHIFT);
2012f1089a02Skettenis SXIWRITE4(sc, H3_PLL_CPUX_CTRL_REG, reg);
2013f1089a02Skettenis
20149424095dSuaa /* Ungate the PLL */
20159424095dSuaa reg |= H3_PLL_CPUX_ENABLE;
20169424095dSuaa SXIWRITE4(sc, H3_PLL_CPUX_CTRL_REG, reg);
20179424095dSuaa
20189424095dSuaa /* Wait for PLL to lock. (LOCK flag is unreliable) */
20199424095dSuaa lock_time = SXIREAD4(sc, H3_PLL_STABLE_TIME_REG1);
20209424095dSuaa delay(H3_PLL_STABLE_TIME_REG1_TIME(lock_time));
2021f1089a02Skettenis
2022f1089a02Skettenis return 0;
2023f1089a02Skettenis case H3_CLK_CPUX:
2024f1089a02Skettenis /* Switch to 24 MHz clock. */
2025f1089a02Skettenis reg = SXIREAD4(sc, H3_CPUX_AXI_CFG_REG);
2026f1089a02Skettenis reg &= ~H3_CPUX_CLK_SRC_SEL;
2027f1089a02Skettenis reg |= H3_CPUX_CLK_SRC_SEL_OSC24M;
2028f1089a02Skettenis SXIWRITE4(sc, H3_CPUX_AXI_CFG_REG, reg);
20299424095dSuaa /* Must wait at least 8 cycles of the current clock. */
20309424095dSuaa delay(1);
2031f1089a02Skettenis
2032f1089a02Skettenis error = sxiccmu_h3_set_frequency(sc, H3_CLK_PLL_CPUX, freq);
2033f1089a02Skettenis
2034f1089a02Skettenis /* Switch back to PLL. */
2035f1089a02Skettenis reg = SXIREAD4(sc, H3_CPUX_AXI_CFG_REG);
2036f1089a02Skettenis reg &= ~H3_CPUX_CLK_SRC_SEL;
2037f1089a02Skettenis reg |= H3_CPUX_CLK_SRC_SEL_PLL_CPUX;
2038f1089a02Skettenis SXIWRITE4(sc, H3_CPUX_AXI_CFG_REG, reg);
20399424095dSuaa /* Must wait at least 8 cycles of the current clock. */
20409424095dSuaa delay(1);
2041f1089a02Skettenis return error;
20420ea1c705Spatrick case H3_CLK_MMC0:
20430ea1c705Spatrick case H3_CLK_MMC1:
20440ea1c705Spatrick case H3_CLK_MMC2:
20450ea1c705Spatrick clock.sc_iot = sc->sc_iot;
20460ea1c705Spatrick bus_space_subregion(sc->sc_iot, sc->sc_ioh,
20470ea1c705Spatrick sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
20480ea1c705Spatrick parent = H3_CLK_PLL_PERIPH0;
20490ea1c705Spatrick parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
20500ea1c705Spatrick return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
20510ea1c705Spatrick }
20520ea1c705Spatrick
20530ea1c705Spatrick printf("%s: 0x%08x\n", __func__, idx);
20540ea1c705Spatrick return -1;
20550ea1c705Spatrick }
20560ea1c705Spatrick
20578b22851bSkettenis #define H6_SMHC0_CLK_REG 0x0830
20588b22851bSkettenis #define H6_SMHC1_CLK_REG 0x0834
20598b22851bSkettenis #define H6_SMHC2_CLK_REG 0x0838
20608b22851bSkettenis #define H6_SMHC_CLK_SRC_SEL (0x3 << 24)
20618b22851bSkettenis #define H6_SMHC_CLK_SRC_SEL_OSC24M (0x0 << 24)
20628b22851bSkettenis #define H6_SMHC_CLK_SRC_SEL_PLL_PERIPH0_2X (0x1 << 24)
20638b22851bSkettenis #define H6_SMHC_FACTOR_N_MASK (0x3 << 8)
20648b22851bSkettenis #define H6_SMHC_FACTOR_N_SHIFT 8
20658b22851bSkettenis #define H6_SMHC_FACTOR_M_MASK (0xf << 0)
20668b22851bSkettenis #define H6_SMHC_FACTOR_M_SHIFT 0
20678b22851bSkettenis
20688b22851bSkettenis int
sxiccmu_h6_mmc_set_frequency(struct sxiccmu_softc * sc,bus_size_t offset,uint32_t freq,uint32_t parent_freq)20698b22851bSkettenis sxiccmu_h6_mmc_set_frequency(struct sxiccmu_softc *sc, bus_size_t offset,
207022652d99Suaa uint32_t freq, uint32_t parent_freq)
20718b22851bSkettenis {
20728b22851bSkettenis uint32_t reg, m, n;
20738b22851bSkettenis uint32_t clk_src;
20748b22851bSkettenis
20758b22851bSkettenis switch (freq) {
20768b22851bSkettenis case 400000:
20778b22851bSkettenis n = 2, m = 15;
20788b22851bSkettenis clk_src = H6_SMHC_CLK_SRC_SEL_OSC24M;
20798b22851bSkettenis break;
20808b22851bSkettenis case 20000000:
20818b22851bSkettenis case 25000000:
20828b22851bSkettenis case 26000000:
20838b22851bSkettenis case 50000000:
20848b22851bSkettenis case 52000000:
20858b22851bSkettenis n = 0, m = 0;
20868b22851bSkettenis clk_src = H6_SMHC_CLK_SRC_SEL_PLL_PERIPH0_2X;
20878b22851bSkettenis while ((parent_freq / (1 << n) / 16) > freq)
20888b22851bSkettenis n++;
20898b22851bSkettenis while ((parent_freq / (1 << n) / (m + 1)) > freq)
20908b22851bSkettenis m++;
20918b22851bSkettenis break;
20928b22851bSkettenis default:
20938b22851bSkettenis return -1;
20948b22851bSkettenis }
20958b22851bSkettenis
20968b22851bSkettenis reg = SXIREAD4(sc, offset);
20978b22851bSkettenis reg &= ~H6_SMHC_CLK_SRC_SEL;
20988b22851bSkettenis reg |= clk_src;
20998b22851bSkettenis reg &= ~H6_SMHC_FACTOR_N_MASK;
21008b22851bSkettenis reg |= n << H6_SMHC_FACTOR_N_SHIFT;
21018b22851bSkettenis reg &= ~H6_SMHC_FACTOR_M_MASK;
21028b22851bSkettenis reg |= m << H6_SMHC_FACTOR_M_SHIFT;
21038b22851bSkettenis SXIWRITE4(sc, offset, reg);
21048b22851bSkettenis
21058b22851bSkettenis return 0;
21068b22851bSkettenis }
21078b22851bSkettenis
21088b22851bSkettenis int
sxiccmu_h6_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)21098b22851bSkettenis sxiccmu_h6_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
21108b22851bSkettenis {
211122652d99Suaa uint32_t parent_freq;
211222652d99Suaa
211322652d99Suaa parent_freq = sxiccmu_h6_get_frequency(sc, H6_CLK_PLL_PERIPH0_2X);
211422652d99Suaa
21158b22851bSkettenis switch (idx) {
21168b22851bSkettenis case H6_CLK_MMC0:
211722652d99Suaa return sxiccmu_h6_mmc_set_frequency(sc, H6_SMHC0_CLK_REG,
211822652d99Suaa freq, parent_freq);
21198b22851bSkettenis case H6_CLK_MMC1:
212022652d99Suaa return sxiccmu_h6_mmc_set_frequency(sc, H6_SMHC1_CLK_REG,
212122652d99Suaa freq, parent_freq);
21228b22851bSkettenis case H6_CLK_MMC2:
212322652d99Suaa return sxiccmu_h6_mmc_set_frequency(sc, H6_SMHC2_CLK_REG,
212422652d99Suaa freq, parent_freq);
212522652d99Suaa }
212622652d99Suaa
212722652d99Suaa printf("%s: 0x%08x\n", __func__, idx);
212822652d99Suaa return -1;
212922652d99Suaa }
213022652d99Suaa
213122652d99Suaa #define H616_SMHC0_CLK_REG 0x0830
213222652d99Suaa #define H616_SMHC1_CLK_REG 0x0834
213322652d99Suaa #define H616_SMHC2_CLK_REG 0x0838
213422652d99Suaa
213522652d99Suaa int
sxiccmu_h616_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)213622652d99Suaa sxiccmu_h616_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
213722652d99Suaa {
213822652d99Suaa uint32_t parent_freq;
213922652d99Suaa
214022652d99Suaa parent_freq = sxiccmu_h616_get_frequency(sc, H616_CLK_PLL_PERIPH0_2X);
214122652d99Suaa
214222652d99Suaa switch (idx) {
214322652d99Suaa case H616_CLK_MMC0:
214422652d99Suaa return sxiccmu_h6_mmc_set_frequency(sc, H616_SMHC0_CLK_REG,
214522652d99Suaa freq, parent_freq);
214622652d99Suaa case H616_CLK_MMC1:
214722652d99Suaa return sxiccmu_h6_mmc_set_frequency(sc, H616_SMHC1_CLK_REG,
214822652d99Suaa freq, parent_freq);
214922652d99Suaa case H616_CLK_MMC2:
215022652d99Suaa return sxiccmu_h6_mmc_set_frequency(sc, H616_SMHC2_CLK_REG,
215122652d99Suaa freq, parent_freq);
21528b22851bSkettenis }
21538b22851bSkettenis
21548b22851bSkettenis printf("%s: 0x%08x\n", __func__, idx);
21558b22851bSkettenis return -1;
21568b22851bSkettenis }
21578b22851bSkettenis
21582c064ff8Skettenis int
sxiccmu_r40_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)2159a6d148daSkettenis sxiccmu_r40_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
2160a6d148daSkettenis {
2161a6d148daSkettenis struct sxiccmu_clock clock;
2162a6d148daSkettenis uint32_t parent, parent_freq;
2163a6d148daSkettenis
2164a6d148daSkettenis switch (idx) {
2165a6d148daSkettenis case R40_CLK_MMC0:
2166a6d148daSkettenis case R40_CLK_MMC1:
2167a6d148daSkettenis case R40_CLK_MMC2:
2168a6d148daSkettenis case R40_CLK_MMC3:
2169a6d148daSkettenis clock.sc_iot = sc->sc_iot;
2170a6d148daSkettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
2171a6d148daSkettenis sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
2172a6d148daSkettenis parent = R40_CLK_PLL_PERIPH0_2X;
2173a6d148daSkettenis parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
2174a6d148daSkettenis return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
2175a6d148daSkettenis }
2176a6d148daSkettenis
2177a6d148daSkettenis printf("%s: 0x%08x\n", __func__, idx);
2178a6d148daSkettenis return -1;
2179a6d148daSkettenis }
2180a6d148daSkettenis
2181a6d148daSkettenis int
sxiccmu_v3s_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)21823fb3ba8fSkettenis sxiccmu_v3s_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
21833fb3ba8fSkettenis {
21843fb3ba8fSkettenis struct sxiccmu_clock clock;
21853fb3ba8fSkettenis uint32_t parent, parent_freq;
21863fb3ba8fSkettenis
21873fb3ba8fSkettenis switch (idx) {
21883fb3ba8fSkettenis case V3S_CLK_MMC0:
21893fb3ba8fSkettenis case V3S_CLK_MMC1:
21903fb3ba8fSkettenis case V3S_CLK_MMC2:
21913fb3ba8fSkettenis clock.sc_iot = sc->sc_iot;
21923fb3ba8fSkettenis bus_space_subregion(sc->sc_iot, sc->sc_ioh,
21933fb3ba8fSkettenis sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
21943fb3ba8fSkettenis parent = V3S_CLK_PLL_PERIPH0;
21953fb3ba8fSkettenis parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
21963fb3ba8fSkettenis return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
21973fb3ba8fSkettenis }
21983fb3ba8fSkettenis
21993fb3ba8fSkettenis printf("%s: 0x%08x\n", __func__, idx);
22003fb3ba8fSkettenis return -1;
22013fb3ba8fSkettenis }
22023fb3ba8fSkettenis
22033fb3ba8fSkettenis int
sxiccmu_nop_set_frequency(struct sxiccmu_softc * sc,uint32_t idx,uint32_t freq)22042c064ff8Skettenis sxiccmu_nop_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
22052c064ff8Skettenis {
22062c064ff8Skettenis printf("%s: 0x%08x\n", __func__, idx);
22072c064ff8Skettenis return -1;
22082c064ff8Skettenis }
22092c064ff8Skettenis
22100ea1c705Spatrick void
sxiccmu_ccu_enable(void * cookie,uint32_t * cells,int on)22110ea1c705Spatrick sxiccmu_ccu_enable(void *cookie, uint32_t *cells, int on)
22120ea1c705Spatrick {
22130ea1c705Spatrick struct sxiccmu_softc *sc = cookie;
22140ea1c705Spatrick uint32_t idx = cells[0];
22150ea1c705Spatrick int reg, bit;
22160ea1c705Spatrick
22172c064ff8Skettenis clock_enable_all(sc->sc_node);
22182c064ff8Skettenis
22192c064ff8Skettenis if (idx >= sc->sc_ngates ||
22202c064ff8Skettenis (sc->sc_gates[idx].reg == 0 && sc->sc_gates[idx].bit == 0)) {
22210ea1c705Spatrick printf("%s: 0x%08x\n", __func__, cells[0]);
22220ea1c705Spatrick return;
22230ea1c705Spatrick }
22240ea1c705Spatrick
22258b22851bSkettenis /* If the clock can't be gated, simply return. */
22268b22851bSkettenis if (sc->sc_gates[idx].reg == 0xffff && sc->sc_gates[idx].bit == 0xff)
22278b22851bSkettenis return;
22288b22851bSkettenis
22290ea1c705Spatrick reg = sc->sc_gates[idx].reg;
22300ea1c705Spatrick bit = sc->sc_gates[idx].bit;
22310ea1c705Spatrick
22320ea1c705Spatrick if (on)
22330ea1c705Spatrick SXISET4(sc, reg, (1U << bit));
22340ea1c705Spatrick else
22350ea1c705Spatrick SXICLR4(sc, reg, (1U << bit));
22360ea1c705Spatrick }
22370ea1c705Spatrick
22380ea1c705Spatrick void
sxiccmu_ccu_reset(void * cookie,uint32_t * cells,int assert)22390ea1c705Spatrick sxiccmu_ccu_reset(void *cookie, uint32_t *cells, int assert)
22400ea1c705Spatrick {
22410ea1c705Spatrick struct sxiccmu_softc *sc = cookie;
22420ea1c705Spatrick uint32_t idx = cells[0];
22430ea1c705Spatrick int reg, bit;
22440ea1c705Spatrick
22452c064ff8Skettenis reset_deassert_all(sc->sc_node);
22462c064ff8Skettenis
22472c064ff8Skettenis if (idx >= sc->sc_nresets ||
22482c064ff8Skettenis (sc->sc_resets[idx].reg == 0 && sc->sc_gates[idx].bit == 0)) {
22490ea1c705Spatrick printf("%s: 0x%08x\n", __func__, cells[0]);
22500ea1c705Spatrick return;
22510ea1c705Spatrick }
22520ea1c705Spatrick
22530ea1c705Spatrick reg = sc->sc_resets[idx].reg;
22540ea1c705Spatrick bit = sc->sc_resets[idx].bit;
22550ea1c705Spatrick
22560ea1c705Spatrick if (assert)
22570ea1c705Spatrick SXICLR4(sc, reg, (1U << bit));
22580ea1c705Spatrick else
22590ea1c705Spatrick SXISET4(sc, reg, (1U << bit));
22600ea1c705Spatrick }
2261