xref: /openbsd/sys/arch/armv7/xilinx/zqclock.c (revision 73471bf0)
1 /*	$OpenBSD: zqclock.c,v 1.1 2021/04/30 13:25:24 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Visa Hankala
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Driver for Xilinx Zynq-7000 clock controller.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/mutex.h>
27 
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30 
31 #include <dev/ofw/fdt.h>
32 #include <dev/ofw/openfirm.h>
33 #include <dev/ofw/ofw_clock.h>
34 #include <dev/ofw/ofw_misc.h>
35 
36 #include <armv7/xilinx/slcreg.h>
37 
38 #define CLK_ARM_PLL			0
39 #define CLK_DDR_PLL			1
40 #define CLK_IO_PLL			2
41 #define CLK_CPU_6OR4X			3
42 #define CLK_CPU_3OR2X			4
43 #define CLK_CPU_2X			5
44 #define CLK_CPU_1X			6
45 #define CLK_DDR_2X			7
46 #define CLK_DDR_3X			8
47 #define CLK_DCI				9
48 #define CLK_LQSPI			10
49 #define CLK_SMC				11
50 #define CLK_PCAP			12
51 #define CLK_GEM0			13
52 #define CLK_GEM1			14
53 #define CLK_FCLK0			15
54 #define CLK_FCLK1			16
55 #define CLK_FCLK2			17
56 #define CLK_FCLK3			18
57 #define CLK_CAN0			19
58 #define CLK_CAN1			20
59 #define CLK_SDIO0			21
60 #define CLK_SDIO1			22
61 #define CLK_UART0			23
62 #define CLK_UART1			24
63 #define CLK_SPI0			25
64 #define CLK_SPI1			26
65 #define CLK_DMA				27
66 
67 struct zqclock_softc {
68 	struct device		sc_dev;
69 	struct regmap		*sc_rm;
70 
71 	struct clock_device	sc_cd;
72 	uint32_t		sc_psclk_freq;		/* in Hz */
73 };
74 
75 int	zqclock_match(struct device *, void *, void *);
76 void	zqclock_attach(struct device *, struct device *, void *);
77 
78 void	zqclock_enable(void *, uint32_t *, int);
79 uint32_t zqclock_get_frequency(void *, uint32_t *);
80 int	zqclock_set_frequency(void *, uint32_t *, uint32_t);
81 
82 const struct cfattach zqclock_ca = {
83 	sizeof(struct zqclock_softc), zqclock_match, zqclock_attach
84 };
85 
86 struct cfdriver zqclock_cd = {
87 	NULL, "zqclock", DV_DULL
88 };
89 
90 struct zqclock_clock {
91 	uint16_t		clk_ctl_reg;
92 	uint8_t			clk_has_div1;
93 	uint8_t			clk_index;
94 };
95 
96 const struct zqclock_clock zqclock_clocks[] = {
97 	[CLK_GEM0]		= { SLCR_GEM0_CLK_CTRL, 1, 0 },
98 	[CLK_GEM1]		= { SLCR_GEM1_CLK_CTRL, 1, 0 },
99 	[CLK_SDIO0]		= { SLCR_SDIO_CLK_CTRL, 0, 0 },
100 	[CLK_SDIO1]		= { SLCR_SDIO_CLK_CTRL, 0, 1 },
101 	[CLK_UART0]		= { SLCR_UART_CLK_CTRL, 0, 0 },
102 	[CLK_UART1]		= { SLCR_UART_CLK_CTRL, 0, 1 },
103 };
104 
105 int
106 zqclock_match(struct device *parent, void *match, void *aux)
107 {
108 	struct fdt_attach_args *faa = aux;
109 
110 	return OF_is_compatible(faa->fa_node, "xlnx,ps7-clkc");
111 }
112 
113 void
114 zqclock_attach(struct device *parent, struct device *self, void *aux)
115 {
116 	struct fdt_attach_args *faa = aux;
117 	struct zqclock_softc *sc = (struct zqclock_softc *)self;
118 
119 	sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
120 	if (sc->sc_rm == NULL) {
121 		printf(": can't get regmap\n");
122 		return;
123 	}
124 
125 	sc->sc_psclk_freq = OF_getpropint(faa->fa_node, "ps-clk-frequency",
126 	    33333333);
127 
128 	printf(": %u MHz PS clock\n", (sc->sc_psclk_freq + 500000) / 1000000);
129 
130 	sc->sc_cd.cd_node = faa->fa_node;
131 	sc->sc_cd.cd_cookie = sc;
132 	sc->sc_cd.cd_enable = zqclock_enable;
133 	sc->sc_cd.cd_get_frequency = zqclock_get_frequency;
134 	sc->sc_cd.cd_set_frequency = zqclock_set_frequency;
135 	clock_register(&sc->sc_cd);
136 }
137 
138 const struct zqclock_clock *
139 zqclock_get_clock(uint32_t idx)
140 {
141 	const struct zqclock_clock *clock;
142 
143 	if (idx >= nitems(zqclock_clocks))
144 		return NULL;
145 
146 	clock = &zqclock_clocks[idx];
147 	if (clock->clk_ctl_reg == 0)
148 		return NULL;
149 
150 	return clock;
151 }
152 
153 uint32_t
154 zqclock_get_pll_frequency(struct zqclock_softc *sc, uint32_t clk_ctrl)
155 {
156 	uint32_t reg, val;
157 
158 	switch (clk_ctrl & SLCR_CLK_CTRL_SRCSEL_MASK) {
159 	case SLCR_CLK_CTRL_SRCSEL_ARM:
160 		reg = SLCR_ARM_PLL_CTRL;
161 		break;
162 	case SLCR_CLK_CTRL_SRCSEL_DDR:
163 		reg = SLCR_DDR_PLL_CTRL;
164 		break;
165 	default:
166 		reg = SLCR_IO_PLL_CTRL;
167 		break;
168 	}
169 
170 	val = zynq_slcr_read(sc->sc_rm, reg);
171 	return sc->sc_psclk_freq * ((val >> SLCR_PLL_CTRL_FDIV_SHIFT) &
172 	    SLCR_PLL_CTRL_FDIV_MASK);
173 }
174 
175 uint32_t
176 zqclock_get_frequency(void *cookie, uint32_t *cells)
177 {
178 	const struct zqclock_clock *clock;
179 	struct zqclock_softc *sc = cookie;
180 	uint32_t idx = cells[0];
181 	uint32_t ctl, div, freq;
182 
183 	clock = zqclock_get_clock(idx);
184 	if (clock == NULL)
185 		return 0;
186 
187 	mtx_enter(&zynq_slcr_lock);
188 
189 	ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg);
190 
191 	div = SLCR_CLK_CTRL_DIVISOR(ctl);
192 	if (clock->clk_has_div1)
193 		div *= SLCR_CLK_CTRL_DIVISOR1(ctl);
194 
195 	freq = zqclock_get_pll_frequency(sc, ctl);
196 	freq = (freq + div / 2) / div;
197 
198 	mtx_leave(&zynq_slcr_lock);
199 
200 	return freq;
201 }
202 
203 int
204 zqclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
205 {
206 	static const uint32_t srcsels[] = {
207 		SLCR_CLK_CTRL_SRCSEL_IO,
208 		SLCR_CLK_CTRL_SRCSEL_ARM,
209 		SLCR_CLK_CTRL_SRCSEL_DDR,
210 	};
211 	const struct zqclock_clock *clock;
212 	struct zqclock_softc *sc = cookie;
213 	uint32_t idx = cells[0];
214 	uint32_t best_delta = ~0U;
215 	uint32_t best_div1 = 0;
216 	uint32_t best_si = 0;
217 	uint32_t best_pllf = 0;
218 	uint32_t ctl, div, div1, maxdiv1, si;
219 	int error = 0;
220 
221 	clock = zqclock_get_clock(idx);
222 	if (clock == NULL)
223 		return EINVAL;
224 
225 	if (freq == 0)
226 		return EINVAL;
227 
228 	mtx_enter(&zynq_slcr_lock);
229 
230 	maxdiv1 = 1;
231 	if (clock->clk_has_div1)
232 		maxdiv1 = SLCR_DIV_MASK;
233 
234 	/* Find PLL and divisors that give best frequency. */
235 	for (si = 0; si < nitems(srcsels); si++) {
236 		uint32_t delta, f, pllf;
237 
238 		pllf = zqclock_get_pll_frequency(sc, srcsels[si]);
239 		if (freq > pllf)
240 			continue;
241 
242 		for (div1 = 1; div1 <= maxdiv1; div1++) {
243 			div = (pllf + (freq * div1 / 2)) / (freq * div1);
244 			if (div > SLCR_DIV_MASK)
245 				continue;
246 			if (div == 0)
247 				break;
248 
249 			f = (pllf + (div * div1 / 2)) / (div * div1);
250 			delta = abs(f - freq);
251 			if (best_div1 == 0 || delta < best_delta) {
252 				best_delta = delta;
253 				best_div1 = div1;
254 				best_pllf = pllf;
255 				best_si = si;
256 
257 				if (delta == 0)
258 					goto found;
259 			}
260 		}
261 	}
262 
263 	if (best_div1 == 0) {
264 		error = EINVAL;
265 		goto out;
266 	}
267 
268 found:
269 	div1 = best_div1;
270 	div = (best_pllf + (freq * div1 / 2)) / (freq * div1);
271 
272 	KASSERT(div > 0 && div <= SLCR_DIV_MASK);
273 	KASSERT(div1 > 0 && div1 <= SLCR_DIV_MASK);
274 
275 	ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg);
276 
277 	ctl &= ~SLCR_CLK_CTRL_SRCSEL_MASK;
278 	ctl |= srcsels[best_si];
279 	ctl &= ~(SLCR_DIV_MASK << SLCR_CLK_CTRL_DIVISOR_SHIFT);
280 	ctl |= (div & SLCR_DIV_MASK) << SLCR_CLK_CTRL_DIVISOR_SHIFT;
281 	if (clock->clk_has_div1) {
282 		ctl &= ~(SLCR_DIV_MASK << SLCR_CLK_CTRL_DIVISOR1_SHIFT);
283 		ctl |= (div1 & SLCR_DIV_MASK) << SLCR_CLK_CTRL_DIVISOR1_SHIFT;
284 	}
285 
286 	zynq_slcr_write(sc->sc_rm, clock->clk_ctl_reg, ctl);
287 
288 out:
289 	mtx_leave(&zynq_slcr_lock);
290 
291 	return error;
292 }
293 
294 void
295 zqclock_enable(void *cookie, uint32_t *cells, int on)
296 {
297 	const struct zqclock_clock *clock;
298 	struct zqclock_softc *sc = cookie;
299 	uint32_t idx = cells[0];
300 	uint32_t ctl;
301 
302 	clock = zqclock_get_clock(idx);
303 	if (clock == NULL)
304 		return;
305 
306 	mtx_enter(&zynq_slcr_lock);
307 
308 	ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg);
309 	if (on)
310 		ctl |= SLCR_CLK_CTRL_CLKACT(clock->clk_index);
311 	else
312 		ctl &= ~SLCR_CLK_CTRL_CLKACT(clock->clk_index);
313 	zynq_slcr_write(sc->sc_rm, clock->clk_ctl_reg, ctl);
314 
315 	mtx_leave(&zynq_slcr_lock);
316 }
317