-/* $OpenBSD: sxiccmu.c,v 1.15 2016/08/26 08:30:24 mglocker Exp $ */
+/* $OpenBSD: sxiccmu.c,v 1.16 2016/08/27 11:39:59 kettenis Exp $ */
/*
* Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
* Copyright (c) 2013 Artturi Alm
+ * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#define CCMU_SDx_CLK_FACTOR_M (7 << 0)
#define CCMU_SDx_CLK_FACTOR_M_SHIFT 0
+struct sxiccmu_ccu_bit {
+ uint16_t reg;
+ uint8_t bit;
+ uint8_t parent;
+};
+
+#include "sxiccmu_clocks.h"
+
struct sxiccmu_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
+
+ struct sxiccmu_ccu_bit *sc_gates;
+ int sc_ngates;
+ struct clock_device sc_cd;
+
+ struct sxiccmu_ccu_bit *sc_resets;
+ int sc_nresets;
+ struct reset_device sc_rd;
};
void sxiccmu_attach(struct device *, struct device *, void *);
void sxiccmu_attach_clock(struct sxiccmu_softc *, int);
+uint32_t sxiccmu_ccu_get_frequency(void *, uint32_t *);
+int sxiccmu_ccu_set_frequency(void *, uint32_t *, uint32_t);
+void sxiccmu_ccu_enable(void *, uint32_t *, int);
+void sxiccmu_ccu_reset(void *, uint32_t *, int);
+
void
sxiccmu_attach(struct device *parent, struct device *self, void *args)
{
for (node = OF_child(node); node; node = OF_peer(node))
sxiccmu_attach_clock(sc, node);
+
+ node = OF_finddevice("/soc/clock@01c20000");
+ if (node == -1)
+ return;
+
+ if (OF_is_compatible(node, "allwinner,sun8i-h3-ccu")) {
+ sc->sc_gates = sun8i_h3_gates;
+ sc->sc_ngates = nitems(sun8i_h3_gates);
+ sc->sc_resets = sun8i_h3_resets;
+ sc->sc_nresets = nitems(sun8i_h3_resets);
+ }
+
+ if (sc->sc_gates) {
+ sc->sc_cd.cd_node = node;
+ sc->sc_cd.cd_cookie = sc;
+ sc->sc_cd.cd_get_frequency = sxiccmu_ccu_get_frequency;
+ sc->sc_cd.cd_set_frequency = sxiccmu_ccu_set_frequency;
+ sc->sc_cd.cd_enable = sxiccmu_ccu_enable;
+ clock_register(&sc->sc_cd);
+ }
+
+ if (sc->sc_resets) {
+ sc->sc_rd.rd_node = node;
+ sc->sc_rd.rd_cookie = sc;
+ sc->sc_rd.rd_reset = sxiccmu_ccu_reset;
+ reset_register(&sc->sc_rd);
+ }
}
+/*
+ * Device trees for the Allwinner SoCs have basically a clock node per
+ * register of the clock control unit. Attaching a separate driver to
+ * each of them would be crazy, so we handle them here.
+ */
+
struct sxiccmu_clock {
int sc_node;
bus_space_tag_t sc_iot;
.enable = sxiccmu_gate_enable,
.reset = sxiccmu_reset
},
+ {
+ .compat = "allwinner,sun6i-a31-ahb1-reset",
+ .reset = sxiccmu_reset
+ },
{
.compat = "allwinner,sun7i-a20-ahb-gates-clk",
.get_frequency = sxiccmu_gen_get_frequency,
.compat = "allwinner,sun7i-a20-gmac-clk",
.set_frequency = sxiccmu_gmac_set_frequency
},
+ {
+ .compat = "allwinner,sun8i-h3-apb0-gates-clk",
+ .get_frequency = sxiccmu_gen_get_frequency,
+ .enable = sxiccmu_gate_enable
+ },
};
void
SXISET4(sc, reg * 4, (1U << bit));
}
+/*
+ * Device trees for the Allwinner A80 have most of the clock nodes
+ * replaced with a single clock control unit node.
+ */
+
+uint32_t
+sxiccmu_ccu_get_frequency(void *cookie, uint32_t *cells)
+{
+ struct sxiccmu_softc *sc = cookie;
+ uint32_t idx = cells[0];
+ uint32_t parent;
+
+ if (idx < sc->sc_ngates && sc->sc_gates[idx].parent) {
+ parent = sc->sc_gates[idx].parent;
+ return sxiccmu_ccu_get_frequency(sc, &parent);
+ }
+
+ switch (idx) {
+ case H3_CLK_APB2:
+ /* XXX Controlled by a MUX. */
+ return 24000000;
+ }
+
+ printf("%s: 0x%08x\n", __func__, cells[0]);
+ return 0;
+}
+
+int
+sxiccmu_ccu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
+{
+ printf("%s: 0x%08x\n", __func__, cells[0]);
+ return 0;
+}
+
+void
+sxiccmu_ccu_enable(void *cookie, uint32_t *cells, int on)
+{
+ struct sxiccmu_softc *sc = cookie;
+ uint32_t idx = cells[0];
+ int reg, bit;
+
+ if (idx >= sc->sc_ngates || sc->sc_gates[idx].reg == 0) {
+ printf("%s: 0x%08x\n", __func__, cells[0]);
+ return;
+ }
+
+ reg = sc->sc_gates[idx].reg;
+ bit = sc->sc_gates[idx].bit;
+
+ if (on)
+ SXISET4(sc, reg, (1U << bit));
+ else
+ SXICLR4(sc, reg, (1U << bit));
+}
+
+void
+sxiccmu_ccu_reset(void *cookie, uint32_t *cells, int assert)
+{
+ struct sxiccmu_softc *sc = cookie;
+ uint32_t idx = cells[0];
+ int reg, bit;
+
+ if (idx >= sc->sc_nresets || sc->sc_resets[idx].reg == 0) {
+ printf("%s: 0x%08x\n", __func__, cells[0]);
+ return;
+ }
+
+ reg = sc->sc_resets[idx].reg;
+ bit = sc->sc_resets[idx].bit;
+
+ if (assert)
+ SXICLR4(sc, reg, (1U << bit));
+ else
+ SXISET4(sc, reg, (1U << bit));
+}
+
+
void
sxiccmu_set_sd_clock(int mod, int freq)
{
--- /dev/null
+/* Public Domain */
+
+
+/*
+ * Clocks Signals
+ */
+
+#define H3_PLL_PERIPH0 9
+
+#define H3_CLK_APB2 18
+
+#define H3_CLK_BUS_MMC0 22
+#define H3_CLK_BUS_MMC1 23
+#define H3_CLK_BUS_MMC2 24
+
+#define H3_CLK_BUS_EHCI0 33
+#define H3_CLK_BUS_EHCI1 34
+#define H3_CLK_BUS_EHCI2 35
+#define H3_CLK_BUS_EHCI3 36
+#define H3_CLK_BUS_OHCI0 37
+#define H3_CLK_BUS_OHCI1 38
+#define H3_CLK_BUS_OHCI2 39
+#define H3_CLK_BUS_OHCI3 40
+
+#define H3_CLK_BUS_UART0 62
+#define H3_CLK_BUS_UART1 63
+#define H3_CLK_BUS_UART2 64
+#define H3_CLK_BUS_UART3 65
+
+#define H3_CLK_USB_PHY0 88
+#define H3_CLK_USB_PHY1 89
+#define H3_CLK_USB_PHY2 90
+#define H3_CLK_USB_PHY3 91
+
+struct sxiccmu_ccu_bit sun8i_h3_gates[] = {
+ [H3_CLK_BUS_MMC0] = { 0x0060, 8 },
+ [H3_CLK_BUS_MMC1] = { 0x0060, 9 },
+ [H3_CLK_BUS_MMC2] = { 0x0060, 10 },
+ [H3_CLK_BUS_EHCI0] = { 0x0060, 24 },
+ [H3_CLK_BUS_EHCI1] = { 0x0060, 25 },
+ [H3_CLK_BUS_EHCI2] = { 0x0060, 26 },
+ [H3_CLK_BUS_EHCI3] = { 0x0060, 27 },
+ [H3_CLK_BUS_OHCI0] = { 0x0060, 28 },
+ [H3_CLK_BUS_OHCI1] = { 0x0060, 29 },
+ [H3_CLK_BUS_OHCI2] = { 0x0060, 30 },
+ [H3_CLK_BUS_OHCI3] = { 0x0060, 31 },
+ [H3_CLK_BUS_UART0] = { 0x006c, 16, H3_CLK_APB2 },
+ [H3_CLK_BUS_UART1] = { 0x006c, 17, H3_CLK_APB2 },
+ [H3_CLK_BUS_UART2] = { 0x006c, 18, H3_CLK_APB2 },
+ [H3_CLK_BUS_UART3] = { 0x006c, 19, H3_CLK_APB2 },
+ [H3_CLK_USB_PHY0] = { 0x00cc, 8 },
+ [H3_CLK_USB_PHY1] = { 0x00cc, 9 },
+ [H3_CLK_USB_PHY2] = { 0x00cc, 10 },
+ [H3_CLK_USB_PHY3] = { 0x00cc, 11 },
+};
+
+/*
+ * Reset Signals
+ */
+
+#define H3_RST_USB_PHY0 0
+#define H3_RST_USB_PHY1 1
+#define H3_RST_USB_PHY2 2
+#define H3_RST_USB_PHY3 3
+
+#define H3_RST_BUS_EHCI0 18
+#define H3_RST_BUS_EHCI1 19
+#define H3_RST_BUS_EHCI2 20
+#define H3_RST_BUS_EHCI3 21
+#define H3_RST_BUS_OHCI0 22
+#define H3_RST_BUS_OHCI1 23
+#define H3_RST_BUS_OHCI2 24
+#define H3_RST_BUS_OHCI3 25
+
+struct sxiccmu_ccu_bit sun8i_h3_resets[] = {
+ [H3_RST_USB_PHY0] = { 0x00cc, 0 },
+ [H3_RST_USB_PHY1] = { 0x00cc, 1 },
+ [H3_RST_USB_PHY2] = { 0x00cc, 2 },
+ [H3_RST_USB_PHY3] = { 0x00cc, 3 },
+ [H3_RST_BUS_EHCI0] = { 0x02c0, 24 },
+ [H3_RST_BUS_EHCI1] = { 0x02c0, 25 },
+ [H3_RST_BUS_EHCI2] = { 0x02c0, 26 },
+ [H3_RST_BUS_EHCI3] = { 0x02c0, 27 },
+ [H3_RST_BUS_OHCI0] = { 0x02c0, 28 },
+ [H3_RST_BUS_OHCI1] = { 0x02c0, 29 },
+ [H3_RST_BUS_OHCI2] = { 0x02c0, 30 },
+ [H3_RST_BUS_OHCI3] = { 0x02c0, 31 },
+};