The following diff adds a preliminary driver for the system-level
control registers of Xilinx Zynq-7000. It enables system reset. It also
adds clock bits for use with the SDIO and Gigabit Ethernet controllers.

On some arm64 and armv7 platforms, there are separate drivers for clocks
and resets. However, on Zynq-7000 it looks more natural to use a single
driver. Below is an outline of the relevant part of the device tree:

                slcr: slcr@f8000000 {
                        #address-cells = <1>;                                  
                        #size-cells = <1>;                                     
                        compatible = "xlnx,zynq-slcr", "syscon", "simple-mfd"; 
                        reg = <0xF8000000 0x1000>;                             
                        ranges;                                                
                        clkc: clkc@100 {                                       
                                #clock-cells = <1>;                            
                                compatible = "xlnx,ps7-clkc";                  
                                fclk-enable = <0>;                             
                                clock-output-names = "armpll", "ddrpll", ...;
                                reg = <0x100 0x100>;                           
                        };                                                     
                                                                               
                        rstc: rstc@200 {                                       
                                compatible = "xlnx,zynq-reset";                
                                reg = <0x200 0x48>;                            
                                #reset-cells = <1>;                            
                                syscon = <&slcr>;                              
                        };                                                     
                                                                               
                        pinctrl0: pinctrl@700 {                                
                                compatible = "xlnx,pinctrl-zynq";              
                                reg = <0x700 0x200>;                           
                                syscon = <&slcr>;                              
                        };                                                     
                };

OK?

Index: share/man/man4/man4.armv7/Makefile
===================================================================
RCS file: src/share/man/man4/man4.armv7/Makefile,v
retrieving revision 1.28
diff -u -p -r1.28 Makefile
--- share/man/man4/man4.armv7/Makefile  10 Apr 2020 22:26:46 -0000      1.28
+++ share/man/man4/man4.armv7/Makefile  26 Apr 2021 14:14:29 -0000
@@ -6,7 +6,7 @@ MAN=    agtimer.4 amdisplay.4 ampintc.4 amp
        omap.4 omclock.4 omcm.4 omdog.4 omgpio.4 ommmc.4 omrng.4 omsysc.4 \
        omwugen.4 prcm.4 \
        sxie.4 sxiintc.4 \
-       sxitimer.4 sxits.4 sysreg.4
+       sxitimer.4 sxits.4 sysreg.4 zqsyscon.4
 
 MANSUBDIR=armv7
 
Index: share/man/man4/man4.armv7/zqsyscon.4
===================================================================
RCS file: share/man/man4/man4.armv7/zqsyscon.4
diff -N share/man/man4/man4.armv7/zqsyscon.4
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ share/man/man4/man4.armv7/zqsyscon.4        26 Apr 2021 14:14:29 -0000
@@ -0,0 +1,38 @@
+.\"    $OpenBSD$
+.\"
+.\" Copyright (c) 2021 Visa Hankala
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate$
+.Dt ZQSYSCON 4
+.Os
+.Sh NAME
+.Nm zqsyscon
+.Nd Xilinx Zynq-7000 system controller
+.Sh SYNOPSIS
+.Cd "zqsyscon* at fdt?"
+.Sh DESCRIPTION
+The
+.Nm
+driver
+provides access to a set of miscellaneous hardware registers
+to other device drivers.
+It also provides system reset functionality.
+.Sh SEE ALSO
+.Xr intro 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 7.0 .
Index: sys/arch/armv7/conf/GENERIC
===================================================================
RCS file: src/sys/arch/armv7/conf/GENERIC,v
retrieving revision 1.135
diff -u -p -r1.135 GENERIC
--- sys/arch/armv7/conf/GENERIC 24 Apr 2021 07:49:11 -0000      1.135
+++ sys/arch/armv7/conf/GENERIC 26 Apr 2021 14:14:29 -0000
@@ -213,6 +213,7 @@ dwdog*              at fdt?
 
 # Xilinx Zynq-7000
 cduart*                at fdt?
+zqsyscon*      at fdt?
 
 # I2C devices
 abcrtc*                at iic?                 # Abracon x80x RTC
Index: sys/arch/armv7/conf/RAMDISK
===================================================================
RCS file: src/sys/arch/armv7/conf/RAMDISK,v
retrieving revision 1.121
diff -u -p -r1.121 RAMDISK
--- sys/arch/armv7/conf/RAMDISK 24 Apr 2021 07:49:11 -0000      1.121
+++ sys/arch/armv7/conf/RAMDISK 26 Apr 2021 14:14:29 -0000
@@ -198,6 +198,7 @@ dwdog*              at fdt?
 
 # Xilinx Zynq-7000
 cduart*                at fdt?
+zqsyscon*      at fdt?
 
 axppmic*       at iic?                 # axp209 pmic
 crosec*                at iic?
Index: sys/arch/armv7/conf/files.armv7
===================================================================
RCS file: src/sys/arch/armv7/conf/files.armv7,v
retrieving revision 1.37
diff -u -p -r1.37 files.armv7
--- sys/arch/armv7/conf/files.armv7     5 Jun 2018 20:41:19 -0000       1.37
+++ sys/arch/armv7/conf/files.armv7     26 Apr 2021 14:14:29 -0000
@@ -75,3 +75,4 @@ include "arch/armv7/exynos/files.exynos"
 include "arch/armv7/vexpress/files.vexpress"
 include "arch/armv7/broadcom/files.broadcom"
 include "arch/armv7/marvell/files.marvell"
+include "arch/armv7/xilinx/files.xilinx"
Index: sys/arch/armv7/xilinx/files.xilinx
===================================================================
RCS file: sys/arch/armv7/xilinx/files.xilinx
diff -N sys/arch/armv7/xilinx/files.xilinx
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/armv7/xilinx/files.xilinx  26 Apr 2021 14:14:29 -0000
@@ -0,0 +1,5 @@
+#      $OpenBSD$
+
+device zqsyscon
+attach zqsyscon at fdt
+file   arch/armv7/xilinx/zqsyscon.c            zqsyscon
Index: sys/arch/armv7/xilinx/zqsyscon.c
===================================================================
RCS file: sys/arch/armv7/xilinx/zqsyscon.c
diff -N sys/arch/armv7/xilinx/zqsyscon.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/armv7/xilinx/zqsyscon.c    26 Apr 2021 14:14:29 -0000
@@ -0,0 +1,407 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2021 Visa Hankala
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for Xilinx Zynq-7000 System Level Control Registers.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/fdt.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+
+extern void (*cpuresetfn)(void);
+
+#define SLCR_LOCK                      0x0004
+#define  SLCR_LOCK_KEY                         0x767b
+#define SLCR_UNLOCK                    0x0008
+#define  SLCR_UNLOCK_KEY                       0xdf0d
+#define SLCR_ARM_PLL_CTRL              0x0100
+#define SLCR_DDR_PLL_CTRL              0x0104
+#define SLCR_IO_PLL_CTRL               0x0108
+#define  SLCR_PLL_CTRL_FDIV_MASK               0x7f
+#define  SLCR_PLL_CTRL_FDIV_SHIFT              12
+#define SLCR_GEM0_CLK_CTRL             0x0140
+#define SLCR_GEM1_CLK_CTRL             0x0144
+#define SLCR_SDIO_CLK_CTRL             0x0150
+#define SLCR_UART_CLK_CTRL             0x0154
+#define  SLCR_CLK_CTRL_DIVISOR1(x)             (((x) >> 20) & 0x3f)
+#define  SLCR_CLK_CTRL_DIVISOR1_SHIFT          20
+#define  SLCR_CLK_CTRL_DIVISOR(x)              (((x) >> 8) & 0x3f)
+#define  SLCR_CLK_CTRL_DIVISOR_SHIFT           8
+#define  SLCR_CLK_CTRL_SRCSEL_MASK             (0x7 << 4)
+#define  SLCR_CLK_CTRL_SRCSEL_DDR              (0x3 << 4)
+#define  SLCR_CLK_CTRL_SRCSEL_ARM              (0x2 << 4)
+#define  SLCR_CLK_CTRL_SRCSEL_IO               (0x1 << 4)
+#define  SLCR_CLK_CTRL_CLKACT(i)               (0x1 << (i))
+#define SLCR_PSS_RST_CTRL              0x0200
+#define  SLCR_PSS_RST_CTRL_SOFT_RST            (1 << 0)
+
+#define SLCR_DIV_MASK                  0x3f
+
+#define CLK_ARM_PLL                    0
+#define CLK_DDR_PLL                    1
+#define CLK_IO_PLL                     2
+#define CLK_CPU_6OR4X                  3
+#define CLK_CPU_3OR2X                  4
+#define CLK_CPU_2X                     5
+#define CLK_CPU_1X                     6
+#define CLK_DDR_2X                     7
+#define CLK_DDR_3X                     8
+#define CLK_DCI                                9
+#define CLK_LQSPI                      10
+#define CLK_SMC                                11
+#define CLK_PCAP                       12
+#define CLK_GEM0                       13
+#define CLK_GEM1                       14
+#define CLK_FCLK0                      15
+#define CLK_FCLK1                      16
+#define CLK_FCLK2                      17
+#define CLK_FCLK3                      18
+#define CLK_CAN0                       19
+#define CLK_CAN1                       20
+#define CLK_SDIO0                      21
+#define CLK_SDIO1                      22
+#define CLK_UART0                      23
+#define CLK_UART1                      24
+#define CLK_SPI0                       25
+#define CLK_SPI1                       26
+#define CLK_DMA                                27
+
+struct zqsyscon_softc {
+       struct device           sc_dev;
+       bus_space_tag_t         sc_iot;
+       bus_space_handle_t      sc_ioh;
+       struct mutex            sc_mtx;
+
+       uint32_t                sc_psclk_freq;          /* in Hz */
+
+       struct clock_device     sc_cd;
+};
+
+int    zqsyscon_match(struct device *, void *, void *);
+void   zqsyscon_attach(struct device *, struct device *, void *);
+
+void   zqsyscon_enable(void *, uint32_t *, int);
+uint32_t zqsyscon_get_frequency(void *, uint32_t *);
+int    zqsyscon_set_frequency(void *, uint32_t *, uint32_t);
+
+void   zqsyscon_cpureset(void);
+
+struct zqsyscon_clock {
+       uint16_t                clk_ctl_reg;
+       uint8_t                 clk_has_div1;
+       uint8_t                 clk_index;
+};
+
+const struct zqsyscon_clock zqsyscon_clocks[] = {
+       [CLK_GEM0]              = { SLCR_GEM0_CLK_CTRL, 1, 0 },
+       [CLK_GEM1]              = { SLCR_GEM1_CLK_CTRL, 1, 0 },
+       [CLK_SDIO0]             = { SLCR_SDIO_CLK_CTRL, 0, 0 },
+       [CLK_SDIO1]             = { SLCR_SDIO_CLK_CTRL, 0, 1 },
+       [CLK_UART0]             = { SLCR_UART_CLK_CTRL, 0, 0 },
+       [CLK_UART1]             = { SLCR_UART_CLK_CTRL, 0, 1 },
+};
+
+const struct cfattach zqsyscon_ca = {
+       sizeof(struct zqsyscon_softc), zqsyscon_match, zqsyscon_attach
+};
+
+struct cfdriver zqsyscon_cd = {
+       NULL, "zqsyscon", DV_DULL
+};
+
+struct zqsyscon_softc  *zqsyscon_sc;
+
+static uint32_t
+zqsyscon_read(struct zqsyscon_softc *sc, uint32_t reg)
+{
+       return bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
+}
+
+static void
+zqsyscon_write(struct zqsyscon_softc *sc, uint32_t reg, uint32_t val)
+{
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, SLCR_UNLOCK, SLCR_UNLOCK_KEY);
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, SLCR_LOCK, SLCR_LOCK_KEY);
+}
+
+int
+zqsyscon_match(struct device *parent, void *match, void *aux)
+{
+       struct fdt_attach_args *faa = aux;
+
+       if (OF_is_compatible(faa->fa_node, "xlnx,zynq-slcr"))
+               return 10;      /* Must beat syscon(4). */
+
+       return 0;
+}
+
+void
+zqsyscon_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct fdt_attach_args *faa = aux;
+       struct zqsyscon_softc *sc = (struct zqsyscon_softc *)self;
+       int node;
+
+       if (faa->fa_nreg < 1) {
+               printf(": no registers\n");
+               return;
+       }
+
+       sc->sc_iot = faa->fa_iot;
+       if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+           faa->fa_reg[0].size, 0, &sc->sc_ioh) != 0) {
+               printf(": can't map registers\n");
+               return;
+       }
+
+       for (node = OF_child(faa->fa_node); node != 0; node = OF_peer(node)) {
+               if (OF_is_compatible(node, "xlnx,ps7-clkc"))
+                       break;
+       }
+       if (node == 0) {
+               printf(": can't find clkc node\n");
+               goto fail;
+       }
+
+       mtx_init(&sc->sc_mtx, IPL_HIGH);
+
+       sc->sc_psclk_freq = OF_getpropint(node, "ps-clk-frequency", 33333333);
+
+       printf(": %u MHz PS clock\n", (sc->sc_psclk_freq + 500000) / 1000000);
+
+       zqsyscon_sc = sc;
+       cpuresetfn = zqsyscon_cpureset;
+
+       sc->sc_cd.cd_node = node;
+       sc->sc_cd.cd_cookie = sc;
+       sc->sc_cd.cd_enable = zqsyscon_enable;
+       sc->sc_cd.cd_get_frequency = zqsyscon_get_frequency;
+       sc->sc_cd.cd_set_frequency = zqsyscon_set_frequency;
+       clock_register(&sc->sc_cd);
+
+       return;
+
+fail:
+       if (sc->sc_ioh != 0)
+               bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
+}
+
+const struct zqsyscon_clock *
+zqsyscon_get_clock(uint32_t idx)
+{
+       const struct zqsyscon_clock *clock;
+
+       if (idx >= nitems(zqsyscon_clocks))
+               return NULL;
+
+       clock = &zqsyscon_clocks[idx];
+       if (clock->clk_ctl_reg == 0)
+               return NULL;
+
+       return clock;
+}
+
+uint32_t
+zqsyscon_get_pll_frequency(struct zqsyscon_softc *sc, uint32_t clk_ctrl)
+{
+       uint32_t reg, val;
+
+       switch (clk_ctrl & SLCR_CLK_CTRL_SRCSEL_MASK) {
+       case SLCR_CLK_CTRL_SRCSEL_ARM:
+               reg = SLCR_ARM_PLL_CTRL;
+               break;
+       case SLCR_CLK_CTRL_SRCSEL_DDR:
+               reg = SLCR_DDR_PLL_CTRL;
+               break;
+       default:
+               reg = SLCR_IO_PLL_CTRL;
+               break;
+       }
+
+       val = zqsyscon_read(sc, reg);
+       return sc->sc_psclk_freq * ((val >> SLCR_PLL_CTRL_FDIV_SHIFT) &
+           SLCR_PLL_CTRL_FDIV_MASK);
+}
+
+uint32_t
+zqsyscon_get_frequency(void *cookie, uint32_t *cells)
+{
+       const struct zqsyscon_clock *clock;
+       struct zqsyscon_softc *sc = cookie;
+       uint32_t idx = cells[0];
+       uint32_t ctl, div, freq;
+
+       clock = zqsyscon_get_clock(idx);
+       if (clock == NULL)
+               return 0;
+
+       mtx_enter(&sc->sc_mtx);
+
+       ctl = zqsyscon_read(sc, clock->clk_ctl_reg);
+
+       div = SLCR_CLK_CTRL_DIVISOR(ctl);
+       if (clock->clk_has_div1)
+               div *= SLCR_CLK_CTRL_DIVISOR1(ctl);
+
+       freq = zqsyscon_get_pll_frequency(sc, ctl);
+       freq = (freq + div / 2) / div;
+
+       mtx_leave(&sc->sc_mtx);
+
+       return freq;
+}
+
+int
+zqsyscon_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
+{
+       static const uint32_t srcsels[] = {
+               SLCR_CLK_CTRL_SRCSEL_IO,
+               SLCR_CLK_CTRL_SRCSEL_ARM,
+               SLCR_CLK_CTRL_SRCSEL_DDR,
+       };
+       const struct zqsyscon_clock *clock;
+       struct zqsyscon_softc *sc = cookie;
+       uint32_t idx = cells[0];
+       uint32_t best_delta = ~0U;
+       uint32_t best_div1 = 0;
+       uint32_t best_si = 0;
+       uint32_t best_pllf = 0;
+       uint32_t ctl, div, div1, maxdiv1, si;
+       int error = 0;
+
+       clock = zqsyscon_get_clock(idx);
+       if (clock == NULL)
+               return EINVAL;
+
+       if (freq == 0)
+               return EINVAL;
+
+       mtx_enter(&sc->sc_mtx);
+
+       maxdiv1 = 1;
+       if (clock->clk_has_div1)
+               maxdiv1 = SLCR_DIV_MASK;
+
+       /* Find PLL and divisors that give best frequency. */
+       for (si = 0; si < nitems(srcsels); si++) {
+               uint32_t delta, f, pllf;
+
+               pllf = zqsyscon_get_pll_frequency(sc, srcsels[si]);
+               if (freq > pllf)
+                       continue;
+
+               for (div1 = 1; div1 <= maxdiv1; div1++) {
+                       div = (pllf + (freq * div1 / 2)) / (freq * div1);
+                       if (div > SLCR_DIV_MASK)
+                               continue;
+                       if (div == 0)
+                               break;
+
+                       f = (pllf + (div * div1 / 2)) / (div * div1);
+                       delta = abs(f - freq);
+                       if (best_div1 == 0 || delta < best_delta) {
+                               best_delta = delta;
+                               best_div1 = div1;
+                               best_pllf = pllf;
+                               best_si = si;
+
+                               if (delta == 0)
+                                       goto found;
+                       }
+               }
+       }
+
+       if (best_div1 == 0) {
+               error = EINVAL;
+               goto out;
+       }
+
+found:
+       div1 = best_div1;
+       div = (best_pllf + (freq * div1 / 2)) / (freq * div1);
+
+       KASSERT(div > 0 && div <= SLCR_DIV_MASK);
+       KASSERT(div1 > 0 && div1 <= SLCR_DIV_MASK);
+
+       ctl = zqsyscon_read(sc, clock->clk_ctl_reg);
+
+       ctl &= ~SLCR_CLK_CTRL_SRCSEL_MASK;
+       ctl |= srcsels[best_si];
+       ctl &= ~(SLCR_DIV_MASK << SLCR_CLK_CTRL_DIVISOR_SHIFT);
+       ctl |= (div & SLCR_DIV_MASK) << SLCR_CLK_CTRL_DIVISOR_SHIFT;
+       if (clock->clk_has_div1) {
+               ctl &= ~(SLCR_DIV_MASK << SLCR_CLK_CTRL_DIVISOR1_SHIFT);
+               ctl |= (div1 & SLCR_DIV_MASK) << SLCR_CLK_CTRL_DIVISOR1_SHIFT;
+       }
+
+       zqsyscon_write(sc, clock->clk_ctl_reg, ctl);
+
+out:
+       mtx_leave(&sc->sc_mtx);
+
+       return error;
+}
+
+void
+zqsyscon_enable(void *cookie, uint32_t *cells, int on)
+{
+       const struct zqsyscon_clock *clock;
+       struct zqsyscon_softc *sc = cookie;
+       uint32_t idx = cells[0];
+       uint32_t ctl;
+
+       if (idx >= nitems(zqsyscon_clocks))
+               return;
+
+       clock = &zqsyscon_clocks[idx];
+       if (clock->clk_ctl_reg == 0)
+               return;
+
+       mtx_enter(&sc->sc_mtx);
+
+       ctl = zqsyscon_read(sc, clock->clk_ctl_reg);
+       if (on)
+               ctl |= SLCR_CLK_CTRL_CLKACT(clock->clk_index);
+       else
+               ctl &= ~SLCR_CLK_CTRL_CLKACT(clock->clk_index);
+       zqsyscon_write(sc, clock->clk_ctl_reg, ctl);
+
+       mtx_leave(&sc->sc_mtx);
+}
+
+void
+zqsyscon_cpureset(void)
+{
+       struct zqsyscon_softc *sc = zqsyscon_sc;
+
+       mtx_enter(&sc->sc_mtx);
+       zqsyscon_write(sc, SLCR_PSS_RST_CTRL, SLCR_PSS_RST_CTRL_SOFT_RST);
+       mtx_leave(&sc->sc_mtx);
+}

Reply via email to