xref: /netbsd/sys/arch/arm/xilinx/zynq7000_clkc.c (revision a0fe19da)
1*a0fe19daSjmcneill /* $NetBSD: zynq7000_clkc.c,v 1.5 2022/11/11 20:29:47 jmcneill Exp $ */
204c770d6Sjmcneill 
304c770d6Sjmcneill /*-
404c770d6Sjmcneill  * Copyright (c) 2022 Jared McNeill <jmcneill@invisible.ca>
504c770d6Sjmcneill  * All rights reserved.
604c770d6Sjmcneill  *
704c770d6Sjmcneill  * Redistribution and use in source and binary forms, with or without
804c770d6Sjmcneill  * modification, are permitted provided that the following conditions
904c770d6Sjmcneill  * are met:
1004c770d6Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1104c770d6Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1204c770d6Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1304c770d6Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1404c770d6Sjmcneill  *    documentation and/or other materials provided with the distribution.
1504c770d6Sjmcneill  *
1604c770d6Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1704c770d6Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1804c770d6Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1904c770d6Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2004c770d6Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2104c770d6Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2204c770d6Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2304c770d6Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2404c770d6Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2504c770d6Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2604c770d6Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
2704c770d6Sjmcneill  */
2804c770d6Sjmcneill 
2904c770d6Sjmcneill #include <sys/cdefs.h>
3004c770d6Sjmcneill 
31*a0fe19daSjmcneill __KERNEL_RCSID(0, "$NetBSD: zynq7000_clkc.c,v 1.5 2022/11/11 20:29:47 jmcneill Exp $");
3204c770d6Sjmcneill 
3304c770d6Sjmcneill #include <sys/param.h>
3404c770d6Sjmcneill #include <sys/bus.h>
3504c770d6Sjmcneill #include <sys/device.h>
3604c770d6Sjmcneill #include <sys/intr.h>
3704c770d6Sjmcneill #include <sys/systm.h>
3804c770d6Sjmcneill #include <sys/time.h>
3904c770d6Sjmcneill #include <sys/kmem.h>
4004c770d6Sjmcneill 
4104c770d6Sjmcneill #include <dev/clk/clk_backend.h>
4204c770d6Sjmcneill 
4304c770d6Sjmcneill #include <dev/fdt/fdtvar.h>
44a60aa55cSjmcneill #include <dev/fdt/syscon.h>
4504c770d6Sjmcneill 
46a60aa55cSjmcneill #define	ARM_PLL_CTRL	0x100
47a60aa55cSjmcneill #define	DDR_PLL_CTRL	0x104
48a60aa55cSjmcneill #define	IO_PLL_CTRL	0x108
4904c770d6Sjmcneill #define	 PLL_FDIV		__BITS(18,12)
50a60aa55cSjmcneill #define	ARM_CLK_CTRL	0x120
5104c770d6Sjmcneill #define	 ARM_CLK_CTRL_CPU_1XCLKACT	__BIT(27)
5204c770d6Sjmcneill #define	 ARM_CLK_CTRL_CPU_2XCLKACT	__BIT(26)
5304c770d6Sjmcneill #define	 ARM_CLK_CTRL_CPU_3OR2XCLKACT	__BIT(25)
5404c770d6Sjmcneill #define	 ARM_CLK_CTRL_CPU_6OR4XCLKACT	__BIT(24)
55a60aa55cSjmcneill #define	 ARM_CLK_CTRL_DIVISOR		__BITS(13,8)
56a60aa55cSjmcneill #define	APER_CLK_CTRL	0x12c
5704c770d6Sjmcneill #define	 UART1_CPU_1XCLKACT	__BIT(21)
5804c770d6Sjmcneill #define	 UART0_CPU_1XCLKACT	__BIT(20)
5988c82104Sjmcneill #define	 I2C1_CPU_1XCLKACT	__BIT(19)
6088c82104Sjmcneill #define	 I2C0_CPU_1XCLKACT	__BIT(18)
61350c3bceSjmcneill #define	 SDI1_CPU_1XCLKACT	__BIT(11)
62350c3bceSjmcneill #define	 SDI0_CPU_1XCLKACT	__BIT(10)
63a60aa55cSjmcneill #define	SDIO_CLK_CTRL	0x150
64a60aa55cSjmcneill #define	UART_CLK_CTRL	0x154
65*a0fe19daSjmcneill #define	PCAP_CLK_CTRL	0x168
66350c3bceSjmcneill #define	 CLK_CTRL_DIVISOR	__BITS(13,8)
67350c3bceSjmcneill #define	 CLK_CTRL_SRCSEL	__BITS(5,4)
68350c3bceSjmcneill #define	 CLK_CTRL_CLKACT1	__BIT(1)
69350c3bceSjmcneill #define	 CLK_CTRL_CLKACT0	__BIT(0)
70a60aa55cSjmcneill #define	CLK_621_TRUE	0x1C4
7104c770d6Sjmcneill #define	 CLK_621_TRUE_EN	__BIT(0)
7204c770d6Sjmcneill 
7304c770d6Sjmcneill enum xynq7000_clkid {
7404c770d6Sjmcneill 	clkid_armpll,
7504c770d6Sjmcneill 	clkid_ddrpll,
7604c770d6Sjmcneill 	clkid_iopll,
7704c770d6Sjmcneill 	clkid_cpu_6or4x,
7804c770d6Sjmcneill 	clkid_cpu_3or2x,
7904c770d6Sjmcneill 	clkid_cpu_2x,
8004c770d6Sjmcneill 	clkid_cpu_1x,
8104c770d6Sjmcneill 	clkid_ddr2x,
8204c770d6Sjmcneill 	clkid_ddr3x,
8304c770d6Sjmcneill 	clkid_dci,
8404c770d6Sjmcneill 	clkid_lqspi,
8504c770d6Sjmcneill 	clkid_smc,
8604c770d6Sjmcneill 	clkid_pcap,
8704c770d6Sjmcneill 	clkid_gem0,
8804c770d6Sjmcneill 	clkid_gem1,
8904c770d6Sjmcneill 	clkid_fclk0,
9004c770d6Sjmcneill 	clkid_fclk1,
9104c770d6Sjmcneill 	clkid_fclk2,
9204c770d6Sjmcneill 	clkid_fclk3,
9304c770d6Sjmcneill 	clkid_can0,
9404c770d6Sjmcneill 	clkid_can1,
9504c770d6Sjmcneill 	clkid_sdio0,
9604c770d6Sjmcneill 	clkid_sdio1,
9704c770d6Sjmcneill 	clkid_uart0,
9804c770d6Sjmcneill 	clkid_uart1,
9904c770d6Sjmcneill 	clkid_spi0,
10004c770d6Sjmcneill 	clkid_spi1,
10104c770d6Sjmcneill 	clkid_dma,
10204c770d6Sjmcneill 	clkid_usb0_aper,
10304c770d6Sjmcneill 	clkid_usb1_aper,
10404c770d6Sjmcneill 	clkid_gem0_aper,
10504c770d6Sjmcneill 	clkid_gem1_aper,
10604c770d6Sjmcneill 	clkid_sdio0_aper,
10704c770d6Sjmcneill 	clkid_sdio1_aper,
10804c770d6Sjmcneill 	clkid_spi0_aper,
10904c770d6Sjmcneill 	clkid_spi1_aper,
11004c770d6Sjmcneill 	clkid_can0_aper,
11104c770d6Sjmcneill 	clkid_can1_aper,
11204c770d6Sjmcneill 	clkid_i2c0_aper,
11304c770d6Sjmcneill 	clkid_i2c1_aper,
11404c770d6Sjmcneill 	clkid_uart0_aper,
11504c770d6Sjmcneill 	clkid_uart1_aper,
11604c770d6Sjmcneill 	clkid_gpio_aper,
11704c770d6Sjmcneill 	clkid_lqspi_aper,
11804c770d6Sjmcneill 	clkid_smc_aper,
11904c770d6Sjmcneill 	clkid_swdt,
12004c770d6Sjmcneill 	clkid_dbg_trc,
12104c770d6Sjmcneill 	clkid_dbg_apb,
12204c770d6Sjmcneill 	num_clkid
12304c770d6Sjmcneill };
12404c770d6Sjmcneill CTASSERT(clkid_dbg_apb == 47);
12504c770d6Sjmcneill 
12604c770d6Sjmcneill static int zynq7000_clkc_match(device_t, cfdata_t, void *);
12704c770d6Sjmcneill static void zynq7000_clkc_attach(device_t, device_t, void *);
12804c770d6Sjmcneill 
129350c3bceSjmcneill static u_int zynq7000_clkc_clk_get_rate(void *, struct clk *);
130350c3bceSjmcneill 
13104c770d6Sjmcneill static const struct device_compatible_entry compat_data[] = {
13204c770d6Sjmcneill 	{ .compat = "xlnx,ps7-clkc" },
13304c770d6Sjmcneill 	DEVICE_COMPAT_EOL
13404c770d6Sjmcneill };
13504c770d6Sjmcneill 
13604c770d6Sjmcneill struct zynq7000_clkc_softc {
13704c770d6Sjmcneill 	device_t		sc_dev;
13804c770d6Sjmcneill 	struct clk_domain	sc_clkdom;
13904c770d6Sjmcneill 	struct clk		sc_clk[num_clkid];
14004c770d6Sjmcneill 
14104c770d6Sjmcneill 	u_int			sc_ps_clk_frequency;
142a60aa55cSjmcneill 	struct syscon		*sc_syscon;
14304c770d6Sjmcneill };
14404c770d6Sjmcneill 
14504c770d6Sjmcneill CFATTACH_DECL_NEW(zynq7000_clkc, sizeof(struct zynq7000_clkc_softc),
14604c770d6Sjmcneill 	zynq7000_clkc_match, zynq7000_clkc_attach, NULL, NULL);
14704c770d6Sjmcneill 
14804c770d6Sjmcneill static struct clk *
zynq7000_clkc_clk_get(void * priv,const char * name)14904c770d6Sjmcneill zynq7000_clkc_clk_get(void *priv, const char *name)
15004c770d6Sjmcneill {
15104c770d6Sjmcneill 	struct zynq7000_clkc_softc * const sc = priv;
15204c770d6Sjmcneill 	u_int clkid;
15304c770d6Sjmcneill 
15404c770d6Sjmcneill 	for (clkid = 0; clkid < num_clkid; clkid++) {
15504c770d6Sjmcneill 		if (strcmp(name, sc->sc_clk[clkid].name) == 0) {
15604c770d6Sjmcneill 			return &sc->sc_clk[clkid];
15704c770d6Sjmcneill 		}
15804c770d6Sjmcneill 	}
15904c770d6Sjmcneill 
16004c770d6Sjmcneill 	return NULL;
16104c770d6Sjmcneill }
16204c770d6Sjmcneill 
16304c770d6Sjmcneill static void
zynq7000_clkc_clk_put(void * priv,struct clk * clk)16404c770d6Sjmcneill zynq7000_clkc_clk_put(void *priv, struct clk *clk)
16504c770d6Sjmcneill {
16604c770d6Sjmcneill }
16704c770d6Sjmcneill 
16804c770d6Sjmcneill static u_int
zynq7000_clkc_get_rate_pll(struct zynq7000_clkc_softc * sc,bus_addr_t reg)16904c770d6Sjmcneill zynq7000_clkc_get_rate_pll(struct zynq7000_clkc_softc *sc,
17004c770d6Sjmcneill     bus_addr_t reg)
17104c770d6Sjmcneill {
17204c770d6Sjmcneill 	uint32_t val;
17304c770d6Sjmcneill 
174a60aa55cSjmcneill 	syscon_lock(sc->sc_syscon);
175a60aa55cSjmcneill 	val = syscon_read_4(sc->sc_syscon, reg);
176a60aa55cSjmcneill 	syscon_unlock(sc->sc_syscon);
17704c770d6Sjmcneill 
17804c770d6Sjmcneill 	return sc->sc_ps_clk_frequency * __SHIFTOUT(val, PLL_FDIV);
17904c770d6Sjmcneill }
18004c770d6Sjmcneill 
18104c770d6Sjmcneill static u_int
zynq7000_clkc_get_rate_iop(struct zynq7000_clkc_softc * sc,bus_addr_t reg)182350c3bceSjmcneill zynq7000_clkc_get_rate_iop(struct zynq7000_clkc_softc *sc,
183350c3bceSjmcneill     bus_addr_t reg)
184350c3bceSjmcneill {
185350c3bceSjmcneill 	uint32_t val;
186350c3bceSjmcneill 	u_int prate, sel;
187350c3bceSjmcneill 
188a60aa55cSjmcneill 	syscon_lock(sc->sc_syscon);
189a60aa55cSjmcneill 	val = syscon_read_4(sc->sc_syscon, reg);
190a60aa55cSjmcneill 	syscon_unlock(sc->sc_syscon);
191a60aa55cSjmcneill 
192350c3bceSjmcneill 	sel = __SHIFTOUT(val, CLK_CTRL_SRCSEL);
193350c3bceSjmcneill 	if (sel == 2) {
194350c3bceSjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
195350c3bceSjmcneill 		    &sc->sc_clk[clkid_armpll]);
196350c3bceSjmcneill 	} else if (sel == 3) {
197350c3bceSjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
198350c3bceSjmcneill 		    &sc->sc_clk[clkid_ddrpll]);
199350c3bceSjmcneill 	} else {
200350c3bceSjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
201350c3bceSjmcneill 		    &sc->sc_clk[clkid_iopll]);
202350c3bceSjmcneill 	}
203350c3bceSjmcneill 
204350c3bceSjmcneill 	return prate / __SHIFTOUT(val, CLK_CTRL_DIVISOR);
205350c3bceSjmcneill }
206350c3bceSjmcneill 
207350c3bceSjmcneill static u_int
zynq7000_clkc_clk_get_rate(void * priv,struct clk * clk)20804c770d6Sjmcneill zynq7000_clkc_clk_get_rate(void *priv, struct clk *clk)
20904c770d6Sjmcneill {
21004c770d6Sjmcneill 	struct zynq7000_clkc_softc * const sc = priv;
21104c770d6Sjmcneill 	uint32_t val;
212350c3bceSjmcneill 	u_int prate;
21304c770d6Sjmcneill 
21404c770d6Sjmcneill 	if (clk == &sc->sc_clk[clkid_armpll]) {
21504c770d6Sjmcneill 		return zynq7000_clkc_get_rate_pll(sc, ARM_PLL_CTRL);
21604c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_iopll]) {
21704c770d6Sjmcneill 		return zynq7000_clkc_get_rate_pll(sc, IO_PLL_CTRL);
21804c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_6or4x]) {
21904c770d6Sjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
22004c770d6Sjmcneill 		    &sc->sc_clk[clkid_cpu_1x]);
221a60aa55cSjmcneill 		syscon_lock(sc->sc_syscon);
222a60aa55cSjmcneill 		val = syscon_read_4(sc->sc_syscon, CLK_621_TRUE);
223a60aa55cSjmcneill 		syscon_unlock(sc->sc_syscon);
22404c770d6Sjmcneill 		return (val & CLK_621_TRUE_EN) != 0 ?
22504c770d6Sjmcneill 		    prate * 6 : prate * 4;
22604c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_3or2x]) {
22704c770d6Sjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
22804c770d6Sjmcneill 		    &sc->sc_clk[clkid_cpu_1x]);
229a60aa55cSjmcneill 		syscon_lock(sc->sc_syscon);
230a60aa55cSjmcneill 		val = syscon_read_4(sc->sc_syscon, CLK_621_TRUE);
231a60aa55cSjmcneill 		syscon_unlock(sc->sc_syscon);
23204c770d6Sjmcneill 		return (val & CLK_621_TRUE_EN) != 0 ?
23304c770d6Sjmcneill 		    prate * 3 : prate * 2;
23404c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_2x]) {
23504c770d6Sjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
23604c770d6Sjmcneill 		    &sc->sc_clk[clkid_cpu_1x]);
23704c770d6Sjmcneill 		return prate * 2;
23804c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_1x]) {
23904c770d6Sjmcneill 		prate = zynq7000_clkc_clk_get_rate(sc,
24004c770d6Sjmcneill 		    &sc->sc_clk[clkid_armpll]);
241a60aa55cSjmcneill 		syscon_lock(sc->sc_syscon);
242a60aa55cSjmcneill 		val = syscon_read_4(sc->sc_syscon, ARM_CLK_CTRL);
243a60aa55cSjmcneill 		syscon_unlock(sc->sc_syscon);
244a60aa55cSjmcneill 		return prate / __SHIFTOUT(val, ARM_CLK_CTRL_DIVISOR) / 6;
245350c3bceSjmcneill 	} else if (clk == &sc->sc_clk[clkid_sdio0] ||
246350c3bceSjmcneill 		   clk == &sc->sc_clk[clkid_sdio1]) {
247350c3bceSjmcneill 		return zynq7000_clkc_get_rate_iop(sc, SDIO_CLK_CTRL);
24804c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_uart0] ||
24904c770d6Sjmcneill 		   clk == &sc->sc_clk[clkid_uart1]) {
250350c3bceSjmcneill 		return zynq7000_clkc_get_rate_iop(sc, UART_CLK_CTRL);
251*a0fe19daSjmcneill 	} else if (clk == &sc->sc_clk[clkid_pcap]) {
252*a0fe19daSjmcneill 		return zynq7000_clkc_get_rate_iop(sc, PCAP_CLK_CTRL);
25304c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_uart0_aper] ||
25488c82104Sjmcneill 		   clk == &sc->sc_clk[clkid_uart1_aper] ||
25588c82104Sjmcneill 		   clk == &sc->sc_clk[clkid_i2c0_aper] ||
25688c82104Sjmcneill 		   clk == &sc->sc_clk[clkid_i2c1_aper]) {
25704c770d6Sjmcneill 		return zynq7000_clkc_clk_get_rate(sc,
25804c770d6Sjmcneill 		    &sc->sc_clk[clkid_cpu_1x]);
25904c770d6Sjmcneill 	} else {
26004c770d6Sjmcneill 		/* Not implemented. */
26104c770d6Sjmcneill 		return 0;
26204c770d6Sjmcneill 	}
26304c770d6Sjmcneill }
26404c770d6Sjmcneill 
26504c770d6Sjmcneill static int
zynq7000_clkc_clk_enable(void * priv,struct clk * clk)26604c770d6Sjmcneill zynq7000_clkc_clk_enable(void *priv, struct clk *clk)
26704c770d6Sjmcneill {
26804c770d6Sjmcneill 	struct zynq7000_clkc_softc * const sc = priv;
26904c770d6Sjmcneill 	uint32_t val, mask;
27004c770d6Sjmcneill 	bus_addr_t reg;
27104c770d6Sjmcneill 
27204c770d6Sjmcneill 	if (clk == &sc->sc_clk[clkid_cpu_1x]) {
27304c770d6Sjmcneill 		reg = ARM_CLK_CTRL;
27404c770d6Sjmcneill 		mask = ARM_CLK_CTRL_CPU_1XCLKACT;
27504c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_2x]) {
27604c770d6Sjmcneill 		reg = ARM_CLK_CTRL;
27704c770d6Sjmcneill 		mask = ARM_CLK_CTRL_CPU_2XCLKACT;
27804c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_3or2x]) {
27904c770d6Sjmcneill 		reg = ARM_CLK_CTRL;
28004c770d6Sjmcneill 		mask = ARM_CLK_CTRL_CPU_3OR2XCLKACT;
28104c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_cpu_6or4x]) {
28204c770d6Sjmcneill 		reg = ARM_CLK_CTRL;
28304c770d6Sjmcneill 		mask = ARM_CLK_CTRL_CPU_6OR4XCLKACT;
284350c3bceSjmcneill 	} else if (clk == &sc->sc_clk[clkid_sdio0]) {
285350c3bceSjmcneill 		reg = SDIO_CLK_CTRL;
286350c3bceSjmcneill 		mask = CLK_CTRL_CLKACT0;
287350c3bceSjmcneill 	} else if (clk == &sc->sc_clk[clkid_sdio1]) {
288350c3bceSjmcneill 		reg = SDIO_CLK_CTRL;
289350c3bceSjmcneill 		mask = CLK_CTRL_CLKACT1;
29004c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_uart0]) {
29104c770d6Sjmcneill 		reg = UART_CLK_CTRL;
292350c3bceSjmcneill 		mask = CLK_CTRL_CLKACT0;
29304c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_uart1]) {
29404c770d6Sjmcneill 		reg = UART_CLK_CTRL;
295350c3bceSjmcneill 		mask = CLK_CTRL_CLKACT1;
296*a0fe19daSjmcneill 	} else if (clk == &sc->sc_clk[clkid_pcap]) {
297*a0fe19daSjmcneill 		reg = PCAP_CLK_CTRL;
298*a0fe19daSjmcneill 		mask = CLK_CTRL_CLKACT0;
299350c3bceSjmcneill 	} else if (clk == &sc->sc_clk[clkid_sdio0_aper]) {
300350c3bceSjmcneill 		reg = APER_CLK_CTRL;
301350c3bceSjmcneill 		mask = SDI0_CPU_1XCLKACT;
302350c3bceSjmcneill 	} else if (clk == &sc->sc_clk[clkid_sdio1_aper]) {
303350c3bceSjmcneill 		reg = APER_CLK_CTRL;
304350c3bceSjmcneill 		mask = SDI1_CPU_1XCLKACT;
30504c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_uart0_aper]) {
30604c770d6Sjmcneill 		reg = APER_CLK_CTRL;
30704c770d6Sjmcneill 		mask = UART0_CPU_1XCLKACT;
30804c770d6Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_uart1_aper]) {
30904c770d6Sjmcneill 		reg = APER_CLK_CTRL;
31004c770d6Sjmcneill 		mask = UART1_CPU_1XCLKACT;
31188c82104Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_i2c0_aper]) {
31288c82104Sjmcneill 		reg = APER_CLK_CTRL;
31388c82104Sjmcneill 		mask = I2C0_CPU_1XCLKACT;
31488c82104Sjmcneill 	} else if (clk == &sc->sc_clk[clkid_i2c1_aper]) {
31588c82104Sjmcneill 		reg = APER_CLK_CTRL;
31688c82104Sjmcneill 		mask = I2C1_CPU_1XCLKACT;
31704c770d6Sjmcneill 	} else {
31804c770d6Sjmcneill 		return ENXIO;
31904c770d6Sjmcneill 	}
32004c770d6Sjmcneill 
321a60aa55cSjmcneill 	syscon_lock(sc->sc_syscon);
322a60aa55cSjmcneill 	val = syscon_read_4(sc->sc_syscon, reg);
323a60aa55cSjmcneill 	syscon_write_4(sc->sc_syscon, reg, val | mask);
324a60aa55cSjmcneill 	syscon_unlock(sc->sc_syscon);
32504c770d6Sjmcneill 
32604c770d6Sjmcneill 	return 0;
32704c770d6Sjmcneill }
32804c770d6Sjmcneill 
32904c770d6Sjmcneill static int
zynq7000_clkc_clk_disable(void * priv,struct clk * clk)33004c770d6Sjmcneill zynq7000_clkc_clk_disable(void *priv, struct clk *clk)
33104c770d6Sjmcneill {
33204c770d6Sjmcneill 	return ENXIO;
33304c770d6Sjmcneill }
33404c770d6Sjmcneill 
33504c770d6Sjmcneill static const struct clk_funcs zynq7000_clkc_clk_funcs = {
33604c770d6Sjmcneill 	.get = zynq7000_clkc_clk_get,
33704c770d6Sjmcneill 	.put = zynq7000_clkc_clk_put,
33804c770d6Sjmcneill 	.get_rate = zynq7000_clkc_clk_get_rate,
33904c770d6Sjmcneill 	.enable = zynq7000_clkc_clk_enable,
34004c770d6Sjmcneill 	.disable = zynq7000_clkc_clk_disable,
34104c770d6Sjmcneill };
34204c770d6Sjmcneill 
34304c770d6Sjmcneill static struct clk *
zynq7000_clkc_fdt_decode(device_t dev,int cc_phandle,const void * data,size_t len)34404c770d6Sjmcneill zynq7000_clkc_fdt_decode(device_t dev, int cc_phandle, const void *data, size_t len)
34504c770d6Sjmcneill {
34604c770d6Sjmcneill 	struct zynq7000_clkc_softc * const sc = device_private(dev);
34704c770d6Sjmcneill 	u_int clkid;
34804c770d6Sjmcneill 
34904c770d6Sjmcneill 	if (len != 4) {
35004c770d6Sjmcneill 		return NULL;
35104c770d6Sjmcneill 	}
35204c770d6Sjmcneill 
35304c770d6Sjmcneill 	clkid = be32dec(data);
35404c770d6Sjmcneill 	if (clkid >= num_clkid) {
35504c770d6Sjmcneill 		return NULL;
35604c770d6Sjmcneill 	}
35704c770d6Sjmcneill 
35804c770d6Sjmcneill 	return &sc->sc_clk[clkid];
35904c770d6Sjmcneill }
36004c770d6Sjmcneill 
36104c770d6Sjmcneill static const struct fdtbus_clock_controller_func zynq7000_clkc_fdt_funcs = {
36204c770d6Sjmcneill 	.decode = zynq7000_clkc_fdt_decode
36304c770d6Sjmcneill };
36404c770d6Sjmcneill 
36504c770d6Sjmcneill static int
zynq7000_clkc_match(device_t parent,cfdata_t cf,void * aux)36604c770d6Sjmcneill zynq7000_clkc_match(device_t parent, cfdata_t cf, void *aux)
36704c770d6Sjmcneill {
36804c770d6Sjmcneill 	struct fdt_attach_args * const faa = aux;
36904c770d6Sjmcneill 
37004c770d6Sjmcneill 	return of_compatible_match(faa->faa_phandle, compat_data);
37104c770d6Sjmcneill }
37204c770d6Sjmcneill 
37304c770d6Sjmcneill static void
zynq7000_clkc_attach(device_t parent,device_t self,void * aux)37404c770d6Sjmcneill zynq7000_clkc_attach(device_t parent, device_t self, void *aux)
37504c770d6Sjmcneill {
37604c770d6Sjmcneill 	struct zynq7000_clkc_softc * const sc = device_private(self);
37704c770d6Sjmcneill 	struct fdt_attach_args * const faa = aux;
37804c770d6Sjmcneill 	const int phandle = faa->faa_phandle;
37904c770d6Sjmcneill 	const char *clkname;
38004c770d6Sjmcneill 	bus_addr_t addr;
38104c770d6Sjmcneill 	bus_size_t size;
38204c770d6Sjmcneill 	u_int clkid;
38304c770d6Sjmcneill 	int error;
38404c770d6Sjmcneill 
38504c770d6Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
38604c770d6Sjmcneill 		aprint_error(": couldn't get registers\n");
38704c770d6Sjmcneill 		return;
38804c770d6Sjmcneill 	}
38904c770d6Sjmcneill 
39004c770d6Sjmcneill 	sc->sc_dev = self;
391a60aa55cSjmcneill 	sc->sc_syscon = fdtbus_syscon_lookup(OF_parent(phandle));
392a60aa55cSjmcneill 	if (sc->sc_syscon == NULL) {
393a60aa55cSjmcneill 		aprint_error(": couldn't get syscon registers\n");
39404c770d6Sjmcneill 		return;
39504c770d6Sjmcneill 	}
39604c770d6Sjmcneill 
39704c770d6Sjmcneill 	error = of_getprop_uint32(phandle, "ps-clk-frequency",
39804c770d6Sjmcneill 	    &sc->sc_ps_clk_frequency);
39904c770d6Sjmcneill 	if (error != 0) {
40004c770d6Sjmcneill 		aprint_error(": couldn't get ps-clk-frequency\n");
40104c770d6Sjmcneill 		return;
40204c770d6Sjmcneill 	}
40304c770d6Sjmcneill 
40404c770d6Sjmcneill 	sc->sc_clkdom.name = device_xname(self);
40504c770d6Sjmcneill 	sc->sc_clkdom.funcs = &zynq7000_clkc_clk_funcs;
40604c770d6Sjmcneill 	sc->sc_clkdom.priv = sc;
40704c770d6Sjmcneill 	for (clkid = 0; clkid < num_clkid; clkid++) {
40804c770d6Sjmcneill 		clkname = fdtbus_get_string_index(phandle,
40904c770d6Sjmcneill 		    "clock-output-names", clkid);
41004c770d6Sjmcneill 		sc->sc_clk[clkid].domain = &sc->sc_clkdom;
41104c770d6Sjmcneill 		if (clkname != NULL) {
41204c770d6Sjmcneill 			sc->sc_clk[clkid].name = kmem_asprintf("%s", clkname);
41304c770d6Sjmcneill 		}
41404c770d6Sjmcneill 		clk_attach(&sc->sc_clk[clkid]);
41504c770d6Sjmcneill 	}
41604c770d6Sjmcneill 
41704c770d6Sjmcneill 	aprint_naive("\n");
41804c770d6Sjmcneill 	aprint_normal(": Zynq-7000 PS clock subsystem (PS_CLK %u Hz)\n",
41904c770d6Sjmcneill 	    sc->sc_ps_clk_frequency);
42004c770d6Sjmcneill 
421a60aa55cSjmcneill 	for (clkid = 0; clkid < num_clkid; clkid++) {
422a60aa55cSjmcneill 		aprint_debug_dev(self, "clkid %u [%s]: %u Hz\n", clkid,
423a60aa55cSjmcneill 		    sc->sc_clk[clkid].name ? sc->sc_clk[clkid].name : "<none>",
424a60aa55cSjmcneill 		    clk_get_rate(&sc->sc_clk[clkid]));
425a60aa55cSjmcneill 	}
426a60aa55cSjmcneill 
42704c770d6Sjmcneill 	fdtbus_register_clock_controller(self, phandle, &zynq7000_clkc_fdt_funcs);
42804c770d6Sjmcneill }
429