xref: /openbsd-src/sys/dev/fdt/imxccm.c (revision 897fc685943471cf985a0fe38ba076ea6fe74fa5)
1 /* $OpenBSD: imxccm.c,v 1.1 2018/04/02 16:47:39 patrick Exp $ */
2 /*
3  * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/queue.h>
21 #include <sys/malloc.h>
22 #include <sys/sysctl.h>
23 #include <sys/device.h>
24 #include <sys/evcount.h>
25 #include <sys/socket.h>
26 #include <sys/timeout.h>
27 
28 #include <machine/intr.h>
29 #include <machine/bus.h>
30 #include <machine/fdt.h>
31 
32 #include <dev/ofw/openfirm.h>
33 #include <dev/ofw/ofw_clock.h>
34 #include <dev/ofw/fdt.h>
35 
36 /* registers */
37 #define CCM_CCR		0x00
38 #define CCM_CCDR	0x04
39 #define CCM_CSR		0x08
40 #define CCM_CCSR	0x0c
41 #define CCM_CACRR	0x10
42 #define CCM_CBCDR	0x14
43 #define CCM_CBCMR	0x18
44 #define CCM_CSCMR1	0x1c
45 #define CCM_CSCMR2	0x20
46 #define CCM_CSCDR1	0x24
47 #define CCM_CS1CDR	0x28
48 #define CCM_CS2CDR	0x2c
49 #define CCM_CDCDR	0x30
50 #define CCM_CHSCCDR	0x34
51 #define CCM_CSCDR2	0x38
52 #define CCM_CSCDR3	0x3c
53 #define CCM_CSCDR4	0x40
54 #define CCM_CDHIPR	0x48
55 #define CCM_CDCR	0x4c
56 #define CCM_CTOR	0x50
57 #define CCM_CLPCR	0x54
58 #define CCM_CISR	0x58
59 #define CCM_CIMR	0x5c
60 #define CCM_CCOSR	0x60
61 #define CCM_CGPR	0x64
62 #define CCM_CCGR0	0x68
63 #define CCM_CCGR1	0x6c
64 #define CCM_CCGR2	0x70
65 #define CCM_CCGR3	0x74
66 #define CCM_CCGR4	0x78
67 #define CCM_CCGR5	0x7c
68 #define CCM_CCGR6	0x80
69 #define CCM_CCGR7	0x84
70 #define CCM_CMEOR	0x88
71 
72 /* ANALOG */
73 #define CCM_ANALOG_PLL_ARM			0x4000
74 #define CCM_ANALOG_PLL_ARM_SET			0x4004
75 #define CCM_ANALOG_PLL_ARM_CLR			0x4008
76 #define CCM_ANALOG_PLL_USB1			0x4010
77 #define CCM_ANALOG_PLL_USB1_SET			0x4014
78 #define CCM_ANALOG_PLL_USB1_CLR			0x4018
79 #define CCM_ANALOG_PLL_USB2			0x4020
80 #define CCM_ANALOG_PLL_USB2_SET			0x4024
81 #define CCM_ANALOG_PLL_USB2_CLR			0x4028
82 #define CCM_ANALOG_PLL_SYS			0x4030
83 #define CCM_ANALOG_USB1_CHRG_DETECT		0x41b0
84 #define CCM_ANALOG_USB1_CHRG_DETECT_SET		0x41b4
85 #define CCM_ANALOG_USB1_CHRG_DETECT_CLR		0x41b8
86 #define CCM_ANALOG_USB2_CHRG_DETECT		0x4210
87 #define CCM_ANALOG_USB2_CHRG_DETECT_SET		0x4214
88 #define CCM_ANALOG_USB2_CHRG_DETECT_CLR		0x4218
89 #define CCM_ANALOG_DIGPROG			0x4260
90 #define CCM_ANALOG_PLL_ENET			0x40e0
91 #define CCM_ANALOG_PLL_ENET_SET			0x40e4
92 #define CCM_ANALOG_PLL_ENET_CLR			0x40e8
93 #define CCM_ANALOG_PFD_480			0x40f0
94 #define CCM_ANALOG_PFD_480_SET			0x40f4
95 #define CCM_ANALOG_PFD_480_CLR			0x40f8
96 #define CCM_ANALOG_PFD_528			0x4100
97 #define CCM_ANALOG_PFD_528_SET			0x4104
98 #define CCM_ANALOG_PFD_528_CLR			0x4108
99 #define CCM_PMU_MISC1				0x4160
100 
101 /* bits and bytes */
102 #define CCM_CCSR_PLL3_SW_CLK_SEL		(1 << 0)
103 #define CCM_CCSR_PLL2_SW_CLK_SEL		(1 << 1)
104 #define CCM_CCSR_PLL1_SW_CLK_SEL		(1 << 2)
105 #define CCM_CCSR_STEP_SEL			(1 << 8)
106 #define CCM_CBCDR_IPG_PODF_SHIFT		8
107 #define CCM_CBCDR_IPG_PODF_MASK			0x3
108 #define CCM_CBCDR_AHB_PODF_SHIFT		10
109 #define CCM_CBCDR_AHB_PODF_MASK			0x7
110 #define CCM_CBCDR_PERIPH_CLK_SEL_SHIFT		25
111 #define CCM_CBCDR_PERIPH_CLK_SEL_MASK		0x1
112 #define CCM_CBCMR_PERIPH_CLK2_SEL_SHIFT		12
113 #define CCM_CBCMR_PERIPH_CLK2_SEL_MASK		0x3
114 #define CCM_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT	18
115 #define CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK	0x3
116 #define CCM_CSCDR1_USDHCx_CLK_SEL_SHIFT(x)	((x) + 15)
117 #define CCM_CSCDR1_USDHCx_CLK_SEL_MASK		0x1
118 #define CCM_CSCDR1_USDHCx_PODF_MASK		0x7
119 #define CCM_CSCDR1_UART_PODF_MASK		0x7
120 #define CCM_CCGR1_ENET				(3 << 10)
121 #define CCM_CCGR4_125M_PCIE			(3 << 0)
122 #define CCM_CCGR5_100M_SATA			(3 << 4)
123 #define CCM_CSCMR1_PERCLK_CLK_PODF_MASK		0x1f
124 #define CCM_CSCMR1_PERCLK_CLK_SEL_MASK		(1 << 6)
125 #define CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK	0x7f
126 #define CCM_ANALOG_PLL_ARM_BYPASS		(1 << 16)
127 #define CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK	0x1
128 #define CCM_ANALOG_PLL_USB1_EN_USB_CLKS		(1 << 6)
129 #define CCM_ANALOG_PLL_USB1_POWER		(1 << 12)
130 #define CCM_ANALOG_PLL_USB1_ENABLE		(1 << 13)
131 #define CCM_ANALOG_PLL_USB1_BYPASS		(1 << 16)
132 #define CCM_ANALOG_PLL_USB1_LOCK		(1 << 31)
133 #define CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK	0x1
134 #define CCM_ANALOG_PLL_USB2_EN_USB_CLKS		(1 << 6)
135 #define CCM_ANALOG_PLL_USB2_POWER		(1 << 12)
136 #define CCM_ANALOG_PLL_USB2_ENABLE		(1 << 13)
137 #define CCM_ANALOG_PLL_USB2_BYPASS		(1 << 16)
138 #define CCM_ANALOG_PLL_USB2_LOCK		(1U << 31)
139 #define CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK	0x1
140 #define CCM_ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B	(1 << 19)
141 #define CCM_ANALOG_USB1_CHRG_DETECT_EN_B	(1 << 20)
142 #define CCM_ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B	(1 << 19)
143 #define CCM_ANALOG_USB2_CHRG_DETECT_EN_B	(1 << 20)
144 #define CCM_ANALOG_DIGPROG_MINOR_MASK		0xff
145 #define CCM_ANALOG_PLL_ENET_DIV_125M		(1 << 11)
146 #define CCM_ANALOG_PLL_ENET_POWERDOWN		(1 << 12)
147 #define CCM_ANALOG_PLL_ENET_ENABLE		(1 << 13)
148 #define CCM_ANALOG_PLL_ENET_BYPASS		(1 << 16)
149 #define CCM_ANALOG_PLL_ENET_125M_PCIE		(1 << 19)
150 #define CCM_ANALOG_PLL_ENET_100M_SATA		(1 << 20)
151 #define CCM_ANALOG_PLL_ENET_LOCK		(1U << 31)
152 #define CCM_ANALOG_PFD_480_PFDx_FRAC(x, y)	(((x) >> ((y) << 3)) & 0x3f)
153 #define CCM_ANALOG_PFD_528_PFDx_FRAC(x, y)	(((x) >> ((y) << 3)) & 0x3f)
154 #define CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_SATA	(0xB << 0)
155 #define CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_MASK	(0x1f << 0)
156 #define CCM_PMU_MISC1_LVDSCLK1_OBEN		(1 << 10)
157 #define CCM_PMU_MISC1_LVDSCLK1_IBEN		(1 << 12)
158 
159 #define HCLK_FREQ				24000000
160 #define PLL3_80M				80000000
161 
162 #define HREAD4(sc, reg)							\
163 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
164 #define HWRITE4(sc, reg, val)						\
165 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
166 #define HSET4(sc, reg, bits)						\
167 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
168 #define HCLR4(sc, reg, bits)						\
169 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
170 
171 struct imxccm_gate {
172 	uint8_t reg;
173 	uint8_t pos;
174 	uint8_t parent;
175 };
176 
177 #include "imxccm_clocks.h"
178 
179 struct imxccm_softc {
180 	struct device		sc_dev;
181 	bus_space_tag_t		sc_iot;
182 	bus_space_handle_t	sc_ioh;
183 	int			sc_node;
184 
185 	struct imxccm_gate	*sc_gates;
186 	int			sc_ngates;
187 	struct clock_device	sc_cd;
188 };
189 
190 enum clocks {
191 	/* OSC */
192 	OSC,		/* 24 MHz OSC */
193 
194 	/* PLLs */
195 	ARM_PLL1,	/* ARM core PLL */
196 	SYS_PLL2,	/* System PLL: 528 MHz */
197 	USB1_PLL3,	/* OTG USB PLL: 480 MHz */
198 	USB2_PLL,	/* Host USB PLL: 480 MHz */
199 	AUD_PLL4,	/* Audio PLL */
200 	VID_PLL5,	/* Video PLL */
201 	ENET_PLL6,	/* ENET PLL */
202 	MLB_PLL,	/* MLB PLL */
203 
204 	/* SYS_PLL2 PFDs */
205 	SYS_PLL2_PFD0,	/* 352 MHz */
206 	SYS_PLL2_PFD1,	/* 594 MHz */
207 	SYS_PLL2_PFD2,	/* 396 MHz */
208 
209 	/* USB1_PLL3 PFDs */
210 	USB1_PLL3_PFD0,	/* 720 MHz */
211 	USB1_PLL3_PFD1,	/* 540 MHz */
212 	USB1_PLL3_PFD2,	/* 508.2 MHz */
213 	USB1_PLL3_PFD3,	/* 454.7 MHz */
214 };
215 
216 int	imxccm_match(struct device *, void *, void *);
217 void	imxccm_attach(struct device *parent, struct device *self, void *args);
218 
219 struct cfattach	imxccm_ca = {
220 	sizeof (struct imxccm_softc), imxccm_match, imxccm_attach
221 };
222 
223 struct cfdriver imxccm_cd = {
224 	NULL, "imxccm", DV_DULL
225 };
226 
227 uint32_t imxccm_decode_pll(struct imxccm_softc *, enum clocks, uint32_t);
228 uint32_t imxccm_get_pll2_pfd(struct imxccm_softc *, unsigned int);
229 uint32_t imxccm_get_pll3_pfd(struct imxccm_softc *, unsigned int);
230 uint32_t imxccm_get_armclk(struct imxccm_softc *);
231 void imxccm_armclk_set_parent(struct imxccm_softc *, enum clocks);
232 uint32_t imxccm_get_usdhx(struct imxccm_softc *, int x);
233 uint32_t imxccm_get_periphclk(struct imxccm_softc *);
234 uint32_t imxccm_get_ahbclk(struct imxccm_softc *);
235 uint32_t imxccm_get_ipgclk(struct imxccm_softc *);
236 uint32_t imxccm_get_ipg_perclk(struct imxccm_softc *);
237 uint32_t imxccm_get_uartclk(struct imxccm_softc *);
238 void imxccm_enable(void *, uint32_t *, int);
239 uint32_t imxccm_get_frequency(void *, uint32_t *);
240 void imxccm_enable_pll_usb1(struct imxccm_softc *);
241 void imxccm_enable_pll_usb2(struct imxccm_softc *);
242 void imxccm_enable_pll_enet(struct imxccm_softc *);
243 void imxccm_enable_enet(struct imxccm_softc *);
244 void imxccm_enable_sata(struct imxccm_softc *);
245 
246 int
247 imxccm_match(struct device *parent, void *match, void *aux)
248 {
249 	struct fdt_attach_args *faa = aux;
250 
251 	return (OF_is_compatible(faa->fa_node, "fsl,imx6q-ccm") ||
252 	    OF_is_compatible(faa->fa_node, "fsl,imx6sl-ccm") ||
253 	    OF_is_compatible(faa->fa_node, "fsl,imx6sx-ccm") ||
254 	    OF_is_compatible(faa->fa_node, "fsl,imx6ul-ccm"));
255 }
256 
257 void
258 imxccm_attach(struct device *parent, struct device *self, void *aux)
259 {
260 	struct imxccm_softc *sc = (struct imxccm_softc *)self;
261 	struct fdt_attach_args *faa = aux;
262 
263 	KASSERT(faa->fa_nreg >= 1);
264 
265 	sc->sc_node = faa->fa_node;
266 	sc->sc_iot = faa->fa_iot;
267 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
268 	    faa->fa_reg[0].size + 0x1000, 0, &sc->sc_ioh))
269 		panic("%s: bus_space_map failed!", __func__);
270 
271 	if (OF_is_compatible(sc->sc_node, "fsl,imx6ul-ccm")) {
272 		sc->sc_gates = imx6ul_gates;
273 		sc->sc_ngates = nitems(imx6ul_gates);
274 	} else {
275 		sc->sc_gates = imx6_gates;
276 		sc->sc_ngates = nitems(imx6_gates);
277 	}
278 
279 	printf(": imx6 rev 1.%d CPU freq: %d MHz",
280 	    HREAD4(sc, CCM_ANALOG_DIGPROG) & CCM_ANALOG_DIGPROG_MINOR_MASK,
281 	    imxccm_get_armclk(sc) / 1000000);
282 
283 	printf("\n");
284 
285 	sc->sc_cd.cd_node = faa->fa_node;
286 	sc->sc_cd.cd_cookie = sc;
287 	sc->sc_cd.cd_enable = imxccm_enable;
288 	sc->sc_cd.cd_get_frequency = imxccm_get_frequency;
289 	clock_register(&sc->sc_cd);
290 }
291 
292 uint32_t
293 imxccm_decode_pll(struct imxccm_softc *sc, enum clocks pll, uint32_t freq)
294 {
295 	uint32_t div;
296 
297 	switch (pll) {
298 	case ARM_PLL1:
299 		if (HREAD4(sc, CCM_ANALOG_PLL_ARM)
300 		    & CCM_ANALOG_PLL_ARM_BYPASS)
301 			return freq;
302 		div = HREAD4(sc, CCM_ANALOG_PLL_ARM)
303 		    & CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK;
304 		return (freq * div) / 2;
305 	case SYS_PLL2:
306 		div = HREAD4(sc, CCM_ANALOG_PLL_SYS)
307 		    & CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK;
308 		return freq * (20 + (div << 1));
309 	case USB1_PLL3:
310 		div = HREAD4(sc, CCM_ANALOG_PLL_USB2)
311 		    & CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK;
312 		return freq * (20 + (div << 1));
313 	default:
314 		return 0;
315 	}
316 }
317 
318 uint32_t
319 imxccm_get_pll2_pfd(struct imxccm_softc *sc, unsigned int pfd)
320 {
321 	return imxccm_decode_pll(sc, SYS_PLL2, HCLK_FREQ) * 18ULL
322 	    / CCM_ANALOG_PFD_528_PFDx_FRAC(HREAD4(sc, CCM_ANALOG_PFD_528), pfd);
323 }
324 
325 uint32_t
326 imxccm_get_pll3_pfd(struct imxccm_softc *sc, unsigned int pfd)
327 {
328 	return imxccm_decode_pll(sc, USB1_PLL3, HCLK_FREQ) * 18ULL
329 	    / CCM_ANALOG_PFD_480_PFDx_FRAC(HREAD4(sc, CCM_ANALOG_PFD_480), pfd);
330 }
331 
332 uint32_t
333 imxccm_get_armclk(struct imxccm_softc *sc)
334 {
335 	uint32_t ccsr = HREAD4(sc, CCM_CCSR);
336 
337 	if (!(ccsr & CCM_CCSR_PLL1_SW_CLK_SEL))
338 		return imxccm_decode_pll(sc, ARM_PLL1, HCLK_FREQ);
339 	else if (ccsr & CCM_CCSR_STEP_SEL)
340 		return imxccm_get_pll2_pfd(sc, 2);
341 	else
342 		return HCLK_FREQ;
343 }
344 
345 void
346 imxccm_armclk_set_parent(struct imxccm_softc *sc, enum clocks clock)
347 {
348 	switch (clock)
349 	{
350 	case ARM_PLL1:
351 		/* jump onto pll1 */
352 		HCLR4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL);
353 		/* put step clk on OSC, power saving */
354 		HCLR4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL);
355 		break;
356 	case OSC:
357 		/* put step clk on OSC */
358 		HCLR4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL);
359 		/* jump onto step clk */
360 		HSET4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL);
361 		break;
362 	case SYS_PLL2_PFD2:
363 		/* put step clk on pll2-pfd2 400 MHz */
364 		HSET4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL);
365 		/* jump onto step clk */
366 		HSET4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL);
367 		break;
368 	default:
369 		panic("%s: parent not possible for arm clk", __func__);
370 	}
371 }
372 
373 unsigned int
374 imxccm_get_usdhx(struct imxccm_softc *sc, int x)
375 {
376 	uint32_t cscmr1 = HREAD4(sc, CCM_CSCMR1);
377 	uint32_t cscdr1 = HREAD4(sc, CCM_CSCDR1);
378 	uint32_t podf, clkroot;
379 
380 	// Odd bitsetting. Damn you.
381 	if (x == 1)
382 		podf = ((cscdr1 >> 11) & CCM_CSCDR1_USDHCx_PODF_MASK);
383 	else
384 		podf = ((cscdr1 >> (10 + 3*x)) & CCM_CSCDR1_USDHCx_PODF_MASK);
385 
386 	if (cscmr1 & (1 << CCM_CSCDR1_USDHCx_CLK_SEL_SHIFT(x)))
387 		clkroot = imxccm_get_pll2_pfd(sc, 0); // 352 MHz
388 	else
389 		clkroot = imxccm_get_pll2_pfd(sc, 2); // 396 MHz
390 
391 	return clkroot / (podf + 1);
392 }
393 
394 uint32_t
395 imxccm_get_uartclk(struct imxccm_softc *sc)
396 {
397 	uint32_t clkroot = PLL3_80M;
398 	uint32_t podf = HREAD4(sc, CCM_CSCDR1) & CCM_CSCDR1_UART_PODF_MASK;
399 
400 	return clkroot / (podf + 1);
401 }
402 
403 uint32_t
404 imxccm_get_periphclk(struct imxccm_softc *sc)
405 {
406 	if ((HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_PERIPH_CLK_SEL_SHIFT)
407 		    & CCM_CBCDR_PERIPH_CLK_SEL_MASK) {
408 		switch((HREAD4(sc, CCM_CBCMR)
409 		    >> CCM_CBCMR_PERIPH_CLK2_SEL_SHIFT) & CCM_CBCMR_PERIPH_CLK2_SEL_MASK) {
410 		case 0:
411 			return imxccm_decode_pll(sc, USB1_PLL3, HCLK_FREQ);
412 		case 1:
413 		case 2:
414 			return HCLK_FREQ;
415 		default:
416 			return 0;
417 		}
418 
419 	} else {
420 		switch((HREAD4(sc, CCM_CBCMR)
421 		    >> CCM_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT) & CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK) {
422 		default:
423 		case 0:
424 			return imxccm_decode_pll(sc, SYS_PLL2, HCLK_FREQ);
425 		case 1:
426 			return imxccm_get_pll2_pfd(sc, 2); // 396 MHz
427 		case 2:
428 			return imxccm_get_pll2_pfd(sc, 0); // 352 MHz
429 		case 3:
430 			return imxccm_get_pll2_pfd(sc, 2) / 2; // 198 MHz
431 		}
432 	}
433 }
434 
435 uint32_t
436 imxccm_get_ahbclk(struct imxccm_softc *sc)
437 {
438 	uint32_t ahb_podf;
439 
440 	ahb_podf = (HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_AHB_PODF_SHIFT)
441 	    & CCM_CBCDR_AHB_PODF_MASK;
442 	return imxccm_get_periphclk(sc) / (ahb_podf + 1);
443 }
444 
445 uint32_t
446 imxccm_get_ipgclk(struct imxccm_softc *sc)
447 {
448 	uint32_t ipg_podf;
449 
450 	ipg_podf = (HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_IPG_PODF_SHIFT)
451 	    & CCM_CBCDR_IPG_PODF_MASK;
452 	return imxccm_get_ahbclk(sc) / (ipg_podf + 1);
453 }
454 
455 uint32_t
456 imxccm_get_ipg_perclk(struct imxccm_softc *sc)
457 {
458 	uint32_t cscmr1 = HREAD4(sc, CCM_CSCMR1);
459 	uint32_t freq, ipg_podf;
460 
461 	if (sc->sc_gates == imx6ul_gates &&
462 	    cscmr1 & CCM_CSCMR1_PERCLK_CLK_SEL_MASK)
463 		freq = HCLK_FREQ;
464 	else
465 		freq = imxccm_get_ipgclk(sc);
466 
467 	ipg_podf = cscmr1 & CCM_CSCMR1_PERCLK_CLK_PODF_MASK;
468 
469 	return freq / (ipg_podf + 1);
470 }
471 
472 void
473 imxccm_enable(void *cookie, uint32_t *cells, int on)
474 {
475 	struct imxccm_softc *sc = cookie;
476 	uint32_t idx = cells[0];
477 	uint8_t reg, pos;
478 
479 	/* Dummy clock. */
480 	if (idx == 0)
481 		return;
482 
483 	if (sc->sc_gates == imx6_gates) {
484 		switch (idx) {
485 		case IMX6_CLK_USBPHY1:
486 			imxccm_enable_pll_usb1(sc);
487 			return;
488 		case IMX6_CLK_USBPHY2:
489 			imxccm_enable_pll_usb2(sc);
490 			return;
491 		case IMX6_CLK_SATA_REF_100:
492 			imxccm_enable_sata(sc);
493 			return;
494 		case IMX6_CLK_ENET_REF:
495 			imxccm_enable_enet(sc);
496 			return;
497 		default:
498 			break;
499 		}
500 	}
501 
502 	if (idx >= sc->sc_ngates || sc->sc_gates[idx].reg == 0) {
503 		printf("%s: 0x%08x\n", __func__, idx);
504 		return;
505 	}
506 
507 	reg = sc->sc_gates[idx].reg;
508 	pos = sc->sc_gates[idx].pos;
509 
510 	if (on)
511 		HSET4(sc, reg, 0x3 << (2 * pos));
512 	else
513 		HCLR4(sc, reg, 0x3 << (2 * pos));
514 }
515 
516 uint32_t
517 imxccm_get_frequency(void *cookie, uint32_t *cells)
518 {
519 	struct imxccm_softc *sc = cookie;
520 	uint32_t idx = cells[0];
521 	uint32_t parent;
522 
523 	/* Dummy clock. */
524 	if (idx == 0)
525 		return 0;
526 
527 	if (idx < sc->sc_ngates && sc->sc_gates[idx].parent) {
528 		parent = sc->sc_gates[idx].parent;
529 		return imxccm_get_frequency(sc, &parent);
530 	}
531 
532 	if (sc->sc_gates == imx6ul_gates) {
533 		switch (idx) {
534 		case IMX6UL_CLK_ARM:
535 			return imxccm_get_armclk(sc);
536 		case IMX6UL_CLK_IPG:
537 			return imxccm_get_ipgclk(sc);
538 		case IMX6UL_CLK_PERCLK:
539 			return imxccm_get_ipg_perclk(sc);
540 		case IMX6UL_CLK_UART1_SERIAL:
541 			return imxccm_get_uartclk(sc);
542 		case IMX6UL_CLK_USDHC1:
543 		case IMX6UL_CLK_USDHC2:
544 			return imxccm_get_usdhx(sc, idx - IMX6UL_CLK_USDHC1 + 1);
545 		}
546 	} else {
547 		switch (idx) {
548 		case IMX6_CLK_AHB:
549 			return imxccm_get_ahbclk(sc);
550 		case IMX6_CLK_ARM:
551 			return imxccm_get_armclk(sc);
552 		case IMX6_CLK_IPG:
553 			return imxccm_get_ipgclk(sc);
554 		case IMX6_CLK_IPG_PER:
555 			return imxccm_get_ipg_perclk(sc);
556 		case IMX6_CLK_UART_SERIAL:
557 			return imxccm_get_uartclk(sc);
558 		case IMX6_CLK_USDHC1:
559 		case IMX6_CLK_USDHC2:
560 		case IMX6_CLK_USDHC3:
561 		case IMX6_CLK_USDHC4:
562 			return imxccm_get_usdhx(sc, idx - IMX6_CLK_USDHC1 + 1);
563 		}
564 	}
565 
566 	printf("%s: 0x%08x\n", __func__, idx);
567 	return 0;
568 }
569 
570 void
571 imxccm_enable_pll_enet(struct imxccm_softc *sc)
572 {
573 	if (HREAD4(sc, CCM_ANALOG_PLL_ENET) & CCM_ANALOG_PLL_ENET_ENABLE)
574 		return;
575 
576 	HCLR4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_POWERDOWN);
577 
578 	HSET4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_ENABLE);
579 
580 	while(!(HREAD4(sc, CCM_ANALOG_PLL_ENET) & CCM_ANALOG_PLL_ENET_LOCK));
581 
582 	HCLR4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_BYPASS);
583 }
584 
585 void
586 imxccm_enable_enet(struct imxccm_softc *sc)
587 {
588 	imxccm_enable_pll_enet(sc);
589 	HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_DIV_125M);
590 }
591 
592 void
593 imxccm_enable_sata(struct imxccm_softc *sc)
594 {
595 	imxccm_enable_pll_enet(sc);
596 	HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_100M_SATA);
597 }
598 
599 void
600 imxccm_enable_pll_usb1(struct imxccm_softc *sc)
601 {
602 	HWRITE4(sc, CCM_ANALOG_PLL_USB1_CLR, CCM_ANALOG_PLL_USB1_BYPASS);
603 
604 	HWRITE4(sc, CCM_ANALOG_PLL_USB1_SET,
605 	      CCM_ANALOG_PLL_USB1_ENABLE
606 	    | CCM_ANALOG_PLL_USB1_POWER
607 	    | CCM_ANALOG_PLL_USB1_EN_USB_CLKS);
608 }
609 
610 void
611 imxccm_enable_pll_usb2(struct imxccm_softc *sc)
612 {
613 	HWRITE4(sc, CCM_ANALOG_PLL_USB2_CLR, CCM_ANALOG_PLL_USB2_BYPASS);
614 
615 	HWRITE4(sc, CCM_ANALOG_PLL_USB2_SET,
616 	      CCM_ANALOG_PLL_USB2_ENABLE
617 	    | CCM_ANALOG_PLL_USB2_POWER
618 	    | CCM_ANALOG_PLL_USB2_EN_USB_CLKS);
619 }
620