Add support for sun8i-h3, the Allwinner H3. For this SoC, the device tree
authorkettenis <kettenis@openbsd.org>
Sat, 27 Aug 2016 11:39:59 +0000 (11:39 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 27 Aug 2016 11:39:59 +0000 (11:39 +0000)
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@

sys/arch/armv7/sunxi/sunxi.c
sys/arch/armv7/sunxi/sxiccmu.c
sys/arch/armv7/sunxi/sxiccmu_clocks.h [new file with mode: 0644]

index edfa4fd..35473ab 100644 (file)
@@ -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 },
 };
 
index 25c63b3..117e0d9 100644 (file)
@@ -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
 #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;
@@ -191,6 +246,10 @@ struct sxiccmu_device sxiccmu_devices[] = {
                .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,
@@ -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 (file)
index 0000000..0d2a74e
--- /dev/null
@@ -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 },
+};