> Date: Tue, 27 Apr 2021 13:34:01 +0000
> From: Visa Hankala <[email protected]>
> 
> On Mon, Apr 26, 2021 at 05:25:18PM +0200, Mark Kettenis wrote:
> > > 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.
> 
> I see, adding pinctrl logic would be easier if clocks and resets were
> handled in separate drivers. I have now reorganized the code to reflect
> this.
> 
> Both the clock and reset drivers access the control registers by using
> common routines. I have put them in the reset driver file. However,
> if this looks too ugly, I can add them in a separate .c file.
> 
> The use of the mutex might be overzealous. The main point is to prevent
> accidental interleaving when lifting the write protection for register
> update.
> 
> OK?

Folks will run into issues if they'd build a kernel without zqreset(4)
but I don't care ;).

Not sure if the mutex makes all that much sense given that armv7
doesn't have SMP support yet and that we don't even anticipate running
this kind of code without the kernel lock, but it isn't wrong.

ok kettenis@

> 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        27 Apr 2021 12:42:20 -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 zqclock.4 zqreset.4
>  
>  MANSUBDIR=armv7
>  
> Index: share/man/man4/man4.armv7/zqclock.4
> ===================================================================
> RCS file: share/man/man4/man4.armv7/zqclock.4
> diff -N share/man/man4/man4.armv7/zqclock.4
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ share/man/man4/man4.armv7/zqclock.4       27 Apr 2021 12:42:20 -0000
> @@ -0,0 +1,37 @@
> +.\"  $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 ZQCLOCK 4
> +.Os
> +.Sh NAME
> +.Nm zqclock
> +.Nd Xilinx Zynq-7000 clock controller
> +.Sh SYNOPSIS
> +.Cd "zqclock* at fdt?"
> +.Sh DESCRIPTION
> +The
> +.Nm
> +driver controls the clock signals for the integrated components
> +of Zynq-7000 SoCs.
> +.Sh SEE ALSO
> +.Xr intro 4 ,
> +.Xr zqreset 4
> +.Sh HISTORY
> +The
> +.Nm
> +driver first appeared in
> +.Ox 7.0 .
> Index: share/man/man4/man4.armv7/zqreset.4
> ===================================================================
> RCS file: share/man/man4/man4.armv7/zqreset.4
> diff -N share/man/man4/man4.armv7/zqreset.4
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ share/man/man4/man4.armv7/zqreset.4       27 Apr 2021 12:42:20 -0000
> @@ -0,0 +1,37 @@
> +.\"  $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 ZQRESET 4
> +.Os
> +.Sh NAME
> +.Nm zqreset
> +.Nd Xilinx Zynq-7000 reset controller
> +.Sh SYNOPSIS
> +.Cd "zqreset* at fdt?"
> +.Sh DESCRIPTION
> +The
> +.Nm
> +driver controls the reset signals for the integrated components
> +of Zynq-7000 SoCs.
> +.Sh SEE ALSO
> +.Xr intro 4 ,
> +.Xr zqclock 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       27 Apr 2021 12:42:21 -0000
> @@ -213,6 +213,8 @@ dwdog*            at fdt?
>  
>  # Xilinx Zynq-7000
>  cduart*              at fdt?
> +zqclock*     at fdt?
> +zqreset*     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       27 Apr 2021 12:42:21 -0000
> @@ -198,6 +198,8 @@ dwdog*            at fdt?
>  
>  # Xilinx Zynq-7000
>  cduart*              at fdt?
> +zqclock*     at fdt?
> +zqreset*     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   27 Apr 2021 12:42:21 -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        27 Apr 2021 12:42:21 -0000
> @@ -0,0 +1,9 @@
> +#    $OpenBSD$
> +
> +device zqclock
> +attach zqclock at fdt
> +file arch/armv7/xilinx/zqclock.c             zqclock
> +
> +device zqreset
> +attach zqreset at fdt
> +file arch/armv7/xilinx/zqreset.c             zqreset
> Index: sys/arch/armv7/xilinx/slcreg.h
> ===================================================================
> RCS file: sys/arch/armv7/xilinx/slcreg.h
> diff -N sys/arch/armv7/xilinx/slcreg.h
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ sys/arch/armv7/xilinx/slcreg.h    27 Apr 2021 12:42:21 -0000
> @@ -0,0 +1,49 @@
> +/*   $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.
> + */
> +
> +#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
> +
> +extern struct mutex zynq_slcr_lock;
> +
> +uint32_t zynq_slcr_read(struct regmap *, uint32_t);
> +void zynq_slcr_write(struct regmap *, uint32_t, uint32_t);
> Index: sys/arch/armv7/xilinx/zqclock.c
> ===================================================================
> RCS file: sys/arch/armv7/xilinx/zqclock.c
> diff -N sys/arch/armv7/xilinx/zqclock.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ sys/arch/armv7/xilinx/zqclock.c   27 Apr 2021 12:42:21 -0000
> @@ -0,0 +1,316 @@
> +/*   $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 clock controller.
> + */
> +
> +#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>
> +#include <dev/ofw/ofw_misc.h>
> +
> +#include <armv7/xilinx/slcreg.h>
> +
> +#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 zqclock_softc {
> +     struct device           sc_dev;
> +     struct regmap           *sc_rm;
> +
> +     struct clock_device     sc_cd;
> +     uint32_t                sc_psclk_freq;          /* in Hz */
> +};
> +
> +int  zqclock_match(struct device *, void *, void *);
> +void zqclock_attach(struct device *, struct device *, void *);
> +
> +void zqclock_enable(void *, uint32_t *, int);
> +uint32_t zqclock_get_frequency(void *, uint32_t *);
> +int  zqclock_set_frequency(void *, uint32_t *, uint32_t);
> +
> +const struct cfattach zqclock_ca = {
> +     sizeof(struct zqclock_softc), zqclock_match, zqclock_attach
> +};
> +
> +struct cfdriver zqclock_cd = {
> +     NULL, "zqclock", DV_DULL
> +};
> +
> +struct zqclock_clock {
> +     uint16_t                clk_ctl_reg;
> +     uint8_t                 clk_has_div1;
> +     uint8_t                 clk_index;
> +};
> +
> +const struct zqclock_clock zqclock_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 },
> +};
> +
> +int
> +zqclock_match(struct device *parent, void *match, void *aux)
> +{
> +     struct fdt_attach_args *faa = aux;
> +
> +     return OF_is_compatible(faa->fa_node, "xlnx,ps7-clkc");
> +}
> +
> +void
> +zqclock_attach(struct device *parent, struct device *self, void *aux)
> +{
> +     struct fdt_attach_args *faa = aux;
> +     struct zqclock_softc *sc = (struct zqclock_softc *)self;
> +
> +     sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
> +     if (sc->sc_rm == NULL) {
> +             printf(": can't get regmap\n");
> +             return;
> +     }
> +
> +     sc->sc_psclk_freq = OF_getpropint(faa->fa_node, "ps-clk-frequency",
> +         33333333);
> +
> +     printf(": %u MHz PS clock\n", (sc->sc_psclk_freq + 500000) / 1000000);
> +
> +     sc->sc_cd.cd_node = faa->fa_node;
> +     sc->sc_cd.cd_cookie = sc;
> +     sc->sc_cd.cd_enable = zqclock_enable;
> +     sc->sc_cd.cd_get_frequency = zqclock_get_frequency;
> +     sc->sc_cd.cd_set_frequency = zqclock_set_frequency;
> +     clock_register(&sc->sc_cd);
> +}
> +
> +const struct zqclock_clock *
> +zqclock_get_clock(uint32_t idx)
> +{
> +     const struct zqclock_clock *clock;
> +
> +     if (idx >= nitems(zqclock_clocks))
> +             return NULL;
> +
> +     clock = &zqclock_clocks[idx];
> +     if (clock->clk_ctl_reg == 0)
> +             return NULL;
> +
> +     return clock;
> +}
> +
> +uint32_t
> +zqclock_get_pll_frequency(struct zqclock_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 = zynq_slcr_read(sc->sc_rm, reg);
> +     return sc->sc_psclk_freq * ((val >> SLCR_PLL_CTRL_FDIV_SHIFT) &
> +         SLCR_PLL_CTRL_FDIV_MASK);
> +}
> +
> +uint32_t
> +zqclock_get_frequency(void *cookie, uint32_t *cells)
> +{
> +     const struct zqclock_clock *clock;
> +     struct zqclock_softc *sc = cookie;
> +     uint32_t idx = cells[0];
> +     uint32_t ctl, div, freq;
> +
> +     clock = zqclock_get_clock(idx);
> +     if (clock == NULL)
> +             return 0;
> +
> +     mtx_enter(&zynq_slcr_lock);
> +
> +     ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg);
> +
> +     div = SLCR_CLK_CTRL_DIVISOR(ctl);
> +     if (clock->clk_has_div1)
> +             div *= SLCR_CLK_CTRL_DIVISOR1(ctl);
> +
> +     freq = zqclock_get_pll_frequency(sc, ctl);
> +     freq = (freq + div / 2) / div;
> +
> +     mtx_leave(&zynq_slcr_lock);
> +
> +     return freq;
> +}
> +
> +int
> +zqclock_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 zqclock_clock *clock;
> +     struct zqclock_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 = zqclock_get_clock(idx);
> +     if (clock == NULL)
> +             return EINVAL;
> +
> +     if (freq == 0)
> +             return EINVAL;
> +
> +     mtx_enter(&zynq_slcr_lock);
> +
> +     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 = zqclock_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 = zynq_slcr_read(sc->sc_rm, 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;
> +     }
> +
> +     zynq_slcr_write(sc->sc_rm, clock->clk_ctl_reg, ctl);
> +
> +out:
> +     mtx_leave(&zynq_slcr_lock);
> +
> +     return error;
> +}
> +
> +void
> +zqclock_enable(void *cookie, uint32_t *cells, int on)
> +{
> +     const struct zqclock_clock *clock;
> +     struct zqclock_softc *sc = cookie;
> +     uint32_t idx = cells[0];
> +     uint32_t ctl;
> +
> +     clock = zqclock_get_clock(idx);
> +     if (clock == NULL)
> +             return;
> +
> +     mtx_enter(&zynq_slcr_lock);
> +
> +     ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg);
> +     if (on)
> +             ctl |= SLCR_CLK_CTRL_CLKACT(clock->clk_index);
> +     else
> +             ctl &= ~SLCR_CLK_CTRL_CLKACT(clock->clk_index);
> +     zynq_slcr_write(sc->sc_rm, clock->clk_ctl_reg, ctl);
> +
> +     mtx_leave(&zynq_slcr_lock);
> +}
> Index: sys/arch/armv7/xilinx/zqreset.c
> ===================================================================
> RCS file: sys/arch/armv7/xilinx/zqreset.c
> diff -N sys/arch/armv7/xilinx/zqreset.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ sys/arch/armv7/xilinx/zqreset.c   27 Apr 2021 12:42:21 -0000
> @@ -0,0 +1,112 @@
> +/*   $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 reset controller.
> + */
> +
> +#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_misc.h>
> +
> +#include <armv7/xilinx/slcreg.h>
> +
> +extern void (*cpuresetfn)(void);
> +
> +struct zqreset_softc {
> +     struct device           sc_dev;
> +     struct regmap           *sc_rm;
> +};
> +
> +int  zqreset_match(struct device *, void *, void *);
> +void zqreset_attach(struct device *, struct device *, void *);
> +
> +void zqreset_cpureset(void);
> +
> +const struct cfattach zqreset_ca = {
> +     sizeof(struct zqreset_softc), zqreset_match, zqreset_attach
> +};
> +
> +struct cfdriver zqreset_cd = {
> +     NULL, "zqreset", DV_DULL
> +};
> +
> +struct zqreset_softc *zqreset_sc;
> +
> +struct mutex zynq_slcr_lock = MUTEX_INITIALIZER(IPL_HIGH);
> +
> +int
> +zqreset_match(struct device *parent, void *match, void *aux)
> +{
> +     struct fdt_attach_args *faa = aux;
> +
> +     return OF_is_compatible(faa->fa_node, "xlnx,zynq-reset");
> +}
> +
> +void
> +zqreset_attach(struct device *parent, struct device *self, void *aux)
> +{
> +     struct fdt_attach_args *faa = aux;
> +     struct zqreset_softc *sc = (struct zqreset_softc *)self;
> +
> +     sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
> +     if (sc->sc_rm == NULL) {
> +             printf(": can't get regmap\n");
> +             return;
> +     }
> +
> +     printf("\n");
> +
> +     zqreset_sc = sc;
> +     cpuresetfn = zqreset_cpureset;
> +}
> +
> +void
> +zqreset_cpureset(void)
> +{
> +     struct zqreset_softc *sc = zqreset_sc;
> +
> +     mtx_enter(&zynq_slcr_lock);
> +     zynq_slcr_write(sc->sc_rm, SLCR_PSS_RST_CTRL,
> +         SLCR_PSS_RST_CTRL_SOFT_RST);
> +     mtx_leave(&zynq_slcr_lock);
> +}
> +
> +uint32_t
> +zynq_slcr_read(struct regmap *rm, uint32_t reg)
> +{
> +     return regmap_read_4(rm, reg);
> +}
> +
> +void
> +zynq_slcr_write(struct regmap *rm, uint32_t reg, uint32_t val)
> +{
> +     MUTEX_ASSERT_LOCKED(&zynq_slcr_lock);
> +
> +     regmap_write_4(rm, SLCR_UNLOCK, SLCR_UNLOCK_KEY);
> +     regmap_write_4(rm, reg, val);
> +     regmap_write_4(rm, SLCR_LOCK, SLCR_LOCK_KEY);
> +}
> 

Reply via email to