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
zqclock_match(struct device * parent,void * match,void * aux)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
zqclock_attach(struct device * parent,struct device * self,void * aux)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 *
zqclock_get_clock(uint32_t idx)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
zqclock_get_pll_frequency(struct zqclock_softc * sc,uint32_t clk_ctrl)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
zqclock_get_frequency(void * cookie,uint32_t * cells)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
zqclock_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)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
zqclock_enable(void * cookie,uint32_t * cells,int on)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