> Date: Mon, 26 Apr 2021 14:19:38 +0000
> From: Visa Hankala <[email protected]>
>
> 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?
Hmm, I'm not sure. Your driver doesn't provide pinctrl support. I'm
not sure how much code you'd need for that, but if it is a significant
amount of code, having separate clock and pinctrl drivers would make
sense.
> 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);
> +}
>
>