summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2016-08-27 11:40:00 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2016-08-27 11:40:00 +0000
commitd73ca0f05de11870795e8c36e9e4e35bd6d5c655 (patch)
tree460dd6850058e787ae19635d8666b25c63a181fb /sys
parent5188fd76284175ca52eb99236e0405fc50183b93 (diff)
Add support for sun8i-h3, the Allwinner H3. For this SoC, the device tree
contains a single clock control unit node that handles most clocks. The driver handles this through tables that describe the gating, reset signals and the clock hierarchy. This description is (deliberately) incomplete. We will add clocks on an as-needed basis. You will need a recent device tree, that includes a "allwinner,sun8i-h3-ccu" compatible node for things to work. ok patrick@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/armv7/sunxi/sunxi.c7
-rw-r--r--sys/arch/armv7/sunxi/sxiccmu.c143
-rw-r--r--sys/arch/armv7/sunxi/sxiccmu_clocks.h88
3 files changed, 236 insertions, 2 deletions
diff --git a/sys/arch/armv7/sunxi/sunxi.c b/sys/arch/armv7/sunxi/sunxi.c
index edfa4fd8fa7..35473abac3d 100644
--- a/sys/arch/armv7/sunxi/sunxi.c
+++ b/sys/arch/armv7/sunxi/sunxi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sunxi.c,v 1.17 2016/08/05 21:45:37 kettenis Exp $ */
+/* $OpenBSD: sunxi.c,v 1.18 2016/08/27 11:39:59 kettenis Exp $ */
/*
* Copyright (c) 2005,2008 Dale Rahn <drahn@openbsd.com>
*
@@ -90,6 +90,11 @@ struct sunxi_soc sunxi_socs[] = {
sun7i_devs,
sxia20_init,
},
+ {
+ "allwinner,sun8i-h3",
+ sun7i_devs,
+ sxia20_init,
+ },
{ NULL, NULL, NULL },
};
diff --git a/sys/arch/armv7/sunxi/sxiccmu.c b/sys/arch/armv7/sunxi/sxiccmu.c
index 25c63b3f0c7..117e0d991d9 100644
--- a/sys/arch/armv7/sunxi/sxiccmu.c
+++ b/sys/arch/armv7/sunxi/sxiccmu.c
@@ -1,7 +1,8 @@
-/* $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
@@ -53,10 +54,26 @@
#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 *);
@@ -71,6 +88,11 @@ struct cfdriver sxiccmu_cd = {
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)
{
@@ -92,8 +114,41 @@ 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;
@@ -192,6 +247,10 @@ struct sxiccmu_device sxiccmu_devices[] = {
.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,
.enable = sxiccmu_gate_enable
@@ -210,6 +269,11 @@ struct sxiccmu_device sxiccmu_devices[] = {
.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
@@ -416,6 +480,83 @@ sxiccmu_reset(void *cookie, uint32_t *cells, int assert)
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)
{
diff --git a/sys/arch/armv7/sunxi/sxiccmu_clocks.h b/sys/arch/armv7/sunxi/sxiccmu_clocks.h
new file mode 100644
index 00000000000..0d2a74edbee
--- /dev/null
+++ b/sys/arch/armv7/sunxi/sxiccmu_clocks.h
@@ -0,0 +1,88 @@
+/* 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 },
+};