xref: /openbsd-src/sys/dev/fdt/rkclock.c (revision 41b6ab1f8c2f06576ac4d0c0556985ff0146e4fc)
1*41b6ab1fSkettenis /*	$OpenBSD: rkclock.c,v 1.91 2024/11/24 22:19:59 kettenis Exp $	*/
2592afca7Skettenis /*
3da841e6eSkettenis  * Copyright (c) 2017, 2018 Mark Kettenis <kettenis@openbsd.org>
4592afca7Skettenis  *
5592afca7Skettenis  * Permission to use, copy, modify, and distribute this software for any
6592afca7Skettenis  * purpose with or without fee is hereby granted, provided that the above
7592afca7Skettenis  * copyright notice and this permission notice appear in all copies.
8592afca7Skettenis  *
9592afca7Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10592afca7Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11592afca7Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12592afca7Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13592afca7Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14592afca7Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15592afca7Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16592afca7Skettenis  */
17592afca7Skettenis 
18592afca7Skettenis #include <sys/param.h>
19592afca7Skettenis #include <sys/systm.h>
20592afca7Skettenis #include <sys/sysctl.h>
21592afca7Skettenis #include <sys/device.h>
22592afca7Skettenis 
23592afca7Skettenis #include <machine/intr.h>
24592afca7Skettenis #include <machine/bus.h>
25592afca7Skettenis #include <machine/fdt.h>
26592afca7Skettenis 
27592afca7Skettenis #include <dev/ofw/openfirm.h>
28592afca7Skettenis #include <dev/ofw/ofw_clock.h>
292fcaff65Skettenis #include <dev/ofw/ofw_misc.h>
30592afca7Skettenis #include <dev/ofw/fdt.h>
31592afca7Skettenis 
32bd903cc4Skettenis /* RK3288 registers */
33227c6ee3Skettenis #define RK3288_CRU_APLL_CON(i)		(0x0000 + (i) * 4)
34bd903cc4Skettenis #define RK3288_CRU_CPLL_CON(i)		(0x0020 + (i) * 4)
35bd903cc4Skettenis #define RK3288_CRU_GPLL_CON(i)		(0x0030 + (i) * 4)
3672f108a7Skettenis #define RK3288_CRU_NPLL_CON(i)		(0x0040 + (i) * 4)
37bf9e0ff8Skettenis #define  RK3288_CRU_PLL_CLKR_MASK		(0x3f << 8)
38bf9e0ff8Skettenis #define  RK3288_CRU_PLL_CLKR_SHIFT		8
39bf9e0ff8Skettenis #define  RK3288_CRU_PLL_CLKOD_MASK		(0xf << 0)
40bf9e0ff8Skettenis #define  RK3288_CRU_PLL_CLKOD_SHIFT		0
41bf9e0ff8Skettenis #define  RK3288_CRU_PLL_CLKF_MASK		(0x1fff << 0)
42bf9e0ff8Skettenis #define  RK3288_CRU_PLL_CLKF_SHIFT		0
43bf9e0ff8Skettenis #define  RK3288_CRU_PLL_RESET			(1 << 5)
44bf9e0ff8Skettenis #define RK3288_CRU_MODE_CON		0x0050
45bf9e0ff8Skettenis #define  RK3288_CRU_MODE_PLL_WORK_MODE_MASK	0x3
46bf9e0ff8Skettenis #define  RK3288_CRU_MODE_PLL_WORK_MODE_SLOW	0x0
47bf9e0ff8Skettenis #define  RK3288_CRU_MODE_PLL_WORK_MODE_NORMAL	0x1
48bd903cc4Skettenis #define RK3288_CRU_CLKSEL_CON(i)	(0x0060 + (i) * 4)
4949b48721Skettenis #define RK3288_CRU_SOFTRST_CON(i)	(0x01b8 + (i) * 4)
50bd903cc4Skettenis 
511235808cSjmatthew /* RK3308 registers */
521235808cSjmatthew #define RK3308_CRU_APLL_CON(i)		(0x0000 + (i) * 4)
531235808cSjmatthew #define RK3308_CRU_DPLL_CON(i)		(0x0020 + (i) * 4)
541235808cSjmatthew #define RK3308_CRU_VPLL0_CON(i)		(0x0040 + (i) * 4)
551235808cSjmatthew #define RK3308_CRU_VPLL1_CON(i)		(0x0060 + (i) * 4)
561235808cSjmatthew #define  RK3308_CRU_PLL_POSTDIV1_MASK		(0x7 << 12)
571235808cSjmatthew #define  RK3308_CRU_PLL_POSTDIV1_SHIFT		12
581235808cSjmatthew #define  RK3308_CRU_PLL_FBDIV_MASK		(0xfff << 0)
591235808cSjmatthew #define  RK3308_CRU_PLL_FBDIV_SHIFT		0
601235808cSjmatthew #define  RK3308_CRU_PLL_DSMPD			(1 << 12)
611235808cSjmatthew #define  RK3308_CRU_PLL_PLL_LOCK		(1 << 10)
621235808cSjmatthew #define  RK3308_CRU_PLL_POSTDIV2_MASK		(0x7 << 6)
631235808cSjmatthew #define  RK3308_CRU_PLL_POSTDIV2_SHIFT		6
641235808cSjmatthew #define  RK3308_CRU_PLL_REFDIV_MASK		(0x3f << 0)
651235808cSjmatthew #define  RK3308_CRU_PLL_REFDIV_SHIFT		0
661235808cSjmatthew #define  RK3308_CRU_PLL_FRACDIV_MASK		(0xffffff << 0)
671235808cSjmatthew #define  RK3308_CRU_PLL_FRACDIV_SHIFT		0
681235808cSjmatthew #define RK3308_CRU_CRU_MODE		0x00a0
691235808cSjmatthew #define RK3308_CRU_CRU_MODE_MASK	0x3
701235808cSjmatthew #define RK3308_CRU_CRU_MODE_SLOW	0x0
711235808cSjmatthew #define RK3308_CRU_CRU_MODE_NORMAL	0x1
721235808cSjmatthew #define RK3308_CRU_CRU_MODE_DEEP	0x2
731235808cSjmatthew #define RK3308_CRU_CLKSEL_CON(i)	(0x0100 + (i) * 4)
741235808cSjmatthew #define  RK3308_CRU_ACLK_CORE_DIV_CON_MASK	(0x07 << 12)
751235808cSjmatthew #define  RK3308_CRU_ACLK_CORE_DIV_CON_SHIFT	12
761235808cSjmatthew #define  RK3308_CRU_CLK_CORE_DBG_DIV_CON_MASK	(0x0f << 8)
771235808cSjmatthew #define  RK3308_CRU_CLK_CORE_DBG_DIV_CON_SHIFT  8
781235808cSjmatthew #define  RK3308_CRU_CORE_CLK_PLL_SEL_MASK	(0x03 << 6)
791235808cSjmatthew #define  RK3308_CRU_CORE_CLK_PLL_SEL_SHIFT	6
801235808cSjmatthew #define  RK3308_CRU_CLK_CORE_DIV_CON_MASK	(0x0f << 0)
811235808cSjmatthew #define  RK3308_CRU_CLK_CORE_DIV_CON_SHIFT	0
821235808cSjmatthew #define RK3308_CRU_CLKGATE_CON(i)	(0x0300 + (i) * 4)
831235808cSjmatthew #define RK3308_CRU_SOFTRST_CON(i)	(0x0400 + (i) * 4)
841235808cSjmatthew 
85da841e6eSkettenis /* RK3328 registers */
86a2f8fc4bSkettenis #define RK3328_CRU_APLL_CON(i)		(0x0000 + (i) * 4)
87a2f8fc4bSkettenis #define RK3328_CRU_DPLL_CON(i)		(0x0020 + (i) * 4)
88da841e6eSkettenis #define RK3328_CRU_CPLL_CON(i)		(0x0040 + (i) * 4)
89da841e6eSkettenis #define RK3328_CRU_GPLL_CON(i)		(0x0060 + (i) * 4)
90f231fa3aSkettenis #define RK3328_CRU_NPLL_CON(i)		(0x00a0 + (i) * 4)
91da841e6eSkettenis #define  RK3328_CRU_PLL_POSTDIV1_MASK		(0x7 << 12)
92da841e6eSkettenis #define  RK3328_CRU_PLL_POSTDIV1_SHIFT		12
93da841e6eSkettenis #define  RK3328_CRU_PLL_FBDIV_MASK		(0xfff << 0)
94da841e6eSkettenis #define  RK3328_CRU_PLL_FBDIV_SHIFT		0
954cecc5e3Skettenis #define  RK3328_CRU_PLL_DSMPD			(1 << 12)
964cecc5e3Skettenis #define  RK3328_CRU_PLL_PLL_LOCK		(1 << 10)
97da841e6eSkettenis #define  RK3328_CRU_PLL_POSTDIV2_MASK		(0x7 << 6)
98da841e6eSkettenis #define  RK3328_CRU_PLL_POSTDIV2_SHIFT		6
99da841e6eSkettenis #define  RK3328_CRU_PLL_REFDIV_MASK		(0x3f << 0)
100da841e6eSkettenis #define  RK3328_CRU_PLL_REFDIV_SHIFT		0
1014cecc5e3Skettenis #define  RK3328_CRU_PLL_FRACDIV_MASK		(0xffffff << 0)
1024cecc5e3Skettenis #define  RK3328_CRU_PLL_FRACDIV_SHIFT		0
103f231fa3aSkettenis #define RK3328_CRU_CRU_MODE		0x0080
104f231fa3aSkettenis #define  RK3328_CRU_CRU_MODE_MASK		0x1
105f231fa3aSkettenis #define  RK3328_CRU_CRU_MODE_SLOW		0x0
106f231fa3aSkettenis #define  RK3328_CRU_CRU_MODE_NORMAL		0x1
107f231fa3aSkettenis #define RK3328_CRU_CLKSEL_CON(i)	(0x0100 + (i) * 4)
108f231fa3aSkettenis #define  RK3328_CRU_CORE_CLK_PLL_SEL_MASK	(0x3 << 6)
109f231fa3aSkettenis #define  RK3328_CRU_CORE_CLK_PLL_SEL_SHIFT	6
110f231fa3aSkettenis #define  RK3328_CRU_CLK_CORE_DIV_CON_MASK	(0x1f << 0)
111f231fa3aSkettenis #define  RK3328_CRU_CLK_CORE_DIV_CON_SHIFT	0
112f231fa3aSkettenis #define  RK3328_CRU_ACLK_CORE_DIV_CON_MASK	(0x7 << 4)
113f231fa3aSkettenis #define  RK3328_CRU_ACLK_CORE_DIV_CON_SHIFT	4
114f231fa3aSkettenis #define  RK3328_CRU_CLK_CORE_DBG_DIV_CON_MASK	(0xf << 0)
115f231fa3aSkettenis #define  RK3328_CRU_CLK_CORE_DBG_DIV_CON_SHIFT	0
1164cecc5e3Skettenis #define  RK3328_CRU_VOP_DCLK_SRC_SEL_MASK	(0x1 << 1)
1174cecc5e3Skettenis #define  RK3328_CRU_VOP_DCLK_SRC_SEL_SHIFT	1
118da841e6eSkettenis #define RK3328_CRU_CLKGATE_CON(i)	(0x0200 + (i) * 4)
119da841e6eSkettenis #define RK3328_CRU_SOFTRST_CON(i)	(0x0300 + (i) * 4)
120da841e6eSkettenis 
121015b970dSkettenis #define RK3328_GRF_SOC_CON4		0x0410
122015b970dSkettenis #define  RK3328_GRF_GMAC2IO_MAC_CLK_OUTPUT_EN	(1 << 14)
1232fcaff65Skettenis #define RK3328_GRF_MAC_CON1		0x0904
124015b970dSkettenis #define  RK3328_GRF_GMAC2IO_RMII_EXTCLK_SEL	(1 << 10)
1252fcaff65Skettenis 
126bd903cc4Skettenis /* RK3399 registers */
12713d6baf8Skettenis #define RK3399_CRU_LPLL_CON(i)		(0x0000 + (i) * 4)
12813d6baf8Skettenis #define RK3399_CRU_BPLL_CON(i)		(0x0020 + (i) * 4)
12913d6baf8Skettenis #define RK3399_CRU_DPLL_CON(i)		(0x0020 + (i) * 4)
130d9457073Skettenis #define RK3399_CRU_CPLL_CON(i)		(0x0060 + (i) * 4)
131d9457073Skettenis #define RK3399_CRU_GPLL_CON(i)		(0x0080 + (i) * 4)
132f89cc881Skettenis #define RK3399_CRU_NPLL_CON(i)		(0x00a0 + (i) * 4)
13398a76083Skettenis #define RK3399_CRU_VPLL_CON(i)		(0x00c0 + (i) * 4)
134f8c4fba2Skettenis #define  RK3399_CRU_PLL_FBDIV_MASK		(0xfff << 0)
135f8c4fba2Skettenis #define  RK3399_CRU_PLL_FBDIV_SHIFT		0
136f8c4fba2Skettenis #define  RK3399_CRU_PLL_POSTDIV2_MASK		(0x7 << 12)
137f8c4fba2Skettenis #define  RK3399_CRU_PLL_POSTDIV2_SHIFT		12
138f8c4fba2Skettenis #define  RK3399_CRU_PLL_POSTDIV1_MASK		(0x7 << 8)
139f8c4fba2Skettenis #define  RK3399_CRU_PLL_POSTDIV1_SHIFT		8
140f8c4fba2Skettenis #define  RK3399_CRU_PLL_REFDIV_MASK		(0x3f << 0)
141f8c4fba2Skettenis #define  RK3399_CRU_PLL_REFDIV_SHIFT		0
14269da300fSkettenis #define  RK3399_CRU_PLL_PLL_WORK_MODE_MASK	(0x3 << 8)
14369da300fSkettenis #define  RK3399_CRU_PLL_PLL_WORK_MODE_SLOW	(0x0 << 8)
14469da300fSkettenis #define  RK3399_CRU_PLL_PLL_WORK_MODE_NORMAL	(0x1 << 8)
14569da300fSkettenis #define  RK3399_CRU_PLL_PLL_WORK_MODE_DEEP_SLOW	(0x2 << 8)
146f8c4fba2Skettenis #define  RK3399_CRU_PLL_PLL_LOCK		(1U << 31)
147592afca7Skettenis #define RK3399_CRU_CLKSEL_CON(i)	(0x0100 + (i) * 4)
148f8c4fba2Skettenis #define  RK3399_CRU_ACLKM_CORE_DIV_CON_MASK	(0x1f << 8)
149f8c4fba2Skettenis #define  RK3399_CRU_ACLKM_CORE_DIV_CON_SHIFT	8
150f8c4fba2Skettenis #define  RK3399_CRU_CORE_PLL_SEL_MASK		(0x3 << 6)
1511af18cc1Skettenis #define  RK3399_CRU_CORE_PLL_SEL_APLL		(0x0 << 6)
1521af18cc1Skettenis #define  RK3399_CRU_CORE_PLL_SEL_BPLL		(0x1 << 6)
1531af18cc1Skettenis #define  RK3399_CRU_CORE_PLL_SEL_DPLL		(0x2 << 6)
1541af18cc1Skettenis #define  RK3399_CRU_CORE_PLL_SEL_GPLL		(0x3 << 6)
155f8c4fba2Skettenis #define  RK3399_CRU_CORE_PLL_SEL_SHIFT		6
156f8c4fba2Skettenis #define  RK3399_CRU_CLK_CORE_DIV_CON_MASK	(0x1f << 0)
157f8c4fba2Skettenis #define  RK3399_CRU_CLK_CORE_DIV_CON_SHIFT	0
158f8c4fba2Skettenis #define  RK3399_CRU_PCLK_DBG_DIV_CON_MASK	(0x1f << 8)
159f8c4fba2Skettenis #define  RK3399_CRU_PCLK_DBG_DIV_CON_SHIFT	8
160f8c4fba2Skettenis #define  RK3399_CRU_ATCLK_CORE_DIV_CON_MASK	(0x1f << 0)
161f8c4fba2Skettenis #define  RK3399_CRU_ATCLK_CORE_DIV_CON_SHIFT	0
162dac87a37Skettenis #define  RK3399_CRU_CLK_SD_PLL_SEL_MASK		(0x7 << 8)
163dac87a37Skettenis #define  RK3399_CRU_CLK_SD_PLL_SEL_SHIFT	8
16498a76083Skettenis #define  RK3399_CRU_CLK_SD_DIV_CON_MASK		(0x7f << 0)
165dac87a37Skettenis #define  RK3399_CRU_CLK_SD_DIV_CON_SHIFT	0
166c4f96b6eSkettenis #define RK3399_CRU_CLKGATE_CON(i)	(0x0300 + (i) * 4)
16764b62fe2Skettenis #define RK3399_CRU_SOFTRST_CON(i)	(0x0400 + (i) * 4)
168d9457073Skettenis #define RK3399_CRU_SDMMC_CON(i)		(0x0580 + (i) * 4)
169592afca7Skettenis 
170ad8cadefSkettenis #define RK3399_PMUCRU_PPLL_CON(i)	(0x0000 + (i) * 4)
171ad8cadefSkettenis #define RK3399_PMUCRU_CLKSEL_CON(i)	(0x0080 + (i) * 4)
172ad8cadefSkettenis 
17333081393Skettenis /* RK3568 registers */
17433081393Skettenis #define RK3568_CRU_APLL_CON(i)		(0x0000 + (i) * 4)
17533081393Skettenis #define RK3568_CRU_DPLL_CON(i)		(0x0020 + (i) * 4)
17633081393Skettenis #define RK3568_CRU_GPLL_CON(i)		(0x0040 + (i) * 4)
17733081393Skettenis #define RK3568_CRU_CPLL_CON(i)		(0x0060 + (i) * 4)
17833081393Skettenis #define RK3568_CRU_NPLL_CON(i)		(0x0080 + (i) * 4)
17933081393Skettenis #define RK3568_CRU_VPLL_CON(i)		(0x00a0 + (i) * 4)
18033081393Skettenis #define RK3568_CRU_MODE_CON		0x00c0
18133081393Skettenis #define RK3568_CRU_CLKSEL_CON(i)	(0x0100 + (i) * 4)
18233081393Skettenis #define RK3568_CRU_GATE_CON(i)		(0x0300 + (i) * 4)
18333081393Skettenis #define RK3568_CRU_SOFTRST_CON(i)	(0x0400 + (i) * 4)
18433081393Skettenis 
18533081393Skettenis #define RK3568_PMUCRU_PPLL_CON(i)	(0x0000 + (i) * 4)
18633081393Skettenis #define RK3568_PMUCRU_HPLL_CON(i)	(0x0040 + (i) * 4)
18733081393Skettenis #define RK3568_PMUCRU_MODE_CON		0x0080
18833081393Skettenis #define RK3568_PMUCRU_CLKSEL_CON(i)	(0x0100 + (i) * 4)
189e1414c50Skettenis #define RK3568_PMUCRU_GATE_CON(i)	(0x0180 + (i) * 4)
19033081393Skettenis 
191e3f8cdb8Skettenis /* RK3588 registers */
192e3f8cdb8Skettenis #define RK3588_CRU_AUPLL_CON(i)		(0x00180 + (i) * 4)
193e3f8cdb8Skettenis #define RK3588_CRU_CPLL_CON(i)		(0x001a0 + (i) * 4)
194e3f8cdb8Skettenis #define RK3588_CRU_GPLL_CON(i)		(0x001c0 + (i) * 4)
195e3f8cdb8Skettenis #define RK3588_CRU_NPLL_CON(i)		(0x001e0 + (i) * 4)
196e3f8cdb8Skettenis #define  RK3588_CRU_PLL_M_MASK			(0x3ff << 0)
197e3f8cdb8Skettenis #define  RK3588_CRU_PLL_M_SHIFT			0
198e3f8cdb8Skettenis #define  RK3588_CRU_PLL_RESETB			(1 << 13)
199e3f8cdb8Skettenis #define  RK3588_CRU_PLL_S_MASK			(0x7 << 6)
200e3f8cdb8Skettenis #define  RK3588_CRU_PLL_S_SHIFT			6
201e3f8cdb8Skettenis #define  RK3588_CRU_PLL_P_MASK			(0x3f << 0)
202e3f8cdb8Skettenis #define  RK3588_CRU_PLL_P_SHIFT			0
203e3f8cdb8Skettenis #define  RK3588_CRU_PLL_K_MASK			(0xffff << 0)
204e3f8cdb8Skettenis #define  RK3588_CRU_PLL_K_SHIFT			0
205e3f8cdb8Skettenis #define  RK3588_CRU_PLL_PLL_LOCK		(1 << 15)
206e3f8cdb8Skettenis #define RK3588_CRU_MODE_CON		0x00280
207e3f8cdb8Skettenis #define  RK3588_CRU_MODE_MASK			0x3
208e3f8cdb8Skettenis #define  RK3588_CRU_MODE_SLOW			0x0
209e3f8cdb8Skettenis #define  RK3588_CRU_MODE_NORMAL			0x1
210e3f8cdb8Skettenis 
211e3f8cdb8Skettenis #define RK3588_CRU_CLKSEL_CON(i)	(0x00300 + (i) * 4)
212e3f8cdb8Skettenis #define RK3588_CRU_GATE_CON(i)		(0x00800 + (i) * 4)
21383b03bd4Skettenis #define RK3588_CRU_SOFTRST_CON(i)	(0x00a00 + (i) * 4)
214e3f8cdb8Skettenis 
215e3f8cdb8Skettenis #define RK3588_PHPTOPCRU_PPLL_CON(i)	(0x08200 + (i) * 4)
21683b03bd4Skettenis #define RK3588_PHPTOPCRU_SOFTRST_CON(i)	(0x08a00 + (i) * 4)
217e3f8cdb8Skettenis #define RK3588_PMUCRU_CLKSEL_CON(i)	(0x30300 + (i) * 4)
218e3f8cdb8Skettenis 
219592afca7Skettenis #include "rkclock_clocks.h"
220592afca7Skettenis 
221e82d29a5Skettenis struct rkclock {
222e82d29a5Skettenis 	uint16_t idx;
223e3f8cdb8Skettenis 	uint32_t reg;
224e82d29a5Skettenis 	uint16_t sel_mask;
225e82d29a5Skettenis 	uint16_t div_mask;
2267371cc19Skettenis 	uint16_t parents[8];
2273f8209efSkettenis 	uint32_t flags;
228e82d29a5Skettenis };
229e82d29a5Skettenis 
230e82d29a5Skettenis #define SEL(l, f)	(((1 << (l - f + 1)) - 1) << f)
231e82d29a5Skettenis #define DIV(l, f)	SEL(l, f)
232e82d29a5Skettenis 
2333f8209efSkettenis #define FIXED_PARENT	(1 << 0)
2343f8209efSkettenis #define SET_PARENT	(1 << 1)
2353f8209efSkettenis 
236592afca7Skettenis #define HREAD4(sc, reg)							\
237592afca7Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
238592afca7Skettenis #define HWRITE4(sc, reg, val)						\
239592afca7Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
240592afca7Skettenis #define HSET4(sc, reg, bits)						\
241592afca7Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
242592afca7Skettenis #define HCLR4(sc, reg, bits)						\
243592afca7Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
244592afca7Skettenis 
245592afca7Skettenis struct rkclock_softc {
246592afca7Skettenis 	struct device		sc_dev;
247592afca7Skettenis 	bus_space_tag_t		sc_iot;
248592afca7Skettenis 	bus_space_handle_t	sc_ioh;
2492fcaff65Skettenis 	struct regmap		*sc_grf;
250592afca7Skettenis 
2519822ab30Skettenis 	uint32_t		sc_phandle;
25237c734d3Snaddy 	const struct rkclock	*sc_clocks;
2539822ab30Skettenis 
254592afca7Skettenis 	struct clock_device	sc_cd;
255592afca7Skettenis 	struct reset_device	sc_rd;
256592afca7Skettenis };
257592afca7Skettenis 
258592afca7Skettenis int rkclock_match(struct device *, void *, void *);
259592afca7Skettenis void rkclock_attach(struct device *, struct device *, void *);
260592afca7Skettenis 
2619fdf0c62Smpi const struct cfattach	rkclock_ca = {
262592afca7Skettenis 	sizeof (struct rkclock_softc), rkclock_match, rkclock_attach
263592afca7Skettenis };
264592afca7Skettenis 
265592afca7Skettenis struct cfdriver rkclock_cd = {
266592afca7Skettenis 	NULL, "rkclock", DV_DULL
267592afca7Skettenis };
268592afca7Skettenis 
269bf9e0ff8Skettenis void	rk3288_init(struct rkclock_softc *);
270bd903cc4Skettenis uint32_t rk3288_get_frequency(void *, uint32_t *);
271bd903cc4Skettenis int	rk3288_set_frequency(void *, uint32_t *, uint32_t);
272bd903cc4Skettenis void	rk3288_enable(void *, uint32_t *, int);
273bd903cc4Skettenis void	rk3288_reset(void *, uint32_t *, int);
274bd903cc4Skettenis 
2751235808cSjmatthew void	rk3308_init(struct rkclock_softc *);
2761235808cSjmatthew uint32_t rk3308_get_frequency(void *, uint32_t *);
2771235808cSjmatthew int	rk3308_set_frequency(void *, uint32_t *, uint32_t);
2781235808cSjmatthew int	rk3308_set_parent(void *, uint32_t *, uint32_t *);
2791235808cSjmatthew void	rk3308_enable(void *, uint32_t *, int);
2801235808cSjmatthew void	rk3308_reset(void *, uint32_t *, int);
2811235808cSjmatthew 
282da841e6eSkettenis void	rk3328_init(struct rkclock_softc *);
283da841e6eSkettenis uint32_t rk3328_get_frequency(void *, uint32_t *);
284da841e6eSkettenis int	rk3328_set_frequency(void *, uint32_t *, uint32_t);
2859822ab30Skettenis int	rk3328_set_parent(void *, uint32_t *, uint32_t *);
286da841e6eSkettenis void	rk3328_enable(void *, uint32_t *, int);
287da841e6eSkettenis void	rk3328_reset(void *, uint32_t *, int);
288da841e6eSkettenis 
28969da300fSkettenis void	rk3399_init(struct rkclock_softc *);
290592afca7Skettenis uint32_t rk3399_get_frequency(void *, uint32_t *);
291592afca7Skettenis int	rk3399_set_frequency(void *, uint32_t *, uint32_t);
2925486a9fbSpatrick int	rk3399_set_parent(void *, uint32_t *, uint32_t *);
293592afca7Skettenis void	rk3399_enable(void *, uint32_t *, int);
294592afca7Skettenis void	rk3399_reset(void *, uint32_t *, int);
295592afca7Skettenis 
296ad8cadefSkettenis void	rk3399_pmu_init(struct rkclock_softc *);
297592afca7Skettenis uint32_t rk3399_pmu_get_frequency(void *, uint32_t *);
298592afca7Skettenis int	rk3399_pmu_set_frequency(void *, uint32_t *, uint32_t);
299592afca7Skettenis void	rk3399_pmu_enable(void *, uint32_t *, int);
300592afca7Skettenis void	rk3399_pmu_reset(void *, uint32_t *, int);
301592afca7Skettenis 
30233081393Skettenis void	rk3568_init(struct rkclock_softc *);
30333081393Skettenis uint32_t rk3568_get_frequency(void *, uint32_t *);
30433081393Skettenis int	rk3568_set_frequency(void *, uint32_t *, uint32_t);
305bc4366cbSkettenis int	rk3568_set_parent(void *, uint32_t *, uint32_t *);
30633081393Skettenis void	rk3568_enable(void *, uint32_t *, int);
30733081393Skettenis void	rk3568_reset(void *, uint32_t *, int);
30833081393Skettenis 
30933081393Skettenis void	rk3568_pmu_init(struct rkclock_softc *);
31033081393Skettenis uint32_t rk3568_pmu_get_frequency(void *, uint32_t *);
31133081393Skettenis int	rk3568_pmu_set_frequency(void *, uint32_t *, uint32_t);
31233081393Skettenis void	rk3568_pmu_enable(void *, uint32_t *, int);
31333081393Skettenis void	rk3568_pmu_reset(void *, uint32_t *, int);
31433081393Skettenis 
315e3f8cdb8Skettenis void	rk3588_init(struct rkclock_softc *);
316e3f8cdb8Skettenis uint32_t rk3588_get_frequency(void *, uint32_t *);
317e3f8cdb8Skettenis int	rk3588_set_frequency(void *, uint32_t *, uint32_t);
318e3f8cdb8Skettenis void	rk3588_enable(void *, uint32_t *, int);
319e3f8cdb8Skettenis void	rk3588_reset(void *, uint32_t *, int);
320e3f8cdb8Skettenis 
321592afca7Skettenis struct rkclock_compat {
322592afca7Skettenis 	const char *compat;
323d921c7cfSdlg 	const char *name;
3241af18cc1Skettenis 	int	assign;
32569da300fSkettenis 	void	(*init)(struct rkclock_softc *);
326592afca7Skettenis 	void	(*enable)(void *, uint32_t *, int);
327592afca7Skettenis 	uint32_t (*get_frequency)(void *, uint32_t *);
328592afca7Skettenis 	int	(*set_frequency)(void *, uint32_t *, uint32_t);
3299822ab30Skettenis 	int	(*set_parent)(void *, uint32_t *, uint32_t *);
330592afca7Skettenis 	void	(*reset)(void *, uint32_t *, int);
331592afca7Skettenis };
332592afca7Skettenis 
33337c734d3Snaddy const struct rkclock_compat rkclock_compat[] = {
334592afca7Skettenis 	{
335d921c7cfSdlg 		"rockchip,rk3288-cru", NULL, 0, rk3288_init,
336bd903cc4Skettenis 		rk3288_enable, rk3288_get_frequency,
3379822ab30Skettenis 		rk3288_set_frequency, NULL,
3389822ab30Skettenis 		rk3288_reset
339bd903cc4Skettenis 	},
340bd903cc4Skettenis 	{
341d921c7cfSdlg 		"rockchip,rk3308-cru", NULL, 1, rk3308_init,
3421235808cSjmatthew 		rk3308_enable, rk3308_get_frequency,
3431235808cSjmatthew 		rk3308_set_frequency, rk3308_set_parent,
3441235808cSjmatthew 		rk3308_reset
3451235808cSjmatthew 	},
3461235808cSjmatthew 	{
347d921c7cfSdlg 		"rockchip,rk3328-cru", NULL, 1, rk3328_init,
348da841e6eSkettenis 		rk3328_enable, rk3328_get_frequency,
3499822ab30Skettenis 		rk3328_set_frequency, rk3328_set_parent,
3509822ab30Skettenis 		rk3328_reset
351da841e6eSkettenis 	},
352da841e6eSkettenis 	{
353d921c7cfSdlg 		"rockchip,rk3399-cru", NULL, 1, rk3399_init,
354592afca7Skettenis 		rk3399_enable, rk3399_get_frequency,
3555486a9fbSpatrick 		rk3399_set_frequency, rk3399_set_parent,
3569822ab30Skettenis 		rk3399_reset
357592afca7Skettenis 	},
358592afca7Skettenis 	{
359d921c7cfSdlg 		"rockchip,rk3399-pmucru", NULL, 1, rk3399_pmu_init,
360592afca7Skettenis 		rk3399_pmu_enable, rk3399_pmu_get_frequency,
3619822ab30Skettenis 		rk3399_pmu_set_frequency, NULL,
3629822ab30Skettenis 		rk3399_pmu_reset
36333081393Skettenis 	},
36433081393Skettenis 	{
365d921c7cfSdlg 		"rockchip,rk3568-cru", "CRU", 1, rk3568_init,
36633081393Skettenis 		rk3568_enable, rk3568_get_frequency,
367bc4366cbSkettenis 		rk3568_set_frequency, rk3568_set_parent,
36833081393Skettenis 		rk3568_reset
36933081393Skettenis 	},
37033081393Skettenis 	{
371d921c7cfSdlg 		"rockchip,rk3568-pmucru", "PMUCRU", 1, rk3568_pmu_init,
37233081393Skettenis 		rk3568_pmu_enable, rk3568_pmu_get_frequency,
37333081393Skettenis 		rk3568_pmu_set_frequency, NULL,
37433081393Skettenis 		rk3568_pmu_reset
375e3f8cdb8Skettenis 	},
376e3f8cdb8Skettenis 	{
377d921c7cfSdlg 		"rockchip,rk3588-cru", NULL, 1, rk3588_init,
378e3f8cdb8Skettenis 		rk3588_enable, rk3588_get_frequency,
379e3f8cdb8Skettenis 		rk3588_set_frequency, NULL,
380e3f8cdb8Skettenis 		rk3588_reset
381e3f8cdb8Skettenis 	},
382592afca7Skettenis };
383592afca7Skettenis 
384592afca7Skettenis int
385592afca7Skettenis rkclock_match(struct device *parent, void *match, void *aux)
386592afca7Skettenis {
387592afca7Skettenis 	struct fdt_attach_args *faa = aux;
388592afca7Skettenis 	int i;
389592afca7Skettenis 
390592afca7Skettenis 	for (i = 0; i < nitems(rkclock_compat); i++) {
391592afca7Skettenis 		if (OF_is_compatible(faa->fa_node, rkclock_compat[i].compat))
392da841e6eSkettenis 			return 10;
393592afca7Skettenis 	}
394592afca7Skettenis 
395592afca7Skettenis 	return 0;
396592afca7Skettenis }
397592afca7Skettenis 
398592afca7Skettenis void
399592afca7Skettenis rkclock_attach(struct device *parent, struct device *self, void *aux)
400592afca7Skettenis {
401592afca7Skettenis 	struct rkclock_softc *sc = (struct rkclock_softc *)self;
402592afca7Skettenis 	struct fdt_attach_args *faa = aux;
4032fcaff65Skettenis 	uint32_t grf;
404592afca7Skettenis 	int i;
405592afca7Skettenis 
406592afca7Skettenis 	if (faa->fa_nreg < 1) {
407592afca7Skettenis 		printf(": no registers\n");
408592afca7Skettenis 		return;
409592afca7Skettenis 	}
410592afca7Skettenis 
411592afca7Skettenis 	sc->sc_iot = faa->fa_iot;
412592afca7Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
413592afca7Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
414592afca7Skettenis 		printf(": can't map registers\n");
415592afca7Skettenis 		return;
416592afca7Skettenis 	}
417592afca7Skettenis 
4182fcaff65Skettenis 	grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0);
4192fcaff65Skettenis 	sc->sc_grf = regmap_byphandle(grf);
4202fcaff65Skettenis 
4219822ab30Skettenis 	sc->sc_phandle = OF_getpropint(faa->fa_node, "phandle", 0);
4229822ab30Skettenis 
423592afca7Skettenis 	for (i = 0; i < nitems(rkclock_compat); i++) {
424592afca7Skettenis 		if (OF_is_compatible(faa->fa_node, rkclock_compat[i].compat)) {
425592afca7Skettenis 			break;
426592afca7Skettenis 		}
427592afca7Skettenis 	}
42869da300fSkettenis 	KASSERT(i < nitems(rkclock_compat));
42969da300fSkettenis 
430d921c7cfSdlg 	if (rkclock_compat[i].name != NULL)
431d921c7cfSdlg 		printf(": %s", rkclock_compat[i].name);
432d921c7cfSdlg 
433d921c7cfSdlg 	printf("\n");
434d921c7cfSdlg 
43569da300fSkettenis 	if (rkclock_compat[i].init)
43669da300fSkettenis 		rkclock_compat[i].init(sc);
437592afca7Skettenis 
438592afca7Skettenis 	sc->sc_cd.cd_node = faa->fa_node;
439592afca7Skettenis 	sc->sc_cd.cd_cookie = sc;
44069da300fSkettenis 	sc->sc_cd.cd_enable = rkclock_compat[i].enable;
44169da300fSkettenis 	sc->sc_cd.cd_get_frequency = rkclock_compat[i].get_frequency;
44269da300fSkettenis 	sc->sc_cd.cd_set_frequency = rkclock_compat[i].set_frequency;
4439822ab30Skettenis 	sc->sc_cd.cd_set_parent = rkclock_compat[i].set_parent;
444592afca7Skettenis 	clock_register(&sc->sc_cd);
445592afca7Skettenis 
446592afca7Skettenis 	sc->sc_rd.rd_node = faa->fa_node;
447592afca7Skettenis 	sc->sc_rd.rd_cookie = sc;
44869da300fSkettenis 	sc->sc_rd.rd_reset = rkclock_compat[i].reset;
449592afca7Skettenis 	reset_register(&sc->sc_rd);
4501af18cc1Skettenis 
4511af18cc1Skettenis 	if (rkclock_compat[i].assign)
4521af18cc1Skettenis 		clock_set_assigned(faa->fa_node);
453592afca7Skettenis }
454592afca7Skettenis 
45537c734d3Snaddy const struct rkclock *
456e82d29a5Skettenis rkclock_lookup(struct rkclock_softc *sc, uint32_t idx)
457e82d29a5Skettenis {
45837c734d3Snaddy 	const struct rkclock *clk;
459e82d29a5Skettenis 
460e82d29a5Skettenis 	for (clk = sc->sc_clocks; clk->idx; clk++) {
461e82d29a5Skettenis 		if (clk->idx == idx)
462e82d29a5Skettenis 			return clk;
463e82d29a5Skettenis 	}
464e82d29a5Skettenis 
465e82d29a5Skettenis 	return NULL;
466e82d29a5Skettenis }
467e82d29a5Skettenis 
468e82d29a5Skettenis uint32_t
469c935b871Skettenis rkclock_external_frequency(const char *name)
470c935b871Skettenis {
471c935b871Skettenis 	char buf[64] = {};
472c935b871Skettenis 	int len, node;
473c935b871Skettenis 
474c935b871Skettenis 	/*
475c935b871Skettenis 	 * Hunt through the device tree to find a fixed-rate clock
476c935b871Skettenis 	 * that has the requested clock output signal name.  This may
477c935b871Skettenis 	 * be too simple.
478c935b871Skettenis 	 */
479c935b871Skettenis 	node = OF_peer(0);
480c935b871Skettenis 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
481c935b871Skettenis 		len = OF_getproplen(node, "clock-output-names");
482c935b871Skettenis 		if (len <= 0 || len > sizeof(buf))
483c935b871Skettenis 			continue;
484c935b871Skettenis 		OF_getprop(node, "clock-output-names", buf, sizeof(buf));
485c935b871Skettenis 		if (strcmp(buf, name) != 0)
486c935b871Skettenis 			continue;
487c935b871Skettenis 		if (OF_is_compatible(node, "fixed-clock"))
488c935b871Skettenis 			return OF_getpropint(node, "clock-frequency", 0);
489c935b871Skettenis 	}
490c935b871Skettenis 
491c935b871Skettenis 	return 0;
492c935b871Skettenis }
493c935b871Skettenis 
494c935b871Skettenis uint32_t
49537c734d3Snaddy rkclock_div_con(struct rkclock_softc *sc, const struct rkclock *clk,
496f9988e9aSkettenis     uint32_t mux, uint32_t freq)
497e82d29a5Skettenis {
498f9988e9aSkettenis 	uint32_t parent_freq, div, div_con, max_div_con;
499f9988e9aSkettenis 	uint32_t idx = clk->parents[mux];
500f9988e9aSkettenis 
501f9988e9aSkettenis 	/* Derive maximum value from mask. */
502f9988e9aSkettenis 	max_div_con = clk->div_mask >> (ffs(clk->div_mask) - 1);
503e82d29a5Skettenis 
504e82d29a5Skettenis 	parent_freq = sc->sc_cd.cd_get_frequency(sc, &idx);
505e82d29a5Skettenis 	div = (parent_freq + freq - 1) / freq;
506f9988e9aSkettenis 	div_con = (div > 0 ? div - 1 : 0);
507f9988e9aSkettenis 	return (div_con < max_div_con) ? div_con : max_div_con;
508e82d29a5Skettenis }
509e82d29a5Skettenis 
510e82d29a5Skettenis uint32_t
51137c734d3Snaddy rkclock_freq(struct rkclock_softc *sc, const struct rkclock *clk,
5123f8209efSkettenis     uint32_t mux, uint32_t freq)
5133f8209efSkettenis {
5143f8209efSkettenis 	uint32_t parent_freq, div_con;
5153f8209efSkettenis 	uint32_t idx = clk->parents[mux];
5163f8209efSkettenis 
5173f8209efSkettenis 	parent_freq = sc->sc_cd.cd_get_frequency(sc, &idx);
5183f8209efSkettenis 	div_con = rkclock_div_con(sc, clk, mux, freq);
5193f8209efSkettenis 	return parent_freq / (div_con + 1);
5203f8209efSkettenis }
5213f8209efSkettenis 
5223f8209efSkettenis uint32_t
523e82d29a5Skettenis rkclock_get_frequency(struct rkclock_softc *sc, uint32_t idx)
524e82d29a5Skettenis {
52537c734d3Snaddy 	const struct rkclock *clk;
526e82d29a5Skettenis 	uint32_t reg, mux, div_con;
527e82d29a5Skettenis 	int shift;
528e82d29a5Skettenis 
529e82d29a5Skettenis 	clk = rkclock_lookup(sc, idx);
530e82d29a5Skettenis 	if (clk == NULL) {
531bbd59784Sdlg 		printf("%s(%s, %u)\n", __func__, sc->sc_dev.dv_xname, idx);
532e82d29a5Skettenis 		return 0;
533e82d29a5Skettenis 	}
534e82d29a5Skettenis 
535e82d29a5Skettenis 	reg = HREAD4(sc, clk->reg);
536e82d29a5Skettenis 	shift = ffs(clk->sel_mask) - 1;
537e82d29a5Skettenis 	if (shift == -1)
538e82d29a5Skettenis 		mux = 0;
539e82d29a5Skettenis 	else
540e82d29a5Skettenis 		mux = (reg & clk->sel_mask) >> shift;
541e82d29a5Skettenis 	shift = ffs(clk->div_mask) - 1;
542e82d29a5Skettenis 	if (shift == -1)
543e82d29a5Skettenis 		div_con = 0;
544e82d29a5Skettenis 	else
545e82d29a5Skettenis 		div_con = (reg & clk->div_mask) >> shift;
546e82d29a5Skettenis 
547e82d29a5Skettenis 	if (clk->parents[mux] == 0) {
548e82d29a5Skettenis 		printf("%s: parent 0x%08x\n", __func__, idx);
549e82d29a5Skettenis 		return 0;
550e82d29a5Skettenis 	}
551e82d29a5Skettenis 	idx = clk->parents[mux];
552e82d29a5Skettenis 	return sc->sc_cd.cd_get_frequency(sc, &idx) / (div_con + 1);
553e82d29a5Skettenis }
554e82d29a5Skettenis 
555e82d29a5Skettenis int
556e82d29a5Skettenis rkclock_set_frequency(struct rkclock_softc *sc, uint32_t idx, uint32_t freq)
557e82d29a5Skettenis {
55837c734d3Snaddy 	const struct rkclock *clk;
559e82d29a5Skettenis 	uint32_t reg, mux, div_con;
5603f8209efSkettenis 	uint32_t best_freq, best_mux, f;
56133081393Skettenis 	uint32_t parent;
5623f8209efSkettenis 	int sel_shift, div_shift, i;
563e82d29a5Skettenis 
564e82d29a5Skettenis 	clk = rkclock_lookup(sc, idx);
565137e484dSpatrick 	if (clk == NULL) {
566d921c7cfSdlg 		printf("%s(%s, %u, %u)\n", __func__, sc->sc_dev.dv_xname,
567d921c7cfSdlg 		    idx, freq);
568e82d29a5Skettenis 		return -1;
569e82d29a5Skettenis 	}
570e82d29a5Skettenis 
571e82d29a5Skettenis 	reg = HREAD4(sc, clk->reg);
5723f8209efSkettenis 	sel_shift = ffs(clk->sel_mask) - 1;
5733f8209efSkettenis 	if (sel_shift == -1)
5743f8209efSkettenis 		mux = sel_shift = 0;
575e82d29a5Skettenis 	else
5763f8209efSkettenis 		mux = (reg & clk->sel_mask) >> sel_shift;
577e82d29a5Skettenis 
578e82d29a5Skettenis 	if (clk->parents[mux] == 0) {
579d921c7cfSdlg 		printf("%s(%s, %u, %u) parent\n", __func__,
580d921c7cfSdlg 		    sc->sc_dev.dv_xname, idx, freq);
581e82d29a5Skettenis 		return 0;
582e82d29a5Skettenis 	}
583e82d29a5Skettenis 
5843f8209efSkettenis 	if (clk->flags & SET_PARENT) {
58533081393Skettenis 		parent = clk->parents[mux];
58633081393Skettenis 		sc->sc_cd.cd_set_frequency(sc, &parent, freq);
587137e484dSpatrick 		if (clk->div_mask == 0)
588137e484dSpatrick 			return 0;
589137e484dSpatrick 	}
590137e484dSpatrick 
59133081393Skettenis 	/*
592e3f8cdb8Skettenis 	 * If there is no divider, pick the parent with the frequency
593e3f8cdb8Skettenis 	 * closest to the target frequency.
59433081393Skettenis 	 */
595137e484dSpatrick 	if (clk->div_mask == 0) {
596e3f8cdb8Skettenis 		/*
597e3f8cdb8Skettenis 		 * Start out with the current parent.  This prevents
598e3f8cdb8Skettenis 		 * unnecessary switching to a different parent.
599e3f8cdb8Skettenis 		 */
60033081393Skettenis 		parent = clk->parents[mux];
601e3f8cdb8Skettenis 		best_freq = sc->sc_cd.cd_get_frequency(sc, &parent);
602e3f8cdb8Skettenis 		best_mux = mux;
60333081393Skettenis 
60433081393Skettenis 		for (i = 0; i < nitems(clk->parents); i++) {
60533081393Skettenis 			if (clk->parents[i] == 0)
60633081393Skettenis 				continue;
60733081393Skettenis 			parent = clk->parents[i];
608e3f8cdb8Skettenis 			f = sc->sc_cd.cd_get_frequency(sc, &parent);
609e3f8cdb8Skettenis 			if ((best_freq > freq && f < best_freq) ||
610e3f8cdb8Skettenis 			    (f > best_freq && f <= freq)) {
611e3f8cdb8Skettenis 				best_freq = f;
612e3f8cdb8Skettenis 				best_mux = i;
61333081393Skettenis 			}
61433081393Skettenis 		}
61533081393Skettenis 
616e3f8cdb8Skettenis 		HWRITE4(sc, clk->reg,
617e3f8cdb8Skettenis 		    clk->sel_mask << 16 | best_mux << sel_shift);
618e3f8cdb8Skettenis 		return 0;
6193f8209efSkettenis 	}
6203f8209efSkettenis 
6213f8209efSkettenis 	/*
6223f8209efSkettenis 	 * Start out with the current parent.  This prevents
6234b1a56afSjsg 	 * unnecessary switching to a different parent.
6243f8209efSkettenis 	 */
6253f8209efSkettenis 	best_freq = rkclock_freq(sc, clk, mux, freq);
6263f8209efSkettenis 	best_mux = mux;
6273f8209efSkettenis 
6283f8209efSkettenis 	/*
6293f8209efSkettenis 	 * Find the parent that allows configuration of a frequency
6303f8209efSkettenis 	 * closest to the target frequency.
6313f8209efSkettenis 	 */
6323f8209efSkettenis 	if ((clk->flags & FIXED_PARENT) == 0) {
6333f8209efSkettenis 		for (i = 0; i < nitems(clk->parents); i++) {
6343f8209efSkettenis 			if (clk->parents[i] == 0)
6353f8209efSkettenis 				continue;
6363f8209efSkettenis 			f = rkclock_freq(sc, clk, i, freq);
637e3f8cdb8Skettenis 			if ((best_freq > freq && f < best_freq) ||
638e3f8cdb8Skettenis 			    (f > best_freq && f <= freq)) {
6393f8209efSkettenis 				best_freq = f;
6403f8209efSkettenis 				best_mux = i;
6413f8209efSkettenis 			}
6423f8209efSkettenis 		}
6433f8209efSkettenis 	}
6443f8209efSkettenis 
6453f8209efSkettenis 	div_con = rkclock_div_con(sc, clk, best_mux, freq);
6463f8209efSkettenis 	div_shift = ffs(clk->div_mask) - 1;
6473f8209efSkettenis 	HWRITE4(sc, clk->reg,
6483f8209efSkettenis 	    clk->sel_mask << 16 | best_mux << sel_shift |
6493f8209efSkettenis 	    clk->div_mask << 16 | div_con << div_shift);
650e82d29a5Skettenis 	return 0;
651e82d29a5Skettenis }
652e82d29a5Skettenis 
653e82d29a5Skettenis int
654e82d29a5Skettenis rkclock_set_parent(struct rkclock_softc *sc, uint32_t idx, uint32_t parent)
655e82d29a5Skettenis {
65637c734d3Snaddy 	const struct rkclock *clk;
657e82d29a5Skettenis 	uint32_t mux;
658e82d29a5Skettenis 	int shift;
659e82d29a5Skettenis 
660e82d29a5Skettenis 	clk = rkclock_lookup(sc, idx);
661e82d29a5Skettenis 	if (clk == NULL || clk->sel_mask == 0) {
662e82d29a5Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
663e82d29a5Skettenis 		return -1;
664e82d29a5Skettenis 	}
665e82d29a5Skettenis 
666e82d29a5Skettenis 	for (mux = 0; mux < nitems(clk->parents); mux++) {
667e82d29a5Skettenis 		if (clk->parents[mux] == parent)
668e82d29a5Skettenis 			break;
669e82d29a5Skettenis 	}
670e82d29a5Skettenis 	if (mux == nitems(clk->parents) || parent == 0) {
671e82d29a5Skettenis 		printf("%s: 0x%08x parent 0x%08x\n", __func__, idx, parent);
672e82d29a5Skettenis 		return -1;
673e82d29a5Skettenis 	}
674e82d29a5Skettenis 
675e82d29a5Skettenis 	shift = ffs(clk->sel_mask) - 1;
676e82d29a5Skettenis 	HWRITE4(sc, clk->reg, clk->sel_mask << 16 | mux << shift);
677e82d29a5Skettenis 	return 0;
678e82d29a5Skettenis }
679e82d29a5Skettenis 
680592afca7Skettenis /*
681bd903cc4Skettenis  * Rockchip RK3288
682bd903cc4Skettenis  */
683bd903cc4Skettenis 
68437c734d3Snaddy const struct rkclock rk3288_clocks[] = {
685b1151de3Skettenis 	{
686b1151de3Skettenis 		RK3288_CLK_SDMMC, RK3288_CRU_CLKSEL_CON(11),
687b1151de3Skettenis 		SEL(7, 6), DIV(5, 0),
688b1151de3Skettenis 		{ RK3288_PLL_CPLL, RK3288_PLL_GPLL, RK3288_XIN24M }
689b1151de3Skettenis 	}
690b1151de3Skettenis };
691b1151de3Skettenis 
692bf9e0ff8Skettenis void
693bf9e0ff8Skettenis rk3288_init(struct rkclock_softc *sc)
694bf9e0ff8Skettenis {
695bf9e0ff8Skettenis 	int node;
696bf9e0ff8Skettenis 
697bf9e0ff8Skettenis 	/*
698bf9e0ff8Skettenis 	 * Since the hardware comes up with a really conservative CPU
699bf9e0ff8Skettenis 	 * clock frequency, and U-Boot doesn't set it to a more
700bf9e0ff8Skettenis 	 * reasonable default, try to do so here.  These defaults were
701bf9e0ff8Skettenis 	 * chosen assuming that the CPU voltage is at least 1.1 V.
702bf9e0ff8Skettenis 	 * Only do this on the Tinker-RK3288 for now where this is
703bf9e0ff8Skettenis 	 * likely to be true given the default voltages for the
704bf9e0ff8Skettenis 	 * regulators on that board.
705bf9e0ff8Skettenis 	 */
706bf9e0ff8Skettenis 	node = OF_finddevice("/");
707bf9e0ff8Skettenis 	if (OF_is_compatible(node, "rockchip,rk3288-tinker")) {
708bf9e0ff8Skettenis 		uint32_t idx;
709bf9e0ff8Skettenis 
710bf9e0ff8Skettenis 		/* Run at 1.2 GHz. */
711bf9e0ff8Skettenis 		idx = RK3288_ARMCLK;
712bf9e0ff8Skettenis 		rk3288_set_frequency(sc, &idx, 1200000000);
713bf9e0ff8Skettenis 	}
714b1151de3Skettenis 
715b1151de3Skettenis 	sc->sc_clocks = rk3288_clocks;
716bf9e0ff8Skettenis }
717bf9e0ff8Skettenis 
718bd903cc4Skettenis uint32_t
719bd903cc4Skettenis rk3288_get_pll(struct rkclock_softc *sc, bus_size_t base)
720bd903cc4Skettenis {
721bd903cc4Skettenis 	uint32_t clkod, clkr, clkf;
722bd903cc4Skettenis 	uint32_t reg;
723bd903cc4Skettenis 
724bd903cc4Skettenis 	reg = HREAD4(sc, base);
725bf9e0ff8Skettenis 	clkod = (reg & RK3288_CRU_PLL_CLKOD_MASK) >>
726bf9e0ff8Skettenis 	    RK3288_CRU_PLL_CLKOD_SHIFT;
727bf9e0ff8Skettenis 	clkr = (reg & RK3288_CRU_PLL_CLKR_MASK) >>
728bf9e0ff8Skettenis 	    RK3288_CRU_PLL_CLKR_SHIFT;
729bd903cc4Skettenis 	reg = HREAD4(sc, base + 4);
730bf9e0ff8Skettenis 	clkf = (reg & RK3288_CRU_PLL_CLKF_MASK) >>
731bf9e0ff8Skettenis 	    RK3288_CRU_PLL_CLKF_SHIFT;
732bd903cc4Skettenis 	return 24000000ULL * (clkf + 1) / (clkr + 1) / (clkod + 1);
733bd903cc4Skettenis }
734bd903cc4Skettenis 
735bf9e0ff8Skettenis int
736bf9e0ff8Skettenis rk3288_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
737bf9e0ff8Skettenis {
738bf9e0ff8Skettenis 	int shift = 4 * (base / RK3288_CRU_CPLL_CON(0));
739bf9e0ff8Skettenis 	uint32_t no, nr, nf;
740bf9e0ff8Skettenis 
741bf9e0ff8Skettenis 	/*
742bf9e0ff8Skettenis 	 * It is not clear whether all combinations of the clock
743bf9e0ff8Skettenis 	 * dividers result in a stable clock.  Therefore this function
744bf9e0ff8Skettenis 	 * only supports a limited set of PLL clock rates.  For now
745bf9e0ff8Skettenis 	 * this set covers all the CPU frequencies supported by the
746bf9e0ff8Skettenis 	 * Linux kernel.
747bf9e0ff8Skettenis 	 */
748bf9e0ff8Skettenis 	switch (freq) {
749bf9e0ff8Skettenis 	case 1800000000:
750bf9e0ff8Skettenis 	case 1704000000:
751bf9e0ff8Skettenis 	case 1608000000:
752bf9e0ff8Skettenis 	case 1512000000:
753bf9e0ff8Skettenis 	case 1488000000:
754bf9e0ff8Skettenis 	case 1416000000:
755bf9e0ff8Skettenis 	case 1200000000:
756bf9e0ff8Skettenis 		nr = no = 1;
757bf9e0ff8Skettenis 		break;
758bf9e0ff8Skettenis 	case 1008000000:
759bf9e0ff8Skettenis 	case 816000000:
760bf9e0ff8Skettenis 	case 696000000:
761bf9e0ff8Skettenis 	case 600000000:
762bf9e0ff8Skettenis 		nr = 1; no = 2;
763bf9e0ff8Skettenis 		break;
764bf9e0ff8Skettenis 	case 408000000:
765bf9e0ff8Skettenis 	case 312000000:
766bf9e0ff8Skettenis 		nr = 1; no = 4;
767bf9e0ff8Skettenis 		break;
768bf9e0ff8Skettenis 	case 216000000:
769bf9e0ff8Skettenis 	case 126000000:
770bf9e0ff8Skettenis 		nr = 1; no = 8;
771bf9e0ff8Skettenis 		break;
772bf9e0ff8Skettenis 	default:
7739822ab30Skettenis 		printf("%s: %u Hz\n", __func__, freq);
774bf9e0ff8Skettenis 		return -1;
775bf9e0ff8Skettenis 	}
776bf9e0ff8Skettenis 
777bf9e0ff8Skettenis 	/* Calculate feedback divider. */
778bf9e0ff8Skettenis 	nf = freq * nr * no / 24000000;
779bf9e0ff8Skettenis 
780bf9e0ff8Skettenis 	/*
781bf9e0ff8Skettenis 	 * Select slow mode to guarantee a stable clock while we're
782bf9e0ff8Skettenis 	 * adjusting the PLL.
783bf9e0ff8Skettenis 	 */
784bf9e0ff8Skettenis 	HWRITE4(sc, RK3288_CRU_MODE_CON,
785bf9e0ff8Skettenis 	    (RK3288_CRU_MODE_PLL_WORK_MODE_MASK << 16 |
786bf9e0ff8Skettenis 	     RK3288_CRU_MODE_PLL_WORK_MODE_SLOW) << shift);
787bf9e0ff8Skettenis 
788bf9e0ff8Skettenis 	/* Assert reset. */
789bf9e0ff8Skettenis 	HWRITE4(sc, base + 0x000c,
790bf9e0ff8Skettenis 	    RK3288_CRU_PLL_RESET << 16 | RK3288_CRU_PLL_RESET);
791bf9e0ff8Skettenis 
792bf9e0ff8Skettenis 	/* Set PLL rate. */
793bf9e0ff8Skettenis 	HWRITE4(sc, base + 0x0000,
794bf9e0ff8Skettenis 	    RK3288_CRU_PLL_CLKR_MASK << 16 |
795bf9e0ff8Skettenis 	    (nr - 1) << RK3288_CRU_PLL_CLKR_SHIFT |
796bf9e0ff8Skettenis 	    RK3288_CRU_PLL_CLKOD_MASK << 16 |
797bf9e0ff8Skettenis 	    (no - 1) << RK3288_CRU_PLL_CLKOD_SHIFT);
798bf9e0ff8Skettenis 	HWRITE4(sc, base + 0x0004,
799bf9e0ff8Skettenis 	    RK3288_CRU_PLL_CLKF_MASK << 16 |
800bf9e0ff8Skettenis 	    (nf - 1) << RK3288_CRU_PLL_CLKF_SHIFT);
801bf9e0ff8Skettenis 
802bf9e0ff8Skettenis 	/* Deassert reset and wait. */
803bf9e0ff8Skettenis 	HWRITE4(sc, base + 0x000c,
804bf9e0ff8Skettenis 	    RK3288_CRU_PLL_RESET << 16);
805bf9e0ff8Skettenis 	delay((nr * 500 / 24) + 1);
806bf9e0ff8Skettenis 
807bf9e0ff8Skettenis 	/* Switch back to normal mode. */
808bf9e0ff8Skettenis 	HWRITE4(sc, RK3288_CRU_MODE_CON,
809bf9e0ff8Skettenis 	    (RK3288_CRU_MODE_PLL_WORK_MODE_MASK << 16 |
810bf9e0ff8Skettenis 	     RK3288_CRU_MODE_PLL_WORK_MODE_NORMAL) << shift);
811bf9e0ff8Skettenis 
812bf9e0ff8Skettenis 	return 0;
813bf9e0ff8Skettenis }
814bf9e0ff8Skettenis 
815bd903cc4Skettenis uint32_t
816bd903cc4Skettenis rk3288_get_frequency(void *cookie, uint32_t *cells)
817bd903cc4Skettenis {
818bd903cc4Skettenis 	struct rkclock_softc *sc = cookie;
819bd903cc4Skettenis 	uint32_t idx = cells[0];
8204764d29aSjsg 	uint32_t reg, mux, div_con, aclk_div_con;
821bd903cc4Skettenis 
822bd903cc4Skettenis 	switch (idx) {
823227c6ee3Skettenis 	case RK3288_PLL_APLL:
824227c6ee3Skettenis 		return rk3288_get_pll(sc, RK3288_CRU_APLL_CON(0));
825bd903cc4Skettenis 	case RK3288_PLL_CPLL:
826bd903cc4Skettenis 		return rk3288_get_pll(sc, RK3288_CRU_CPLL_CON(0));
827bd903cc4Skettenis 	case RK3288_PLL_GPLL:
828bd903cc4Skettenis 		return rk3288_get_pll(sc, RK3288_CRU_GPLL_CON(0));
82972f108a7Skettenis 	case RK3288_PLL_NPLL:
83072f108a7Skettenis 		return rk3288_get_pll(sc, RK3288_CRU_NPLL_CON(0));
831227c6ee3Skettenis 	case RK3288_ARMCLK:
832227c6ee3Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(0));
833227c6ee3Skettenis 		mux = (reg >> 15) & 0x1;
834227c6ee3Skettenis 		div_con = (reg >> 8) & 0x1f;
835bf9e0ff8Skettenis 		idx = (mux == 0) ? RK3288_PLL_APLL : RK3288_PLL_GPLL;
836227c6ee3Skettenis 		return rk3288_get_frequency(sc, &idx) / (div_con + 1);
837b1151de3Skettenis 	case RK3288_XIN24M:
838b1151de3Skettenis 		return 24000000;
839bd903cc4Skettenis 	case RK3288_CLK_UART0:
840bd903cc4Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(13));
841bd903cc4Skettenis 		mux = (reg >> 8) & 0x3;
842bd903cc4Skettenis 		div_con = reg & 0x7f;
843bd903cc4Skettenis 		if (mux == 2)
844bd903cc4Skettenis 			return 24000000 / (div_con + 1);
845bd903cc4Skettenis 		break;
846bd903cc4Skettenis 	case RK3288_CLK_UART1:
847bd903cc4Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(14));
848bd903cc4Skettenis 		mux = (reg >> 8) & 0x3;
849bd903cc4Skettenis 		div_con = reg & 0x7f;
850bd903cc4Skettenis 		if (mux == 2)
851bd903cc4Skettenis 			return 24000000 / (div_con + 1);
852bd903cc4Skettenis 		break;
853bd903cc4Skettenis 	case RK3288_CLK_UART2:
854bd903cc4Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(15));
855bd903cc4Skettenis 		mux = (reg >> 8) & 0x3;
856bd903cc4Skettenis 		div_con = reg & 0x7f;
857bd903cc4Skettenis 		if (mux == 2)
858bd903cc4Skettenis 			return 24000000 / (div_con + 1);
859bd903cc4Skettenis 		break;
860bd903cc4Skettenis 	case RK3288_CLK_UART3:
861bd903cc4Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(16));
862bd903cc4Skettenis 		mux = (reg >> 8) & 0x3;
863bd903cc4Skettenis 		div_con = reg & 0x7f;
864bd903cc4Skettenis 		if (mux == 2)
865bd903cc4Skettenis 			return 24000000 / (div_con + 1);
866bd903cc4Skettenis 		break;
867bd903cc4Skettenis 	case RK3288_CLK_UART4:
868bd903cc4Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(3));
869bd903cc4Skettenis 		mux = (reg >> 8) & 0x3;
870bd903cc4Skettenis 		div_con = reg & 0x7f;
871bd903cc4Skettenis 		if (mux == 2)
872bd903cc4Skettenis 			return 24000000 / (div_con + 1);
873bd903cc4Skettenis 		break;
87472f108a7Skettenis 	case RK3288_CLK_MAC:
87572f108a7Skettenis 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(21));
87672f108a7Skettenis 		if (reg & 0x10)
877ab8e3451Sderaadt 			return 125000000;
87872f108a7Skettenis 		mux = (reg >> 0) & 0x3;
87972f108a7Skettenis 		div_con = (reg >> 8) & 0x1f;
88072f108a7Skettenis 		switch (mux) {
88172f108a7Skettenis 		case 0:
88272f108a7Skettenis 			idx = RK3288_PLL_NPLL;
88372f108a7Skettenis 			break;
88472f108a7Skettenis 		case 1:
88572f108a7Skettenis 			idx = RK3288_PLL_CPLL;
88672f108a7Skettenis 			break;
88772f108a7Skettenis 		case 2:
88872f108a7Skettenis 			idx = RK3288_PLL_GPLL;
88972f108a7Skettenis 			break;
89072f108a7Skettenis 		default:
89172f108a7Skettenis 			return 0;
89272f108a7Skettenis 		}
89372f108a7Skettenis 		return rk3288_get_frequency(sc, &idx) / (div_con + 1);
8944764d29aSjsg 	case RK3288_PCLK_I2C0:
8954764d29aSjsg 	case RK3288_PCLK_I2C2:
8964764d29aSjsg 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(1));
8974764d29aSjsg 		mux = (reg >> 15) & 0x1;
8984764d29aSjsg 		/* pd_bus_pclk_div_con */
8994764d29aSjsg 		div_con = (reg >> 12) & 0x7;
9004764d29aSjsg 		if (mux == 1)
9014764d29aSjsg 			idx = RK3288_PLL_GPLL;
9024764d29aSjsg 		else
9034764d29aSjsg 			idx = RK3288_PLL_CPLL;
9044764d29aSjsg 		return rk3288_get_frequency(sc, &idx) / (div_con + 1);
9054764d29aSjsg 	case RK3288_PCLK_I2C1:
9064764d29aSjsg 	case RK3288_PCLK_I2C3:
9074764d29aSjsg 	case RK3288_PCLK_I2C4:
9084764d29aSjsg 	case RK3288_PCLK_I2C5:
9094764d29aSjsg 		reg = HREAD4(sc, RK3288_CRU_CLKSEL_CON(10));
9104764d29aSjsg 		mux = (reg >> 15) & 0x1;
9114764d29aSjsg 		/* peri_pclk_div_con */
9124764d29aSjsg 		div_con = (reg >> 12) & 0x3;
9134764d29aSjsg 		/* peri_aclk_div_con */
9144764d29aSjsg 		aclk_div_con = reg & 0xf;
9154764d29aSjsg 		if (mux == 1)
9164764d29aSjsg 			idx = RK3288_PLL_GPLL;
9174764d29aSjsg 		else
9184764d29aSjsg 			idx = RK3288_PLL_CPLL;
9194764d29aSjsg 		return (rk3288_get_frequency(sc, &idx) / (aclk_div_con + 1)) >>
9204764d29aSjsg 		    div_con;
921bd903cc4Skettenis 	default:
922bd903cc4Skettenis 		break;
923bd903cc4Skettenis 	}
924bd903cc4Skettenis 
925b1151de3Skettenis 	return rkclock_get_frequency(sc, idx);
926bd903cc4Skettenis }
927bd903cc4Skettenis 
928bd903cc4Skettenis int
929bd903cc4Skettenis rk3288_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
930bd903cc4Skettenis {
931bf9e0ff8Skettenis 	struct rkclock_softc *sc = cookie;
932bd903cc4Skettenis 	uint32_t idx = cells[0];
933bf9e0ff8Skettenis 	int error;
934bf9e0ff8Skettenis 
935bf9e0ff8Skettenis 	switch (idx) {
936bf9e0ff8Skettenis 	case RK3288_PLL_APLL:
937bf9e0ff8Skettenis 		return rk3288_set_pll(sc, RK3288_CRU_APLL_CON(0), freq);
938bf9e0ff8Skettenis 	case RK3288_ARMCLK:
939bf9e0ff8Skettenis 		idx = RK3288_PLL_APLL;
940bf9e0ff8Skettenis 		error = rk3288_set_frequency(sc, &idx, freq);
941bf9e0ff8Skettenis 		if (error == 0) {
942bf9e0ff8Skettenis 			HWRITE4(sc, RK3288_CRU_CLKSEL_CON(0),
943bf9e0ff8Skettenis 			    ((1 << 15) | (0x1f << 8)) << 16);
944bf9e0ff8Skettenis 		}
945bf9e0ff8Skettenis 		return error;
946b1151de3Skettenis 	default:
947b1151de3Skettenis 		break;
948bf9e0ff8Skettenis 	}
949bd903cc4Skettenis 
950b1151de3Skettenis 	return rkclock_set_frequency(sc, idx, freq);
951bd903cc4Skettenis }
952bd903cc4Skettenis 
953bd903cc4Skettenis void
954bd903cc4Skettenis rk3288_enable(void *cookie, uint32_t *cells, int on)
955bd903cc4Skettenis {
956bd903cc4Skettenis 	uint32_t idx = cells[0];
957bd903cc4Skettenis 
958bd903cc4Skettenis 	switch (idx) {
959bd903cc4Skettenis 	case RK3288_CLK_SDMMC:
96049b48721Skettenis 	case RK3288_CLK_TSADC:
961bd903cc4Skettenis 	case RK3288_CLK_UART0:
962bd903cc4Skettenis 	case RK3288_CLK_UART1:
963bd903cc4Skettenis 	case RK3288_CLK_UART2:
964bd903cc4Skettenis 	case RK3288_CLK_UART3:
965bd903cc4Skettenis 	case RK3288_CLK_UART4:
966e721b2d9Skettenis 	case RK3288_CLK_MAC_RX:
967e721b2d9Skettenis 	case RK3288_CLK_MAC_TX:
968bd903cc4Skettenis 	case RK3288_CLK_SDMMC_DRV:
969bd903cc4Skettenis 	case RK3288_CLK_SDMMC_SAMPLE:
970e721b2d9Skettenis 	case RK3288_CLK_MAC:
971e721b2d9Skettenis 	case RK3288_ACLK_GMAC:
972e721b2d9Skettenis 	case RK3288_PCLK_GMAC:
9734764d29aSjsg 	case RK3288_PCLK_I2C0:
9744764d29aSjsg 	case RK3288_PCLK_I2C1:
9754764d29aSjsg 	case RK3288_PCLK_I2C2:
9764764d29aSjsg 	case RK3288_PCLK_I2C3:
9774764d29aSjsg 	case RK3288_PCLK_I2C4:
9784764d29aSjsg 	case RK3288_PCLK_I2C5:
97949b48721Skettenis 	case RK3288_PCLK_TSADC:
980bd903cc4Skettenis 	case RK3288_HCLK_HOST0:
981bd903cc4Skettenis 	case RK3288_HCLK_SDMMC:
982bd903cc4Skettenis 		/* Enabled by default. */
983bd903cc4Skettenis 		break;
984bd903cc4Skettenis 	default:
985bd903cc4Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
986bd903cc4Skettenis 		break;
987bd903cc4Skettenis 	}
988bd903cc4Skettenis }
989bd903cc4Skettenis 
990bd903cc4Skettenis void
991bd903cc4Skettenis rk3288_reset(void *cookie, uint32_t *cells, int on)
992bd903cc4Skettenis {
99349b48721Skettenis 	struct rkclock_softc *sc = cookie;
994bd903cc4Skettenis 	uint32_t idx = cells[0];
99549b48721Skettenis 	uint32_t mask = (1 << (idx % 16));
996bd903cc4Skettenis 
99749b48721Skettenis 	HWRITE4(sc, RK3288_CRU_SOFTRST_CON(idx / 16),
99849b48721Skettenis 	    mask << 16 | (on ? mask : 0));
999bd903cc4Skettenis }
1000bd903cc4Skettenis 
1001bd903cc4Skettenis /*
10021235808cSjmatthew  * Rockchip RK3308
10031235808cSjmatthew  */
10041235808cSjmatthew 
100537c734d3Snaddy const struct rkclock rk3308_clocks[] = {
10061235808cSjmatthew 	{
10071235808cSjmatthew 		RK3308_CLK_RTC32K, RK3308_CRU_CLKSEL_CON(2),
10081235808cSjmatthew 		SEL(10, 9), 0,
10091235808cSjmatthew 		{ RK3308_PLL_VPLL0, RK3308_PLL_VPLL1 }
10101235808cSjmatthew 	},
10111235808cSjmatthew 	{
10121235808cSjmatthew 		RK3308_CLK_UART0, RK3308_CRU_CLKSEL_CON(10),
10131235808cSjmatthew 		SEL(15, 13), DIV(4, 0),
10141235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10151235808cSjmatthew 		  RK3308_USB480M, RK3308_XIN24M }
10161235808cSjmatthew 	},
10171235808cSjmatthew 	{
10181235808cSjmatthew 		RK3308_CLK_UART1, RK3308_CRU_CLKSEL_CON(13),
10191235808cSjmatthew 		SEL(15, 13), DIV(4, 0),
10201235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10211235808cSjmatthew 		  RK3308_USB480M, RK3308_XIN24M }
10221235808cSjmatthew 	},
10231235808cSjmatthew 	{
10241235808cSjmatthew 		RK3308_CLK_UART2, RK3308_CRU_CLKSEL_CON(16),
10251235808cSjmatthew 		SEL(15, 13), DIV(4, 0),
10261235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10271235808cSjmatthew 		  RK3308_USB480M, RK3308_XIN24M }
10281235808cSjmatthew 	},
10291235808cSjmatthew 	{
10301235808cSjmatthew 		RK3308_CLK_UART3, RK3308_CRU_CLKSEL_CON(19),
10311235808cSjmatthew 		SEL(15, 13), DIV(4, 0),
10321235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10331235808cSjmatthew 		  RK3308_USB480M, RK3308_XIN24M }
10341235808cSjmatthew 	},
10351235808cSjmatthew 	{
10361235808cSjmatthew 		RK3308_CLK_UART4, RK3308_CRU_CLKSEL_CON(22),
10371235808cSjmatthew 		SEL(15, 13), DIV(4, 0),
10381235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10391235808cSjmatthew 		  RK3308_USB480M, RK3308_XIN24M }
10401235808cSjmatthew 	},
10411235808cSjmatthew 	{
10421235808cSjmatthew 		RK3308_CLK_PWM0, RK3308_CRU_CLKSEL_CON(29),
10431235808cSjmatthew 		SEL(15, 14), DIV(6, 0),
10441235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_XIN24M }
10451235808cSjmatthew 	},
10461235808cSjmatthew 	{
104717449d5cSkettenis 		RK3308_CLK_SPI0, RK3308_CRU_CLKSEL_CON(30),
104817449d5cSkettenis 		SEL(15, 14), DIV(6, 0),
104917449d5cSkettenis 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_XIN24M }
105017449d5cSkettenis 	},
105117449d5cSkettenis 	{
105217449d5cSkettenis 		RK3308_CLK_SPI1, RK3308_CRU_CLKSEL_CON(31),
105317449d5cSkettenis 		SEL(15, 14), DIV(6, 0),
105417449d5cSkettenis 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_XIN24M }
105517449d5cSkettenis 	},
105617449d5cSkettenis 	{
105717449d5cSkettenis 		RK3308_CLK_SPI2, RK3308_CRU_CLKSEL_CON(32),
105817449d5cSkettenis 		SEL(15, 14), DIV(6, 0),
105917449d5cSkettenis 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_XIN24M }
106017449d5cSkettenis 	},
106117449d5cSkettenis 	{
10621235808cSjmatthew 		RK3308_CLK_TSADC, RK3308_CRU_CLKSEL_CON(33),
10631235808cSjmatthew 		0, DIV(10, 0),
10641235808cSjmatthew 		{ RK3308_XIN24M }
10651235808cSjmatthew 	},
10661235808cSjmatthew 	{
10671235808cSjmatthew 		RK3308_CLK_SARADC, RK3308_CRU_CLKSEL_CON(34),
10681235808cSjmatthew 		0, DIV(10, 0),
10691235808cSjmatthew 		{ RK3308_XIN24M }
10701235808cSjmatthew 	},
10711235808cSjmatthew 	{
10721235808cSjmatthew 		RK3308_CLK_CRYPTO, RK3308_CRU_CLKSEL_CON(7),
10731235808cSjmatthew 		SEL(7, 6), DIV(4, 0),
10741235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1, 0 }
10751235808cSjmatthew 	},
10761235808cSjmatthew 	{
10771235808cSjmatthew 		RK3308_CLK_CRYPTO_APK, RK3308_CRU_CLKSEL_CON(7),
10781235808cSjmatthew 		SEL(15, 14), DIV(12, 8),
10791235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1, 0 }
10801235808cSjmatthew 	},
10811235808cSjmatthew 	{
10821235808cSjmatthew 		RK3308_CLK_SDMMC, RK3308_CRU_CLKSEL_CON(39),
10831235808cSjmatthew 		SEL(9, 8), DIV(7, 0),
10841235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10851235808cSjmatthew 		  RK3308_XIN24M }
10861235808cSjmatthew 	},
10871235808cSjmatthew 	{
10881235808cSjmatthew 		RK3308_CLK_SDIO, RK3308_CRU_CLKSEL_CON(40),
10891235808cSjmatthew 		SEL(9, 8), DIV(7, 0),
10901235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10911235808cSjmatthew 		  RK3308_XIN24M }
10921235808cSjmatthew 	},
10931235808cSjmatthew 	{
10941235808cSjmatthew 		RK3308_CLK_EMMC, RK3308_CRU_CLKSEL_CON(41),
10951235808cSjmatthew 		SEL(9, 8), DIV(7, 0),
10961235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1,
10971235808cSjmatthew 		  RK3308_XIN24M }
10981235808cSjmatthew 	},
10991235808cSjmatthew 	{
11001235808cSjmatthew 		RK3308_CLK_MAC_SRC, RK3308_CRU_CLKSEL_CON(43),
11011235808cSjmatthew 		SEL(7, 6), DIV(4, 0),
11021235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1, 0 }
11031235808cSjmatthew 	},
11041235808cSjmatthew 	{
11051235808cSjmatthew 		RK3308_CLK_MAC, RK3308_CRU_CLKSEL_CON(43),
11061235808cSjmatthew 		SEL(14, 13), 0,
11071235808cSjmatthew 		{ RK3308_CLK_MAC_SRC, 0 },
11081235808cSjmatthew 		SET_PARENT
11091235808cSjmatthew 	},
11101235808cSjmatthew 	{
11111235808cSjmatthew 		RK3308_ACLK_PERI_SRC, RK3308_CRU_CLKSEL_CON(36),
11121235808cSjmatthew 		SEL(7, 6), 0,
11131235808cSjmatthew 		{ RK3308_PLL_DPLL, RK3308_PLL_VPLL0, RK3308_PLL_VPLL1, 0 }
11141235808cSjmatthew 	},
11151235808cSjmatthew 	{
11161235808cSjmatthew 		RK3308_PCLK_PERI, RK3308_CRU_CLKSEL_CON(37),
11171235808cSjmatthew 		0, DIV(12, 8),
11181235808cSjmatthew 		{ RK3308_ACLK_PERI_SRC }
11191235808cSjmatthew 	},
11201235808cSjmatthew 	{
11211235808cSjmatthew 		RK3308_PCLK_MAC, 0, 0, 0,
11221235808cSjmatthew 		{ RK3308_PCLK_PERI }
11231235808cSjmatthew 	},
11241235808cSjmatthew 
11251235808cSjmatthew 	{
11261235808cSjmatthew 		/* Sentinel */
11271235808cSjmatthew 	}
11281235808cSjmatthew };
11291235808cSjmatthew 
11301235808cSjmatthew void
11311235808cSjmatthew rk3308_init(struct rkclock_softc *sc)
11321235808cSjmatthew {
11331235808cSjmatthew 	int i;
11341235808cSjmatthew 
11351235808cSjmatthew 	/* The code below assumes all clocks are enabled.  Check this!. */
11361235808cSjmatthew 	for (i = 0; i <= 14; i++) {
11371235808cSjmatthew 		if (HREAD4(sc, RK3308_CRU_CLKGATE_CON(i)) != 0x00000000) {
11381235808cSjmatthew 			printf("CRU_CLKGATE_CON%d: 0x%08x\n", i,
11391235808cSjmatthew 			    HREAD4(sc, RK3308_CRU_CLKGATE_CON(i)));
11401235808cSjmatthew 		}
11411235808cSjmatthew 	}
11421235808cSjmatthew 	sc->sc_clocks = rk3308_clocks;
11431235808cSjmatthew }
11441235808cSjmatthew 
11451235808cSjmatthew uint32_t
11461235808cSjmatthew rk3308_armclk_parent(uint32_t mux)
11471235808cSjmatthew {
11481235808cSjmatthew 	switch (mux) {
11491235808cSjmatthew 	case 0:
11501235808cSjmatthew 		return RK3308_PLL_APLL;
11511235808cSjmatthew 	case 1:
11521235808cSjmatthew 		return RK3308_PLL_VPLL0;
11531235808cSjmatthew 	case 2:
11541235808cSjmatthew 		return RK3308_PLL_VPLL1;
11551235808cSjmatthew 	}
11561235808cSjmatthew 
11571235808cSjmatthew 	return 0;
11581235808cSjmatthew }
11591235808cSjmatthew 
11601235808cSjmatthew uint32_t
11611235808cSjmatthew rk3308_get_armclk(struct rkclock_softc *sc)
11621235808cSjmatthew {
11631235808cSjmatthew 	uint32_t reg, mux, div_con;
11641235808cSjmatthew 	uint32_t idx;
11651235808cSjmatthew 
11661235808cSjmatthew 	reg = HREAD4(sc, RK3308_CRU_CLKSEL_CON(0));
11671235808cSjmatthew 	mux = (reg & RK3308_CRU_CORE_CLK_PLL_SEL_MASK) >>
11681235808cSjmatthew 	    RK3308_CRU_CORE_CLK_PLL_SEL_SHIFT;
11691235808cSjmatthew 	div_con = (reg & RK3308_CRU_CLK_CORE_DIV_CON_MASK) >>
11701235808cSjmatthew 	    RK3308_CRU_CLK_CORE_DIV_CON_SHIFT;
11711235808cSjmatthew 	idx = rk3308_armclk_parent(mux);
11721235808cSjmatthew 
11731235808cSjmatthew 	return rk3308_get_frequency(sc, &idx) / (div_con + 1);
11741235808cSjmatthew }
11751235808cSjmatthew 
11761235808cSjmatthew int
11771235808cSjmatthew rk3308_set_armclk(struct rkclock_softc *sc, uint32_t freq)
11781235808cSjmatthew {
11791235808cSjmatthew 	uint32_t reg, mux;
11801235808cSjmatthew 	uint32_t old_freq, div;
11811235808cSjmatthew 	uint32_t idx;
11821235808cSjmatthew 
11831235808cSjmatthew 	old_freq = rk3308_get_armclk(sc);
11841235808cSjmatthew 	if (freq == old_freq)
11851235808cSjmatthew 		return 0;
11861235808cSjmatthew 
11871235808cSjmatthew 	reg = HREAD4(sc, RK3308_CRU_CLKSEL_CON(0));
11881235808cSjmatthew 	mux = (reg & RK3308_CRU_CORE_CLK_PLL_SEL_MASK) >>
11891235808cSjmatthew 	    RK3308_CRU_CORE_CLK_PLL_SEL_SHIFT;
11901235808cSjmatthew 
11911235808cSjmatthew 	/* Keep the pclk_dbg clock at or below 300 MHz. */
11921235808cSjmatthew 	div = 1;
11931235808cSjmatthew 	while (freq / (div + 1) > 300000000)
11941235808cSjmatthew 		div++;
11951235808cSjmatthew 	/* and make sure we use an odd divider. */
11961235808cSjmatthew 	if ((div % 2) == 0)
11971235808cSjmatthew 		div++;
11981235808cSjmatthew 
11991235808cSjmatthew 	/* When ramping up, set clock dividers first. */
12001235808cSjmatthew 	if (freq > old_freq) {
12011235808cSjmatthew 		HWRITE4(sc, RK3308_CRU_CLKSEL_CON(0),
12021235808cSjmatthew 		    RK3308_CRU_CLK_CORE_DIV_CON_MASK << 16 |
12031235808cSjmatthew 		    0 << RK3308_CRU_CLK_CORE_DIV_CON_SHIFT |
12041235808cSjmatthew 		    RK3308_CRU_ACLK_CORE_DIV_CON_MASK << 16 |
12051235808cSjmatthew 		    1 << RK3308_CRU_ACLK_CORE_DIV_CON_SHIFT |
12061235808cSjmatthew 		    RK3308_CRU_CLK_CORE_DBG_DIV_CON_MASK << 16 |
12071235808cSjmatthew 		    div << RK3308_CRU_CLK_CORE_DBG_DIV_CON_SHIFT);
12081235808cSjmatthew 	}
12091235808cSjmatthew 
12101235808cSjmatthew 	/* We always use VPLL1 and force the switch below if needed. */
12111235808cSjmatthew 	idx = RK3308_PLL_VPLL1;
12121235808cSjmatthew 	rk3308_set_frequency(sc, &idx, freq);
12131235808cSjmatthew 
12141235808cSjmatthew 	/* When ramping down, set clock dividers last. */
12151235808cSjmatthew 	if (freq < old_freq || mux != 2) {
12161235808cSjmatthew 		HWRITE4(sc, RK3308_CRU_CLKSEL_CON(0),
12171235808cSjmatthew 		    RK3308_CRU_CORE_CLK_PLL_SEL_MASK << 16 |
12181235808cSjmatthew 		    2 << RK3308_CRU_CORE_CLK_PLL_SEL_SHIFT |
12191235808cSjmatthew 		    RK3308_CRU_CLK_CORE_DIV_CON_MASK << 16 |
12201235808cSjmatthew 		    0 << RK3308_CRU_CLK_CORE_DIV_CON_SHIFT |
12211235808cSjmatthew 		    RK3308_CRU_ACLK_CORE_DIV_CON_MASK << 16 |
12221235808cSjmatthew 		    1 << RK3308_CRU_ACLK_CORE_DIV_CON_SHIFT |
12231235808cSjmatthew 		    RK3308_CRU_CLK_CORE_DBG_DIV_CON_MASK << 16 |
12241235808cSjmatthew 		    div << RK3308_CRU_CLK_CORE_DBG_DIV_CON_SHIFT);
12251235808cSjmatthew 	}
12261235808cSjmatthew 
12271235808cSjmatthew 	return 0;
12281235808cSjmatthew }
12291235808cSjmatthew 
12301235808cSjmatthew uint32_t
12311235808cSjmatthew rk3308_get_pll(struct rkclock_softc *sc, bus_size_t base)
12321235808cSjmatthew {
12331235808cSjmatthew 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
12341235808cSjmatthew 	uint32_t dsmpd, fracdiv;
12351235808cSjmatthew 	uint64_t frac = 0;
12361235808cSjmatthew 	uint32_t reg;
12371235808cSjmatthew 
12381235808cSjmatthew 	reg = HREAD4(sc, base + 0x0000);
12391235808cSjmatthew 	postdiv1 = (reg & RK3308_CRU_PLL_POSTDIV1_MASK) >>
12401235808cSjmatthew 	    RK3308_CRU_PLL_POSTDIV1_SHIFT;
12411235808cSjmatthew 	fbdiv = (reg & RK3308_CRU_PLL_FBDIV_MASK) >>
12421235808cSjmatthew 	    RK3308_CRU_PLL_FBDIV_SHIFT;
12431235808cSjmatthew 	reg = HREAD4(sc, base + 0x0004);
12441235808cSjmatthew 	dsmpd = (reg & RK3308_CRU_PLL_DSMPD);
12451235808cSjmatthew 	postdiv2 = (reg & RK3308_CRU_PLL_POSTDIV2_MASK) >>
12461235808cSjmatthew 	    RK3308_CRU_PLL_POSTDIV2_SHIFT;
12471235808cSjmatthew 	refdiv = (reg & RK3308_CRU_PLL_REFDIV_MASK) >>
12481235808cSjmatthew 	    RK3308_CRU_PLL_REFDIV_SHIFT;
12491235808cSjmatthew 	reg = HREAD4(sc, base + 0x0008);
12501235808cSjmatthew 	fracdiv = (reg & RK3308_CRU_PLL_FRACDIV_MASK) >>
12511235808cSjmatthew 	    RK3308_CRU_PLL_FRACDIV_SHIFT;
12521235808cSjmatthew 
12531235808cSjmatthew 	if (dsmpd == 0)
12541235808cSjmatthew 		frac = (24000000ULL * fracdiv / refdiv) >> 24;
12551235808cSjmatthew 	return ((24000000ULL * fbdiv / refdiv) + frac) / postdiv1 / postdiv2;
12561235808cSjmatthew }
12571235808cSjmatthew 
12581235808cSjmatthew int
12591235808cSjmatthew rk3308_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
12601235808cSjmatthew {
12611235808cSjmatthew 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
12621235808cSjmatthew 	int mode_shift = -1;
12631235808cSjmatthew 
12641235808cSjmatthew 	switch (base) {
12651235808cSjmatthew 	case RK3308_CRU_APLL_CON(0):
12661235808cSjmatthew 		mode_shift = 0;
12671235808cSjmatthew 		break;
12681235808cSjmatthew 	case RK3308_CRU_DPLL_CON(0):
12691235808cSjmatthew 		mode_shift = 2;
12701235808cSjmatthew 		break;
12711235808cSjmatthew 	case RK3308_CRU_VPLL0_CON(0):
12721235808cSjmatthew 		mode_shift = 4;
12731235808cSjmatthew 		break;
12741235808cSjmatthew 	case RK3308_CRU_VPLL1_CON(0):
12751235808cSjmatthew 		mode_shift = 6;
12761235808cSjmatthew 		break;
12771235808cSjmatthew 	}
12781235808cSjmatthew 	KASSERT(mode_shift != -1);
12791235808cSjmatthew 
12801235808cSjmatthew 	/*
12811235808cSjmatthew 	 * It is not clear whether all combinations of the clock
12821235808cSjmatthew 	 * dividers result in a stable clock.  Therefore this function
12831235808cSjmatthew 	 * only supports a limited set of PLL clock rates.  For now
12841235808cSjmatthew 	 * this set covers all the CPU frequencies supported by the
12851235808cSjmatthew 	 * Linux kernel.
12861235808cSjmatthew 	 */
12871235808cSjmatthew 	switch (freq) {
12881235808cSjmatthew 	case 1608000000U:
12891235808cSjmatthew 	case 1584000000U:
12901235808cSjmatthew 	case 1560000000U:
12911235808cSjmatthew 	case 1536000000U:
12921235808cSjmatthew 	case 1512000000U:
12931235808cSjmatthew 	case 1488000000U:
12941235808cSjmatthew 	case 1464000000U:
12951235808cSjmatthew 	case 1440000000U:
12961235808cSjmatthew 	case 1416000000U:
12971235808cSjmatthew 	case 1392000000U:
12981235808cSjmatthew 	case 1368000000U:
12991235808cSjmatthew 	case 1344000000U:
13001235808cSjmatthew 	case 1320000000U:
13011235808cSjmatthew 	case 1296000000U:
13021235808cSjmatthew 	case 1272000000U:
13031235808cSjmatthew 	case 1248000000U:
13041235808cSjmatthew 	case 1200000000U:
13051235808cSjmatthew 	case 1104000000U:
13061235808cSjmatthew 		postdiv1 = postdiv2 = refdiv = 1;
13071235808cSjmatthew 		break;
13081235808cSjmatthew 	case 1188000000U:
13091235808cSjmatthew 		refdiv = 2; postdiv1 = postdiv2 = 1;
13101235808cSjmatthew 		break;
13111235808cSjmatthew 	case 1100000000U:
13121235808cSjmatthew 		refdiv = 12; postdiv1 = postdiv2 = 1;
13131235808cSjmatthew 		break;
13141235808cSjmatthew 	case 1000000000U:
13151235808cSjmatthew 		refdiv = 6; postdiv1 = postdiv2 = 1;
13161235808cSjmatthew 		break;
13171235808cSjmatthew 	case 1008000000U:
13181235808cSjmatthew 	case 984000000U:
13191235808cSjmatthew 	case 960000000U:
13201235808cSjmatthew 	case 936000000U:
13211235808cSjmatthew 	case 912000000U:
13221235808cSjmatthew 	case 888000000U:
13231235808cSjmatthew 	case 864000000U:
13241235808cSjmatthew 	case 840000000U:
13251235808cSjmatthew 	case 816000000U:
13261235808cSjmatthew 	case 696000000U:
13271235808cSjmatthew 	case 624000000U:
13281235808cSjmatthew 		postdiv1 = 2; postdiv2 = refdiv = 1;
13291235808cSjmatthew 		break;
13301235808cSjmatthew 	case 900000000U:
13311235808cSjmatthew 		refdiv = 4; postdiv1 = 2; postdiv2 = 1;
13321235808cSjmatthew 		break;
13331235808cSjmatthew 	case 800000000U:
13341235808cSjmatthew 	case 700000000U:
13351235808cSjmatthew 	case 500000000U:
13361235808cSjmatthew 		refdiv = 6; postdiv1 = 2; postdiv2 = 1;
13371235808cSjmatthew 		break;
13381235808cSjmatthew 	case 600000000U:
13391235808cSjmatthew 	case 504000000U:
13401235808cSjmatthew 		postdiv1 = 3; postdiv2 = refdiv = 1;
13411235808cSjmatthew 		break;
13421235808cSjmatthew 	case 594000000U:
13431235808cSjmatthew 		refdiv = 2; postdiv1 = 2; postdiv2 = 1;
13441235808cSjmatthew 		break;
13451235808cSjmatthew 	case 408000000U:
13461235808cSjmatthew 	case 312000000U:
13471235808cSjmatthew 		postdiv1 = postdiv2 = 2; refdiv = 1;
13481235808cSjmatthew 		break;
13491235808cSjmatthew 	case 216000000U:
13501235808cSjmatthew 		postdiv1 = 4; postdiv2 = 2; refdiv = 1;
13511235808cSjmatthew 		break;
13521235808cSjmatthew 	case 96000000U:
13531235808cSjmatthew 		postdiv1 = postdiv2 = 4; refdiv = 1;
13541235808cSjmatthew 		break;
13551235808cSjmatthew 	default:
13561235808cSjmatthew 		printf("%s: %u Hz\n", __func__, freq);
13571235808cSjmatthew 		return -1;
13581235808cSjmatthew 	}
13591235808cSjmatthew 
13601235808cSjmatthew 	/* Calculate feedback divider. */
13611235808cSjmatthew 	fbdiv = freq * postdiv1 * postdiv2 * refdiv / 24000000;
13621235808cSjmatthew 
13631235808cSjmatthew 	/*
13641235808cSjmatthew 	 * Select slow mode to guarantee a stable clock while we're
13651235808cSjmatthew 	 * adjusting the PLL.
13661235808cSjmatthew 	 */
13671235808cSjmatthew 	HWRITE4(sc, RK3308_CRU_CRU_MODE,
13681235808cSjmatthew 	   (RK3308_CRU_CRU_MODE_MASK << 16 |
13691235808cSjmatthew 	   RK3308_CRU_CRU_MODE_SLOW) << mode_shift);
13701235808cSjmatthew 
13711235808cSjmatthew 	/* Set PLL rate. */
13721235808cSjmatthew 	HWRITE4(sc, base + 0x0000,
13731235808cSjmatthew 	    RK3308_CRU_PLL_POSTDIV1_MASK << 16 |
13741235808cSjmatthew 	    postdiv1 << RK3308_CRU_PLL_POSTDIV1_SHIFT |
13751235808cSjmatthew 	    RK3308_CRU_PLL_FBDIV_MASK << 16 |
13761235808cSjmatthew 	    fbdiv << RK3308_CRU_PLL_FBDIV_SHIFT);
13771235808cSjmatthew 	HWRITE4(sc, base + 0x0004,
13781235808cSjmatthew 	    RK3308_CRU_PLL_DSMPD << 16 | RK3308_CRU_PLL_DSMPD |
13791235808cSjmatthew 	    RK3308_CRU_PLL_POSTDIV2_MASK << 16 |
13801235808cSjmatthew 	    postdiv2 << RK3308_CRU_PLL_POSTDIV2_SHIFT |
13811235808cSjmatthew 	    RK3308_CRU_PLL_REFDIV_MASK << 16 |
13821235808cSjmatthew 	    refdiv << RK3308_CRU_PLL_REFDIV_SHIFT);
13831235808cSjmatthew 
13841235808cSjmatthew 	/* Wait for PLL to stabilize. */
13851235808cSjmatthew 	while ((HREAD4(sc, base + 0x0004) & RK3308_CRU_PLL_PLL_LOCK) == 0)
13861235808cSjmatthew 		delay(10);
13871235808cSjmatthew 
13881235808cSjmatthew 	/* Switch back to normal mode. */
13891235808cSjmatthew 	HWRITE4(sc, RK3308_CRU_CRU_MODE,
13901235808cSjmatthew 	   (RK3308_CRU_CRU_MODE_MASK << 16 |
13911235808cSjmatthew 	   RK3308_CRU_CRU_MODE_NORMAL) << mode_shift);
13921235808cSjmatthew 
13931235808cSjmatthew 	return 0;
13941235808cSjmatthew }
13951235808cSjmatthew 
13961235808cSjmatthew uint32_t
13971235808cSjmatthew rk3308_get_rtc32k(struct rkclock_softc *sc)
13981235808cSjmatthew {
13991235808cSjmatthew 	uint32_t reg, mux, pll, div_con;
14001235808cSjmatthew 
14011235808cSjmatthew 	reg = HREAD4(sc, RK3308_CRU_CLKSEL_CON(2));
14023f41a742Sjsg 	mux = (reg & 0x300) >> 8;
14031235808cSjmatthew 	if (mux != 3) {
14041235808cSjmatthew 		printf("%s: RTC32K not using clk_32k_div\n", __func__);
14051235808cSjmatthew 		return 0;
14061235808cSjmatthew 	}
14071235808cSjmatthew 
14081235808cSjmatthew 	if ((reg >> 10) & 1)
14091235808cSjmatthew 		pll = RK3308_PLL_VPLL1;
14101235808cSjmatthew 	else
14111235808cSjmatthew 		pll = RK3308_PLL_VPLL0;
14121235808cSjmatthew 
14131235808cSjmatthew 	div_con = HREAD4(sc, RK3308_CRU_CLKSEL_CON(4)) & 0xffff;
14141235808cSjmatthew 	return rk3308_get_frequency(sc, &pll) / (div_con + 1);
14151235808cSjmatthew }
14161235808cSjmatthew 
14171235808cSjmatthew int
14181235808cSjmatthew rk3308_set_rtc32k(struct rkclock_softc *sc, uint32_t freq)
14191235808cSjmatthew {
142037c734d3Snaddy 	const struct rkclock *clk;
14211235808cSjmatthew 	uint32_t vpll0_freq, vpll1_freq, mux, div_con;
14221235808cSjmatthew 
14231235808cSjmatthew 	clk = rkclock_lookup(sc, RK3308_CLK_RTC32K);
14241235808cSjmatthew 	vpll0_freq = rkclock_freq(sc, clk, 0, freq);
14251235808cSjmatthew 	vpll1_freq = rkclock_freq(sc, clk, 1, freq);
14261235808cSjmatthew 	mux = 0;
14271235808cSjmatthew 	freq = vpll0_freq;
14281235808cSjmatthew 
14291235808cSjmatthew 	if ((vpll1_freq > vpll0_freq && vpll1_freq <= freq) ||
14301235808cSjmatthew 	    (vpll1_freq < vpll0_freq && vpll1_freq >= freq)) {
14311235808cSjmatthew 		mux = 1;
14321235808cSjmatthew 		freq = vpll1_freq;
14331235808cSjmatthew 	}
14341235808cSjmatthew 
14351235808cSjmatthew 	div_con = rkclock_div_con(sc, clk, mux, freq);
14361235808cSjmatthew 	HWRITE4(sc, RK3308_CRU_CLKSEL_CON(2), 1 << 26 | (mux << 10));
14371235808cSjmatthew 	HWRITE4(sc, RK3308_CRU_CLKSEL_CON(4), 0xffff0000 | div_con);
14381235808cSjmatthew 	return 0;
14391235808cSjmatthew }
14401235808cSjmatthew 
14411235808cSjmatthew uint32_t
14421235808cSjmatthew rk3308_get_frequency(void *cookie, uint32_t *cells)
14431235808cSjmatthew {
14441235808cSjmatthew 	struct rkclock_softc *sc = cookie;
14451235808cSjmatthew 	uint32_t idx = cells[0];
14461235808cSjmatthew 
14471235808cSjmatthew 	switch (idx) {
14481235808cSjmatthew 	case RK3308_PLL_APLL:
14491235808cSjmatthew 		return rk3308_get_pll(sc, RK3308_CRU_APLL_CON(0));
14501235808cSjmatthew 	case RK3308_PLL_DPLL:
14511235808cSjmatthew 		return rk3308_get_pll(sc, RK3308_CRU_DPLL_CON(0));
14521235808cSjmatthew 	case RK3308_PLL_VPLL0:
14531235808cSjmatthew 		return rk3308_get_pll(sc, RK3308_CRU_VPLL0_CON(0));
14541235808cSjmatthew 	case RK3308_PLL_VPLL1:
14551235808cSjmatthew 		return rk3308_get_pll(sc, RK3308_CRU_VPLL1_CON(0));
14561235808cSjmatthew 	case RK3308_ARMCLK:
14571235808cSjmatthew 		return rk3308_get_armclk(sc);
14581235808cSjmatthew 	case RK3308_XIN24M:
14591235808cSjmatthew 		return 24000000;
14601235808cSjmatthew 	case RK3308_CLK_RTC32K:
14611235808cSjmatthew 		return rk3308_get_rtc32k(sc);
14621235808cSjmatthew 
14631235808cSjmatthew 	/*
14641235808cSjmatthew 	 * XXX The USB480M clock is external.  Returning zero here will cause
14651235808cSjmatthew 	 * it to be ignored for reparenting purposes.
14661235808cSjmatthew 	 */
14671235808cSjmatthew 	case RK3308_USB480M:
14681235808cSjmatthew 		return 0;
14691235808cSjmatthew 	default:
14701235808cSjmatthew 		break;
14711235808cSjmatthew 	}
14721235808cSjmatthew 
14731235808cSjmatthew 	return rkclock_get_frequency(sc, idx);
14741235808cSjmatthew }
14751235808cSjmatthew 
14761235808cSjmatthew int
14771235808cSjmatthew rk3308_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
14781235808cSjmatthew {
14791235808cSjmatthew 	struct rkclock_softc *sc = cookie;
14801235808cSjmatthew 	uint32_t idx = cells[0];
14811235808cSjmatthew 
14821235808cSjmatthew 	switch (idx) {
14831235808cSjmatthew 	case RK3308_PLL_APLL:
14841235808cSjmatthew 		return rk3308_set_pll(sc, RK3308_CRU_APLL_CON(0), freq);
14851235808cSjmatthew 	case RK3308_PLL_DPLL:
14861235808cSjmatthew 		return rk3308_set_pll(sc, RK3308_CRU_DPLL_CON(0), freq);
14871235808cSjmatthew 	case RK3308_PLL_VPLL0:
14881235808cSjmatthew 		return rk3308_set_pll(sc, RK3308_CRU_VPLL0_CON(0), freq);
14891235808cSjmatthew 	case RK3308_PLL_VPLL1:
14901235808cSjmatthew 		return rk3308_set_pll(sc, RK3308_CRU_VPLL1_CON(0), freq);
14911235808cSjmatthew 	case RK3308_ARMCLK:
14921235808cSjmatthew 		return rk3308_set_armclk(sc, freq);
14931235808cSjmatthew 	case RK3308_CLK_RTC32K:
14941235808cSjmatthew 		return rk3308_set_rtc32k(sc, freq);
14951235808cSjmatthew 	default:
14961235808cSjmatthew 		break;
14971235808cSjmatthew 	}
14981235808cSjmatthew 
14991235808cSjmatthew 	return rkclock_set_frequency(sc, idx, freq);
15001235808cSjmatthew }
15011235808cSjmatthew 
15021235808cSjmatthew 
15031235808cSjmatthew int
15041235808cSjmatthew rk3308_set_parent(void *cookie, uint32_t *cells, uint32_t *pcells)
15051235808cSjmatthew {
15061235808cSjmatthew 	struct rkclock_softc *sc = cookie;
15071235808cSjmatthew 
15081235808cSjmatthew 	if (pcells[0] != sc->sc_phandle)
15091235808cSjmatthew 		return -1;
15101235808cSjmatthew 
15111235808cSjmatthew 	return rkclock_set_parent(sc, cells[0], pcells[1]);
15121235808cSjmatthew }
15131235808cSjmatthew 
15141235808cSjmatthew void
15151235808cSjmatthew rk3308_enable(void *cookie, uint32_t *cells, int on)
15161235808cSjmatthew {
15171235808cSjmatthew 	uint32_t idx = cells[0];
15181235808cSjmatthew 
15191235808cSjmatthew 	/*
15201235808cSjmatthew 	 * All clocks are enabled by default, so there is nothing for
15211235808cSjmatthew 	 * us to do until we start disabling clocks.
15221235808cSjmatthew 	 */
15231235808cSjmatthew 	if (!on)
15241235808cSjmatthew 		printf("%s: 0x%08x\n", __func__, idx);
15251235808cSjmatthew }
15261235808cSjmatthew 
15271235808cSjmatthew void
15281235808cSjmatthew rk3308_reset(void *cookie, uint32_t *cells, int on)
15291235808cSjmatthew {
15301235808cSjmatthew 	struct rkclock_softc *sc = cookie;
15311235808cSjmatthew 	uint32_t idx = cells[0];
15321235808cSjmatthew 	uint32_t mask = (1 << (idx % 16));
15331235808cSjmatthew 
15341235808cSjmatthew 	HWRITE4(sc, RK3308_CRU_SOFTRST_CON(idx / 16),
15351235808cSjmatthew 	    mask << 16 | (on ? mask : 0));
15361235808cSjmatthew }
15371235808cSjmatthew 
15381235808cSjmatthew 
15391235808cSjmatthew /*
1540da841e6eSkettenis  * Rockchip RK3328
1541da841e6eSkettenis  */
1542da841e6eSkettenis 
154337c734d3Snaddy const struct rkclock rk3328_clocks[] = {
1544e82d29a5Skettenis 	{
15454cecc5e3Skettenis 		RK3328_CLK_RTC32K, RK3328_CRU_CLKSEL_CON(38),
15464cecc5e3Skettenis 		SEL(15, 14), DIV(13, 0),
15477371cc19Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_XIN24M }
15484cecc5e3Skettenis 	},
15494cecc5e3Skettenis 	{
155017449d5cSkettenis 		RK3328_CLK_SPI, RK3328_CRU_CLKSEL_CON(24),
155117449d5cSkettenis 		SEL(7, 7), DIV(6, 0),
155217449d5cSkettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
155317449d5cSkettenis 	},
155417449d5cSkettenis 	{
1555e82d29a5Skettenis 		RK3328_CLK_SDMMC, RK3328_CRU_CLKSEL_CON(30),
1556e82d29a5Skettenis 		SEL(9, 8), DIV(7, 0),
15577371cc19Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_XIN24M,
1558e82d29a5Skettenis 		  RK3328_USB480M }
1559e82d29a5Skettenis 	},
1560e82d29a5Skettenis 	{
1561e82d29a5Skettenis 		RK3328_CLK_SDIO, RK3328_CRU_CLKSEL_CON(31),
1562e82d29a5Skettenis 		SEL(9, 8), DIV(7, 0),
15637371cc19Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_XIN24M,
1564e82d29a5Skettenis 		  RK3328_USB480M }
1565e82d29a5Skettenis 	},
1566e82d29a5Skettenis 	{
1567e82d29a5Skettenis 		RK3328_CLK_EMMC, RK3328_CRU_CLKSEL_CON(32),
1568e82d29a5Skettenis 		SEL(9, 8), DIV(7, 0),
15697371cc19Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_XIN24M,
1570e82d29a5Skettenis 		  RK3328_USB480M }
1571e82d29a5Skettenis 	},
1572e82d29a5Skettenis 	{
1573326e2a79Skettenis 		RK3328_CLK_TSADC, RK3328_CRU_CLKSEL_CON(22),
1574326e2a79Skettenis 		0, DIV(9, 0),
1575326e2a79Skettenis 		{ RK3328_CLK_24M }
1576326e2a79Skettenis 	},
1577326e2a79Skettenis 	{
1578e82d29a5Skettenis 		RK3328_CLK_UART0, RK3328_CRU_CLKSEL_CON(14),
1579e82d29a5Skettenis 		SEL(9, 8), 0,
15807371cc19Skettenis 		{ 0, 0, RK3328_XIN24M, RK3328_XIN24M }
1581e82d29a5Skettenis 	},
1582e82d29a5Skettenis 	{
1583e82d29a5Skettenis 		RK3328_CLK_UART1, RK3328_CRU_CLKSEL_CON(16),
1584e82d29a5Skettenis 		SEL(9, 8), 0,
15857371cc19Skettenis 		{ 0, 0, RK3328_XIN24M, RK3328_XIN24M }
1586e82d29a5Skettenis 	},
1587e82d29a5Skettenis 	{
1588e82d29a5Skettenis 		RK3328_CLK_UART2, RK3328_CRU_CLKSEL_CON(18),
1589e82d29a5Skettenis 		SEL(9, 8), 0,
15907371cc19Skettenis 		{ 0, 0, RK3328_XIN24M, RK3328_XIN24M }
1591e82d29a5Skettenis 	},
1592e82d29a5Skettenis 	{
15934cecc5e3Skettenis 		RK3328_CLK_WIFI, RK3328_CRU_CLKSEL_CON(52),
15944cecc5e3Skettenis 		SEL(7, 6), DIV(5, 0),
15954cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_USB480M }
15964cecc5e3Skettenis 	},
15974cecc5e3Skettenis 	{
1598e82d29a5Skettenis 		RK3328_CLK_I2C0, RK3328_CRU_CLKSEL_CON(34),
1599e82d29a5Skettenis 		SEL(7, 7), DIV(6, 0),
1600e82d29a5Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
1601e82d29a5Skettenis 	},
1602e82d29a5Skettenis 	{
1603e82d29a5Skettenis 		RK3328_CLK_I2C1, RK3328_CRU_CLKSEL_CON(34),
1604e82d29a5Skettenis 		SEL(15, 15), DIV(14, 8),
1605e82d29a5Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
1606e82d29a5Skettenis 	},
1607e82d29a5Skettenis 	{
1608e82d29a5Skettenis 		RK3328_CLK_I2C2, RK3328_CRU_CLKSEL_CON(35),
1609e82d29a5Skettenis 		SEL(7, 7), DIV(6, 0),
1610e82d29a5Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
1611e82d29a5Skettenis 	},
1612e82d29a5Skettenis 	{
1613e82d29a5Skettenis 		RK3328_CLK_I2C3, RK3328_CRU_CLKSEL_CON(35),
1614e82d29a5Skettenis 		SEL(15, 15), DIV(14, 8),
1615e82d29a5Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
1616e82d29a5Skettenis 	},
1617e82d29a5Skettenis 	{
16184d55a102Skettenis 		RK3328_CLK_CRYPTO, RK3328_CRU_CLKSEL_CON(20),
16194d55a102Skettenis 		SEL(7, 7), DIV(4, 0),
16204d55a102Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
16214d55a102Skettenis 	},
16224d55a102Skettenis 	{
1623e82d29a5Skettenis 		RK3328_CLK_PDM, RK3328_CRU_CLKSEL_CON(20),
1624e82d29a5Skettenis 		SEL(15, 14), DIV(12, 8),
16253f8209efSkettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_PLL_APLL },
16263f8209efSkettenis 		FIXED_PARENT | SET_PARENT
1627e82d29a5Skettenis 	},
1628e82d29a5Skettenis 	{
16294cecc5e3Skettenis 		RK3328_CLK_VDEC_CABAC, RK3328_CRU_CLKSEL_CON(48),
16304cecc5e3Skettenis 		SEL(15, 14), DIV(12, 8),
16314cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16324cecc5e3Skettenis 		  RK3328_USB480M }
16334cecc5e3Skettenis 	},
16344cecc5e3Skettenis 	{
16354cecc5e3Skettenis 		RK3328_CLK_VDEC_CORE, RK3328_CRU_CLKSEL_CON(49),
16364cecc5e3Skettenis 		SEL(7, 6), DIV(4, 0),
16374cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16384cecc5e3Skettenis 		  RK3328_USB480M }
16394cecc5e3Skettenis 	},
16404cecc5e3Skettenis 	{
16414cecc5e3Skettenis 		RK3328_CLK_VENC_DSP, RK3328_CRU_CLKSEL_CON(52),
16424cecc5e3Skettenis 		SEL(15, 14), DIV(12, 8),
16434cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16444cecc5e3Skettenis 		  RK3328_USB480M }
16454cecc5e3Skettenis 	},
16464cecc5e3Skettenis 	{
16474cecc5e3Skettenis 		RK3328_CLK_VENC_CORE, RK3328_CRU_CLKSEL_CON(51),
16484cecc5e3Skettenis 		SEL(15, 14), DIV(12, 8),
16494cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16504cecc5e3Skettenis 		  RK3328_USB480M }
16514cecc5e3Skettenis 	},
16524cecc5e3Skettenis 	{
16534cecc5e3Skettenis 		RK3328_CLK_TSP, RK3328_CRU_CLKSEL_CON(21),
16544cecc5e3Skettenis 		SEL(15, 15), DIV(12, 8),
16554cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
16564cecc5e3Skettenis 	},
16574cecc5e3Skettenis 	{
16582fcaff65Skettenis 		RK3328_CLK_MAC2IO_SRC, RK3328_CRU_CLKSEL_CON(27),
16592fcaff65Skettenis 		SEL(7, 7), DIV(4, 0),
16602fcaff65Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL }
16612fcaff65Skettenis 	},
16622fcaff65Skettenis 	{
16634cecc5e3Skettenis 		RK3328_DCLK_LCDC, RK3328_CRU_CLKSEL_CON(40),
16644cecc5e3Skettenis 		SEL(1, 1), 0,
16654cecc5e3Skettenis 		{ RK3328_HDMIPHY, RK3328_DCLK_LCDC_SRC }
16664cecc5e3Skettenis 	},
16674cecc5e3Skettenis 	{
16684cecc5e3Skettenis 		RK3328_ACLK_VOP_PRE, RK3328_CRU_CLKSEL_CON(39),
16694cecc5e3Skettenis 		SEL(7, 6), DIV(4, 0),
16704cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16714cecc5e3Skettenis 		  RK3328_USB480M }
16724cecc5e3Skettenis 	},
16734cecc5e3Skettenis 	{
16744cecc5e3Skettenis 		RK3328_ACLK_RGA_PRE, RK3328_CRU_CLKSEL_CON(36),
16754cecc5e3Skettenis 		SEL(15, 14), DIV(12, 8),
16764cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16774cecc5e3Skettenis 		  RK3328_USB480M }
16784cecc5e3Skettenis 	},
16794cecc5e3Skettenis 	{
1680e82d29a5Skettenis 		RK3328_ACLK_BUS_PRE, RK3328_CRU_CLKSEL_CON(0),
1681e82d29a5Skettenis 		SEL(14, 13), DIV(12, 8),
1682e82d29a5Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY }
1683e82d29a5Skettenis 	},
1684e82d29a5Skettenis 	{
1685e82d29a5Skettenis 		RK3328_ACLK_PERI_PRE, RK3328_CRU_CLKSEL_CON(28),
1686e82d29a5Skettenis 		SEL(7, 6), DIV(4, 0),
1687e82d29a5Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY }
1688e82d29a5Skettenis 	},
1689e82d29a5Skettenis 	{
16904cecc5e3Skettenis 		RK3328_ACLK_RKVDEC_PRE, RK3328_CRU_CLKSEL_CON(48),
16914cecc5e3Skettenis 		SEL(7, 6), DIV(4, 0),
16924cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16934cecc5e3Skettenis 		  RK3328_USB480M }
16944cecc5e3Skettenis 	},
16954cecc5e3Skettenis 	{
16964cecc5e3Skettenis 		RK3328_ACLK_RKVENC, RK3328_CRU_CLKSEL_CON(51),
16974cecc5e3Skettenis 		SEL(7, 6), DIV(4, 0),
16984cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
16994cecc5e3Skettenis 		  RK3328_USB480M }
17004cecc5e3Skettenis 	},
17014cecc5e3Skettenis 	{
17024cecc5e3Skettenis 		RK3328_ACLK_VPU_PRE, RK3328_CRU_CLKSEL_CON(50),
17034cecc5e3Skettenis 		SEL(7, 6), DIV(4, 0),
17044cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
17054cecc5e3Skettenis 		  RK3328_USB480M }
17064cecc5e3Skettenis 	},
17074cecc5e3Skettenis 	{
17084cecc5e3Skettenis 		RK3328_ACLK_VIO_PRE, RK3328_CRU_CLKSEL_CON(37),
17094cecc5e3Skettenis 		SEL(7, 6), DIV(4, 0),
17104cecc5e3Skettenis 		{ RK3328_PLL_CPLL, RK3328_PLL_GPLL, RK3328_HDMIPHY,
17114cecc5e3Skettenis 		  RK3328_USB480M }
17124cecc5e3Skettenis 	},
17134cecc5e3Skettenis 	{
1714e82d29a5Skettenis 		RK3328_PCLK_BUS_PRE, RK3328_CRU_CLKSEL_CON(1),
1715e82d29a5Skettenis 		0, DIV(14, 12),
1716e82d29a5Skettenis 		{ RK3328_ACLK_BUS_PRE }
1717e82d29a5Skettenis 	},
1718e82d29a5Skettenis 	{
1719e82d29a5Skettenis 		RK3328_HCLK_BUS_PRE, RK3328_CRU_CLKSEL_CON(1),
1720e82d29a5Skettenis 		0, DIV(9, 8),
1721e82d29a5Skettenis 		{ RK3328_ACLK_BUS_PRE }
1722e82d29a5Skettenis 	},
1723e82d29a5Skettenis 	{
1724e82d29a5Skettenis 		RK3328_PCLK_PERI, RK3328_CRU_CLKSEL_CON(29),
1725e82d29a5Skettenis 		0, DIV(6, 4),
1726e82d29a5Skettenis 		{ RK3328_ACLK_PERI_PRE }
1727e82d29a5Skettenis 	},
1728e82d29a5Skettenis 	{
1729e82d29a5Skettenis 		RK3328_HCLK_PERI, RK3328_CRU_CLKSEL_CON(29),
1730e82d29a5Skettenis 		0, DIV(1, 0),
1731e82d29a5Skettenis 		{ RK3328_ACLK_PERI_PRE }
1732e82d29a5Skettenis 	},
1733e82d29a5Skettenis 	{
1734326e2a79Skettenis 		RK3328_CLK_24M, RK3328_CRU_CLKSEL_CON(2),
1735326e2a79Skettenis 		0, DIV(12, 8),
1736326e2a79Skettenis 		{ RK3328_XIN24M }
1737326e2a79Skettenis 	},
1738326e2a79Skettenis 	{
1739e82d29a5Skettenis 		/* Sentinel */
1740e82d29a5Skettenis 	}
1741e82d29a5Skettenis };
1742e82d29a5Skettenis 
1743da841e6eSkettenis void
1744da841e6eSkettenis rk3328_init(struct rkclock_softc *sc)
1745da841e6eSkettenis {
1746da841e6eSkettenis 	int i;
1747da841e6eSkettenis 
1748da841e6eSkettenis 	/* The code below assumes all clocks are enabled.  Check this!. */
1749da841e6eSkettenis 	for (i = 0; i <= 28; i++) {
1750da841e6eSkettenis 		if (HREAD4(sc, RK3328_CRU_CLKGATE_CON(i)) != 0x00000000) {
1751da841e6eSkettenis 			printf("CRU_CLKGATE_CON%d: 0x%08x\n", i,
1752da841e6eSkettenis 			    HREAD4(sc, RK3328_CRU_CLKGATE_CON(i)));
1753da841e6eSkettenis 		}
1754da841e6eSkettenis 	}
1755e82d29a5Skettenis 
1756e82d29a5Skettenis 	sc->sc_clocks = rk3328_clocks;
1757da841e6eSkettenis }
1758da841e6eSkettenis 
1759da841e6eSkettenis uint32_t
1760f231fa3aSkettenis rk3328_armclk_parent(uint32_t mux)
1761f231fa3aSkettenis {
1762f231fa3aSkettenis 	switch (mux) {
1763f231fa3aSkettenis 	case 0:
1764f231fa3aSkettenis 		return RK3328_PLL_APLL;
1765f231fa3aSkettenis 	case 1:
1766f231fa3aSkettenis 		return RK3328_PLL_GPLL;
1767f231fa3aSkettenis 	case 2:
1768f231fa3aSkettenis 		return RK3328_PLL_DPLL;
1769f231fa3aSkettenis 	case 3:
1770f231fa3aSkettenis 		return RK3328_PLL_NPLL;
1771f231fa3aSkettenis 	}
1772f231fa3aSkettenis 
1773f231fa3aSkettenis 	return 0;
1774f231fa3aSkettenis }
1775f231fa3aSkettenis 
1776f231fa3aSkettenis uint32_t
1777f231fa3aSkettenis rk3328_get_armclk(struct rkclock_softc *sc)
1778f231fa3aSkettenis {
1779f231fa3aSkettenis 	uint32_t reg, mux, div_con;
1780f231fa3aSkettenis 	uint32_t idx;
1781f231fa3aSkettenis 
1782f231fa3aSkettenis 	reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(0));
1783f231fa3aSkettenis 	mux = (reg & RK3328_CRU_CORE_CLK_PLL_SEL_MASK) >>
1784f231fa3aSkettenis 	    RK3328_CRU_CORE_CLK_PLL_SEL_SHIFT;
1785f231fa3aSkettenis 	div_con = (reg & RK3328_CRU_CLK_CORE_DIV_CON_MASK) >>
1786f231fa3aSkettenis 	    RK3328_CRU_CLK_CORE_DIV_CON_SHIFT;
1787f231fa3aSkettenis 	idx = rk3328_armclk_parent(mux);
1788f231fa3aSkettenis 
1789f231fa3aSkettenis 	return rk3328_get_frequency(sc, &idx) / (div_con + 1);
1790f231fa3aSkettenis }
1791f231fa3aSkettenis 
1792f231fa3aSkettenis int
1793f231fa3aSkettenis rk3328_set_armclk(struct rkclock_softc *sc, uint32_t freq)
1794f231fa3aSkettenis {
1795f231fa3aSkettenis 	uint32_t reg, mux;
1796f231fa3aSkettenis 	uint32_t old_freq, div;
1797f231fa3aSkettenis 	uint32_t idx;
1798f231fa3aSkettenis 
1799f231fa3aSkettenis 	old_freq = rk3328_get_armclk(sc);
1800f231fa3aSkettenis 	if (freq == old_freq)
1801f231fa3aSkettenis 		return 0;
1802f231fa3aSkettenis 
1803f231fa3aSkettenis 	reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(0));
1804f231fa3aSkettenis 	mux = (reg & RK3328_CRU_CORE_CLK_PLL_SEL_MASK) >>
1805f231fa3aSkettenis 	    RK3328_CRU_CORE_CLK_PLL_SEL_SHIFT;
1806f231fa3aSkettenis 
1807f231fa3aSkettenis 	/* Keep the pclk_dbg clock at or below 300 MHz. */
1808f231fa3aSkettenis 	div = 1;
1809f231fa3aSkettenis 	while (freq / (div + 1) > 300000000)
1810f231fa3aSkettenis 		div++;
1811f231fa3aSkettenis 	/* and make sure we use an odd divider. */
1812f231fa3aSkettenis 	if ((div % 2) == 0)
1813f231fa3aSkettenis 		div++;
1814f231fa3aSkettenis 
1815f231fa3aSkettenis 	/* When ramping up, set clock dividers first. */
1816f231fa3aSkettenis 	if (freq > old_freq) {
1817f231fa3aSkettenis 		HWRITE4(sc, RK3328_CRU_CLKSEL_CON(0),
1818f231fa3aSkettenis 		    RK3328_CRU_CLK_CORE_DIV_CON_MASK << 16 |
1819f231fa3aSkettenis 		    0 << RK3328_CRU_CLK_CORE_DIV_CON_SHIFT);
1820f231fa3aSkettenis 		HWRITE4(sc, RK3328_CRU_CLKSEL_CON(1),
1821f231fa3aSkettenis 		    RK3328_CRU_ACLK_CORE_DIV_CON_MASK << 16 |
1822f231fa3aSkettenis 		    1 << RK3328_CRU_ACLK_CORE_DIV_CON_SHIFT |
1823f231fa3aSkettenis 		    RK3328_CRU_CLK_CORE_DBG_DIV_CON_MASK << 16 |
1824f231fa3aSkettenis 		    div << RK3328_CRU_CLK_CORE_DBG_DIV_CON_SHIFT);
1825f231fa3aSkettenis 	}
1826f231fa3aSkettenis 
18274cecc5e3Skettenis 	/* We always use NPLL and force the switch below if needed. */
18284cecc5e3Skettenis 	idx = RK3328_PLL_NPLL;
1829f231fa3aSkettenis 	rk3328_set_frequency(sc, &idx, freq);
1830f231fa3aSkettenis 
18314cecc5e3Skettenis 	/* When ramping down, set clock dividers last. */
18324cecc5e3Skettenis 	if (freq < old_freq || mux != 3) {
1833f231fa3aSkettenis 		HWRITE4(sc, RK3328_CRU_CLKSEL_CON(0),
18344cecc5e3Skettenis 		    RK3328_CRU_CORE_CLK_PLL_SEL_MASK << 16 |
18354cecc5e3Skettenis 		    3 << RK3328_CRU_CORE_CLK_PLL_SEL_SHIFT |
1836f231fa3aSkettenis 		    RK3328_CRU_CLK_CORE_DIV_CON_MASK << 16 |
1837f231fa3aSkettenis 		    0 << RK3328_CRU_CLK_CORE_DIV_CON_SHIFT);
1838f231fa3aSkettenis 		HWRITE4(sc, RK3328_CRU_CLKSEL_CON(1),
1839f231fa3aSkettenis 		    RK3328_CRU_ACLK_CORE_DIV_CON_MASK << 16 |
1840f231fa3aSkettenis 		    1 << RK3328_CRU_ACLK_CORE_DIV_CON_SHIFT |
1841f231fa3aSkettenis 		    RK3328_CRU_CLK_CORE_DBG_DIV_CON_MASK << 16 |
1842f231fa3aSkettenis 		    div << RK3328_CRU_CLK_CORE_DBG_DIV_CON_SHIFT);
1843f231fa3aSkettenis 	}
1844f231fa3aSkettenis 
1845f231fa3aSkettenis 	return 0;
1846f231fa3aSkettenis }
1847f231fa3aSkettenis 
1848f231fa3aSkettenis uint32_t
1849da841e6eSkettenis rk3328_get_pll(struct rkclock_softc *sc, bus_size_t base)
1850da841e6eSkettenis {
1851da841e6eSkettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
18524cecc5e3Skettenis 	uint32_t dsmpd, fracdiv;
18534cecc5e3Skettenis 	uint64_t frac = 0;
1854da841e6eSkettenis 	uint32_t reg;
1855da841e6eSkettenis 
1856da841e6eSkettenis 	reg = HREAD4(sc, base + 0x0000);
1857da841e6eSkettenis 	postdiv1 = (reg & RK3328_CRU_PLL_POSTDIV1_MASK) >>
1858da841e6eSkettenis 	    RK3328_CRU_PLL_POSTDIV1_SHIFT;
1859da841e6eSkettenis 	fbdiv = (reg & RK3328_CRU_PLL_FBDIV_MASK) >>
1860da841e6eSkettenis 	    RK3328_CRU_PLL_FBDIV_SHIFT;
1861da841e6eSkettenis 	reg = HREAD4(sc, base + 0x0004);
18624cecc5e3Skettenis 	dsmpd = (reg & RK3328_CRU_PLL_DSMPD);
1863da841e6eSkettenis 	postdiv2 = (reg & RK3328_CRU_PLL_POSTDIV2_MASK) >>
1864da841e6eSkettenis 	    RK3328_CRU_PLL_POSTDIV2_SHIFT;
1865da841e6eSkettenis 	refdiv = (reg & RK3328_CRU_PLL_REFDIV_MASK) >>
1866f231fa3aSkettenis 	    RK3328_CRU_PLL_REFDIV_SHIFT;
18674cecc5e3Skettenis 	reg = HREAD4(sc, base + 0x0008);
18684cecc5e3Skettenis 	fracdiv = (reg & RK3328_CRU_PLL_FRACDIV_MASK) >>
18694cecc5e3Skettenis 	    RK3328_CRU_PLL_FRACDIV_SHIFT;
18704cecc5e3Skettenis 
18714cecc5e3Skettenis 	if (dsmpd == 0)
18724cecc5e3Skettenis 		frac = (24000000ULL * fracdiv / refdiv) >> 24;
18734cecc5e3Skettenis 	return ((24000000ULL * fbdiv / refdiv) + frac) / postdiv1 / postdiv2;
1874da841e6eSkettenis }
1875da841e6eSkettenis 
1876f231fa3aSkettenis int
1877f231fa3aSkettenis rk3328_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
1878f231fa3aSkettenis {
1879f231fa3aSkettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
1880f231fa3aSkettenis 	int mode_shift = -1;
1881f231fa3aSkettenis 
1882f231fa3aSkettenis 	switch (base) {
1883f231fa3aSkettenis 	case RK3328_CRU_APLL_CON(0):
1884f231fa3aSkettenis 		mode_shift = 0;
1885f231fa3aSkettenis 		break;
1886f231fa3aSkettenis 	case RK3328_CRU_DPLL_CON(0):
1887f231fa3aSkettenis 		mode_shift = 4;
1888f231fa3aSkettenis 		break;
1889f231fa3aSkettenis 	case RK3328_CRU_CPLL_CON(0):
1890f231fa3aSkettenis 		mode_shift = 8;
1891f231fa3aSkettenis 		break;
1892f231fa3aSkettenis 	case RK3328_CRU_GPLL_CON(0):
1893f231fa3aSkettenis 		mode_shift = 12;
1894f231fa3aSkettenis 		break;
1895f231fa3aSkettenis 	case RK3328_CRU_NPLL_CON(0):
1896f231fa3aSkettenis 		mode_shift = 1;
1897f231fa3aSkettenis 		break;
1898f231fa3aSkettenis 	}
1899f231fa3aSkettenis 	KASSERT(mode_shift != -1);
1900f231fa3aSkettenis 
1901f231fa3aSkettenis 	/*
1902f231fa3aSkettenis 	 * It is not clear whether all combinations of the clock
1903f231fa3aSkettenis 	 * dividers result in a stable clock.  Therefore this function
1904f231fa3aSkettenis 	 * only supports a limited set of PLL clock rates.  For now
1905f231fa3aSkettenis 	 * this set covers all the CPU frequencies supported by the
1906f231fa3aSkettenis 	 * Linux kernel.
1907f231fa3aSkettenis 	 */
1908f231fa3aSkettenis 	switch (freq) {
1909f231fa3aSkettenis 	case 1800000000U:
1910f231fa3aSkettenis 	case 1704000000U:
1911f231fa3aSkettenis 	case 1608000000U:
1912f231fa3aSkettenis 	case 1512000000U:
1913f231fa3aSkettenis 	case 1488000000U:
1914f231fa3aSkettenis 	case 1416000000U:
1915f231fa3aSkettenis 	case 1392000000U:
1916f231fa3aSkettenis 	case 1296000000U:
1917f231fa3aSkettenis 	case 1200000000U:
1918f231fa3aSkettenis 	case 1104000000U:
1919f231fa3aSkettenis 		postdiv1 = postdiv2 = refdiv = 1;
1920f231fa3aSkettenis 		break;
1921f231fa3aSkettenis 	case 1008000000U:
1922f231fa3aSkettenis 	case 912000000U:
1923f231fa3aSkettenis 	case 816000000U:
1924f231fa3aSkettenis 	case 696000000U:
1925f231fa3aSkettenis 		postdiv1 = 2; postdiv2 = refdiv = 1;
1926f231fa3aSkettenis 		break;
1927f231fa3aSkettenis 	case 600000000U:
1928f231fa3aSkettenis 		postdiv1 = 3; postdiv2 = refdiv = 1;
1929f231fa3aSkettenis 		break;
1930f231fa3aSkettenis 	case 408000000U:
1931f231fa3aSkettenis 	case 312000000U:
1932f231fa3aSkettenis 		postdiv1 = postdiv2 = 2; refdiv = 1;
1933f231fa3aSkettenis 		break;
1934f231fa3aSkettenis 	case 216000000U:
1935f231fa3aSkettenis 		postdiv1 = 4; postdiv2 = 2; refdiv = 1;
1936f231fa3aSkettenis 		break;
1937f231fa3aSkettenis 	case 96000000U:
1938f231fa3aSkettenis 		postdiv1 = postdiv2 = 4; refdiv = 1;
1939f231fa3aSkettenis 		break;
1940f231fa3aSkettenis 	default:
19419822ab30Skettenis 		printf("%s: %u Hz\n", __func__, freq);
1942f231fa3aSkettenis 		return -1;
1943f231fa3aSkettenis 	}
1944f231fa3aSkettenis 
1945f231fa3aSkettenis 	/* Calculate feedback divider. */
1946f231fa3aSkettenis 	fbdiv = freq * postdiv1 * postdiv2 * refdiv / 24000000;
1947f231fa3aSkettenis 
1948f231fa3aSkettenis 	/*
1949f231fa3aSkettenis 	 * Select slow mode to guarantee a stable clock while we're
1950f231fa3aSkettenis 	 * adjusting the PLL.
1951f231fa3aSkettenis 	 */
1952f231fa3aSkettenis 	HWRITE4(sc, RK3328_CRU_CRU_MODE,
1953f231fa3aSkettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
1954f231fa3aSkettenis 	   RK3328_CRU_CRU_MODE_SLOW) << mode_shift);
1955f231fa3aSkettenis 
1956f231fa3aSkettenis 	/* Set PLL rate. */
1957f231fa3aSkettenis 	HWRITE4(sc, base + 0x0000,
1958f231fa3aSkettenis 	    RK3328_CRU_PLL_POSTDIV1_MASK << 16 |
1959f231fa3aSkettenis 	    postdiv1 << RK3328_CRU_PLL_POSTDIV1_SHIFT |
1960f231fa3aSkettenis 	    RK3328_CRU_PLL_FBDIV_MASK << 16 |
1961f231fa3aSkettenis 	    fbdiv << RK3328_CRU_PLL_FBDIV_SHIFT);
1962f231fa3aSkettenis 	HWRITE4(sc, base + 0x0004,
19634cecc5e3Skettenis 	    RK3328_CRU_PLL_DSMPD << 16 | RK3328_CRU_PLL_DSMPD |
1964f231fa3aSkettenis 	    RK3328_CRU_PLL_POSTDIV2_MASK << 16 |
1965f231fa3aSkettenis 	    postdiv2 << RK3328_CRU_PLL_POSTDIV2_SHIFT |
1966f231fa3aSkettenis 	    RK3328_CRU_PLL_REFDIV_MASK << 16 |
1967f231fa3aSkettenis 	    refdiv << RK3328_CRU_PLL_REFDIV_SHIFT);
1968f231fa3aSkettenis 
1969f231fa3aSkettenis 	/* Wait for PLL to stabilize. */
1970f231fa3aSkettenis 	while ((HREAD4(sc, base + 0x0004) & RK3328_CRU_PLL_PLL_LOCK) == 0)
1971f231fa3aSkettenis 		delay(10);
1972f231fa3aSkettenis 
1973f231fa3aSkettenis 	/* Switch back to normal mode. */
1974f231fa3aSkettenis 	HWRITE4(sc, RK3328_CRU_CRU_MODE,
1975f231fa3aSkettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
1976f231fa3aSkettenis 	   RK3328_CRU_CRU_MODE_NORMAL) << mode_shift);
1977f231fa3aSkettenis 
1978f231fa3aSkettenis 	return 0;
1979f231fa3aSkettenis }
1980f231fa3aSkettenis 
19814cecc5e3Skettenis int
19824cecc5e3Skettenis rk3328_set_frac_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
19834cecc5e3Skettenis {
19844cecc5e3Skettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv, fracdiv;
19854cecc5e3Skettenis 	int mode_shift = -1;
19864cecc5e3Skettenis 	uint32_t reg;
19874cecc5e3Skettenis 
19884cecc5e3Skettenis 	switch (base) {
19894cecc5e3Skettenis 	case RK3328_CRU_APLL_CON(0):
19904cecc5e3Skettenis 		mode_shift = 0;
19914cecc5e3Skettenis 		break;
19924cecc5e3Skettenis 	case RK3328_CRU_DPLL_CON(0):
19934cecc5e3Skettenis 		mode_shift = 4;
19944cecc5e3Skettenis 		break;
19954cecc5e3Skettenis 	case RK3328_CRU_CPLL_CON(0):
19964cecc5e3Skettenis 		mode_shift = 8;
19974cecc5e3Skettenis 		break;
19984cecc5e3Skettenis 	case RK3328_CRU_GPLL_CON(0):
19994cecc5e3Skettenis 		mode_shift = 12;
20004cecc5e3Skettenis 		break;
20014cecc5e3Skettenis 	case RK3328_CRU_NPLL_CON(0):
20024cecc5e3Skettenis 		mode_shift = 1;
20034cecc5e3Skettenis 		break;
20044cecc5e3Skettenis 	}
20054cecc5e3Skettenis 	KASSERT(mode_shift != -1);
20064cecc5e3Skettenis 
20074cecc5e3Skettenis 	/*
20084cecc5e3Skettenis 	 * It is not clear whether all combinations of the clock
20094cecc5e3Skettenis 	 * dividers result in a stable clock.  Therefore this function
20104cecc5e3Skettenis 	 * only supports a limited set of PLL clock rates.  This set
20114cecc5e3Skettenis 	 * set covers all the fractional PLL frequencies supported by
20124cecc5e3Skettenis 	 * the Linux kernel.
20134cecc5e3Skettenis 	 */
20144cecc5e3Skettenis 	switch (freq) {
20154cecc5e3Skettenis 	case 1016064000U:
20164cecc5e3Skettenis 		postdiv1 = postdiv2 = 1; refdiv = 3; fracdiv = 134217;
20174cecc5e3Skettenis 		break;
20184cecc5e3Skettenis 	case 983040000U:
20194cecc5e3Skettenis 		postdiv1 = postdiv2 = 1; refdiv = 24; fracdiv = 671088;
20204cecc5e3Skettenis 		break;
20214cecc5e3Skettenis 	case 491520000U:
20224cecc5e3Skettenis 		postdiv1 = 2; postdiv2 = 1; refdiv = 24; fracdiv = 671088;
20234cecc5e3Skettenis 		break;
20244cecc5e3Skettenis 	case 61440000U:
202520464928Skettenis 		postdiv1 = 7; postdiv2 = 2; refdiv = 6; fracdiv = 671088;
20264cecc5e3Skettenis 		break;
20274cecc5e3Skettenis 	case 56448000U:
20284cecc5e3Skettenis 		postdiv1 = postdiv2 = 4; refdiv = 12; fracdiv = 9797894;
20294cecc5e3Skettenis 		break;
20304cecc5e3Skettenis 	case 40960000U:
20314cecc5e3Skettenis 		postdiv1 = 4; postdiv2 = 5; refdiv = 12; fracdiv = 10066239;
20324cecc5e3Skettenis 		break;
20334cecc5e3Skettenis 	default:
20344cecc5e3Skettenis 		printf("%s: %u Hz\n", __func__, freq);
20354cecc5e3Skettenis 		return -1;
20364cecc5e3Skettenis 	}
20374cecc5e3Skettenis 
20384cecc5e3Skettenis 	/* Calculate feedback divider. */
20394cecc5e3Skettenis 	fbdiv = (uint64_t)freq * postdiv1 * postdiv2 * refdiv / 24000000;
20404cecc5e3Skettenis 
20414cecc5e3Skettenis 	/*
20424cecc5e3Skettenis 	 * Select slow mode to guarantee a stable clock while we're
20434cecc5e3Skettenis 	 * adjusting the PLL.
20444cecc5e3Skettenis 	 */
20454cecc5e3Skettenis 	HWRITE4(sc, RK3328_CRU_CRU_MODE,
20464cecc5e3Skettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
20474cecc5e3Skettenis 	   RK3328_CRU_CRU_MODE_SLOW) << mode_shift);
20484cecc5e3Skettenis 
20494cecc5e3Skettenis 	/* Set PLL rate. */
20504cecc5e3Skettenis 	HWRITE4(sc, base + 0x0000,
20514cecc5e3Skettenis 	    RK3328_CRU_PLL_POSTDIV1_MASK << 16 |
20524cecc5e3Skettenis 	    postdiv1 << RK3328_CRU_PLL_POSTDIV1_SHIFT |
20534cecc5e3Skettenis 	    RK3328_CRU_PLL_FBDIV_MASK << 16 |
20544cecc5e3Skettenis 	    fbdiv << RK3328_CRU_PLL_FBDIV_SHIFT);
20554cecc5e3Skettenis 	HWRITE4(sc, base + 0x0004,
20564cecc5e3Skettenis 	    RK3328_CRU_PLL_DSMPD << 16 |
20574cecc5e3Skettenis 	    RK3328_CRU_PLL_POSTDIV2_MASK << 16 |
20584cecc5e3Skettenis 	    postdiv2 << RK3328_CRU_PLL_POSTDIV2_SHIFT |
20594cecc5e3Skettenis 	    RK3328_CRU_PLL_REFDIV_MASK << 16 |
20604cecc5e3Skettenis 	    refdiv << RK3328_CRU_PLL_REFDIV_SHIFT);
20614cecc5e3Skettenis 	reg = HREAD4(sc, base + 0x0008);
20624cecc5e3Skettenis 	reg &= ~RK3328_CRU_PLL_FRACDIV_MASK;
20634cecc5e3Skettenis 	reg |= fracdiv << RK3328_CRU_PLL_FRACDIV_SHIFT;
20644cecc5e3Skettenis 	HWRITE4(sc, base + 0x0008, reg);
20654cecc5e3Skettenis 
20664cecc5e3Skettenis 	/* Wait for PLL to stabilize. */
20674cecc5e3Skettenis 	while ((HREAD4(sc, base + 0x0004) & RK3328_CRU_PLL_PLL_LOCK) == 0)
20684cecc5e3Skettenis 		delay(10);
20694cecc5e3Skettenis 
20704cecc5e3Skettenis 	/* Switch back to normal mode. */
20714cecc5e3Skettenis 	HWRITE4(sc, RK3328_CRU_CRU_MODE,
20724cecc5e3Skettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
20734cecc5e3Skettenis 	   RK3328_CRU_CRU_MODE_NORMAL) << mode_shift);
20744cecc5e3Skettenis 
20754cecc5e3Skettenis 	return 0;
20764cecc5e3Skettenis }
20774cecc5e3Skettenis 
2078da841e6eSkettenis uint32_t
2079da841e6eSkettenis rk3328_get_frequency(void *cookie, uint32_t *cells)
2080da841e6eSkettenis {
2081da841e6eSkettenis 	struct rkclock_softc *sc = cookie;
2082da841e6eSkettenis 	uint32_t idx = cells[0];
20832fcaff65Skettenis 	uint32_t reg;
2084da841e6eSkettenis 
2085da841e6eSkettenis 	switch (idx) {
2086a2f8fc4bSkettenis 	case RK3328_PLL_APLL:
2087a2f8fc4bSkettenis 		return rk3328_get_pll(sc, RK3328_CRU_APLL_CON(0));
2088a2f8fc4bSkettenis 	case RK3328_PLL_DPLL:
2089a2f8fc4bSkettenis 		return rk3328_get_pll(sc, RK3328_CRU_DPLL_CON(0));
2090da841e6eSkettenis 	case RK3328_PLL_CPLL:
2091da841e6eSkettenis 		return rk3328_get_pll(sc, RK3328_CRU_CPLL_CON(0));
2092da841e6eSkettenis 	case RK3328_PLL_GPLL:
2093da841e6eSkettenis 		return rk3328_get_pll(sc, RK3328_CRU_GPLL_CON(0));
2094a2f8fc4bSkettenis 	case RK3328_PLL_NPLL:
2095a2f8fc4bSkettenis 		return rk3328_get_pll(sc, RK3328_CRU_NPLL_CON(0));
2096a2f8fc4bSkettenis 	case RK3328_ARMCLK:
2097f231fa3aSkettenis 		return rk3328_get_armclk(sc);
20987371cc19Skettenis 	case RK3328_XIN24M:
2099a2f8fc4bSkettenis 		return 24000000;
21002fcaff65Skettenis 	case RK3328_GMAC_CLKIN:
21012fcaff65Skettenis 		return 125000000;
21023f8209efSkettenis 	/*
21033f8209efSkettenis 	 * XXX The HDMIPHY and USB480M clocks are external.  Returning
21043f8209efSkettenis 	 * zero here will cause them to be ignored for reparenting
21053f8209efSkettenis 	 * purposes.
21063f8209efSkettenis 	 */
21073f8209efSkettenis 	case RK3328_HDMIPHY:
21083f8209efSkettenis 		return 0;
21093f8209efSkettenis 	case RK3328_USB480M:
21103f8209efSkettenis 		return 0;
21112fcaff65Skettenis 	case RK3328_CLK_MAC2IO:
21122fcaff65Skettenis 		reg = regmap_read_4(sc->sc_grf, RK3328_GRF_MAC_CON1);
21132fcaff65Skettenis 		if (reg & RK3328_GRF_GMAC2IO_RMII_EXTCLK_SEL)
21142fcaff65Skettenis 			idx = RK3328_GMAC_CLKIN;
21152fcaff65Skettenis 		else
21162fcaff65Skettenis 			idx = RK3328_CLK_MAC2IO_SRC;
2117015b970dSkettenis 		return rk3328_get_frequency(sc, &idx);
21187371cc19Skettenis 	default:
21197371cc19Skettenis 		break;
2120da841e6eSkettenis 	}
2121da841e6eSkettenis 
2122e82d29a5Skettenis 	return rkclock_get_frequency(sc, idx);
2123da841e6eSkettenis }
2124da841e6eSkettenis 
2125da841e6eSkettenis int
2126da841e6eSkettenis rk3328_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
2127da841e6eSkettenis {
2128f231fa3aSkettenis 	struct rkclock_softc *sc = cookie;
2129da841e6eSkettenis 	uint32_t idx = cells[0];
21304cecc5e3Skettenis 	uint32_t reg, mux;
2131da841e6eSkettenis 
2132f231fa3aSkettenis 	switch (idx) {
2133f231fa3aSkettenis 	case RK3328_PLL_APLL:
21344cecc5e3Skettenis 		return rk3328_set_frac_pll(sc, RK3328_CRU_APLL_CON(0), freq);
2135f231fa3aSkettenis 	case RK3328_PLL_DPLL:
2136f231fa3aSkettenis 		return rk3328_set_pll(sc, RK3328_CRU_DPLL_CON(0), freq);
2137f231fa3aSkettenis 	case RK3328_PLL_CPLL:
2138f231fa3aSkettenis 		return rk3328_set_pll(sc, RK3328_CRU_CPLL_CON(0), freq);
2139f231fa3aSkettenis 	case RK3328_PLL_GPLL:
21404cecc5e3Skettenis 		return rk3328_set_frac_pll(sc, RK3328_CRU_GPLL_CON(0), freq);
2141f231fa3aSkettenis 	case RK3328_PLL_NPLL:
2142f231fa3aSkettenis 		return rk3328_set_pll(sc, RK3328_CRU_NPLL_CON(0), freq);
2143f231fa3aSkettenis 	case RK3328_ARMCLK:
2144f231fa3aSkettenis 		return rk3328_set_armclk(sc, freq);
21459822ab30Skettenis 	case RK3328_CLK_UART0:
21469822ab30Skettenis 	case RK3328_CLK_UART1:
21479822ab30Skettenis 	case RK3328_CLK_UART2:
21489822ab30Skettenis 		if (freq == rk3328_get_frequency(sc, &idx))
21499822ab30Skettenis 			return 0;
21509822ab30Skettenis 		break;
21514cecc5e3Skettenis 	case RK3328_DCLK_LCDC:
21524cecc5e3Skettenis 		reg = HREAD4(sc, RK3328_CRU_CLKSEL_CON(40));
21534cecc5e3Skettenis 		mux = (reg & RK3328_CRU_VOP_DCLK_SRC_SEL_MASK) >>
21544cecc5e3Skettenis 		    RK3328_CRU_VOP_DCLK_SRC_SEL_SHIFT;
21554cecc5e3Skettenis 		idx = (mux == 0) ? RK3328_HDMIPHY : RK3328_DCLK_LCDC_SRC;
21564cecc5e3Skettenis 		return rk3328_set_frequency(sc, &idx, freq);
21574d55a102Skettenis 	case RK3328_HCLK_CRYPTO_SLV:
21584d55a102Skettenis 		idx = RK3328_HCLK_BUS_PRE;
21594d55a102Skettenis 		return rk3328_set_frequency(sc, &idx, freq);
21607371cc19Skettenis 	default:
21617371cc19Skettenis 		break;
2162f231fa3aSkettenis 	}
2163f231fa3aSkettenis 
2164e82d29a5Skettenis 	return rkclock_set_frequency(sc, idx, freq);
2165da841e6eSkettenis }
2166da841e6eSkettenis 
21679822ab30Skettenis int
21689822ab30Skettenis rk3328_set_parent(void *cookie, uint32_t *cells, uint32_t *pcells)
21699822ab30Skettenis {
21709822ab30Skettenis 	struct rkclock_softc *sc = cookie;
21719822ab30Skettenis 	uint32_t idx = cells[0];
2172e82d29a5Skettenis 	uint32_t parent;
21739822ab30Skettenis 
2174e82d29a5Skettenis 	if (pcells[0] == sc->sc_phandle)
2175e82d29a5Skettenis 		parent = pcells[1];
2176e82d29a5Skettenis 	else {
2177e82d29a5Skettenis 		char name[32];
2178e82d29a5Skettenis 		int node;
2179e82d29a5Skettenis 
2180e82d29a5Skettenis 		node = OF_getnodebyphandle(pcells[0]);
2181e82d29a5Skettenis 		if (node == 0)
2182e82d29a5Skettenis 			return -1;
2183e82d29a5Skettenis 		name[0] = 0;
2184e82d29a5Skettenis 		OF_getprop(node, "clock-output-names", name, sizeof(name));
2185e82d29a5Skettenis 		name[sizeof(name) - 1] = 0;
21862fcaff65Skettenis 		if (strcmp(name, "xin24m") == 0)
21877371cc19Skettenis 			parent = RK3328_XIN24M;
21882fcaff65Skettenis 		else if (strcmp(name, "gmac_clkin") == 0)
21892fcaff65Skettenis 			parent = RK3328_GMAC_CLKIN;
21902fcaff65Skettenis 		else
21912fcaff65Skettenis 			return -1;
21922fcaff65Skettenis 	}
21932fcaff65Skettenis 
21942fcaff65Skettenis 	switch (idx) {
21952fcaff65Skettenis 	case RK3328_CLK_MAC2IO:
21962fcaff65Skettenis 		if (parent == RK3328_GMAC_CLKIN) {
21972fcaff65Skettenis 			regmap_write_4(sc->sc_grf, RK3328_GRF_MAC_CON1,
21982fcaff65Skettenis 			    RK3328_GRF_GMAC2IO_RMII_EXTCLK_SEL << 16 |
21992fcaff65Skettenis 			    RK3328_GRF_GMAC2IO_RMII_EXTCLK_SEL);
22002fcaff65Skettenis 		} else {
22012fcaff65Skettenis 			regmap_write_4(sc->sc_grf, RK3328_GRF_MAC_CON1,
22022fcaff65Skettenis 			    RK3328_GRF_GMAC2IO_RMII_EXTCLK_SEL << 16);
22032fcaff65Skettenis 		}
22042fcaff65Skettenis 		return 0;
2205015b970dSkettenis 	case RK3328_CLK_MAC2IO_EXT:
2206015b970dSkettenis 		if (parent == RK3328_GMAC_CLKIN) {
2207015b970dSkettenis 			regmap_write_4(sc->sc_grf, RK3328_GRF_SOC_CON4,
2208015b970dSkettenis 			    RK3328_GRF_GMAC2IO_MAC_CLK_OUTPUT_EN << 16 |
2209015b970dSkettenis 			    RK3328_GRF_GMAC2IO_MAC_CLK_OUTPUT_EN);
2210015b970dSkettenis 		} else {
2211015b970dSkettenis 			regmap_write_4(sc->sc_grf, RK3328_GRF_SOC_CON4,
2212015b970dSkettenis 			    RK3328_GRF_GMAC2IO_MAC_CLK_OUTPUT_EN << 16);
2213015b970dSkettenis 		}
2214015b970dSkettenis 		return 0;
22159822ab30Skettenis 	}
22169822ab30Skettenis 
2217e82d29a5Skettenis 	return rkclock_set_parent(sc, idx, parent);
22189822ab30Skettenis }
22199822ab30Skettenis 
2220da841e6eSkettenis void
2221da841e6eSkettenis rk3328_enable(void *cookie, uint32_t *cells, int on)
2222da841e6eSkettenis {
2223da841e6eSkettenis 	uint32_t idx = cells[0];
2224da841e6eSkettenis 
2225da841e6eSkettenis 	/*
2226da841e6eSkettenis 	 * All clocks are enabled by default, so there is nothing for
2227da841e6eSkettenis 	 * us to do until we start disabling clocks.
2228da841e6eSkettenis 	 */
2229da841e6eSkettenis 	if (!on)
2230da841e6eSkettenis 		printf("%s: 0x%08x\n", __func__, idx);
2231da841e6eSkettenis }
2232da841e6eSkettenis 
2233da841e6eSkettenis void
2234da841e6eSkettenis rk3328_reset(void *cookie, uint32_t *cells, int on)
2235da841e6eSkettenis {
2236da841e6eSkettenis 	struct rkclock_softc *sc = cookie;
2237da841e6eSkettenis 	uint32_t idx = cells[0];
2238da841e6eSkettenis 	uint32_t mask = (1 << (idx % 16));
2239da841e6eSkettenis 
2240da841e6eSkettenis 	HWRITE4(sc, RK3328_CRU_SOFTRST_CON(idx / 16),
2241da841e6eSkettenis 	    mask << 16 | (on ? mask : 0));
2242da841e6eSkettenis }
2243da841e6eSkettenis 
2244da841e6eSkettenis /*
2245592afca7Skettenis  * Rockchip RK3399
2246592afca7Skettenis  */
2247bd903cc4Skettenis 
224837c734d3Snaddy const struct rkclock rk3399_clocks[] = {
22497371cc19Skettenis 	{
22507371cc19Skettenis 		RK3399_CLK_I2C1, RK3399_CRU_CLKSEL_CON(61),
22517371cc19Skettenis 		SEL(7, 7), DIV(6, 0),
22527371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
22537371cc19Skettenis 	},
22547371cc19Skettenis 	{
22557371cc19Skettenis 		RK3399_CLK_I2C2, RK3399_CRU_CLKSEL_CON(62),
22567371cc19Skettenis 		SEL(7, 7), DIV(6, 0),
22577371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
22587371cc19Skettenis 	},
22597371cc19Skettenis 	{
22607371cc19Skettenis 		RK3399_CLK_I2C3, RK3399_CRU_CLKSEL_CON(63),
22617371cc19Skettenis 		SEL(7, 7), DIV(6, 0),
22627371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
22637371cc19Skettenis 	},
22647371cc19Skettenis 	{
22657371cc19Skettenis 		RK3399_CLK_I2C5, RK3399_CRU_CLKSEL_CON(61),
22667371cc19Skettenis 		SEL(15, 15), DIV(14, 8),
22677371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
22687371cc19Skettenis 	},
22697371cc19Skettenis 	{
22707371cc19Skettenis 		RK3399_CLK_I2C6, RK3399_CRU_CLKSEL_CON(62),
22717371cc19Skettenis 		SEL(15, 15), DIV(14, 8),
22727371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
22737371cc19Skettenis 	},
22747371cc19Skettenis 	{
22757371cc19Skettenis 		RK3399_CLK_I2C7, RK3399_CRU_CLKSEL_CON(63),
22767371cc19Skettenis 		SEL(15, 15), DIV(14, 8),
22777371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
22787371cc19Skettenis 	},
22797371cc19Skettenis 	{
228017449d5cSkettenis 		RK3399_CLK_SPI0, RK3399_CRU_CLKSEL_CON(59),
228117449d5cSkettenis 		SEL(7, 7), DIV(6, 0),
228217449d5cSkettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
228317449d5cSkettenis 	},
228417449d5cSkettenis 	{
228517449d5cSkettenis 		RK3399_CLK_SPI1, RK3399_CRU_CLKSEL_CON(59),
228617449d5cSkettenis 		SEL(15, 15), DIV(14, 8),
228717449d5cSkettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
228817449d5cSkettenis 	},
228917449d5cSkettenis 	{
229017449d5cSkettenis 		RK3399_CLK_SPI2, RK3399_CRU_CLKSEL_CON(60),
229117449d5cSkettenis 		SEL(7, 7), DIV(6, 0),
229217449d5cSkettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
229317449d5cSkettenis 	},
229417449d5cSkettenis 	{
229517449d5cSkettenis 		RK3399_CLK_SPI4, RK3399_CRU_CLKSEL_CON(60),
229617449d5cSkettenis 		SEL(15, 15), DIV(14, 8),
229717449d5cSkettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
229817449d5cSkettenis 	},
229917449d5cSkettenis 	{
230017449d5cSkettenis 		RK3399_CLK_SPI5, RK3399_CRU_CLKSEL_CON(58),
230117449d5cSkettenis 		SEL(15, 15), DIV(14, 8),
230217449d5cSkettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
230317449d5cSkettenis 	},
230417449d5cSkettenis 	{
23057371cc19Skettenis 		RK3399_CLK_SDMMC, RK3399_CRU_CLKSEL_CON(16),
23067371cc19Skettenis 		SEL(10, 8), DIV(6, 0),
23077371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, RK3399_PLL_NPLL,
23087371cc19Skettenis 		  /* RK3399_PLL_PPLL */ 0, /* RK3399_USB_480M */ 0,
23097371cc19Skettenis 		  RK3399_XIN24M }
23107371cc19Skettenis 	},
23117371cc19Skettenis 	{
23127371cc19Skettenis 		RK3399_CLK_SDIO, RK3399_CRU_CLKSEL_CON(15),
23137371cc19Skettenis 		SEL(10, 8), DIV(6, 0),
23147371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, RK3399_PLL_NPLL,
23157371cc19Skettenis 		  /* RK3399_PLL_PPLL */ 0, /* RK3399_USB_480M */ 0,
23167371cc19Skettenis 		  RK3399_XIN24M }
23177371cc19Skettenis 	},
23187371cc19Skettenis 	{
2319162ad205Spatrick 		RK3399_CLK_EMMC, RK3399_CRU_CLKSEL_CON(22),
2320162ad205Spatrick 		SEL(10, 8), DIV(6, 0),
2321162ad205Spatrick 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, RK3399_PLL_NPLL,
2322162ad205Spatrick 		  /* RK3399_USB_480M */ 0, RK3399_XIN24M }
2323162ad205Spatrick 	},
2324162ad205Spatrick 	{
23252aa5f6a3Skettenis 		RK3399_CLK_TSADC, RK3399_CRU_CLKSEL_CON(27),
23262aa5f6a3Skettenis 		SEL(15, 15), DIV(9, 0),
23272aa5f6a3Skettenis 		{ RK3399_XIN24M, RK3399_CLK_32K }
23282aa5f6a3Skettenis 	},
23292aa5f6a3Skettenis 	{
23307371cc19Skettenis 		RK3399_CLK_UART0, RK3399_CRU_CLKSEL_CON(33),
23317371cc19Skettenis 		SEL(9, 8), 0,
23327371cc19Skettenis 		{ 0, 0, RK3399_XIN24M }
23337371cc19Skettenis 	},
23347371cc19Skettenis 	{
23357371cc19Skettenis 		RK3399_CLK_UART1, RK3399_CRU_CLKSEL_CON(34),
23367371cc19Skettenis 		SEL(9, 8), 0,
23377371cc19Skettenis 		{ 0, 0, RK3399_XIN24M }
23387371cc19Skettenis 	},
23397371cc19Skettenis 	{
23407371cc19Skettenis 		RK3399_CLK_UART2, RK3399_CRU_CLKSEL_CON(35),
23417371cc19Skettenis 		SEL(9, 8), 0,
23427371cc19Skettenis 		{ 0, 0, RK3399_XIN24M }
23437371cc19Skettenis 	},
23447371cc19Skettenis 	{
23457371cc19Skettenis 		RK3399_CLK_UART3, RK3399_CRU_CLKSEL_CON(36),
23467371cc19Skettenis 		SEL(9, 8), 0,
23477371cc19Skettenis 		{ 0, 0, RK3399_XIN24M }
23487371cc19Skettenis 	},
23497371cc19Skettenis 	{
23507a7bc0a4Spatrick 		RK3399_CLK_I2S0_8CH, RK3399_CRU_CLKSEL_CON(28),
23517a7bc0a4Spatrick 		SEL(9, 8), 0,
2352caf123d1Skettenis 		{ RK3399_CLK_I2S0_DIV, RK3399_CLK_I2S0_FRAC, 0, RK3399_XIN12M },
23537a7bc0a4Spatrick 		SET_PARENT
23547a7bc0a4Spatrick 	},
23557a7bc0a4Spatrick 	{
23567a7bc0a4Spatrick 		RK3399_CLK_I2S1_8CH, RK3399_CRU_CLKSEL_CON(29),
23577a7bc0a4Spatrick 		SEL(9, 8), 0,
2358caf123d1Skettenis 		{ RK3399_CLK_I2S1_DIV, RK3399_CLK_I2S1_FRAC, 0, RK3399_XIN12M },
23597a7bc0a4Spatrick 		SET_PARENT
23607a7bc0a4Spatrick 	},
23617a7bc0a4Spatrick 	{
23627a7bc0a4Spatrick 		RK3399_CLK_I2S2_8CH, RK3399_CRU_CLKSEL_CON(30),
23637a7bc0a4Spatrick 		SEL(9, 8), 0,
2364caf123d1Skettenis 		{ RK3399_CLK_I2S2_DIV, RK3399_CLK_I2S2_FRAC, 0, RK3399_XIN12M },
23657a7bc0a4Spatrick 		SET_PARENT
23667a7bc0a4Spatrick 	},
23677a7bc0a4Spatrick 	{
23687a7bc0a4Spatrick 		RK3399_CLK_I2S_8CH_OUT, RK3399_CRU_CLKSEL_CON(31),
23697a7bc0a4Spatrick 		SEL(2, 2), 0,
23707a7bc0a4Spatrick 		{ RK3399_CLK_I2SOUT_SRC, RK3399_XIN12M },
23717a7bc0a4Spatrick 		SET_PARENT
23727a7bc0a4Spatrick 	},
23737a7bc0a4Spatrick 	{
2374adb68a6dSkettenis 		RK3399_CLK_MAC, RK3399_CRU_CLKSEL_CON(20),
2375adb68a6dSkettenis 		SEL(15, 14), DIV(12, 8),
2376adb68a6dSkettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, RK3399_PLL_NPLL }
2377adb68a6dSkettenis 	},
2378adb68a6dSkettenis 	{
2379526b8509Spatrick 		RK3399_CLK_UPHY0_TCPDCORE, RK3399_CRU_CLKSEL_CON(64),
2380526b8509Spatrick 		SEL(7, 6), DIV(4, 0),
2381526b8509Spatrick 		{ RK3399_XIN24M, RK3399_CLK_32K, RK3399_PLL_CPLL,
2382526b8509Spatrick 		  RK3399_PLL_GPLL }
2383526b8509Spatrick 	},
2384526b8509Spatrick 	{
2385526b8509Spatrick 		RK3399_CLK_UPHY1_TCPDCORE, RK3399_CRU_CLKSEL_CON(65),
2386526b8509Spatrick 		SEL(7, 6), DIV(4, 0),
2387526b8509Spatrick 		{ RK3399_XIN24M, RK3399_CLK_32K, RK3399_PLL_CPLL,
2388526b8509Spatrick 		  RK3399_PLL_GPLL }
2389526b8509Spatrick 	},
2390526b8509Spatrick 	{
23915486a9fbSpatrick 		RK3399_CLK_PCIEPHY_REF, RK3399_CRU_CLKSEL_CON(18),
23925486a9fbSpatrick 		SEL(10, 10), 0,
23935486a9fbSpatrick 		{ RK3399_XIN24M, RK3399_CLK_PCIEPHY_REF100M },
23945486a9fbSpatrick 		SET_PARENT
23955486a9fbSpatrick 	},
23965486a9fbSpatrick 	{
23975486a9fbSpatrick 		RK3399_CLK_PCIEPHY_REF100M, RK3399_CRU_CLKSEL_CON(18),
23985486a9fbSpatrick 		0, DIV(15, 11),
23995486a9fbSpatrick 		{ RK3399_PLL_NPLL }
24005486a9fbSpatrick 	},
24015486a9fbSpatrick 	{
2402137e484dSpatrick 		RK3399_DCLK_VOP0, RK3399_CRU_CLKSEL_CON(49),
2403137e484dSpatrick 		SEL(11, 11), 0,
2404137e484dSpatrick 		{ RK3399_DCLK_VOP0_DIV, RK3399_DCLK_VOP0_FRAC },
2405137e484dSpatrick 		SET_PARENT
2406137e484dSpatrick 	},
2407137e484dSpatrick 	{
2408137e484dSpatrick 		RK3399_DCLK_VOP1, RK3399_CRU_CLKSEL_CON(50),
2409137e484dSpatrick 		SEL(11, 11), 0,
2410137e484dSpatrick 		{ RK3399_DCLK_VOP1_DIV, RK3399_DCLK_VOP1_FRAC },
2411137e484dSpatrick 		SET_PARENT
2412137e484dSpatrick 	},
2413137e484dSpatrick 	{
2414137e484dSpatrick 		RK3399_DCLK_VOP0_DIV, RK3399_CRU_CLKSEL_CON(49),
2415137e484dSpatrick 		SEL(9, 8), DIV(7, 0),
2416137e484dSpatrick 		{ RK3399_PLL_VPLL, RK3399_PLL_CPLL, RK3399_PLL_GPLL }
2417137e484dSpatrick 	},
2418137e484dSpatrick 	{
2419137e484dSpatrick 		RK3399_DCLK_VOP1_DIV, RK3399_CRU_CLKSEL_CON(50),
2420137e484dSpatrick 		SEL(9, 8), DIV(7, 0),
2421137e484dSpatrick 		{ RK3399_PLL_VPLL, RK3399_PLL_CPLL, RK3399_PLL_GPLL }
2422137e484dSpatrick 	},
2423137e484dSpatrick 	{
24247371cc19Skettenis 		RK3399_ACLK_PERIPH, RK3399_CRU_CLKSEL_CON(14),
24257371cc19Skettenis 		SEL(7, 7), DIV(4, 0),
24267371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
24277371cc19Skettenis 	},
24287371cc19Skettenis 	{
24297371cc19Skettenis 		RK3399_ACLK_PERILP0, RK3399_CRU_CLKSEL_CON(23),
24307371cc19Skettenis 		SEL(7, 7), DIV(4, 0),
24317371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
24327371cc19Skettenis 	},
24337371cc19Skettenis 	{
24347371cc19Skettenis 		RK3399_ACLK_VIO, RK3399_CRU_CLKSEL_CON(42),
24357371cc19Skettenis 		SEL(7, 6), DIV(4, 0),
24367371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, /* RK3399_PLL_PPLL */ }
24377371cc19Skettenis 	},
24387371cc19Skettenis 	{
24397371cc19Skettenis 		RK3399_ACLK_CCI, RK3399_CRU_CLKSEL_CON(5),
24407371cc19Skettenis 		SEL(7, 6), DIV(4, 0),
24417371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, RK3399_PLL_NPLL,
24427371cc19Skettenis 		  RK3399_PLL_VPLL }
24437371cc19Skettenis 	},
24447371cc19Skettenis 	{
2445137e484dSpatrick 		RK3399_ACLK_VOP0, RK3399_CRU_CLKSEL_CON(47),
2446137e484dSpatrick 		SEL(7, 6), DIV(4, 0),
2447137e484dSpatrick 		{ RK3399_PLL_VPLL, RK3399_PLL_CPLL, RK3399_PLL_GPLL,
2448137e484dSpatrick 		  RK3399_PLL_NPLL }
2449137e484dSpatrick 	},
2450137e484dSpatrick 	{
2451137e484dSpatrick 		RK3399_ACLK_VOP1, RK3399_CRU_CLKSEL_CON(48),
2452137e484dSpatrick 		SEL(7, 6), DIV(4, 0),
2453137e484dSpatrick 		{ RK3399_PLL_VPLL, RK3399_PLL_CPLL, RK3399_PLL_GPLL,
2454137e484dSpatrick 		  RK3399_PLL_NPLL }
2455137e484dSpatrick 	},
2456137e484dSpatrick 	{
2457fdb5bc67Skettenis 		RK3399_ACLK_HDCP, RK3399_CRU_CLKSEL_CON(42),
2458fdb5bc67Skettenis 		SEL(15, 14), DIV(12, 8),
2459fdb5bc67Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL, /* RK3399_PLL_PPLL */ }
2460fdb5bc67Skettenis 	},
2461fdb5bc67Skettenis 	{
2462fdb5bc67Skettenis 		RK3399_ACLK_GIC_PRE, RK3399_CRU_CLKSEL_CON(56),
2463fdb5bc67Skettenis 		SEL(15, 15), DIV(12, 8),
2464fdb5bc67Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
2465fdb5bc67Skettenis 	},
2466fdb5bc67Skettenis 	{
24677371cc19Skettenis 		RK3399_PCLK_PERIPH, RK3399_CRU_CLKSEL_CON(14),
24687371cc19Skettenis 		0, DIV(14, 12),
24697371cc19Skettenis 		{ RK3399_ACLK_PERIPH }
24707371cc19Skettenis 	},
24717371cc19Skettenis 	{
24727371cc19Skettenis 		RK3399_PCLK_PERILP0, RK3399_CRU_CLKSEL_CON(23),
24737371cc19Skettenis 		0, DIV(14, 12),
24747371cc19Skettenis 		{ RK3399_ACLK_PERILP0 }
24757371cc19Skettenis 	},
24767371cc19Skettenis 	{
24777371cc19Skettenis 		RK3399_PCLK_PERILP1, RK3399_CRU_CLKSEL_CON(25),
24787371cc19Skettenis 		0, DIV(10, 8),
24797371cc19Skettenis 		{ RK3399_HCLK_PERILP1 }
24807371cc19Skettenis 	},
24817371cc19Skettenis 	{
2482fdb5bc67Skettenis 		RK3399_PCLK_DDR, RK3399_CRU_CLKSEL_CON(6),
2483fdb5bc67Skettenis 		SEL(15, 15), DIV(12, 8),
2484fdb5bc67Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
2485fdb5bc67Skettenis 	},
2486fdb5bc67Skettenis 	{
248743d1b45eSchrisz 		RK3399_PCLK_WDT, RK3399_CRU_CLKSEL_CON(57),
248843d1b45eSchrisz 		0, DIV(4, 0),
248943d1b45eSchrisz 		{ RK3399_PLL_GPLL }
249043d1b45eSchrisz 	},
249143d1b45eSchrisz 	{
24927371cc19Skettenis 		RK3399_HCLK_PERIPH, RK3399_CRU_CLKSEL_CON(14),
24937371cc19Skettenis 		0, DIV(9, 8),
24947371cc19Skettenis 		{ RK3399_ACLK_PERIPH }
24957371cc19Skettenis 	},
24967371cc19Skettenis 	{
24977371cc19Skettenis 		RK3399_HCLK_PERILP0, RK3399_CRU_CLKSEL_CON(23),
24987371cc19Skettenis 		0, DIV(9, 8),
24997371cc19Skettenis 		{ RK3399_ACLK_PERILP0 }
25007371cc19Skettenis 	},
25017371cc19Skettenis 	{
25027371cc19Skettenis 		RK3399_HCLK_PERILP1, RK3399_CRU_CLKSEL_CON(25),
25037371cc19Skettenis 		SEL(7, 7), DIV(4, 0),
25047371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
25057371cc19Skettenis 	},
25067371cc19Skettenis 	{
25077371cc19Skettenis 		RK3399_HCLK_SDMMC, RK3399_CRU_CLKSEL_CON(13),
25087371cc19Skettenis 		SEL(15, 15), DIV(12, 8),
25097371cc19Skettenis 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
25107371cc19Skettenis 	},
25117371cc19Skettenis 	{
2512137e484dSpatrick 		RK3399_HCLK_VOP0, RK3399_CRU_CLKSEL_CON(47),
2513137e484dSpatrick 		0, DIV(12, 8),
2514137e484dSpatrick 		{ RK3399_ACLK_VOP0 }
2515137e484dSpatrick 	},
2516137e484dSpatrick 	{
2517137e484dSpatrick 		RK3399_HCLK_VOP1, RK3399_CRU_CLKSEL_CON(48),
2518137e484dSpatrick 		0, DIV(12, 8),
2519137e484dSpatrick 		{ RK3399_ACLK_VOP1 }
2520137e484dSpatrick 	},
2521137e484dSpatrick 	{
25227a7bc0a4Spatrick 		RK3399_CLK_I2S0_DIV, RK3399_CRU_CLKSEL_CON(28),
25237a7bc0a4Spatrick 		SEL(7, 7), DIV(6, 0),
25247a7bc0a4Spatrick 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
25257a7bc0a4Spatrick 	},
25267a7bc0a4Spatrick 	{
25277a7bc0a4Spatrick 		RK3399_CLK_I2S1_DIV, RK3399_CRU_CLKSEL_CON(29),
25287a7bc0a4Spatrick 		SEL(7, 7), DIV(6, 0),
25297a7bc0a4Spatrick 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
25307a7bc0a4Spatrick 	},
25317a7bc0a4Spatrick 	{
25327a7bc0a4Spatrick 		RK3399_CLK_I2S2_DIV, RK3399_CRU_CLKSEL_CON(30),
25337a7bc0a4Spatrick 		SEL(7, 7), DIV(6, 0),
25347a7bc0a4Spatrick 		{ RK3399_PLL_CPLL, RK3399_PLL_GPLL }
25357a7bc0a4Spatrick 	},
25367a7bc0a4Spatrick 	{
25377a7bc0a4Spatrick 		RK3399_CLK_I2SOUT_SRC, RK3399_CRU_CLKSEL_CON(31),
25387a7bc0a4Spatrick 		SEL(1, 0), 0,
25397a7bc0a4Spatrick 		{ RK3399_CLK_I2S0_8CH, RK3399_CLK_I2S1_8CH,
25407a7bc0a4Spatrick 		  RK3399_CLK_I2S2_8CH },
25417a7bc0a4Spatrick 		SET_PARENT
25427a7bc0a4Spatrick 	},
25437a7bc0a4Spatrick 	{
25447371cc19Skettenis 		/* Sentinel */
25457371cc19Skettenis 	}
25467371cc19Skettenis };
25477371cc19Skettenis 
2548ad8cadefSkettenis /* Some of our parent clocks live in the PMUCRU. */
2549ad8cadefSkettenis struct rkclock_softc *rk3399_pmucru_sc;
2550ad8cadefSkettenis 
255169da300fSkettenis void
255269da300fSkettenis rk3399_init(struct rkclock_softc *sc)
255369da300fSkettenis {
255444eccc75Skettenis 	int i;
2555f8c4fba2Skettenis 
2556ad8cadefSkettenis 	/* PMUCRU instance should attach before us. */
2557ad8cadefSkettenis 	KASSERT(rk3399_pmucru_sc != NULL);
2558ad8cadefSkettenis 
2559f8c4fba2Skettenis 	/*
25601af18cc1Skettenis 	 * The U-Boot shipped on the Theobroma Systems RK3399-Q7
25611af18cc1Skettenis 	 * module is buggy and sets the parent of the clock for the
25621af18cc1Skettenis 	 * "big" cluster to LPLL.  Undo that mistake here such that
25631af18cc1Skettenis 	 * the clocks of both clusters are independent.
25641af18cc1Skettenis 	 */
25651af18cc1Skettenis 	HWRITE4(sc, RK3399_CRU_CLKSEL_CON(2),
25661af18cc1Skettenis 	    RK3399_CRU_CORE_PLL_SEL_MASK << 16 |
25671af18cc1Skettenis 	    RK3399_CRU_CORE_PLL_SEL_BPLL);
25681af18cc1Skettenis 
256944eccc75Skettenis 	/* The code below assumes all clocks are enabled.  Check this!. */
257044eccc75Skettenis 	for (i = 0; i <= 34; i++) {
257144eccc75Skettenis 		if (HREAD4(sc, RK3399_CRU_CLKGATE_CON(i)) != 0x00000000) {
257244eccc75Skettenis 			printf("CRU_CLKGATE_CON%d: 0x%08x\n", i,
257344eccc75Skettenis 			    HREAD4(sc, RK3399_CRU_CLKGATE_CON(i)));
257444eccc75Skettenis 		}
257544eccc75Skettenis 	}
25767371cc19Skettenis 
25777371cc19Skettenis 	sc->sc_clocks = rk3399_clocks;
2578f8c4fba2Skettenis }
257969da300fSkettenis 
2580d9457073Skettenis uint32_t
2581d9457073Skettenis rk3399_get_pll(struct rkclock_softc *sc, bus_size_t base)
2582d9457073Skettenis {
2583d9457073Skettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
258413d6baf8Skettenis 	uint32_t pll_work_mode;
2585d9457073Skettenis 	uint32_t reg;
2586d9457073Skettenis 
258713d6baf8Skettenis 	reg = HREAD4(sc, base + 0x000c);
258869da300fSkettenis 	pll_work_mode = reg & RK3399_CRU_PLL_PLL_WORK_MODE_MASK;
258969da300fSkettenis 	if (pll_work_mode == RK3399_CRU_PLL_PLL_WORK_MODE_SLOW)
259013d6baf8Skettenis 		return 24000000;
259169da300fSkettenis 	if (pll_work_mode == RK3399_CRU_PLL_PLL_WORK_MODE_DEEP_SLOW)
259213d6baf8Skettenis 		return 32768;
259313d6baf8Skettenis 
259413d6baf8Skettenis 	reg = HREAD4(sc, base + 0x0000);
2595f8c4fba2Skettenis 	fbdiv = (reg & RK3399_CRU_PLL_FBDIV_MASK) >>
2596f8c4fba2Skettenis 	    RK3399_CRU_PLL_FBDIV_SHIFT;
259713d6baf8Skettenis 	reg = HREAD4(sc, base + 0x0004);
2598f8c4fba2Skettenis 	postdiv2 = (reg & RK3399_CRU_PLL_POSTDIV2_MASK) >>
2599f8c4fba2Skettenis 	    RK3399_CRU_PLL_POSTDIV2_SHIFT;
2600f8c4fba2Skettenis 	postdiv1 = (reg & RK3399_CRU_PLL_POSTDIV1_MASK) >>
2601f8c4fba2Skettenis 	    RK3399_CRU_PLL_POSTDIV1_SHIFT;
2602f8c4fba2Skettenis 	refdiv = (reg & RK3399_CRU_PLL_REFDIV_MASK) >>
2603f8c4fba2Skettenis 	    RK3399_CRU_PLL_REFDIV_SHIFT;
2604d9457073Skettenis 	return 24000000ULL * fbdiv / refdiv / postdiv1 / postdiv2;
2605d9457073Skettenis }
2606592afca7Skettenis 
2607f8c4fba2Skettenis int
2608f8c4fba2Skettenis rk3399_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
2609f8c4fba2Skettenis {
2610f8c4fba2Skettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
2611f8c4fba2Skettenis 
2612f8c4fba2Skettenis 	/*
2613f8c4fba2Skettenis 	 * It is not clear whether all combinations of the clock
2614f8c4fba2Skettenis 	 * dividers result in a stable clock.  Therefore this function
2615f8c4fba2Skettenis 	 * only supports a limited set of PLL clock rates.  For now
2616f8c4fba2Skettenis 	 * this set covers all the CPU frequencies supported by the
2617f8c4fba2Skettenis 	 * Linux kernel.
2618f8c4fba2Skettenis 	 */
2619f8c4fba2Skettenis 	switch (freq) {
26207a20c0feSkettenis 	case 2208000000U:
26217a20c0feSkettenis 	case 2184000000U:
26227a20c0feSkettenis 	case 2088000000U:
26237a20c0feSkettenis 	case 2040000000U:
26247a20c0feSkettenis 	case 2016000000U:
26257a20c0feSkettenis 	case 1992000000U:
26267a20c0feSkettenis 	case 1896000000U:
26277a20c0feSkettenis 	case 1800000000U:
26287a20c0feSkettenis 	case 1704000000U:
26297a20c0feSkettenis 	case 1608000000U:
26307a20c0feSkettenis 	case 1512000000U:
26317a20c0feSkettenis 	case 1488000000U:
26327a20c0feSkettenis 	case 1416000000U:
26337a20c0feSkettenis 	case 1200000000U:
2634f8c4fba2Skettenis 		postdiv1 = postdiv2 = refdiv = 1;
2635f8c4fba2Skettenis 		break;
26367a20c0feSkettenis 	case 1008000000U:
26377a20c0feSkettenis 	case 816000000U:
26387a20c0feSkettenis 	case 696000000U:
2639f8c4fba2Skettenis 		postdiv1 = 2; postdiv2 = refdiv = 1;
2640f8c4fba2Skettenis 		break;
264198a76083Skettenis 	case 676000000U:
264298a76083Skettenis 		postdiv1 = 2; postdiv2 = 1; refdiv = 3;
264398a76083Skettenis 		break;
264498a76083Skettenis 	case 1000000000U:
264598a76083Skettenis 	case 800000000U:
26467a20c0feSkettenis 	case 600000000U:
2647f8c4fba2Skettenis 		postdiv1 = 3; postdiv2 = refdiv = 1;
2648f8c4fba2Skettenis 		break;
264998a76083Skettenis 	case 594000000U:
265098a76083Skettenis 		postdiv1 = 4; postdiv2 = refdiv = 1;
265198a76083Skettenis 		break;
26527a20c0feSkettenis 	case 408000000U:
2653f8c4fba2Skettenis 		postdiv1 = postdiv2 = 2; refdiv = 1;
2654f8c4fba2Skettenis 		break;
2655549c5039Skettenis 	case 297000000U:
26567a20c0feSkettenis 	case 216000000U:
2657f8c4fba2Skettenis 		postdiv1 = 4; postdiv2 = 2; refdiv = 1;
2658f8c4fba2Skettenis 		break;
2659549c5039Skettenis 	case 148500000U:
26607a20c0feSkettenis 	case 96000000U:
2661f8c4fba2Skettenis 		postdiv1 = postdiv2 = 4; refdiv = 1;
2662f8c4fba2Skettenis 		break;
2663549c5039Skettenis 	case 74250000U:
2664549c5039Skettenis 		postdiv1 = postdiv2 = 4; refdiv = 2;
2665549c5039Skettenis 		break;
2666549c5039Skettenis 	case 65000000U:
2667549c5039Skettenis 	case 54000000U:
2668549c5039Skettenis 	case 27000000U:
2669549c5039Skettenis 		postdiv1 = 6; postdiv2 = 4; refdiv = 1;
2670549c5039Skettenis 		break;
2671f8c4fba2Skettenis 	default:
26729822ab30Skettenis 		printf("%s: %d Hz\n", __func__, freq);
2673f8c4fba2Skettenis 		return -1;
2674f8c4fba2Skettenis 	}
2675f8c4fba2Skettenis 
2676f8c4fba2Skettenis 	/* Calculate feedback divider. */
2677f8c4fba2Skettenis 	fbdiv = freq * postdiv1 * postdiv2 * refdiv / 24000000;
2678f8c4fba2Skettenis 
2679f8c4fba2Skettenis 	/*
2680f8c4fba2Skettenis 	 * Select slow mode to guarantee a stable clock while we're
2681f8c4fba2Skettenis 	 * adjusting the PLL.
2682f8c4fba2Skettenis 	 */
2683f8c4fba2Skettenis 	HWRITE4(sc, base + 0x000c,
2684f8c4fba2Skettenis 	    RK3399_CRU_PLL_PLL_WORK_MODE_MASK << 16 |
2685f8c4fba2Skettenis 	    RK3399_CRU_PLL_PLL_WORK_MODE_SLOW);
2686f8c4fba2Skettenis 
2687f8c4fba2Skettenis 	/* Set PLL rate. */
2688f8c4fba2Skettenis 	HWRITE4(sc, base + 0x0000,
2689f8c4fba2Skettenis 	    RK3399_CRU_PLL_FBDIV_MASK << 16 |
2690f8c4fba2Skettenis 	    fbdiv << RK3399_CRU_PLL_FBDIV_SHIFT);
2691f8c4fba2Skettenis 	HWRITE4(sc, base + 0x0004,
2692f8c4fba2Skettenis 	    RK3399_CRU_PLL_POSTDIV2_MASK << 16 |
2693f8c4fba2Skettenis 	    postdiv2 << RK3399_CRU_PLL_POSTDIV2_SHIFT |
2694f8c4fba2Skettenis 	    RK3399_CRU_PLL_POSTDIV1_MASK << 16 |
2695f8c4fba2Skettenis 	    postdiv1 << RK3399_CRU_PLL_POSTDIV1_SHIFT |
2696f8c4fba2Skettenis 	    RK3399_CRU_PLL_REFDIV_MASK << 16 |
2697f8c4fba2Skettenis 	    refdiv << RK3399_CRU_PLL_REFDIV_SHIFT);
2698f8c4fba2Skettenis 
2699f8c4fba2Skettenis 	/* Wait for PLL to stabilize. */
2700f8c4fba2Skettenis 	while ((HREAD4(sc, base + 0x0008) & RK3399_CRU_PLL_PLL_LOCK) == 0)
2701f8c4fba2Skettenis 		delay(10);
2702f8c4fba2Skettenis 
2703f8c4fba2Skettenis 	/* Switch back to normal mode. */
2704f8c4fba2Skettenis 	HWRITE4(sc, base + 0x000c,
2705f8c4fba2Skettenis 	    RK3399_CRU_PLL_PLL_WORK_MODE_MASK << 16 |
2706f8c4fba2Skettenis 	    RK3399_CRU_PLL_PLL_WORK_MODE_NORMAL);
2707f8c4fba2Skettenis 
2708f8c4fba2Skettenis 	return 0;
2709f8c4fba2Skettenis }
2710f8c4fba2Skettenis 
2711f8c4fba2Skettenis uint32_t
2712f8c4fba2Skettenis rk3399_armclk_parent(uint32_t mux)
2713f8c4fba2Skettenis {
2714f8c4fba2Skettenis 	switch (mux) {
2715f8c4fba2Skettenis 	case 0:
2716f8c4fba2Skettenis 		return RK3399_PLL_ALPLL;
2717f8c4fba2Skettenis 	case 1:
2718f8c4fba2Skettenis 		return RK3399_PLL_ABPLL;
2719f8c4fba2Skettenis 	case 2:
2720f8c4fba2Skettenis 		return RK3399_PLL_DPLL;
2721f8c4fba2Skettenis 	case 3:
2722f8c4fba2Skettenis 		return RK3399_PLL_GPLL;
2723f8c4fba2Skettenis 	}
2724f8c4fba2Skettenis 
2725f8c4fba2Skettenis 	return 0;
2726f8c4fba2Skettenis }
2727f8c4fba2Skettenis 
2728592afca7Skettenis uint32_t
272913d6baf8Skettenis rk3399_get_armclk(struct rkclock_softc *sc, bus_size_t clksel)
273013d6baf8Skettenis {
273113d6baf8Skettenis 	uint32_t reg, mux, div_con;
273213d6baf8Skettenis 	uint32_t idx;
273313d6baf8Skettenis 
273413d6baf8Skettenis 	reg = HREAD4(sc, clksel);
2735f8c4fba2Skettenis 	mux = (reg & RK3399_CRU_CORE_PLL_SEL_MASK) >>
2736f8c4fba2Skettenis 	    RK3399_CRU_CORE_PLL_SEL_SHIFT;
2737f8c4fba2Skettenis 	div_con = (reg & RK3399_CRU_CLK_CORE_DIV_CON_MASK) >>
2738f8c4fba2Skettenis 	    RK3399_CRU_CLK_CORE_DIV_CON_SHIFT;
2739f8c4fba2Skettenis 	idx = rk3399_armclk_parent(mux);
274013d6baf8Skettenis 
274113d6baf8Skettenis 	return rk3399_get_frequency(sc, &idx) / (div_con + 1);
274213d6baf8Skettenis }
274313d6baf8Skettenis 
2744f8c4fba2Skettenis int
2745f8c4fba2Skettenis rk3399_set_armclk(struct rkclock_softc *sc, bus_size_t clksel, uint32_t freq)
2746f8c4fba2Skettenis {
2747f8c4fba2Skettenis 	uint32_t reg, mux;
2748f8c4fba2Skettenis 	uint32_t old_freq, div;
2749f8c4fba2Skettenis 	uint32_t idx;
2750f8c4fba2Skettenis 
2751f8c4fba2Skettenis 	old_freq = rk3399_get_armclk(sc, clksel);
2752f8c4fba2Skettenis 	if (freq == old_freq)
2753f8c4fba2Skettenis 		return 0;
2754f8c4fba2Skettenis 
2755f8c4fba2Skettenis 	reg = HREAD4(sc, clksel);
2756f8c4fba2Skettenis 	mux = (reg & RK3399_CRU_CORE_PLL_SEL_MASK) >>
2757f8c4fba2Skettenis 	    RK3399_CRU_CORE_PLL_SEL_SHIFT;
2758f8c4fba2Skettenis 	idx = rk3399_armclk_parent(mux);
2759f8c4fba2Skettenis 
2760f8c4fba2Skettenis 	/* Keep the atclk_core and pclk_dbg clocks at or below 200 MHz. */
2761f8c4fba2Skettenis 	div = 1;
2762f8c4fba2Skettenis 	while (freq / (div + 1) > 200000000)
2763f8c4fba2Skettenis 		div++;
2764f8c4fba2Skettenis 
2765f8c4fba2Skettenis 	/* When ramping up, set clock dividers first. */
2766f8c4fba2Skettenis 	if (freq > old_freq) {
27672cb496bcSkettenis 		HWRITE4(sc, clksel,
2768f8c4fba2Skettenis 		    RK3399_CRU_CLK_CORE_DIV_CON_MASK << 16 |
2769f8c4fba2Skettenis 		    0 << RK3399_CRU_CLK_CORE_DIV_CON_SHIFT |
2770f8c4fba2Skettenis 		    RK3399_CRU_ACLKM_CORE_DIV_CON_MASK << 16 |
2771f8c4fba2Skettenis 		    1 << RK3399_CRU_ACLKM_CORE_DIV_CON_SHIFT);
27722cb496bcSkettenis 		HWRITE4(sc, clksel + 0x0004,
2773f8c4fba2Skettenis 		    RK3399_CRU_PCLK_DBG_DIV_CON_MASK << 16 |
2774f8c4fba2Skettenis 		    div << RK3399_CRU_PCLK_DBG_DIV_CON_SHIFT |
2775f8c4fba2Skettenis 		    RK3399_CRU_ATCLK_CORE_DIV_CON_MASK << 16 |
2776f8c4fba2Skettenis 		    div << RK3399_CRU_ATCLK_CORE_DIV_CON_SHIFT);
2777f8c4fba2Skettenis 	}
2778f8c4fba2Skettenis 
2779f8c4fba2Skettenis 	rk3399_set_frequency(sc, &idx, freq);
2780f8c4fba2Skettenis 
27814cecc5e3Skettenis 	/* When ramping down, set clock dividers last. */
2782f8c4fba2Skettenis 	if (freq < old_freq) {
27832cb496bcSkettenis 		HWRITE4(sc, clksel,
2784f8c4fba2Skettenis 		    RK3399_CRU_CLK_CORE_DIV_CON_MASK << 16 |
2785f8c4fba2Skettenis 		    0 << RK3399_CRU_CLK_CORE_DIV_CON_SHIFT |
2786f8c4fba2Skettenis 		    RK3399_CRU_ACLKM_CORE_DIV_CON_MASK << 16 |
2787f8c4fba2Skettenis 		    1 << RK3399_CRU_ACLKM_CORE_DIV_CON_SHIFT);
27882cb496bcSkettenis 		HWRITE4(sc, clksel + 0x0004,
2789f8c4fba2Skettenis 		    RK3399_CRU_PCLK_DBG_DIV_CON_MASK << 16 |
2790f8c4fba2Skettenis 		    div << RK3399_CRU_PCLK_DBG_DIV_CON_SHIFT |
2791f8c4fba2Skettenis 		    RK3399_CRU_ATCLK_CORE_DIV_CON_MASK << 16 |
2792f8c4fba2Skettenis 		    div << RK3399_CRU_ATCLK_CORE_DIV_CON_SHIFT);
2793f8c4fba2Skettenis 	}
2794f8c4fba2Skettenis 
2795f8c4fba2Skettenis 	return 0;
2796f8c4fba2Skettenis }
2797f8c4fba2Skettenis 
279813d6baf8Skettenis uint32_t
2799a21015c5Skettenis rk3399_get_frac(struct rkclock_softc *sc, uint32_t parent, bus_size_t base)
2800caf123d1Skettenis {
2801a21015c5Skettenis 	uint32_t parent_freq, frac;
2802caf123d1Skettenis 	uint16_t n, d;
2803caf123d1Skettenis 
2804caf123d1Skettenis 	frac = HREAD4(sc, base);
2805caf123d1Skettenis 	n = frac >> 16;
2806caf123d1Skettenis 	d = frac & 0xffff;
2807a21015c5Skettenis 	if (n == 0 || d == 0)
2808a21015c5Skettenis 		n = d = 1;
2809a21015c5Skettenis 	parent_freq = sc->sc_cd.cd_get_frequency(sc, &parent);
2810a21015c5Skettenis 	return ((uint64_t)parent_freq * n) / d;
2811caf123d1Skettenis }
2812caf123d1Skettenis 
2813caf123d1Skettenis int
2814a21015c5Skettenis rk3399_set_frac(struct rkclock_softc *sc, uint32_t parent, bus_size_t base,
2815caf123d1Skettenis     uint32_t freq)
2816caf123d1Skettenis {
2817caf123d1Skettenis 	uint32_t n, d;
2818caf123d1Skettenis 	uint32_t p0, p1, p2;
2819caf123d1Skettenis 	uint32_t q0, q1, q2;
2820caf123d1Skettenis 	uint32_t a, tmp;
2821caf123d1Skettenis 
2822caf123d1Skettenis 	n = freq;
2823a21015c5Skettenis 	d = sc->sc_cd.cd_get_frequency(sc, &parent);
2824caf123d1Skettenis 
2825caf123d1Skettenis 	/*
2826caf123d1Skettenis 	 * The denominator needs to be at least 20 times the numerator
2827caf123d1Skettenis 	 * for a stable clock.
2828caf123d1Skettenis 	 */
2829caf123d1Skettenis 	if (n == 0 || d == 0 || d < 20 * n)
2830caf123d1Skettenis 		return -1;
2831caf123d1Skettenis 
2832caf123d1Skettenis 	/*
2833caf123d1Skettenis 	 * This is a simplified implementation of the algorithm to
2834caf123d1Skettenis 	 * calculate the best rational approximation using continued
2835caf123d1Skettenis 	 * fractions.
2836caf123d1Skettenis 	 */
2837caf123d1Skettenis 
2838caf123d1Skettenis 	p0 = q1 = 0;
2839caf123d1Skettenis 	p1 = q0 = 1;
2840caf123d1Skettenis 
2841caf123d1Skettenis 	while (d != 0) {
2842caf123d1Skettenis 		/*
2843caf123d1Skettenis 		 * Calculate next coefficient in the continued
2844caf123d1Skettenis 		 * fraction and keep track of the remainder.
2845caf123d1Skettenis 		 */
2846caf123d1Skettenis 		tmp = d;
2847caf123d1Skettenis 		a = n / d;
2848caf123d1Skettenis 		d = n % d;
2849caf123d1Skettenis 		n = tmp;
2850caf123d1Skettenis 
2851caf123d1Skettenis 		/*
2852caf123d1Skettenis 		 * Calculate next approximation in the series based on
2853caf123d1Skettenis 		 * the current coefficient.
2854caf123d1Skettenis 		 */
2855caf123d1Skettenis 		p2 = p0 + a * p1;
2856caf123d1Skettenis 		q2 = q0 + a * q1;
2857caf123d1Skettenis 
2858caf123d1Skettenis 		/*
2859caf123d1Skettenis 		 * Terminate if we reached the maximum allowed
2860caf123d1Skettenis 		 * denominator.
2861caf123d1Skettenis 		 */
2862caf123d1Skettenis 		if (q2 > 0xffff)
2863caf123d1Skettenis 			break;
2864caf123d1Skettenis 
2865caf123d1Skettenis 		p0 = p1; p1 = p2;
2866caf123d1Skettenis 		q0 = q1; q1 = q2;
2867caf123d1Skettenis 	}
2868caf123d1Skettenis 
2869caf123d1Skettenis 	HWRITE4(sc, base, p1 << 16 | q1);
2870caf123d1Skettenis 	return 0;
2871caf123d1Skettenis }
2872caf123d1Skettenis 
2873caf123d1Skettenis uint32_t
2874592afca7Skettenis rk3399_get_frequency(void *cookie, uint32_t *cells)
2875592afca7Skettenis {
2876592afca7Skettenis 	struct rkclock_softc *sc = cookie;
2877592afca7Skettenis 	uint32_t idx = cells[0];
2878592afca7Skettenis 
2879592afca7Skettenis 	switch (idx) {
288013d6baf8Skettenis 	case RK3399_PLL_ALPLL:
288113d6baf8Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_LPLL_CON(0));
288213d6baf8Skettenis 	case RK3399_PLL_ABPLL:
288313d6baf8Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_BPLL_CON(0));
288413d6baf8Skettenis 	case RK3399_PLL_DPLL:
288513d6baf8Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_DPLL_CON(0));
2886d9457073Skettenis 	case RK3399_PLL_CPLL:
2887d9457073Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_CPLL_CON(0));
2888d9457073Skettenis 	case RK3399_PLL_GPLL:
2889d9457073Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_GPLL_CON(0));
2890f89cc881Skettenis 	case RK3399_PLL_NPLL:
2891f89cc881Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_NPLL_CON(0));
289298a76083Skettenis 	case RK3399_PLL_VPLL:
289398a76083Skettenis 		return rk3399_get_pll(sc, RK3399_CRU_VPLL_CON(0));
289413d6baf8Skettenis 	case RK3399_ARMCLKL:
289513d6baf8Skettenis 		return rk3399_get_armclk(sc, RK3399_CRU_CLKSEL_CON(0));
289613d6baf8Skettenis 	case RK3399_ARMCLKB:
289713d6baf8Skettenis 		return rk3399_get_armclk(sc, RK3399_CRU_CLKSEL_CON(2));
28987371cc19Skettenis 	case RK3399_XIN24M:
28997371cc19Skettenis 		return 24000000;
29002aa5f6a3Skettenis 	case RK3399_CLK_32K:
29012aa5f6a3Skettenis 		return 32768;
29027a7bc0a4Spatrick 	case RK3399_XIN12M:
29037a7bc0a4Spatrick 		return 12000000;
2904caf123d1Skettenis 	case RK3399_CLK_I2S0_FRAC:
2905caf123d1Skettenis 		return rk3399_get_frac(sc, RK3399_CLK_I2S0_DIV,
2906caf123d1Skettenis 		    RK3399_CRU_CLKSEL_CON(96));
2907caf123d1Skettenis 	case RK3399_CLK_I2S1_FRAC:
2908caf123d1Skettenis 		return rk3399_get_frac(sc, RK3399_CLK_I2S1_DIV,
2909caf123d1Skettenis 		    RK3399_CRU_CLKSEL_CON(97));
2910caf123d1Skettenis 	case RK3399_CLK_I2S2_FRAC:
2911caf123d1Skettenis 		return rk3399_get_frac(sc, RK3399_CLK_I2S2_DIV,
2912caf123d1Skettenis 		    RK3399_CRU_CLKSEL_CON(98));
2913592afca7Skettenis 	default:
2914592afca7Skettenis 		break;
2915592afca7Skettenis 	}
2916592afca7Skettenis 
29177371cc19Skettenis 	return rkclock_get_frequency(sc, idx);
2918592afca7Skettenis }
2919592afca7Skettenis 
2920592afca7Skettenis int
2921592afca7Skettenis rk3399_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
2922592afca7Skettenis {
2923f8c4fba2Skettenis 	struct rkclock_softc *sc = cookie;
2924592afca7Skettenis 	uint32_t idx = cells[0];
2925592afca7Skettenis 
2926f8c4fba2Skettenis 	switch (idx) {
2927f8c4fba2Skettenis 	case RK3399_PLL_ALPLL:
2928f8c4fba2Skettenis 		return rk3399_set_pll(sc, RK3399_CRU_LPLL_CON(0), freq);
2929f8c4fba2Skettenis 	case RK3399_PLL_ABPLL:
2930f8c4fba2Skettenis 		return rk3399_set_pll(sc, RK3399_CRU_BPLL_CON(0), freq);
293198a76083Skettenis 	case RK3399_PLL_CPLL:
293298a76083Skettenis 		return rk3399_set_pll(sc, RK3399_CRU_CPLL_CON(0), freq);
293398a76083Skettenis 	case RK3399_PLL_GPLL:
293498a76083Skettenis 		return rk3399_set_pll(sc, RK3399_CRU_GPLL_CON(0), freq);
293598a76083Skettenis 	case RK3399_PLL_NPLL:
293698a76083Skettenis 		return rk3399_set_pll(sc, RK3399_CRU_NPLL_CON(0), freq);
2937549c5039Skettenis 	case RK3399_PLL_VPLL:
2938549c5039Skettenis 		return rk3399_set_pll(sc, RK3399_CRU_VPLL_CON(0), freq);
2939f8c4fba2Skettenis 	case RK3399_ARMCLKL:
2940f8c4fba2Skettenis 		return rk3399_set_armclk(sc, RK3399_CRU_CLKSEL_CON(0), freq);
2941f8c4fba2Skettenis 	case RK3399_ARMCLKB:
2942f8c4fba2Skettenis 		return rk3399_set_armclk(sc, RK3399_CRU_CLKSEL_CON(2), freq);
2943caf123d1Skettenis 	case RK3399_CLK_I2S0_8CH:
2944caf123d1Skettenis 		rkclock_set_parent(sc, idx, RK3399_CLK_I2S0_FRAC);
2945caf123d1Skettenis 		return rkclock_set_frequency(sc, idx, freq);
2946caf123d1Skettenis 	case RK3399_CLK_I2S1_8CH:
2947caf123d1Skettenis 		rkclock_set_parent(sc, idx, RK3399_CLK_I2S1_FRAC);
2948caf123d1Skettenis 		return rkclock_set_frequency(sc, idx, freq);
2949caf123d1Skettenis 	case RK3399_CLK_I2S2_8CH:
2950caf123d1Skettenis 		rkclock_set_parent(sc, idx, RK3399_CLK_I2S2_FRAC);
2951caf123d1Skettenis 		return rkclock_set_frequency(sc, idx, freq);
29527a7bc0a4Spatrick 	case RK3399_XIN12M:
29537a7bc0a4Spatrick 		if (freq / (1000 * 1000) != 12)
29547a7bc0a4Spatrick 			return -1;
29557a7bc0a4Spatrick 		return 0;
2956caf123d1Skettenis 	case RK3399_CLK_I2S0_FRAC:
2957caf123d1Skettenis 		return rk3399_set_frac(sc, RK3399_CLK_I2S0_DIV,
2958caf123d1Skettenis 		    RK3399_CRU_CLKSEL_CON(96), freq);
2959caf123d1Skettenis 	case RK3399_CLK_I2S1_FRAC:
2960caf123d1Skettenis 		return rk3399_set_frac(sc, RK3399_CLK_I2S1_DIV,
2961caf123d1Skettenis 		    RK3399_CRU_CLKSEL_CON(97), freq);
2962caf123d1Skettenis 	case RK3399_CLK_I2S2_FRAC:
2963caf123d1Skettenis 		return rk3399_set_frac(sc, RK3399_CLK_I2S2_DIV,
2964caf123d1Skettenis 		    RK3399_CRU_CLKSEL_CON(98), freq);
296598a76083Skettenis 	default:
296698a76083Skettenis 		break;
2967f8c4fba2Skettenis 	}
2968f8c4fba2Skettenis 
29697371cc19Skettenis 	return rkclock_set_frequency(sc, idx, freq);
2970592afca7Skettenis }
2971592afca7Skettenis 
29725486a9fbSpatrick 
29735486a9fbSpatrick int
29745486a9fbSpatrick rk3399_set_parent(void *cookie, uint32_t *cells, uint32_t *pcells)
29755486a9fbSpatrick {
29765486a9fbSpatrick 	struct rkclock_softc *sc = cookie;
29775486a9fbSpatrick 
29785486a9fbSpatrick 	if (pcells[0] != sc->sc_phandle)
29795486a9fbSpatrick 		return -1;
29805486a9fbSpatrick 
29815486a9fbSpatrick 	return rkclock_set_parent(sc, cells[0], pcells[1]);
29825486a9fbSpatrick }
29835486a9fbSpatrick 
2984592afca7Skettenis void
2985592afca7Skettenis rk3399_enable(void *cookie, uint32_t *cells, int on)
2986592afca7Skettenis {
2987875e49c6Spatrick 	struct rkclock_softc *sc = cookie;
2988592afca7Skettenis 	uint32_t idx = cells[0];
2989592afca7Skettenis 
299044eccc75Skettenis 	/*
2991875e49c6Spatrick 	 * All clocks are enabled upon hardware reset, but on some boards the
2992875e49c6Spatrick 	 * firmware will disable some of them.  Handle those here.
299344eccc75Skettenis 	 */
2994875e49c6Spatrick 	if (!on) {
2995592afca7Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
2996875e49c6Spatrick 		return;
2997875e49c6Spatrick 	}
2998875e49c6Spatrick 
2999875e49c6Spatrick 	switch (idx) {
30002479590bSkettenis 	case RK3399_CLK_USB2PHY0_REF:
3001*41b6ab1fSkettenis 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(6), (1 << 5) << 16);
30022479590bSkettenis 		break;
30032479590bSkettenis 	case RK3399_CLK_USB2PHY1_REF:
3004*41b6ab1fSkettenis 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(6), (1 << 6) << 16);
30052479590bSkettenis 		break;
30062479590bSkettenis 	case RK3399_CLK_UPHY0_TCPDPHY_REF:
3007*41b6ab1fSkettenis 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(13), (1 << 4) << 16);
30082479590bSkettenis 		break;
30092479590bSkettenis 	case RK3399_CLK_UPHY0_TCPDCORE:
3010*41b6ab1fSkettenis 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(13), (1 << 5) << 16);
30112479590bSkettenis 		break;
30122479590bSkettenis 	case RK3399_CLK_UPHY1_TCPDPHY_REF:
3013*41b6ab1fSkettenis 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(13), (1 << 6) << 16);
30142479590bSkettenis 		break;
30152479590bSkettenis 	case RK3399_CLK_UPHY1_TCPDCORE:
3016*41b6ab1fSkettenis 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(13), (1 << 7) << 16);
30172479590bSkettenis 		break;
3018875e49c6Spatrick 	case RK3399_ACLK_GMAC:
3019875e49c6Spatrick 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(32), (1 << 0) << 16);
3020875e49c6Spatrick 		break;
3021875e49c6Spatrick 	case RK3399_PCLK_GMAC:
3022875e49c6Spatrick 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(32), (1 << 2) << 16);
3023875e49c6Spatrick 		break;
3024875e49c6Spatrick 	case RK3399_CLK_MAC:
3025875e49c6Spatrick 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(5), (1 << 5) << 16);
3026875e49c6Spatrick 		break;
3027875e49c6Spatrick 	case RK3399_CLK_MAC_RX:
3028875e49c6Spatrick 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(5), (1 << 8) << 16);
3029875e49c6Spatrick 		break;
3030875e49c6Spatrick 	case RK3399_CLK_MAC_TX:
3031875e49c6Spatrick 		HWRITE4(sc, RK3399_CRU_CLKGATE_CON(5), (1 << 9) << 16);
3032875e49c6Spatrick 		break;
3033875e49c6Spatrick 	}
3034592afca7Skettenis }
3035592afca7Skettenis 
3036592afca7Skettenis void
3037592afca7Skettenis rk3399_reset(void *cookie, uint32_t *cells, int on)
3038592afca7Skettenis {
303964b62fe2Skettenis 	struct rkclock_softc *sc = cookie;
3040592afca7Skettenis 	uint32_t idx = cells[0];
304164b62fe2Skettenis 	uint32_t mask = (1 << (idx % 16));
3042592afca7Skettenis 
304364b62fe2Skettenis 	HWRITE4(sc, RK3399_CRU_SOFTRST_CON(idx / 16),
304464b62fe2Skettenis 	    mask << 16 | (on ? mask : 0));
3045592afca7Skettenis }
3046592afca7Skettenis 
3047f8c4fba2Skettenis /* PMUCRU */
3048f8c4fba2Skettenis 
304937c734d3Snaddy const struct rkclock rk3399_pmu_clocks[] = {
30507371cc19Skettenis 	{
30517371cc19Skettenis 		RK3399_CLK_I2C0, RK3399_PMUCRU_CLKSEL_CON(2),
30527371cc19Skettenis 		0, DIV(6, 0),
30537371cc19Skettenis 		{ RK3399_PLL_PPLL }
30547371cc19Skettenis 	},
30557371cc19Skettenis 	{
30567371cc19Skettenis 		RK3399_CLK_I2C4, RK3399_PMUCRU_CLKSEL_CON(3),
30577371cc19Skettenis 		0, DIV(6, 0),
30587371cc19Skettenis 		{ RK3399_PLL_PPLL }
30597371cc19Skettenis 	},
30607371cc19Skettenis 	{
30617371cc19Skettenis 		RK3399_CLK_I2C8, RK3399_PMUCRU_CLKSEL_CON(2),
30627371cc19Skettenis 		0, DIV(14, 8),
30637371cc19Skettenis 		{ RK3399_PLL_PPLL }
30647371cc19Skettenis 	},
30657371cc19Skettenis 	{
30668c9e697fSpatrick 		RK3399_PCLK_RKPWM, RK3399_PMUCRU_CLKSEL_CON(0),
30678c9e697fSpatrick 		0, DIV(6, 0),
30688c9e697fSpatrick 		{ RK3399_PLL_PPLL }
30698c9e697fSpatrick 	},
30708c9e697fSpatrick 	{
30717371cc19Skettenis 		/* Sentinel */
30727371cc19Skettenis 	}
30737371cc19Skettenis };
30747371cc19Skettenis 
3075ad8cadefSkettenis void
3076ad8cadefSkettenis rk3399_pmu_init(struct rkclock_softc *sc)
3077ad8cadefSkettenis {
30787371cc19Skettenis 	sc->sc_clocks = rk3399_pmu_clocks;
3079ad8cadefSkettenis 	rk3399_pmucru_sc = sc;
3080ad8cadefSkettenis }
3081ad8cadefSkettenis 
3082ad8cadefSkettenis uint32_t
3083592afca7Skettenis rk3399_pmu_get_frequency(void *cookie, uint32_t *cells)
3084592afca7Skettenis {
3085ad8cadefSkettenis 	struct rkclock_softc *sc = cookie;
3086592afca7Skettenis 	uint32_t idx = cells[0];
3087592afca7Skettenis 
3088ad8cadefSkettenis 	switch (idx) {
3089ad8cadefSkettenis 	case RK3399_PLL_PPLL:
3090ad8cadefSkettenis 		return rk3399_get_pll(sc, RK3399_PMUCRU_PPLL_CON(0));
3091ad8cadefSkettenis 	default:
3092ad8cadefSkettenis 		break;
3093ad8cadefSkettenis 	}
3094ad8cadefSkettenis 
30957371cc19Skettenis 	return rkclock_get_frequency(sc, idx);
3096592afca7Skettenis }
3097592afca7Skettenis 
3098592afca7Skettenis int
3099592afca7Skettenis rk3399_pmu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
3100592afca7Skettenis {
310198a76083Skettenis 	struct rkclock_softc *sc = cookie;
3102592afca7Skettenis 	uint32_t idx = cells[0];
3103592afca7Skettenis 
310498a76083Skettenis 	switch (idx) {
310598a76083Skettenis 	case RK3399_PLL_PPLL:
310698a76083Skettenis 		return rk3399_set_pll(sc, RK3399_PMUCRU_PPLL_CON(0), freq);
310798a76083Skettenis 	default:
310898a76083Skettenis 		break;
310998a76083Skettenis 	}
311098a76083Skettenis 
31117371cc19Skettenis 	return rkclock_set_frequency(sc, idx, freq);
3112592afca7Skettenis }
3113592afca7Skettenis 
3114592afca7Skettenis void
3115592afca7Skettenis rk3399_pmu_enable(void *cookie, uint32_t *cells, int on)
3116592afca7Skettenis {
3117592afca7Skettenis 	uint32_t idx = cells[0];
3118592afca7Skettenis 
3119ad8cadefSkettenis 	switch (idx) {
3120ad8cadefSkettenis 	case RK3399_CLK_I2C0:
3121ad8cadefSkettenis 	case RK3399_CLK_I2C4:
3122ad8cadefSkettenis 	case RK3399_CLK_I2C8:
3123ad8cadefSkettenis 	case RK3399_PCLK_I2C0:
3124ad8cadefSkettenis 	case RK3399_PCLK_I2C4:
3125ad8cadefSkettenis 	case RK3399_PCLK_I2C8:
31268c9e697fSpatrick 	case RK3399_PCLK_RKPWM:
3127ad8cadefSkettenis 		/* Enabled by default. */
3128ad8cadefSkettenis 		break;
3129ad8cadefSkettenis 	default:
3130592afca7Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
3131ad8cadefSkettenis 		break;
3132ad8cadefSkettenis 	}
3133592afca7Skettenis }
3134592afca7Skettenis 
3135592afca7Skettenis void
3136592afca7Skettenis rk3399_pmu_reset(void *cookie, uint32_t *cells, int on)
3137592afca7Skettenis {
3138592afca7Skettenis 	uint32_t idx = cells[0];
3139592afca7Skettenis 
3140592afca7Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
3141592afca7Skettenis }
314233081393Skettenis 
314333081393Skettenis /*
314433081393Skettenis  * Rockchip RK3568
314533081393Skettenis  */
314633081393Skettenis 
314733081393Skettenis const struct rkclock rk3568_clocks[] = {
314833081393Skettenis 	{
31491f8e42a5Sdlg 		RK3568_BCLK_EMMC, RK3568_CRU_CLKSEL_CON(28),
31501f8e42a5Sdlg 		SEL(9, 8), 0,
31511f8e42a5Sdlg 		{ RK3568_GPLL_200M, RK3568_GPLL_150M, RK3568_CPLL_125M }
31521f8e42a5Sdlg 	},
31531f8e42a5Sdlg 	{
31541f8e42a5Sdlg 		RK3568_CCLK_EMMC, RK3568_CRU_CLKSEL_CON(28),
31551f8e42a5Sdlg 		SEL(14, 12), 0,
31561f8e42a5Sdlg 		{ RK3568_XIN24M, RK3568_GPLL_200M, RK3568_GPLL_150M,
31571f8e42a5Sdlg 		  RK3568_CPLL_100M, RK3568_CPLL_50M, RK3568_CLK_OSC0_DIV_375K }
31581f8e42a5Sdlg 	},
31591f8e42a5Sdlg 	{
31601f8e42a5Sdlg 		RK3568_TCLK_EMMC, 0, 0, 0,
31611f8e42a5Sdlg 		{ RK3568_XIN24M }
31621f8e42a5Sdlg 	},
31631f8e42a5Sdlg 
31641f8e42a5Sdlg 	{
3165c935b871Skettenis 		RK3568_ACLK_PHP, RK3568_CRU_CLKSEL_CON(30),
3166c935b871Skettenis 		SEL(1, 0), 0,
3167c935b871Skettenis 		{ RK3568_GPLL_300M, RK3568_GPLL_200M,
3168c935b871Skettenis 		  RK3568_GPLL_100M, RK3568_XIN24M }
3169c935b871Skettenis 	},
3170c935b871Skettenis 	{
3171c935b871Skettenis 		RK3568_PCLK_PHP, RK3568_CRU_CLKSEL_CON(30),
3172c935b871Skettenis 		0, DIV(7, 4),
3173c935b871Skettenis 		{ RK3568_ACLK_PHP }
3174c935b871Skettenis 	},
3175c935b871Skettenis 	{
317633081393Skettenis 		RK3568_CLK_SDMMC0, RK3568_CRU_CLKSEL_CON(30),
317733081393Skettenis 		SEL(10, 8), 0,
317833081393Skettenis 		{ RK3568_XIN24M, RK3568_GPLL_400M, RK3568_GPLL_300M,
317933081393Skettenis 		  RK3568_CPLL_100M, RK3568_CPLL_50M, RK3568_CLK_OSC0_DIV_750K }
318033081393Skettenis 	},
318133081393Skettenis 	{
318233081393Skettenis 		RK3568_CLK_SDMMC1, RK3568_CRU_CLKSEL_CON(30),
318333081393Skettenis 		SEL(14, 12), 0,
318433081393Skettenis 		{ RK3568_XIN24M, RK3568_GPLL_400M, RK3568_GPLL_300M,
318533081393Skettenis 		  RK3568_CPLL_100M, RK3568_CPLL_50M, RK3568_CLK_OSC0_DIV_750K }
318633081393Skettenis 	},
318733081393Skettenis 	{
318815d3c08bSkettenis 		RK3568_CLK_SDMMC2, RK3568_CRU_CLKSEL_CON(32),
318915d3c08bSkettenis 		SEL(10, 8), 0,
319015d3c08bSkettenis 		{ RK3568_XIN24M, RK3568_GPLL_400M, RK3568_GPLL_300M,
319115d3c08bSkettenis 		  RK3568_CPLL_100M, RK3568_CPLL_50M, RK3568_CLK_OSC0_DIV_750K }
319215d3c08bSkettenis 	},
319315d3c08bSkettenis 	{
3194c935b871Skettenis 		RK3568_ACLK_GMAC0, 0, 0, 0,
3195c935b871Skettenis 		{ RK3568_ACLK_PHP }
3196c935b871Skettenis 	},
3197c935b871Skettenis 	{
3198c935b871Skettenis 		RK3568_PCLK_GMAC0, 0, 0, 0,
3199c935b871Skettenis 		{ RK3568_PCLK_PHP }
3200c935b871Skettenis 	},
3201c935b871Skettenis 	{
3202c935b871Skettenis 		RK3568_CLK_MAC0_2TOP, RK3568_CRU_CLKSEL_CON(31),
3203c935b871Skettenis 		SEL(9, 8), 0,
3204c935b871Skettenis 		{ RK3568_CPLL_125M, RK3568_CPLL_50M,
3205c935b871Skettenis 		  RK3568_CPLL_25M, RK3568_XIN24M }
3206c935b871Skettenis 	},
3207c935b871Skettenis 	{
3208c935b871Skettenis 		RK3568_CLK_MAC0_REFOUT, 0, 0, 0,
3209c935b871Skettenis 		{ RK3568_CLK_MAC0_2TOP }
3210c935b871Skettenis 	},
3211c935b871Skettenis 	{
3212c935b871Skettenis 		RK3568_CLK_GMAC0_PTP_REF, RK3568_CRU_CLKSEL_CON(31),
3213c935b871Skettenis 		SEL(13, 12), 0,
3214c935b871Skettenis 		{ RK3568_CPLL_62P5M, RK3568_GPLL_100M,
3215c935b871Skettenis 		  RK3568_CPLL_50M, RK3568_XIN24M }
3216c935b871Skettenis 	},
3217c935b871Skettenis 	{
3218c935b871Skettenis 		RK3568_ACLK_USB, RK3568_CRU_CLKSEL_CON(32),
3219c935b871Skettenis 		SEL(1, 0), 0,
3220c935b871Skettenis 		{ RK3568_GPLL_300M, RK3568_GPLL_200M,
3221c935b871Skettenis 		  RK3568_GPLL_100M, RK3568_XIN24M }
3222c935b871Skettenis 	},
3223c935b871Skettenis 	{
3224c935b871Skettenis 		RK3568_PCLK_USB, RK3568_CRU_CLKSEL_CON(32),
3225c935b871Skettenis 		0, DIV(7, 4),
3226c935b871Skettenis 		{ RK3568_ACLK_USB }
3227c935b871Skettenis 	},
3228c935b871Skettenis 	{
3229c935b871Skettenis 		RK3568_ACLK_GMAC1, 0, 0, 0,
3230c935b871Skettenis 		{ RK3568_ACLK_USB }
3231c935b871Skettenis 	},
3232c935b871Skettenis 	{
3233c935b871Skettenis 		RK3568_PCLK_GMAC1, 0, 0, 0,
3234c935b871Skettenis 		{ RK3568_PCLK_USB }
3235c935b871Skettenis 	},
3236c935b871Skettenis 	{
3237c935b871Skettenis 		RK3568_CLK_MAC1_2TOP, RK3568_CRU_CLKSEL_CON(33),
3238c935b871Skettenis 		SEL(9, 8), 0,
3239c935b871Skettenis 		{ RK3568_CPLL_125M, RK3568_CPLL_50M,
3240c935b871Skettenis 		  RK3568_CPLL_25M, RK3568_XIN24M }
3241c935b871Skettenis 	},
3242c935b871Skettenis 	{
3243c935b871Skettenis 		RK3568_CLK_MAC1_REFOUT, 0, 0, 0,
3244c935b871Skettenis 		{ RK3568_CLK_MAC1_2TOP }
3245c935b871Skettenis 	},
3246c935b871Skettenis 	{
3247c935b871Skettenis 		RK3568_CLK_GMAC1_PTP_REF, RK3568_CRU_CLKSEL_CON(33),
3248c935b871Skettenis 		SEL(13, 12), 0,
3249c935b871Skettenis 		{ RK3568_CPLL_62P5M, RK3568_GPLL_100M,
3250c935b871Skettenis 		  RK3568_CPLL_50M, RK3568_XIN24M }
3251c935b871Skettenis 	},
3252c935b871Skettenis 	{
3253422309e7Skettenis 		RK3568_CLK_TSADC_TSEN, RK3568_CRU_CLKSEL_CON(51),
3254422309e7Skettenis 		SEL(5, 4), DIV(2, 0),
3255422309e7Skettenis 		{ RK3568_XIN24M, RK3568_GPLL_100M, RK3568_CPLL_100M }
3256422309e7Skettenis 	},
3257422309e7Skettenis 	{
3258422309e7Skettenis 		RK3568_CLK_TSADC, RK3568_CRU_CLKSEL_CON(51),
3259422309e7Skettenis 		0, DIV(14, 8),
3260422309e7Skettenis 		{ RK3568_CLK_TSADC_TSEN }
3261422309e7Skettenis 	},
3262422309e7Skettenis 	{
326333081393Skettenis 		RK3568_SCLK_UART1, RK3568_CRU_CLKSEL_CON(52),
326433081393Skettenis 		SEL(13, 12), 0,
326533081393Skettenis 		{ 0, 0, RK3568_XIN24M }
326633081393Skettenis 	},
326733081393Skettenis 	{
326833081393Skettenis 		RK3568_SCLK_UART2, RK3568_CRU_CLKSEL_CON(54),
326933081393Skettenis 		SEL(13, 12), 0,
327033081393Skettenis 		{ 0, 0, RK3568_XIN24M }
327133081393Skettenis 	},
327233081393Skettenis 	{
327333081393Skettenis 		RK3568_SCLK_UART3, RK3568_CRU_CLKSEL_CON(56),
327433081393Skettenis 		SEL(13, 12), 0,
327533081393Skettenis 		{ 0, 0, RK3568_XIN24M }
327633081393Skettenis 	},
327733081393Skettenis 	{
327833081393Skettenis 		RK3568_SCLK_UART4, RK3568_CRU_CLKSEL_CON(58),
327933081393Skettenis 		SEL(13, 12), 0,
328033081393Skettenis 		{ 0, 0, RK3568_XIN24M }
328133081393Skettenis 	},
328233081393Skettenis 	{
328333081393Skettenis 		RK3568_SCLK_UART5, RK3568_CRU_CLKSEL_CON(60),
328433081393Skettenis 		SEL(13, 12), 0,
328533081393Skettenis 		{ 0, 0, RK3568_XIN24M }
328633081393Skettenis 	},
328733081393Skettenis 	{
328833081393Skettenis 		RK3568_SCLK_UART6, RK3568_CRU_CLKSEL_CON(62),
328933081393Skettenis 		SEL(13, 12), 0,
329033081393Skettenis 		{ 0, 0, RK3568_XIN24M }
329133081393Skettenis 	},
329233081393Skettenis 	{
329333081393Skettenis 		RK3568_SCLK_UART7, RK3568_CRU_CLKSEL_CON(64),
329433081393Skettenis 		SEL(13, 12), 0,
329533081393Skettenis 		{ 0, 0, RK3568_XIN24M }
329633081393Skettenis 	},
329733081393Skettenis 	{
329833081393Skettenis 		RK3568_SCLK_UART8, RK3568_CRU_CLKSEL_CON(66),
329933081393Skettenis 		SEL(13, 12), 0,
330033081393Skettenis 		{ 0, 0, RK3568_XIN24M }
330133081393Skettenis 	},
330233081393Skettenis 	{
330333081393Skettenis 		RK3568_SCLK_UART9, RK3568_CRU_CLKSEL_CON(68),
330433081393Skettenis 		SEL(13, 12), 0,
330533081393Skettenis 		{ 0, 0, RK3568_XIN24M }
330633081393Skettenis 	},
330733081393Skettenis 	{
330833081393Skettenis 		RK3568_CLK_I2C, RK3568_CRU_CLKSEL_CON(71),
330933081393Skettenis 		SEL(9, 8), 0,
331033081393Skettenis 		{ 0, 0, RK3568_XIN24M }
331133081393Skettenis 	},
331233081393Skettenis 	{
331333081393Skettenis 		RK3568_CLK_I2C1, 0, 0, 0,
331433081393Skettenis 		{ RK3568_CLK_I2C }
331533081393Skettenis 	},
331633081393Skettenis 	{
331733081393Skettenis 		RK3568_CLK_I2C2, 0, 0, 0,
331833081393Skettenis 		{ RK3568_CLK_I2C }
331933081393Skettenis 	},
332033081393Skettenis 	{
332133081393Skettenis 		RK3568_CLK_I2C3, 0, 0, 0,
332233081393Skettenis 		{ RK3568_CLK_I2C }
332333081393Skettenis 	},
332433081393Skettenis 	{
332533081393Skettenis 		RK3568_CLK_I2C4, 0, 0, 0,
332633081393Skettenis 		{ RK3568_CLK_I2C }
332733081393Skettenis 	},
332833081393Skettenis 	{
332933081393Skettenis 		RK3568_CLK_I2C5, 0, 0, 0,
333033081393Skettenis 		{ RK3568_CLK_I2C }
333133081393Skettenis 	},
333233081393Skettenis 	{
333317449d5cSkettenis 		RK3568_CLK_SPI0, RK3568_CRU_CLKSEL_CON(72),
333417449d5cSkettenis 		SEL(1, 0), 0,
333517449d5cSkettenis 		{ RK3568_GPLL_200M, RK3568_XIN24M, RK3568_CPLL_100M }
333617449d5cSkettenis 	},
333717449d5cSkettenis 	{
333817449d5cSkettenis 		RK3568_CLK_SPI1, RK3568_CRU_CLKSEL_CON(72),
333917449d5cSkettenis 		SEL(3, 2), 0,
334017449d5cSkettenis 		{ RK3568_GPLL_200M, RK3568_XIN24M, RK3568_CPLL_100M }
334117449d5cSkettenis 	},
334217449d5cSkettenis 	{
334317449d5cSkettenis 		RK3568_CLK_SPI2, RK3568_CRU_CLKSEL_CON(72),
334417449d5cSkettenis 		SEL(5, 4), 0,
334517449d5cSkettenis 		{ RK3568_GPLL_200M, RK3568_XIN24M, RK3568_CPLL_100M }
334617449d5cSkettenis 	},
334717449d5cSkettenis 	{
334817449d5cSkettenis 		RK3568_CLK_SPI3, RK3568_CRU_CLKSEL_CON(72),
334917449d5cSkettenis 		SEL(7, 6), 0,
335017449d5cSkettenis 		{ RK3568_GPLL_200M, RK3568_XIN24M, RK3568_CPLL_100M }
335117449d5cSkettenis 	},
335217449d5cSkettenis 	{
3353c935b871Skettenis 		RK3568_SCLK_GMAC0, RK3568_CRU_CLKSEL_CON(31),
3354c935b871Skettenis 		SEL(2, 2), 0,
3355c935b871Skettenis 		{ RK3568_CLK_MAC0_2TOP, RK3568_GMAC0_CLKIN }
3356c935b871Skettenis 	},
3357c935b871Skettenis 	{
3358c935b871Skettenis 		RK3568_SCLK_GMAC0_RGMII_SPEED, RK3568_CRU_CLKSEL_CON(31),
3359c935b871Skettenis 		SEL(5, 4), 0,
3360c935b871Skettenis 		{ RK3568_SCLK_GMAC0, RK3568_SCLK_GMAC0,
3361c935b871Skettenis 		  RK3568_SCLK_GMAC0_DIV_50, RK3568_SCLK_GMAC0_DIV_5 }
3362c935b871Skettenis 	},
3363c935b871Skettenis 	{
3364c935b871Skettenis 		RK3568_SCLK_GMAC0_RMII_SPEED, RK3568_CRU_CLKSEL_CON(31),
3365c935b871Skettenis 		SEL(3, 3), 0,
3366c935b871Skettenis 		{ RK3568_SCLK_GMAC0_DIV_20, RK3568_SCLK_GMAC0_DIV_2 }
3367c935b871Skettenis 	},
3368c935b871Skettenis 	{
3369c935b871Skettenis 		RK3568_SCLK_GMAC0_RX_TX, RK3568_CRU_CLKSEL_CON(31),
3370c935b871Skettenis 		SEL(1, 0), 0,
3371c935b871Skettenis 		{ RK3568_SCLK_GMAC0_RGMII_SPEED, RK3568_SCLK_GMAC0_RMII_SPEED }
3372c935b871Skettenis 	},
3373c935b871Skettenis 	{
3374c935b871Skettenis 		RK3568_SCLK_GMAC1, RK3568_CRU_CLKSEL_CON(33),
3375c935b871Skettenis 		SEL(2, 2), 0,
3376c935b871Skettenis 		{ RK3568_CLK_MAC1_2TOP, RK3568_GMAC1_CLKIN }
3377c935b871Skettenis 	},
3378c935b871Skettenis 	{
3379c935b871Skettenis 		RK3568_SCLK_GMAC1_RGMII_SPEED, RK3568_CRU_CLKSEL_CON(33),
3380c935b871Skettenis 		SEL(5, 4), 0,
3381c935b871Skettenis 		{ RK3568_SCLK_GMAC1, RK3568_SCLK_GMAC1,
3382c935b871Skettenis 		  RK3568_SCLK_GMAC1_DIV_50, RK3568_SCLK_GMAC1_DIV_5 }
3383c935b871Skettenis 	},
3384c935b871Skettenis 	{
3385c935b871Skettenis 		RK3568_SCLK_GMAC1_RMII_SPEED, RK3568_CRU_CLKSEL_CON(33),
3386c935b871Skettenis 		SEL(3, 3), 0,
3387c935b871Skettenis 		{ RK3568_SCLK_GMAC1_DIV_20, RK3568_SCLK_GMAC1_DIV_2 }
3388c935b871Skettenis 	},
3389c935b871Skettenis 	{
3390c935b871Skettenis 		RK3568_SCLK_GMAC1_RX_TX, RK3568_CRU_CLKSEL_CON(33),
3391c935b871Skettenis 		SEL(1, 0), 0,
3392c935b871Skettenis 		{ RK3568_SCLK_GMAC1_RGMII_SPEED, RK3568_SCLK_GMAC1_RMII_SPEED }
3393c935b871Skettenis 	},
3394c935b871Skettenis 	{
3395c935b871Skettenis 		RK3568_CPLL_125M, RK3568_CRU_CLKSEL_CON(80),
3396c935b871Skettenis 		0, DIV(4, 0),
3397c935b871Skettenis 		{ RK3568_PLL_CPLL }
3398c935b871Skettenis 	},
3399c935b871Skettenis 	{
3400c935b871Skettenis 		RK3568_CPLL_62P5M, RK3568_CRU_CLKSEL_CON(80),
3401c935b871Skettenis 		0, DIV(12, 8),
3402c935b871Skettenis 		{ RK3568_PLL_CPLL }
3403c935b871Skettenis 	},
3404c935b871Skettenis 	{
340533081393Skettenis 		RK3568_CPLL_50M, RK3568_CRU_CLKSEL_CON(81),
340633081393Skettenis 		0, DIV(4, 0),
340733081393Skettenis 		{ RK3568_PLL_CPLL }
340833081393Skettenis 	},
340933081393Skettenis 	{
3410c935b871Skettenis 		RK3568_CPLL_25M, RK3568_CRU_CLKSEL_CON(81),
3411c935b871Skettenis 		0, DIV(13, 8),
3412c935b871Skettenis 		{ RK3568_PLL_CPLL }
3413c935b871Skettenis 	},
3414c935b871Skettenis 	{
341533081393Skettenis 		RK3568_CPLL_100M, RK3568_CRU_CLKSEL_CON(82),
341633081393Skettenis 		0, DIV(4, 0),
341733081393Skettenis 		{ RK3568_PLL_CPLL }
341833081393Skettenis 	},
341933081393Skettenis 	{
342033081393Skettenis 		RK3568_GPLL_400M, RK3568_CRU_CLKSEL_CON(75),
342133081393Skettenis 		0, DIV(4, 0),
342233081393Skettenis 		{ RK3568_PLL_GPLL }
342333081393Skettenis 	},
342433081393Skettenis 	{
342533081393Skettenis 		RK3568_GPLL_300M, RK3568_CRU_CLKSEL_CON(75),
342633081393Skettenis 		0, DIV(12, 8),
342733081393Skettenis 		{ RK3568_PLL_GPLL }
342833081393Skettenis 	},
342933081393Skettenis 	{
3430c935b871Skettenis 		RK3568_GPLL_200M, RK3568_CRU_CLKSEL_CON(76),
3431c935b871Skettenis 		0, DIV(4, 0),
3432c935b871Skettenis 		{ RK3568_PLL_GPLL }
3433c935b871Skettenis 	},
3434c935b871Skettenis 	{
34351f8e42a5Sdlg 		RK3568_GPLL_150M, RK3568_CRU_CLKSEL_CON(76),
34361f8e42a5Sdlg 		0, DIV(12, 5),
34371f8e42a5Sdlg 		{ RK3568_PLL_GPLL }
34381f8e42a5Sdlg 	},
34391f8e42a5Sdlg 	{
3440422309e7Skettenis 		RK3568_GPLL_100M, RK3568_CRU_CLKSEL_CON(77),
3441422309e7Skettenis 		0, DIV(4, 0),
3442422309e7Skettenis 		{ RK3568_PLL_GPLL }
3443422309e7Skettenis 	},
3444422309e7Skettenis 	{
344533081393Skettenis 		RK3568_CLK_OSC0_DIV_750K, RK3568_CRU_CLKSEL_CON(82),
344633081393Skettenis 		0, DIV(13, 8),
344733081393Skettenis 		{ RK3568_XIN24M }
344833081393Skettenis 	},
344933081393Skettenis 	{
345033081393Skettenis 		/* Sentinel */
345133081393Skettenis 	}
345233081393Skettenis };
345333081393Skettenis 
345433081393Skettenis void
345533081393Skettenis rk3568_init(struct rkclock_softc *sc)
345633081393Skettenis {
345733081393Skettenis 	int i;
345833081393Skettenis 
345933081393Skettenis 	/* The code below assumes all clocks are enabled.  Check this!. */
346033081393Skettenis 	for (i = 0; i <= 35; i++) {
346133081393Skettenis 		if (HREAD4(sc, RK3568_CRU_GATE_CON(i)) != 0x00000000) {
346233081393Skettenis 			printf("CRU_GATE_CON%d: 0x%08x\n", i,
346333081393Skettenis 			    HREAD4(sc, RK3568_CRU_GATE_CON(i)));
346433081393Skettenis 		}
346533081393Skettenis 	}
346633081393Skettenis 
346733081393Skettenis 	sc->sc_clocks = rk3568_clocks;
346833081393Skettenis }
346933081393Skettenis 
347033081393Skettenis int
347133081393Skettenis rk3568_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
347233081393Skettenis {
347333081393Skettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
347433081393Skettenis 	int mode_shift = -1;
347533081393Skettenis 
347633081393Skettenis 	switch (base) {
347733081393Skettenis 	case RK3568_CRU_APLL_CON(0):
347833081393Skettenis 		mode_shift = 0;
347933081393Skettenis 		break;
348033081393Skettenis 	case RK3568_CRU_DPLL_CON(0):
348133081393Skettenis 		mode_shift = 2;
348233081393Skettenis 		break;
348333081393Skettenis 	case RK3568_CRU_CPLL_CON(0):
348433081393Skettenis 		mode_shift = 4;
348533081393Skettenis 		break;
348633081393Skettenis 	case RK3568_CRU_GPLL_CON(0):
348733081393Skettenis 		mode_shift = 6;
348833081393Skettenis 		break;
348933081393Skettenis 	case RK3568_CRU_NPLL_CON(0):
349033081393Skettenis 		mode_shift = 10;
349133081393Skettenis 		break;
349233081393Skettenis 	case RK3568_CRU_VPLL_CON(0):
349333081393Skettenis 		mode_shift = 12;
349433081393Skettenis 		break;
349533081393Skettenis 	}
349633081393Skettenis 	KASSERT(mode_shift != -1);
349733081393Skettenis 
349833081393Skettenis 	/*
349933081393Skettenis 	 * It is not clear whether all combinations of the clock
350033081393Skettenis 	 * dividers result in a stable clock.  Therefore this function
350133081393Skettenis 	 * only supports a limited set of PLL clock rates.
350233081393Skettenis 	 */
350333081393Skettenis 	switch (freq) {
350433081393Skettenis 	case 1200000000U:
350533081393Skettenis 		postdiv1 = 2; postdiv2 = refdiv = 1;
350633081393Skettenis 		break;
350733081393Skettenis 	default:
350833081393Skettenis 		printf("%s: %u Hz\n", __func__, freq);
350933081393Skettenis 		return -1;
351033081393Skettenis 	}
351133081393Skettenis 
351233081393Skettenis 	/* Calculate feedback divider. */
351333081393Skettenis 	fbdiv = freq * postdiv1 * postdiv2 * refdiv / 24000000;
351433081393Skettenis 
351533081393Skettenis 	/*
351633081393Skettenis 	 * Select slow mode to guarantee a stable clock while we're
351733081393Skettenis 	 * adjusting the PLL.
351833081393Skettenis 	 */
351933081393Skettenis 	HWRITE4(sc, RK3568_CRU_MODE_CON,
352033081393Skettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
352133081393Skettenis 	   RK3328_CRU_CRU_MODE_SLOW) << mode_shift);
352233081393Skettenis 
352333081393Skettenis 	/* Set PLL rate. */
352433081393Skettenis 	HWRITE4(sc, base + 0x0000,
352533081393Skettenis 	    RK3328_CRU_PLL_POSTDIV1_MASK << 16 |
352633081393Skettenis 	    postdiv1 << RK3328_CRU_PLL_POSTDIV1_SHIFT |
352733081393Skettenis 	    RK3328_CRU_PLL_FBDIV_MASK << 16 |
352833081393Skettenis 	    fbdiv << RK3328_CRU_PLL_FBDIV_SHIFT);
352933081393Skettenis 	HWRITE4(sc, base + 0x0004,
353033081393Skettenis 	    RK3328_CRU_PLL_DSMPD << 16 | RK3328_CRU_PLL_DSMPD |
353133081393Skettenis 	    RK3328_CRU_PLL_POSTDIV2_MASK << 16 |
353233081393Skettenis 	    postdiv2 << RK3328_CRU_PLL_POSTDIV2_SHIFT |
353333081393Skettenis 	    RK3328_CRU_PLL_REFDIV_MASK << 16 |
353433081393Skettenis 	    refdiv << RK3328_CRU_PLL_REFDIV_SHIFT);
353533081393Skettenis 
353633081393Skettenis 	/* Wait for PLL to stabilize. */
353733081393Skettenis 	while ((HREAD4(sc, base + 0x0004) & RK3328_CRU_PLL_PLL_LOCK) == 0)
353833081393Skettenis 		delay(10);
353933081393Skettenis 
354033081393Skettenis 	/* Switch back to normal mode. */
354133081393Skettenis 	HWRITE4(sc, RK3568_CRU_MODE_CON,
354233081393Skettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
354333081393Skettenis 	   RK3328_CRU_CRU_MODE_NORMAL) << mode_shift);
354433081393Skettenis 
354533081393Skettenis 	return 0;
354633081393Skettenis }
354733081393Skettenis 
354833081393Skettenis uint32_t
354933081393Skettenis rk3568_get_frequency(void *cookie, uint32_t *cells)
355033081393Skettenis {
355133081393Skettenis 	struct rkclock_softc *sc = cookie;
355233081393Skettenis 	uint32_t idx = cells[0];
355333081393Skettenis 
355433081393Skettenis 	switch (idx) {
355533081393Skettenis 	case RK3568_PLL_APLL:
355633081393Skettenis 		return rk3328_get_pll(sc, RK3568_CRU_APLL_CON(0));
355733081393Skettenis 	case RK3568_PLL_DPLL:
355833081393Skettenis 		return rk3328_get_pll(sc, RK3568_CRU_DPLL_CON(0));
355933081393Skettenis 	case RK3568_PLL_CPLL:
356033081393Skettenis 		return rk3328_get_pll(sc, RK3568_CRU_CPLL_CON(0));
356133081393Skettenis 	case RK3568_PLL_GPLL:
356233081393Skettenis 		return rk3328_get_pll(sc, RK3568_CRU_GPLL_CON(0));
356333081393Skettenis 	case RK3568_PLL_NPLL:
356433081393Skettenis 		return rk3328_get_pll(sc, RK3568_CRU_NPLL_CON(0));
356533081393Skettenis 	case RK3568_PLL_VPLL:
356633081393Skettenis 		return rk3328_get_pll(sc, RK3568_CRU_VPLL_CON(0));
3567c935b871Skettenis 	case RK3568_SCLK_GMAC0_DIV_50:
3568c935b871Skettenis 		idx = RK3568_SCLK_GMAC0;
3569c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 50;
3570c935b871Skettenis 	case RK3568_SCLK_GMAC0_DIV_5:
3571c935b871Skettenis 		idx = RK3568_SCLK_GMAC0;
3572c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 5;
3573c935b871Skettenis 	case RK3568_SCLK_GMAC0_DIV_20:
3574c935b871Skettenis 		idx = RK3568_SCLK_GMAC0;
3575c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 20;
3576c935b871Skettenis 	case RK3568_SCLK_GMAC0_DIV_2:
3577c935b871Skettenis 		idx = RK3568_SCLK_GMAC0;
3578c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 2;
3579c935b871Skettenis 	case RK3568_SCLK_GMAC1_DIV_50:
3580c935b871Skettenis 		idx = RK3568_SCLK_GMAC1;
3581c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 50;
3582c935b871Skettenis 	case RK3568_SCLK_GMAC1_DIV_5:
3583c935b871Skettenis 		idx = RK3568_SCLK_GMAC1;
3584c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 5;
3585c935b871Skettenis 	case RK3568_SCLK_GMAC1_DIV_20:
3586c935b871Skettenis 		idx = RK3568_SCLK_GMAC1;
3587c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 20;
3588c935b871Skettenis 	case RK3568_SCLK_GMAC1_DIV_2:
3589c935b871Skettenis 		idx = RK3568_SCLK_GMAC1;
3590c935b871Skettenis 		return rk3568_get_frequency(sc, &idx) / 2;
35911f8e42a5Sdlg 	case RK3568_CLK_OSC0_DIV_375K:
35921f8e42a5Sdlg 		idx = RK3568_CLK_OSC0_DIV_750K;
35931f8e42a5Sdlg 		return rk3568_get_frequency(sc, &idx) / 2;
3594c935b871Skettenis 	case RK3568_GMAC0_CLKIN:
3595c935b871Skettenis 		return rkclock_external_frequency("gmac0_clkin");
3596c935b871Skettenis 	case RK3568_GMAC1_CLKIN:
3597c935b871Skettenis 		return rkclock_external_frequency("gmac1_clkin");
359833081393Skettenis 	case RK3568_XIN24M:
359933081393Skettenis 		return 24000000;
360033081393Skettenis 	default:
360133081393Skettenis 		break;
360233081393Skettenis 	}
360333081393Skettenis 
360433081393Skettenis 	return rkclock_get_frequency(sc, idx);
360533081393Skettenis }
360633081393Skettenis 
360733081393Skettenis int
360833081393Skettenis rk3568_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
360933081393Skettenis {
361033081393Skettenis 	struct rkclock_softc *sc = cookie;
361133081393Skettenis 	uint32_t idx = cells[0];
361233081393Skettenis 
361333081393Skettenis 	switch (idx) {
361433081393Skettenis 	case RK3568_PLL_GPLL:
361533081393Skettenis 		return rk3568_set_pll(sc, RK3568_CRU_GPLL_CON(0), freq);
361633081393Skettenis 	default:
361733081393Skettenis 		break;
361833081393Skettenis 	}
361933081393Skettenis 
362033081393Skettenis 	return rkclock_set_frequency(sc, idx, freq);
362133081393Skettenis }
362233081393Skettenis 
3623bc4366cbSkettenis int
3624bc4366cbSkettenis rk3568_set_parent(void *cookie, uint32_t *cells, uint32_t *pcells)
3625bc4366cbSkettenis {
3626bc4366cbSkettenis 	struct rkclock_softc *sc = cookie;
3627bc4366cbSkettenis 	char buf[64] = {};
3628bc4366cbSkettenis 	int len, node;
3629bc4366cbSkettenis 
3630bc4366cbSkettenis 	if (pcells[0] != sc->sc_phandle) {
3631bc4366cbSkettenis 		node = OF_getnodebyphandle(pcells[0]);
3632bc4366cbSkettenis 		if (node == 0)
3633bc4366cbSkettenis 			return -1;
3634bc4366cbSkettenis 		len = OF_getproplen(node, "clock-output-names");
3635bc4366cbSkettenis 		if (len <= 0 || len > sizeof(buf))
3636bc4366cbSkettenis 			return -1;
3637bc4366cbSkettenis 		OF_getprop(node, "clock-output-names", buf, sizeof(buf));
3638bc4366cbSkettenis 
3639bc4366cbSkettenis 		if (strcmp(buf, "gmac0_clkin") == 0) {
3640bc4366cbSkettenis 			return rkclock_set_parent(sc, cells[0],
3641bc4366cbSkettenis 			    RK3568_GMAC0_CLKIN);
3642bc4366cbSkettenis 		}
3643bc4366cbSkettenis 		if (strcmp(buf, "gmac1_clkin") == 0) {
3644bc4366cbSkettenis 			return rkclock_set_parent(sc, cells[0],
3645bc4366cbSkettenis 			    RK3568_GMAC1_CLKIN);
3646bc4366cbSkettenis 		}
3647bc4366cbSkettenis 
3648bc4366cbSkettenis 		printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], pcells[0]);
3649bc4366cbSkettenis 		return -1;
3650bc4366cbSkettenis 	}
3651bc4366cbSkettenis 
3652bc4366cbSkettenis 	return rkclock_set_parent(sc, cells[0], pcells[1]);
3653bc4366cbSkettenis }
3654bc4366cbSkettenis 
365533081393Skettenis void
365633081393Skettenis rk3568_enable(void *cookie, uint32_t *cells, int on)
365733081393Skettenis {
365833081393Skettenis 	uint32_t idx = cells[0];
365933081393Skettenis 
366033081393Skettenis 	/* All clocks are enabled upon hardware reset. */
366133081393Skettenis 	if (!on) {
366233081393Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
366333081393Skettenis 		return;
366433081393Skettenis 	}
366533081393Skettenis }
366633081393Skettenis 
366733081393Skettenis void
366833081393Skettenis rk3568_reset(void *cookie, uint32_t *cells, int on)
366933081393Skettenis {
367033081393Skettenis 	struct rkclock_softc *sc = cookie;
367133081393Skettenis 	uint32_t idx = cells[0];
367233081393Skettenis 	uint32_t mask = (1 << (idx % 16));
367333081393Skettenis 
367433081393Skettenis 	HWRITE4(sc, RK3568_CRU_SOFTRST_CON(idx / 16),
367533081393Skettenis 	    mask << 16 | (on ? mask : 0));
367633081393Skettenis }
367733081393Skettenis 
367833081393Skettenis /* PMUCRU */
367933081393Skettenis 
368033081393Skettenis const struct rkclock rk3568_pmu_clocks[] = {
368133081393Skettenis 	{
3682a21015c5Skettenis 		RK3568_CLK_RTC_32K, RK3568_PMUCRU_CLKSEL_CON(0),
3683a21015c5Skettenis 		SEL(7, 6), 0,
3684a21015c5Skettenis 		{ 0, RK3568_XIN32K, RK3568_CLK_RTC32K_FRAC },
3685a21015c5Skettenis 		SET_PARENT
3686a21015c5Skettenis 	},
3687a21015c5Skettenis 	{
368833081393Skettenis 		RK3568_CLK_I2C0, RK3568_PMUCRU_CLKSEL_CON(3),
368933081393Skettenis 		0, DIV(15, 7),
369033081393Skettenis 		{ RK3568_CLK_PDPMU }
369133081393Skettenis 	},
369233081393Skettenis 	{
369333081393Skettenis 		RK3568_SCLK_UART0, RK3568_PMUCRU_CLKSEL_CON(4),
369433081393Skettenis 		SEL(11, 10), 0,
369533081393Skettenis 		{ 0, 0, RK3568_XIN24M }
369633081393Skettenis 	},
369733081393Skettenis 	{
3698e1414c50Skettenis 		RK3568_CLK_PCIEPHY0_OSC0, 0, 0, 0,
3699e1414c50Skettenis 		{ RK3568_XIN24M }
3700e1414c50Skettenis 	},
3701e1414c50Skettenis 	{
3702e1414c50Skettenis 		RK3568_CLK_PCIEPHY0_DIV, RK3568_PMUCRU_CLKSEL_CON(9),
3703e1414c50Skettenis 		0, DIV(2, 0),
3704e1414c50Skettenis 		{ RK3568_PPLL_PH0 }
3705e1414c50Skettenis 	},
3706e1414c50Skettenis 	{
3707e1414c50Skettenis 		RK3568_CLK_PCIEPHY0_REF, RK3568_PMUCRU_CLKSEL_CON(9),
3708e1414c50Skettenis 		SEL(3, 3), 0,
3709e1414c50Skettenis 		{ RK3568_CLK_PCIEPHY0_OSC0, RK3568_CLK_PCIEPHY0_DIV },
3710e1414c50Skettenis 		SET_PARENT
3711e1414c50Skettenis 	},
3712e1414c50Skettenis 	{
3713e1414c50Skettenis 		RK3568_CLK_PCIEPHY1_OSC0, 0, 0, 0,
3714e1414c50Skettenis 		{ RK3568_XIN24M }
3715e1414c50Skettenis 	},
3716e1414c50Skettenis 	{
3717e1414c50Skettenis 		RK3568_CLK_PCIEPHY1_DIV, RK3568_PMUCRU_CLKSEL_CON(9),
3718e1414c50Skettenis 		0, DIV(6, 4),
3719e1414c50Skettenis 		{ RK3568_PPLL_PH0 }
3720e1414c50Skettenis 	},
3721e1414c50Skettenis 	{
3722e1414c50Skettenis 		RK3568_CLK_PCIEPHY1_REF, RK3568_PMUCRU_CLKSEL_CON(9),
3723e1414c50Skettenis 		SEL(7, 7), 0,
3724e1414c50Skettenis 		{ RK3568_CLK_PCIEPHY1_OSC0, RK3568_CLK_PCIEPHY1_DIV },
3725e1414c50Skettenis 		SET_PARENT
3726e1414c50Skettenis 	},
3727e1414c50Skettenis 	{
3728e1414c50Skettenis 		RK3568_CLK_PCIEPHY2_OSC0, 0, 0, 0,
3729e1414c50Skettenis 		{ RK3568_XIN24M }
3730e1414c50Skettenis 	},
3731e1414c50Skettenis 	{
3732e1414c50Skettenis 		RK3568_CLK_PCIEPHY2_DIV, RK3568_PMUCRU_CLKSEL_CON(9),
3733e1414c50Skettenis 		0, DIV(10, 8),
3734e1414c50Skettenis 		{ RK3568_PPLL_PH0 }
3735e1414c50Skettenis 	},
3736e1414c50Skettenis 	{
3737e1414c50Skettenis 		RK3568_CLK_PCIEPHY2_REF, RK3568_PMUCRU_CLKSEL_CON(9),
3738e1414c50Skettenis 		SEL(11, 11), 0,
3739e1414c50Skettenis 		{ RK3568_CLK_PCIEPHY2_OSC0, RK3568_CLK_PCIEPHY2_DIV },
3740e1414c50Skettenis 		SET_PARENT
3741e1414c50Skettenis 	},
3742e1414c50Skettenis 	{
374333081393Skettenis 		RK3568_CLK_PDPMU, RK3568_PMUCRU_CLKSEL_CON(2),
374433081393Skettenis 		SEL(15, 15), 0,
374533081393Skettenis 		{ RK3568_PLL_PPLL, 0 }
374633081393Skettenis 	},
374733081393Skettenis 	{
374833081393Skettenis 		/* Sentinel */
374933081393Skettenis 	}
375033081393Skettenis };
375133081393Skettenis 
375233081393Skettenis void
375333081393Skettenis rk3568_pmu_init(struct rkclock_softc *sc)
375433081393Skettenis {
3755e1414c50Skettenis 	int i;
3756e1414c50Skettenis 
3757e1414c50Skettenis 	/* The code below assumes all clocks are enabled.  Check this!. */
3758e1414c50Skettenis 	for (i = 0; i <= 2; i++) {
3759e1414c50Skettenis 		if (HREAD4(sc, RK3568_PMUCRU_GATE_CON(i)) != 0x00000000) {
3760e1414c50Skettenis 			printf("CRU_GATE_CON%d: 0x%08x\n", i,
3761e1414c50Skettenis 			    HREAD4(sc, RK3568_CRU_GATE_CON(i)));
3762e1414c50Skettenis 		}
3763e1414c50Skettenis 	}
3764e1414c50Skettenis 
376533081393Skettenis 	sc->sc_clocks = rk3568_pmu_clocks;
376633081393Skettenis }
376733081393Skettenis 
376833081393Skettenis int
376933081393Skettenis rk3568_pmu_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
377033081393Skettenis {
377133081393Skettenis 	uint32_t fbdiv, postdiv1, postdiv2, refdiv;
377233081393Skettenis 	int mode_shift = -1;
377333081393Skettenis 
377433081393Skettenis 	switch (base) {
377533081393Skettenis 	case RK3568_PMUCRU_PPLL_CON(0):
377633081393Skettenis 		mode_shift = 0;
377733081393Skettenis 		break;
377833081393Skettenis 	case RK3568_PMUCRU_HPLL_CON(0):
377933081393Skettenis 		mode_shift = 2;
378033081393Skettenis 		break;
378133081393Skettenis 	}
378233081393Skettenis 	KASSERT(mode_shift != -1);
378333081393Skettenis 
378433081393Skettenis 	/*
378533081393Skettenis 	 * It is not clear whether all combinations of the clock
378633081393Skettenis 	 * dividers result in a stable clock.  Therefore this function
378733081393Skettenis 	 * only supports a limited set of PLL clock rates.
378833081393Skettenis 	 */
378933081393Skettenis 	switch (freq) {
379033081393Skettenis 	case 200000000U:
379133081393Skettenis 		postdiv1 = 3; postdiv2 = 4; refdiv = 1;
379233081393Skettenis 		break;
379333081393Skettenis 	default:
379433081393Skettenis 		printf("%s: %u Hz\n", __func__, freq);
379533081393Skettenis 		return -1;
379633081393Skettenis 	}
379733081393Skettenis 
379833081393Skettenis 	/* Calculate feedback divider. */
379933081393Skettenis 	fbdiv = freq * postdiv1 * postdiv2 * refdiv / 24000000;
380033081393Skettenis 
380133081393Skettenis 	/*
380233081393Skettenis 	 * Select slow mode to guarantee a stable clock while we're
380333081393Skettenis 	 * adjusting the PLL.
380433081393Skettenis 	 */
380533081393Skettenis 	HWRITE4(sc, RK3568_PMUCRU_MODE_CON,
380633081393Skettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
380733081393Skettenis 	   RK3328_CRU_CRU_MODE_SLOW) << mode_shift);
380833081393Skettenis 
380933081393Skettenis 	/* Set PLL rate. */
381033081393Skettenis 	HWRITE4(sc, base + 0x0000,
381133081393Skettenis 	    RK3328_CRU_PLL_POSTDIV1_MASK << 16 |
381233081393Skettenis 	    postdiv1 << RK3328_CRU_PLL_POSTDIV1_SHIFT |
381333081393Skettenis 	    RK3328_CRU_PLL_FBDIV_MASK << 16 |
381433081393Skettenis 	    fbdiv << RK3328_CRU_PLL_FBDIV_SHIFT);
381533081393Skettenis 	HWRITE4(sc, base + 0x0004,
381633081393Skettenis 	    RK3328_CRU_PLL_DSMPD << 16 | RK3328_CRU_PLL_DSMPD |
381733081393Skettenis 	    RK3328_CRU_PLL_POSTDIV2_MASK << 16 |
381833081393Skettenis 	    postdiv2 << RK3328_CRU_PLL_POSTDIV2_SHIFT |
381933081393Skettenis 	    RK3328_CRU_PLL_REFDIV_MASK << 16 |
382033081393Skettenis 	    refdiv << RK3328_CRU_PLL_REFDIV_SHIFT);
382133081393Skettenis 
382233081393Skettenis 	/* Wait for PLL to stabilize. */
382333081393Skettenis 	while ((HREAD4(sc, base + 0x0004) & RK3328_CRU_PLL_PLL_LOCK) == 0)
382433081393Skettenis 		delay(10);
382533081393Skettenis 
382633081393Skettenis 	/* Switch back to normal mode. */
382733081393Skettenis 	HWRITE4(sc, RK3568_PMUCRU_MODE_CON,
382833081393Skettenis 	   (RK3328_CRU_CRU_MODE_MASK << 16 |
382933081393Skettenis 	   RK3328_CRU_CRU_MODE_NORMAL) << mode_shift);
383033081393Skettenis 
383133081393Skettenis 	return 0;
383233081393Skettenis }
383333081393Skettenis 
383433081393Skettenis uint32_t
383533081393Skettenis rk3568_pmu_get_frequency(void *cookie, uint32_t *cells)
383633081393Skettenis {
383733081393Skettenis 	struct rkclock_softc *sc = cookie;
383833081393Skettenis 	uint32_t idx = cells[0];
383933081393Skettenis 
384033081393Skettenis 	switch (idx) {
384133081393Skettenis 	case RK3568_PLL_PPLL:
384233081393Skettenis 		return rk3328_get_pll(sc, RK3568_PMUCRU_PPLL_CON(0));
384333081393Skettenis 	case RK3568_PLL_HPLL:
384433081393Skettenis 		return rk3328_get_pll(sc, RK3568_PMUCRU_HPLL_CON(0));
3845a21015c5Skettenis 	case RK3568_CLK_RTC32K_FRAC:
3846a21015c5Skettenis 		return rk3399_get_frac(sc, RK3568_XIN24M,
3847a21015c5Skettenis 		    RK3568_PMUCRU_CLKSEL_CON(1));
3848e1414c50Skettenis 	case RK3568_PPLL_PH0:
3849e1414c50Skettenis 		idx = RK3568_PLL_PPLL;
3850e1414c50Skettenis 		return rk3568_get_frequency(sc, &idx) / 2;
3851a21015c5Skettenis 	case RK3568_XIN32K:
3852a21015c5Skettenis 		return 32768;
385333081393Skettenis 	case RK3568_XIN24M:
385433081393Skettenis 		return 24000000;
385533081393Skettenis 	default:
385633081393Skettenis 		break;
385733081393Skettenis 	}
385833081393Skettenis 
385933081393Skettenis 	return rkclock_get_frequency(sc, idx);
386033081393Skettenis }
386133081393Skettenis 
386233081393Skettenis int
386333081393Skettenis rk3568_pmu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
386433081393Skettenis {
386533081393Skettenis 	struct rkclock_softc *sc = cookie;
386633081393Skettenis 	uint32_t idx = cells[0];
386733081393Skettenis 
386833081393Skettenis 	switch (idx) {
386933081393Skettenis 	case RK3568_PLL_PPLL:
387033081393Skettenis 		return rk3568_pmu_set_pll(sc, RK3568_PMUCRU_PPLL_CON(0), freq);
387133081393Skettenis 	case RK3568_PLL_HPLL:
387233081393Skettenis 		return rk3568_pmu_set_pll(sc, RK3568_PMUCRU_HPLL_CON(0), freq);
3873a21015c5Skettenis 	case RK3568_CLK_RTC32K_FRAC:
3874a21015c5Skettenis 		return rk3399_set_frac(sc, RK3568_XIN24M,
3875a21015c5Skettenis 		    RK3568_PMUCRU_CLKSEL_CON(1), freq);
387633081393Skettenis 	default:
387733081393Skettenis 		break;
387833081393Skettenis 	}
387933081393Skettenis 
388033081393Skettenis 	return rkclock_set_frequency(sc, idx, freq);
388133081393Skettenis }
388233081393Skettenis 
388333081393Skettenis void
388433081393Skettenis rk3568_pmu_enable(void *cookie, uint32_t *cells, int on)
388533081393Skettenis {
388633081393Skettenis 	uint32_t idx = cells[0];
388733081393Skettenis 
388833081393Skettenis 	switch (idx) {
388954e50d89Skettenis 	case RK3568_CLK_USBPHY0_REF:
389054e50d89Skettenis 	case RK3568_CLK_USBPHY1_REF:
3891e1414c50Skettenis 	case RK3568_CLK_PCIEPHY0_REF:
3892e1414c50Skettenis 	case RK3568_CLK_PCIEPHY1_REF:
3893e1414c50Skettenis 	case RK3568_CLK_PCIEPHY2_REF:
389415d3c08bSkettenis 	case RK3568_CLK_PCIE30PHY_REF_M:
389515d3c08bSkettenis 	case RK3568_CLK_PCIE30PHY_REF_N:
389633081393Skettenis 	case RK3568_CLK_I2C0:
389733081393Skettenis 	case RK3568_SCLK_UART0:
389833081393Skettenis 	case RK3568_PCLK_I2C0:
389933081393Skettenis 		/* Enabled by default. */
390033081393Skettenis 		break;
390133081393Skettenis 	default:
390233081393Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
390333081393Skettenis 		break;
390433081393Skettenis 	}
390533081393Skettenis }
390633081393Skettenis 
390733081393Skettenis void
390833081393Skettenis rk3568_pmu_reset(void *cookie, uint32_t *cells, int on)
390933081393Skettenis {
391033081393Skettenis 	uint32_t idx = cells[0];
391133081393Skettenis 
391233081393Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
391333081393Skettenis }
3914e3f8cdb8Skettenis 
3915e3f8cdb8Skettenis /*
3916e3f8cdb8Skettenis  * Rockchip RK3588
3917e3f8cdb8Skettenis  */
3918e3f8cdb8Skettenis 
3919e3f8cdb8Skettenis const struct rkclock rk3588_clocks[] = {
3920e3f8cdb8Skettenis 	{
39216555b60dSpatrick 		RK3588_CLK_PWM1, RK3588_CRU_CLKSEL_CON(59),
39226555b60dSpatrick 		SEL(13, 12), 0,
39236555b60dSpatrick 		{ RK3588_CLK_100M_SRC, RK3588_CLK_50M_SRC, RK3588_XIN24M },
39246555b60dSpatrick 	},
39256555b60dSpatrick 	{
39266555b60dSpatrick 		RK3588_CLK_PWM2, RK3588_CRU_CLKSEL_CON(59),
39276555b60dSpatrick 		SEL(15, 14), 0,
39286555b60dSpatrick 		{ RK3588_CLK_100M_SRC, RK3588_CLK_50M_SRC, RK3588_XIN24M },
39296555b60dSpatrick 	},
39306555b60dSpatrick 	{
39316555b60dSpatrick 		RK3588_CLK_PWM3, RK3588_CRU_CLKSEL_CON(60),
39326555b60dSpatrick 		SEL(1, 0), 0,
39336555b60dSpatrick 		{ RK3588_CLK_100M_SRC, RK3588_CLK_50M_SRC, RK3588_XIN24M },
39346555b60dSpatrick 	},
39356555b60dSpatrick 	{
3936e3f8cdb8Skettenis 		RK3588_ACLK_BUS_ROOT, RK3588_CRU_CLKSEL_CON(38),
3937e3f8cdb8Skettenis 		SEL(5, 5), DIV(4, 0),
3938e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
3939e3f8cdb8Skettenis 	},
3940e3f8cdb8Skettenis 	{
3941cec24affSpatrick 		RK3588_CLK_I2C1, RK3588_CRU_CLKSEL_CON(38),
3942cec24affSpatrick 		SEL(6, 6), 0,
3943cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3944cec24affSpatrick 	},
3945cec24affSpatrick 	{
3946cec24affSpatrick 		RK3588_CLK_I2C2, RK3588_CRU_CLKSEL_CON(38),
3947cec24affSpatrick 		SEL(7, 7), 0,
3948cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3949cec24affSpatrick 	},
3950cec24affSpatrick 	{
3951cec24affSpatrick 		RK3588_CLK_I2C3, RK3588_CRU_CLKSEL_CON(38),
3952cec24affSpatrick 		SEL(8, 8), 0,
3953cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3954cec24affSpatrick 	},
3955cec24affSpatrick 	{
3956cec24affSpatrick 		RK3588_CLK_I2C4, RK3588_CRU_CLKSEL_CON(38),
3957cec24affSpatrick 		SEL(9, 9), 0,
3958cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3959cec24affSpatrick 	},
3960cec24affSpatrick 	{
3961cec24affSpatrick 		RK3588_CLK_I2C5, RK3588_CRU_CLKSEL_CON(38),
3962cec24affSpatrick 		SEL(10, 10), 0,
3963cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3964cec24affSpatrick 	},
3965cec24affSpatrick 	{
3966cec24affSpatrick 		RK3588_CLK_I2C6, RK3588_CRU_CLKSEL_CON(38),
3967cec24affSpatrick 		SEL(11, 11), 0,
3968cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3969cec24affSpatrick 	},
3970cec24affSpatrick 	{
3971cec24affSpatrick 		RK3588_CLK_I2C7, RK3588_CRU_CLKSEL_CON(38),
3972cec24affSpatrick 		SEL(12, 12), 0,
3973cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3974cec24affSpatrick 	},
3975cec24affSpatrick 	{
3976cec24affSpatrick 		RK3588_CLK_I2C8, RK3588_CRU_CLKSEL_CON(38),
3977cec24affSpatrick 		SEL(13, 13), 0,
3978cec24affSpatrick 		{ RK3588_CLK_200M_SRC , RK3588_CLK_100M_SRC },
3979cec24affSpatrick 	},
3980cec24affSpatrick 	{
39814eda67c7Skettenis 		RK3588_CLK_SPI0, RK3588_CRU_CLKSEL_CON(59),
39824eda67c7Skettenis 		SEL(3, 2), 0,
39834eda67c7Skettenis 		{ RK3588_CLK_200M_SRC, RK3588_CLK_150M_SRC, RK3588_XIN24M },
39844eda67c7Skettenis 	},
39854eda67c7Skettenis 	{
39864eda67c7Skettenis 		RK3588_CLK_SPI1, RK3588_CRU_CLKSEL_CON(59),
39874eda67c7Skettenis 		SEL(5, 4), 0,
39884eda67c7Skettenis 		{ RK3588_CLK_200M_SRC, RK3588_CLK_150M_SRC, RK3588_XIN24M },
39894eda67c7Skettenis 	},
39904eda67c7Skettenis 	{
39914eda67c7Skettenis 		RK3588_CLK_SPI2, RK3588_CRU_CLKSEL_CON(59),
39924eda67c7Skettenis 		SEL(7, 6), 0,
39934eda67c7Skettenis 		{ RK3588_CLK_200M_SRC, RK3588_CLK_150M_SRC, RK3588_XIN24M },
39944eda67c7Skettenis 	},
39954eda67c7Skettenis 	{
39964eda67c7Skettenis 		RK3588_CLK_SPI3, RK3588_CRU_CLKSEL_CON(59),
39974eda67c7Skettenis 		SEL(9, 8), 0,
39984eda67c7Skettenis 		{ RK3588_CLK_200M_SRC, RK3588_CLK_150M_SRC, RK3588_XIN24M },
39994eda67c7Skettenis 	},
40004eda67c7Skettenis 	{
40014eda67c7Skettenis 		RK3588_CLK_SPI4, RK3588_CRU_CLKSEL_CON(59),
40024eda67c7Skettenis 		SEL(11, 10), 0,
40034eda67c7Skettenis 		{ RK3588_CLK_200M_SRC, RK3588_CLK_150M_SRC, RK3588_XIN24M },
40044eda67c7Skettenis 	},
40054eda67c7Skettenis 	{
40069efac0bfSkettenis 		RK3588_CLK_TSADC, RK3588_CRU_CLKSEL_CON(41),
40079efac0bfSkettenis 		SEL(8, 8), DIV(7, 0),
40089efac0bfSkettenis 		{ RK3588_PLL_GPLL, RK3588_XIN24M },
40099efac0bfSkettenis 	},
40109efac0bfSkettenis 	{
4011e3f8cdb8Skettenis 		RK3588_CLK_UART1_SRC, RK3588_CRU_CLKSEL_CON(41),
4012e3f8cdb8Skettenis 		SEL(14, 14), DIV(13, 9),
4013e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4014e3f8cdb8Skettenis 	},
4015e3f8cdb8Skettenis 	{
4016e3f8cdb8Skettenis 		RK3588_CLK_UART1, RK3588_CRU_CLKSEL_CON(43),
4017e3f8cdb8Skettenis 		SEL(1, 0), 0,
4018e3f8cdb8Skettenis 		{ RK3588_CLK_UART1_SRC, RK3588_CLK_UART1_FRAC, RK3588_XIN24M }
4019e3f8cdb8Skettenis 	},
4020e3f8cdb8Skettenis 	{
4021e3f8cdb8Skettenis 		RK3588_SCLK_UART1, 0, 0, 0,
4022e3f8cdb8Skettenis 		{ RK3588_CLK_UART1 }
4023e3f8cdb8Skettenis 	},
4024e3f8cdb8Skettenis 	{
4025e3f8cdb8Skettenis 		RK3588_CLK_UART2_SRC, RK3588_CRU_CLKSEL_CON(43),
4026e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4027e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4028e3f8cdb8Skettenis 	},
4029e3f8cdb8Skettenis 	{
4030e3f8cdb8Skettenis 		RK3588_CLK_UART2, RK3588_CRU_CLKSEL_CON(45),
4031e3f8cdb8Skettenis 		SEL(1, 0), 0,
4032e3f8cdb8Skettenis 		{ RK3588_CLK_UART2_SRC, RK3588_CLK_UART2_FRAC, RK3588_XIN24M }
4033e3f8cdb8Skettenis 	},
4034e3f8cdb8Skettenis 	{
4035e3f8cdb8Skettenis 		RK3588_SCLK_UART2, 0, 0, 0,
4036e3f8cdb8Skettenis 		{ RK3588_CLK_UART2 }
4037e3f8cdb8Skettenis 	},
4038e3f8cdb8Skettenis 	{
4039e3f8cdb8Skettenis 		RK3588_CLK_UART3_SRC, RK3588_CRU_CLKSEL_CON(45),
4040e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4041e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4042e3f8cdb8Skettenis 	},
4043e3f8cdb8Skettenis 	{
4044e3f8cdb8Skettenis 		RK3588_CLK_UART3, RK3588_CRU_CLKSEL_CON(47),
4045e3f8cdb8Skettenis 		SEL(1, 0), 0,
4046e3f8cdb8Skettenis 		{ RK3588_CLK_UART3_SRC, RK3588_CLK_UART3_FRAC, RK3588_XIN24M }
4047e3f8cdb8Skettenis 	},
4048e3f8cdb8Skettenis 	{
4049e3f8cdb8Skettenis 		RK3588_SCLK_UART3, 0, 0, 0,
4050e3f8cdb8Skettenis 		{ RK3588_CLK_UART3 }
4051e3f8cdb8Skettenis 	},
4052e3f8cdb8Skettenis 	{
4053e3f8cdb8Skettenis 		RK3588_CLK_UART4_SRC, RK3588_CRU_CLKSEL_CON(47),
4054e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4055e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4056e3f8cdb8Skettenis 	},
4057e3f8cdb8Skettenis 	{
4058e3f8cdb8Skettenis 		RK3588_CLK_UART4, RK3588_CRU_CLKSEL_CON(49),
4059e3f8cdb8Skettenis 		SEL(1, 0), 0,
4060e3f8cdb8Skettenis 		{ RK3588_CLK_UART4_SRC, RK3588_CLK_UART4_FRAC, RK3588_XIN24M }
4061e3f8cdb8Skettenis 	},
4062e3f8cdb8Skettenis 	{
4063e3f8cdb8Skettenis 		RK3588_SCLK_UART4, 0, 0, 0,
4064e3f8cdb8Skettenis 		{ RK3588_CLK_UART4 }
4065e3f8cdb8Skettenis 	},
4066e3f8cdb8Skettenis 	{
4067e3f8cdb8Skettenis 		RK3588_CLK_UART5_SRC, RK3588_CRU_CLKSEL_CON(49),
4068e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4069e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4070e3f8cdb8Skettenis 	},
4071e3f8cdb8Skettenis 	{
4072e3f8cdb8Skettenis 		RK3588_CLK_UART5, RK3588_CRU_CLKSEL_CON(51),
4073e3f8cdb8Skettenis 		SEL(1, 0), 0,
4074e3f8cdb8Skettenis 		{ RK3588_CLK_UART5_SRC, RK3588_CLK_UART5_FRAC, RK3588_XIN24M }
4075e3f8cdb8Skettenis 	},
4076e3f8cdb8Skettenis 	{
4077e3f8cdb8Skettenis 		RK3588_SCLK_UART5, 0, 0, 0,
4078e3f8cdb8Skettenis 		{ RK3588_CLK_UART5 }
4079e3f8cdb8Skettenis 	},
4080e3f8cdb8Skettenis 	{
4081e3f8cdb8Skettenis 		RK3588_CLK_UART6_SRC, RK3588_CRU_CLKSEL_CON(51),
4082e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4083e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4084e3f8cdb8Skettenis 	},
4085e3f8cdb8Skettenis 	{
4086e3f8cdb8Skettenis 		RK3588_CLK_UART6, RK3588_CRU_CLKSEL_CON(53),
4087e3f8cdb8Skettenis 		SEL(1, 0), 0,
4088e3f8cdb8Skettenis 		{ RK3588_CLK_UART6_SRC, RK3588_CLK_UART6_FRAC, RK3588_XIN24M }
4089e3f8cdb8Skettenis 	},
4090e3f8cdb8Skettenis 	{
4091e3f8cdb8Skettenis 		RK3588_SCLK_UART6, 0, 0, 0,
4092e3f8cdb8Skettenis 		{ RK3588_CLK_UART6 }
4093e3f8cdb8Skettenis 	},
4094e3f8cdb8Skettenis 	{
4095e3f8cdb8Skettenis 		RK3588_CLK_UART7_SRC, RK3588_CRU_CLKSEL_CON(53),
4096e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4097e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4098e3f8cdb8Skettenis 	},
4099e3f8cdb8Skettenis 	{
4100e3f8cdb8Skettenis 		RK3588_CLK_UART7, RK3588_CRU_CLKSEL_CON(55),
4101e3f8cdb8Skettenis 		SEL(1, 0), 0,
4102e3f8cdb8Skettenis 		{ RK3588_CLK_UART7_SRC, RK3588_CLK_UART7_FRAC, RK3588_XIN24M }
4103e3f8cdb8Skettenis 	},
4104e3f8cdb8Skettenis 	{
4105e3f8cdb8Skettenis 		RK3588_SCLK_UART7, 0, 0, 0,
4106e3f8cdb8Skettenis 		{ RK3588_CLK_UART7 }
4107e3f8cdb8Skettenis 	},
4108e3f8cdb8Skettenis 	{
4109e3f8cdb8Skettenis 		RK3588_CLK_UART8_SRC, RK3588_CRU_CLKSEL_CON(55),
4110e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4111e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4112e3f8cdb8Skettenis 	},
4113e3f8cdb8Skettenis 	{
4114e3f8cdb8Skettenis 		RK3588_CLK_UART8, RK3588_CRU_CLKSEL_CON(57),
4115e3f8cdb8Skettenis 		SEL(1, 0), 0,
4116e3f8cdb8Skettenis 		{ RK3588_CLK_UART8_SRC, RK3588_CLK_UART8_FRAC, RK3588_XIN24M }
4117e3f8cdb8Skettenis 	},
4118e3f8cdb8Skettenis 	{
4119e3f8cdb8Skettenis 		RK3588_SCLK_UART8, 0, 0, 0,
4120e3f8cdb8Skettenis 		{ RK3588_CLK_UART8 }
4121e3f8cdb8Skettenis 	},
4122e3f8cdb8Skettenis 	{
4123e3f8cdb8Skettenis 		RK3588_CLK_UART9_SRC, RK3588_CRU_CLKSEL_CON(57),
4124e3f8cdb8Skettenis 		SEL(7, 7), DIV(6, 2),
4125e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4126e3f8cdb8Skettenis 	},
4127e3f8cdb8Skettenis 	{
4128e3f8cdb8Skettenis 		RK3588_CLK_UART9, RK3588_CRU_CLKSEL_CON(59),
4129e3f8cdb8Skettenis 		SEL(1, 0), 0,
4130e3f8cdb8Skettenis 		{ RK3588_CLK_UART9_SRC, RK3588_CLK_UART9_FRAC, RK3588_XIN24M }
4131e3f8cdb8Skettenis 	},
4132e3f8cdb8Skettenis 	{
4133e3f8cdb8Skettenis 		RK3588_SCLK_UART9, 0, 0, 0,
4134e3f8cdb8Skettenis 		{ RK3588_CLK_UART9 }
4135e3f8cdb8Skettenis 	},
4136e3f8cdb8Skettenis 	{
4137e3f8cdb8Skettenis 		RK3588_ACLK_CENTER_ROOT, RK3588_CRU_CLKSEL_CON(165),
4138e3f8cdb8Skettenis 		SEL(1, 0), 0,
4139e3f8cdb8Skettenis 		{ RK3588_CLK_700M_SRC, RK3588_CLK_400M_SRC,
4140e3f8cdb8Skettenis 		  RK3588_CLK_200M_SRC, RK3588_XIN24M }
4141e3f8cdb8Skettenis 	},
4142e3f8cdb8Skettenis 	{
4143e3f8cdb8Skettenis 		RK3588_ACLK_CENTER_LOW_ROOT, RK3588_CRU_CLKSEL_CON(165),
4144e3f8cdb8Skettenis 		SEL(3, 2), 0,
4145e3f8cdb8Skettenis 		{ RK3588_CLK_500M_SRC, RK3588_CLK_250M_SRC,
4146e3f8cdb8Skettenis 		  RK3588_CLK_100M_SRC, RK3588_XIN24M }
4147e3f8cdb8Skettenis 	},
4148e3f8cdb8Skettenis 	{
4149e3f8cdb8Skettenis 		RK3588_HCLK_CENTER_ROOT, RK3588_CRU_CLKSEL_CON(165),
4150e3f8cdb8Skettenis 		SEL(5, 4), 0,
4151e3f8cdb8Skettenis 		{ RK3588_CLK_400M_SRC, RK3588_CLK_200M_SRC,
4152e3f8cdb8Skettenis 		  RK3588_CLK_100M_SRC, RK3588_XIN24M }
4153e3f8cdb8Skettenis 	},
4154e3f8cdb8Skettenis 	{
4155e3f8cdb8Skettenis 		RK3588_CLK_50M_SRC, RK3588_CRU_CLKSEL_CON(0),
4156e3f8cdb8Skettenis 		SEL(5, 5), DIV(4, 0),
4157e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4158e3f8cdb8Skettenis 	},
4159e3f8cdb8Skettenis 	{
4160e3f8cdb8Skettenis 		RK3588_CLK_100M_SRC, RK3588_CRU_CLKSEL_CON(0),
4161e3f8cdb8Skettenis 		SEL(11, 11), DIV(10, 6),
4162e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4163e3f8cdb8Skettenis 	},
4164e3f8cdb8Skettenis 	{
4165e3f8cdb8Skettenis 		RK3588_CLK_150M_SRC, RK3588_CRU_CLKSEL_CON(1),
4166e3f8cdb8Skettenis 		SEL(5, 5), DIV(4, 0),
4167e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4168e3f8cdb8Skettenis 	},
4169e3f8cdb8Skettenis 	{
4170e3f8cdb8Skettenis 		RK3588_CLK_200M_SRC, RK3588_CRU_CLKSEL_CON(1),
4171e3f8cdb8Skettenis 		SEL(11, 11), DIV(10, 6),
4172e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4173e3f8cdb8Skettenis 	},
4174e3f8cdb8Skettenis 	{
4175e3f8cdb8Skettenis 		RK3588_CLK_250M_SRC, RK3588_CRU_CLKSEL_CON(2),
4176e3f8cdb8Skettenis 		SEL(5, 5), DIV(4, 0),
4177e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4178e3f8cdb8Skettenis 	},
4179e3f8cdb8Skettenis 	{
4180e3f8cdb8Skettenis 		RK3588_CLK_400M_SRC, RK3588_CRU_CLKSEL_CON(3),
4181e3f8cdb8Skettenis 		SEL(11, 11), DIV(10, 6),
4182e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4183e3f8cdb8Skettenis 	},
4184e3f8cdb8Skettenis 	{
4185e3f8cdb8Skettenis 		RK3588_CLK_500M_SRC, RK3588_CRU_CLKSEL_CON(4),
4186e3f8cdb8Skettenis 		SEL(11, 11), DIV(10, 6),
4187e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4188e3f8cdb8Skettenis 	},
4189e3f8cdb8Skettenis 	{
4190e3f8cdb8Skettenis 		RK3588_CLK_700M_SRC, RK3588_CRU_CLKSEL_CON(6),
4191e3f8cdb8Skettenis 		SEL(5, 5), DIV(4, 0),
4192e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_SPLL }
4193e3f8cdb8Skettenis 	},
4194e3f8cdb8Skettenis 	{
4195e3f8cdb8Skettenis 		RK3588_ACLK_TOP_ROOT, RK3588_CRU_CLKSEL_CON(8),
4196e3f8cdb8Skettenis 		SEL(6, 5), 0,
4197e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL, RK3588_PLL_AUPLL }
4198e3f8cdb8Skettenis 	},
4199e3f8cdb8Skettenis 	{
4200e3f8cdb8Skettenis 		RK3588_PCLK_TOP_ROOT, RK3588_CRU_CLKSEL_CON(8),
4201e3f8cdb8Skettenis 		SEL(8, 7), 0,
4202e3f8cdb8Skettenis 		{ RK3588_CLK_100M_SRC, RK3588_CLK_50M_SRC, RK3588_XIN24M }
4203e3f8cdb8Skettenis 	},
4204e3f8cdb8Skettenis 	{
4205e3f8cdb8Skettenis 		RK3588_ACLK_LOW_TOP_ROOT, RK3588_CRU_CLKSEL_CON(8),
4206e3f8cdb8Skettenis 		SEL(14, 14), DIV(13, 9),
4207e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4208e3f8cdb8Skettenis 	},
4209e3f8cdb8Skettenis 	{
4210e3f8cdb8Skettenis 		RK3588_CLK_GPU_SRC, RK3588_CRU_CLKSEL_CON(158),
4211e3f8cdb8Skettenis 		SEL(7, 5), DIV(4, 0),
4212e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL, RK3588_PLL_AUPLL,
4213e3f8cdb8Skettenis 		  RK3588_PLL_NPLL, RK3588_PLL_SPLL }
4214e3f8cdb8Skettenis 	},
4215e3f8cdb8Skettenis 	{
4216e3f8cdb8Skettenis 		RK3588_CLK_GPU, 0, 0, 0,
4217e3f8cdb8Skettenis 		{ RK3588_CLK_GPU_SRC },
4218e3f8cdb8Skettenis 		SET_PARENT
4219e3f8cdb8Skettenis 	},
4220e3f8cdb8Skettenis 	{
422113600422Spatrick 		RK3588_CCLK_EMMC, RK3588_CRU_CLKSEL_CON(77),
422213600422Spatrick 		SEL(15, 14), DIV(13, 8),
422313600422Spatrick 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL, RK3588_XIN24M }
422413600422Spatrick 	},
422513600422Spatrick 	{
422613600422Spatrick 		RK3588_BCLK_EMMC, RK3588_CRU_CLKSEL_CON(78),
422713600422Spatrick 		SEL(5, 5), DIV(4, 0),
422813600422Spatrick 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
422913600422Spatrick 	},
423013600422Spatrick 	{
423113600422Spatrick 		RK3588_TMCLK_EMMC, 0, 0, 0,
423213600422Spatrick 		{ RK3588_XIN24M }
423313600422Spatrick 	},
423413600422Spatrick 	{
4235761126a9Skettenis 		RK3588_CLK_GMAC_125M, RK3588_CRU_CLKSEL_CON(83),
4236761126a9Skettenis 		SEL(15, 15), DIV(14, 8),
4237761126a9Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL }
4238761126a9Skettenis 	},
4239761126a9Skettenis 	{
42400dbfb11eSkettenis 		RK3588_CCLK_SRC_SDIO, RK3588_CRU_CLKSEL_CON(172),
42410dbfb11eSkettenis 		SEL(9, 8), DIV(7, 2),
42420dbfb11eSkettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL, RK3588_XIN24M }
42430dbfb11eSkettenis 	},
42440dbfb11eSkettenis 	{
4245e3f8cdb8Skettenis 		RK3588_ACLK_VOP_ROOT, RK3588_CRU_CLKSEL_CON(110),
4246e3f8cdb8Skettenis 		SEL(7, 5), DIV(4, 0),
4247e3f8cdb8Skettenis 		{ RK3588_PLL_GPLL, RK3588_PLL_CPLL, RK3588_PLL_AUPLL,
4248e3f8cdb8Skettenis 		  RK3588_PLL_NPLL, RK3588_PLL_SPLL }
4249e3f8cdb8Skettenis 	},
4250e3f8cdb8Skettenis 	{
4251e3f8cdb8Skettenis 		RK3588_ACLK_VOP, 0, 0, 0,
4252e3f8cdb8Skettenis 		{ RK3588_ACLK_VOP_SUB_SRC },
4253e3f8cdb8Skettenis 		SET_PARENT
4254e3f8cdb8Skettenis 	},
4255e3f8cdb8Skettenis 	{
4256e3f8cdb8Skettenis 		RK3588_ACLK_VOP_SUB_SRC, RK3588_CRU_CLKSEL_CON(115),
4257e3f8cdb8Skettenis 		SEL(9, 9), 0,
4258e3f8cdb8Skettenis 		{ RK3588_ACLK_VOP_ROOT, 0 /* RK3588_ACLK_VOP_DIV2_SRC */ },
4259e3f8cdb8Skettenis 		SET_PARENT
4260e3f8cdb8Skettenis 	},
4261e3f8cdb8Skettenis 	{
4262cec24affSpatrick 		RK3588_CLK_I2C0, RK3588_CRU_CLKSEL_CON(3),
4263cec24affSpatrick 		SEL(6, 6), 0,
4264cec24affSpatrick 		{ RK3588_CLK_PMU1_200M_SRC, RK3588_CLK_PMU1_100M_SRC },
4265cec24affSpatrick 	},
4266cec24affSpatrick 	{
4267e3f8cdb8Skettenis 		RK3588_CLK_PMU1_50M_SRC, RK3588_PMUCRU_CLKSEL_CON(0),
4268e3f8cdb8Skettenis 		0, DIV(3, 0),
4269e3f8cdb8Skettenis 		{ RK3588_CLK_PMU1_400M_SRC }
4270e3f8cdb8Skettenis 	},
4271e3f8cdb8Skettenis 	{
4272e3f8cdb8Skettenis 		RK3588_CLK_PMU1_100M_SRC, RK3588_PMUCRU_CLKSEL_CON(0),
4273e3f8cdb8Skettenis 		0, DIV(6, 4),
4274e3f8cdb8Skettenis 		{ RK3588_CLK_PMU1_400M_SRC }
4275e3f8cdb8Skettenis 	},
4276e3f8cdb8Skettenis 	{
4277e3f8cdb8Skettenis 		RK3588_CLK_PMU1_200M_SRC, RK3588_PMUCRU_CLKSEL_CON(0),
4278e3f8cdb8Skettenis 		0, DIV(9, 7),
4279e3f8cdb8Skettenis 		{ RK3588_CLK_PMU1_400M_SRC }
4280e3f8cdb8Skettenis 	},
4281e3f8cdb8Skettenis 	{
4282e3f8cdb8Skettenis 		RK3588_CLK_PMU1_400M_SRC, RK3588_PMUCRU_CLKSEL_CON(1),
4283e3f8cdb8Skettenis 		SEL(5, 5), DIV(4, 0),
4284e3f8cdb8Skettenis 		{ RK3588_CLK_400M_SRC, RK3588_XIN24M }
4285e3f8cdb8Skettenis 	},
4286e3f8cdb8Skettenis 	{
4287e3f8cdb8Skettenis 		RK3588_PCLK_PMU1_ROOT, RK3588_PMUCRU_CLKSEL_CON(1),
4288e3f8cdb8Skettenis 		SEL(9, 8), 0,
4289e3f8cdb8Skettenis 		{ RK3588_CLK_PMU1_100M_SRC, RK3588_CLK_PMU1_50M_SRC,
4290e3f8cdb8Skettenis 		  RK3588_XIN24M }
4291e3f8cdb8Skettenis 	},
4292e3f8cdb8Skettenis 	{
4293e3f8cdb8Skettenis 		RK3588_PCLK_PMU0_ROOT, 0, 0, 0,
4294e3f8cdb8Skettenis 		{ RK3588_PCLK_PMU1_ROOT },
4295e3f8cdb8Skettenis 		SET_PARENT
4296e3f8cdb8Skettenis 	},
4297e3f8cdb8Skettenis 	{
4298e3f8cdb8Skettenis 		RK3588_HCLK_PMU_CM0_ROOT, RK3588_PMUCRU_CLKSEL_CON(1),
4299e3f8cdb8Skettenis 		SEL(11, 10), 0,
4300e3f8cdb8Skettenis 		{ RK3588_CLK_PMU1_400M_SRC, RK3588_CLK_PMU1_200M_SRC,
4301e3f8cdb8Skettenis 		  RK3588_CLK_PMU1_100M_SRC, RK3588_XIN24M }
4302e3f8cdb8Skettenis 	},
4303e3f8cdb8Skettenis 	{
4304402c60e6Spatrick 		RK3588_CLK_PMU1PWM, RK3588_PMUCRU_CLKSEL_CON(2),
4305402c60e6Spatrick 		SEL(10, 9), 0,
4306402c60e6Spatrick 		{ RK3588_CLK_PMU1_100M_SRC, RK3588_CLK_PMU1_50M_SRC,
4307402c60e6Spatrick 		  RK3588_XIN24M }
4308402c60e6Spatrick 	},
4309402c60e6Spatrick 	{
4310e3f8cdb8Skettenis 		RK3588_CLK_UART0_SRC, RK3588_PMUCRU_CLKSEL_CON(3),
4311e3f8cdb8Skettenis 		0, DIV(11, 7),
4312e3f8cdb8Skettenis 		{ RK3588_PLL_CPLL }
4313e3f8cdb8Skettenis 	},
4314e3f8cdb8Skettenis 	{
4315e3f8cdb8Skettenis 		RK3588_CLK_UART0, RK3588_PMUCRU_CLKSEL_CON(5),
4316e3f8cdb8Skettenis 		SEL(1, 0), 0,
4317e3f8cdb8Skettenis 		{ RK3588_CLK_UART0_SRC, RK3588_CLK_UART0_FRAC, RK3588_XIN24M }
4318e3f8cdb8Skettenis 	},
4319e3f8cdb8Skettenis 	{
4320e3f8cdb8Skettenis 		RK3588_SCLK_UART0, 0, 0, 0,
4321e3f8cdb8Skettenis 		{ RK3588_CLK_UART0 }
4322e3f8cdb8Skettenis 	},
4323e3f8cdb8Skettenis 	{
432483b03bd4Skettenis 		RK3588_CLK_REF_PIPE_PHY0_OSC_SRC, 0, 0, 0,
432583b03bd4Skettenis 		{ RK3588_XIN24M }
432683b03bd4Skettenis 	},
432783b03bd4Skettenis 	{
432820dcd189Spatrick 		RK3588_CLK_REF_PIPE_PHY1_OSC_SRC, 0, 0, 0,
432920dcd189Spatrick 		{ RK3588_XIN24M }
433020dcd189Spatrick 	},
433120dcd189Spatrick 	{
433220dcd189Spatrick 		RK3588_CLK_REF_PIPE_PHY2_OSC_SRC, 0, 0, 0,
433320dcd189Spatrick 		{ RK3588_XIN24M }
433420dcd189Spatrick 	},
433520dcd189Spatrick 	{
433683b03bd4Skettenis 		RK3588_CLK_REF_PIPE_PHY0_PLL_SRC, RK3588_CRU_CLKSEL_CON(176),
433783b03bd4Skettenis 		0, DIV(5, 0),
433883b03bd4Skettenis 		{ RK3588_PLL_PPLL }
433983b03bd4Skettenis 	},
434083b03bd4Skettenis 	{
434120dcd189Spatrick 		RK3588_CLK_REF_PIPE_PHY1_PLL_SRC, RK3588_CRU_CLKSEL_CON(176),
434220dcd189Spatrick 		0, DIV(11, 6),
434320dcd189Spatrick 		{ RK3588_PLL_PPLL }
434420dcd189Spatrick 	},
434520dcd189Spatrick 	{
434620dcd189Spatrick 		RK3588_CLK_REF_PIPE_PHY2_PLL_SRC, RK3588_CRU_CLKSEL_CON(177),
434720dcd189Spatrick 		0, DIV(5, 0),
434820dcd189Spatrick 		{ RK3588_PLL_PPLL }
434920dcd189Spatrick 	},
435020dcd189Spatrick 	{
435183b03bd4Skettenis 		RK3588_CLK_REF_PIPE_PHY0, RK3588_CRU_CLKSEL_CON(177),
435283b03bd4Skettenis 		SEL(6, 6), 0,
435383b03bd4Skettenis 		{ RK3588_CLK_REF_PIPE_PHY0_OSC_SRC,
435483b03bd4Skettenis 		  RK3588_CLK_REF_PIPE_PHY0_PLL_SRC },
435583b03bd4Skettenis 	},
435683b03bd4Skettenis 	{
435720dcd189Spatrick 		RK3588_CLK_REF_PIPE_PHY1, RK3588_CRU_CLKSEL_CON(177),
435820dcd189Spatrick 		SEL(7, 7), 0,
435920dcd189Spatrick 		{ RK3588_CLK_REF_PIPE_PHY1_OSC_SRC,
436020dcd189Spatrick 		  RK3588_CLK_REF_PIPE_PHY1_PLL_SRC },
436120dcd189Spatrick 	},
436220dcd189Spatrick 	{
436320dcd189Spatrick 		RK3588_CLK_REF_PIPE_PHY2, RK3588_CRU_CLKSEL_CON(177),
436420dcd189Spatrick 		SEL(8, 8), 0,
436520dcd189Spatrick 		{ RK3588_CLK_REF_PIPE_PHY2_OSC_SRC,
436620dcd189Spatrick 		  RK3588_CLK_REF_PIPE_PHY2_PLL_SRC },
436720dcd189Spatrick 	},
436820dcd189Spatrick 	{
4369e3f8cdb8Skettenis 		/* Sentinel */
4370e3f8cdb8Skettenis 	}
4371e3f8cdb8Skettenis };
4372e3f8cdb8Skettenis 
4373e3f8cdb8Skettenis /* Certain test clocks are disabled. */
4374e3f8cdb8Skettenis const uint32_t rk3588_gates[78] = {
4375e3f8cdb8Skettenis 	[2] = 0x00000050,
4376e3f8cdb8Skettenis 	[22] = 0x00000200,
4377e3f8cdb8Skettenis 	[25] = 0x00000200,
4378e3f8cdb8Skettenis 	[29] = 0x00000004,
4379e3f8cdb8Skettenis 	[66] = 0x00000004,
4380e3f8cdb8Skettenis };
4381e3f8cdb8Skettenis 
4382e3f8cdb8Skettenis void
4383e3f8cdb8Skettenis rk3588_init(struct rkclock_softc *sc)
4384e3f8cdb8Skettenis {
4385e3f8cdb8Skettenis 	int i;
4386e3f8cdb8Skettenis 
4387e3f8cdb8Skettenis 	/* The code below assumes all clocks are enabled.  Check this!. */
4388e3f8cdb8Skettenis 	for (i = 0; i < nitems(rk3588_gates); i++) {
4389e3f8cdb8Skettenis 		if (HREAD4(sc, RK3588_CRU_GATE_CON(i)) != rk3588_gates[i]) {
4390e3f8cdb8Skettenis 			printf("CRU_GATE_CON%d: 0x%08x\n", i,
4391e3f8cdb8Skettenis 			    HREAD4(sc, RK3588_CRU_GATE_CON(i)));
4392e3f8cdb8Skettenis 		}
4393e3f8cdb8Skettenis 	}
4394e3f8cdb8Skettenis 
4395e3f8cdb8Skettenis 	sc->sc_clocks = rk3588_clocks;
4396e3f8cdb8Skettenis }
4397e3f8cdb8Skettenis 
4398e3f8cdb8Skettenis int
4399e3f8cdb8Skettenis rk3588_set_pll(struct rkclock_softc *sc, bus_size_t base, uint32_t freq)
4400e3f8cdb8Skettenis {
4401e3f8cdb8Skettenis 	uint32_t p, m, s, k;
4402e3f8cdb8Skettenis 	int mode_shift = -1;
4403e3f8cdb8Skettenis 
4404e3f8cdb8Skettenis 	switch (base) {
4405e3f8cdb8Skettenis 	case RK3588_CRU_AUPLL_CON(0):
4406e3f8cdb8Skettenis 		mode_shift = 6;
4407e3f8cdb8Skettenis 		break;
4408e3f8cdb8Skettenis 	case RK3588_CRU_GPLL_CON(0):
4409e3f8cdb8Skettenis 		mode_shift = 2;
4410e3f8cdb8Skettenis 		break;
4411e3f8cdb8Skettenis 	case RK3588_CRU_NPLL_CON(0):
4412e3f8cdb8Skettenis 		mode_shift = 0;
4413e3f8cdb8Skettenis 		break;
4414e3f8cdb8Skettenis 	case RK3588_PHPTOPCRU_PPLL_CON(0):
4415e3f8cdb8Skettenis 		mode_shift = 10;
4416e3f8cdb8Skettenis 		break;
4417e3f8cdb8Skettenis 	}
4418e3f8cdb8Skettenis 	KASSERT(mode_shift != -1);
4419e3f8cdb8Skettenis 
4420e3f8cdb8Skettenis 	/*
4421e3f8cdb8Skettenis 	 * It is not clear whether all combinations of the clock
4422e3f8cdb8Skettenis 	 * dividers result in a stable clock.  Therefore this function
4423e3f8cdb8Skettenis 	 * only supports a limited set of PLL clock rates.
4424e3f8cdb8Skettenis 	 */
4425e3f8cdb8Skettenis 	switch (freq) {
4426e3f8cdb8Skettenis 	case 1188000000U:
4427e3f8cdb8Skettenis 		p = 2; m = 198; s = 1; k = 0;
4428e3f8cdb8Skettenis 		break;
44290dbfb11eSkettenis 	case 1100000000U:
44300dbfb11eSkettenis 		p = 3; m = 550; s = 2; k = 0;
44310dbfb11eSkettenis 		break;
4432e3f8cdb8Skettenis 	case 850000000U:
4433e3f8cdb8Skettenis 		p = 3; m = 425; s = 2; k = 0;
4434e3f8cdb8Skettenis 		break;
4435e3f8cdb8Skettenis 	case 786432000U:
4436e3f8cdb8Skettenis 		p = 2; m = 262; s = 2; k = 9437;
4437e3f8cdb8Skettenis 		break;
4438e3f8cdb8Skettenis 	case 100000000U:
4439e3f8cdb8Skettenis 		p = 3; m = 400; s = 5; k = 0;
4440e3f8cdb8Skettenis 		break;
4441e3f8cdb8Skettenis 	default:
4442e3f8cdb8Skettenis 		printf("%s: %u Hz\n", __func__, freq);
4443e3f8cdb8Skettenis 		return -1;
4444e3f8cdb8Skettenis 	}
4445e3f8cdb8Skettenis 
4446e3f8cdb8Skettenis 	/*
4447e3f8cdb8Skettenis 	 * Select slow mode to guarantee a stable clock while we're
4448e3f8cdb8Skettenis 	 * adjusting the PLL.
4449e3f8cdb8Skettenis 	 */
4450e3f8cdb8Skettenis 	HWRITE4(sc, RK3588_CRU_MODE_CON,
4451e3f8cdb8Skettenis 	    (RK3588_CRU_MODE_MASK << 16 |RK3588_CRU_MODE_SLOW) << mode_shift);
4452e3f8cdb8Skettenis 
4453e3f8cdb8Skettenis 	/* Power down PLL. */
4454e3f8cdb8Skettenis 	HWRITE4(sc, base + 0x0004,
4455e3f8cdb8Skettenis 	    RK3588_CRU_PLL_RESETB << 16 | RK3588_CRU_PLL_RESETB);
4456e3f8cdb8Skettenis 
4457e3f8cdb8Skettenis 	/* Set PLL rate. */
4458e3f8cdb8Skettenis 	HWRITE4(sc, base + 0x0000,
4459e3f8cdb8Skettenis 	    RK3588_CRU_PLL_M_MASK << 16 | m << RK3588_CRU_PLL_M_SHIFT);
4460e3f8cdb8Skettenis 	HWRITE4(sc, base + 0x0004,
4461e3f8cdb8Skettenis 	    RK3588_CRU_PLL_S_MASK << 16 | s << RK3588_CRU_PLL_S_SHIFT |
4462e3f8cdb8Skettenis 	    RK3588_CRU_PLL_P_MASK << 16 | p << RK3588_CRU_PLL_P_SHIFT);
4463e3f8cdb8Skettenis 	HWRITE4(sc, base + 0x0008,
4464e3f8cdb8Skettenis 	    RK3588_CRU_PLL_K_MASK << 16 | k << RK3588_CRU_PLL_K_SHIFT);
4465e3f8cdb8Skettenis 
4466e3f8cdb8Skettenis 	/* Power up PLL. */
4467e3f8cdb8Skettenis 	HWRITE4(sc, base + 0x0004, RK3588_CRU_PLL_RESETB << 16);
4468e3f8cdb8Skettenis 
4469e3f8cdb8Skettenis 	/* Wait for PLL to stabilize. */
4470e3f8cdb8Skettenis 	while ((HREAD4(sc, base + 0x0018) & RK3588_CRU_PLL_PLL_LOCK) == 0)
4471e3f8cdb8Skettenis 		delay(10);
4472e3f8cdb8Skettenis 
4473e3f8cdb8Skettenis 	/* Switch back to normal mode. */
4474e3f8cdb8Skettenis 	HWRITE4(sc, RK3588_CRU_MODE_CON,
4475e3f8cdb8Skettenis 	    (RK3588_CRU_MODE_MASK << 16 | RK3588_CRU_MODE_NORMAL) << mode_shift);
4476e3f8cdb8Skettenis 
4477e3f8cdb8Skettenis 	return 0;
4478e3f8cdb8Skettenis }
4479e3f8cdb8Skettenis 
4480e3f8cdb8Skettenis uint32_t
4481e3f8cdb8Skettenis rk3588_get_pll(struct rkclock_softc *sc, bus_size_t base)
4482e3f8cdb8Skettenis {
4483e3f8cdb8Skettenis 	uint64_t freq, frac;
4484e3f8cdb8Skettenis 	uint32_t k, m, p, s;
4485e3f8cdb8Skettenis 	uint32_t reg;
4486e3f8cdb8Skettenis 
4487e3f8cdb8Skettenis 	reg = HREAD4(sc, base);
4488e3f8cdb8Skettenis 	m = (reg & RK3588_CRU_PLL_M_MASK) >> RK3588_CRU_PLL_M_SHIFT;
4489e3f8cdb8Skettenis 	reg = HREAD4(sc, base + 4);
4490e3f8cdb8Skettenis 	p = (reg & RK3588_CRU_PLL_P_MASK) >> RK3588_CRU_PLL_P_SHIFT;
4491e3f8cdb8Skettenis 	s = (reg & RK3588_CRU_PLL_S_MASK) >> RK3588_CRU_PLL_S_SHIFT;
4492e3f8cdb8Skettenis 	reg = HREAD4(sc, base + 8);
4493e3f8cdb8Skettenis 	k = (reg & RK3588_CRU_PLL_K_MASK) >> RK3588_CRU_PLL_K_SHIFT;
4494e3f8cdb8Skettenis 
4495e3f8cdb8Skettenis 	freq = (24000000ULL * m) / p;
4496e3f8cdb8Skettenis 	if (k) {
4497e3f8cdb8Skettenis 		frac = ((24000000ULL * k) / (p * 65535));
4498e3f8cdb8Skettenis 		freq += frac;
4499e3f8cdb8Skettenis 	}
4500e3f8cdb8Skettenis 
4501e3f8cdb8Skettenis 	return freq >> s;
4502e3f8cdb8Skettenis }
4503e3f8cdb8Skettenis 
4504e3f8cdb8Skettenis uint32_t
4505e3f8cdb8Skettenis rk3588_get_frequency(void *cookie, uint32_t *cells)
4506e3f8cdb8Skettenis {
4507e3f8cdb8Skettenis 	struct rkclock_softc *sc = cookie;
4508e3f8cdb8Skettenis 	uint32_t idx = cells[0];
4509e3f8cdb8Skettenis 	uint32_t freq;
4510e3f8cdb8Skettenis 
4511e3f8cdb8Skettenis 	switch (idx) {
4512e3f8cdb8Skettenis 	case RK3588_PLL_AUPLL:
4513e3f8cdb8Skettenis 		return rk3588_get_pll(sc, RK3588_CRU_AUPLL_CON(0));
4514e3f8cdb8Skettenis 	case RK3588_PLL_CPLL:
4515e3f8cdb8Skettenis 		return rk3588_get_pll(sc, RK3588_CRU_CPLL_CON(0));
4516e3f8cdb8Skettenis 	case RK3588_PLL_GPLL:
4517e3f8cdb8Skettenis 		return rk3588_get_pll(sc, RK3588_CRU_GPLL_CON(0));
4518e3f8cdb8Skettenis 	case RK3588_PLL_NPLL:
4519e3f8cdb8Skettenis 		return rk3588_get_pll(sc, RK3588_CRU_NPLL_CON(0));
4520e3f8cdb8Skettenis 	case RK3588_PLL_PPLL:
4521e3f8cdb8Skettenis 		return rk3588_get_pll(sc, RK3588_PHPTOPCRU_PPLL_CON(0));
4522e3f8cdb8Skettenis 	case RK3588_PLL_SPLL:
4523e3f8cdb8Skettenis 		return rkclock_external_frequency("spll");
4524e3f8cdb8Skettenis 	case RK3588_XIN24M:
4525e3f8cdb8Skettenis 		return 24000000;
4526e3f8cdb8Skettenis 	default:
4527e3f8cdb8Skettenis 		break;
4528e3f8cdb8Skettenis 	}
4529e3f8cdb8Skettenis 
4530e3f8cdb8Skettenis 	freq = rkclock_get_frequency(sc, idx);
4531e3f8cdb8Skettenis 	return freq;
4532e3f8cdb8Skettenis }
4533e3f8cdb8Skettenis 
4534e3f8cdb8Skettenis int
4535e3f8cdb8Skettenis rk3588_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
4536e3f8cdb8Skettenis {
4537e3f8cdb8Skettenis 	struct rkclock_softc *sc = cookie;
4538e3f8cdb8Skettenis 	uint32_t idx = cells[0];
4539e3f8cdb8Skettenis 
4540e3f8cdb8Skettenis 	switch (idx) {
4541e3f8cdb8Skettenis 	case RK3588_PLL_AUPLL:
4542e3f8cdb8Skettenis 		return rk3588_set_pll(sc, RK3588_CRU_AUPLL_CON(0), freq);
4543e3f8cdb8Skettenis 	case RK3588_PLL_GPLL:
4544e3f8cdb8Skettenis 		return rk3588_set_pll(sc, RK3588_CRU_GPLL_CON(0), freq);
4545e3f8cdb8Skettenis 	case RK3588_PLL_NPLL:
4546e3f8cdb8Skettenis 		return rk3588_set_pll(sc, RK3588_CRU_NPLL_CON(0), freq);
4547e3f8cdb8Skettenis 	case RK3588_PLL_PPLL:
4548e3f8cdb8Skettenis 		return rk3588_set_pll(sc, RK3588_PHPTOPCRU_PPLL_CON(0), freq);
4549e3f8cdb8Skettenis 	default:
4550e3f8cdb8Skettenis 		break;
4551e3f8cdb8Skettenis 	}
4552e3f8cdb8Skettenis 
4553e3f8cdb8Skettenis 	return rkclock_set_frequency(sc, idx, freq);
4554e3f8cdb8Skettenis }
4555e3f8cdb8Skettenis 
4556e3f8cdb8Skettenis void
4557e3f8cdb8Skettenis rk3588_enable(void *cookie, uint32_t *cells, int on)
4558e3f8cdb8Skettenis {
4559e3f8cdb8Skettenis 	uint32_t idx = cells[0];
4560e3f8cdb8Skettenis 
4561e3f8cdb8Skettenis 	/* All clocks are enabled upon hardware reset. */
4562e3f8cdb8Skettenis 	if (!on) {
4563e3f8cdb8Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
4564e3f8cdb8Skettenis 		return;
4565e3f8cdb8Skettenis 	}
4566e3f8cdb8Skettenis }
4567e3f8cdb8Skettenis 
4568e3f8cdb8Skettenis void
4569e3f8cdb8Skettenis rk3588_reset(void *cookie, uint32_t *cells, int on)
4570e3f8cdb8Skettenis {
457183b03bd4Skettenis 	struct rkclock_softc *sc = cookie;
4572e3f8cdb8Skettenis 	uint32_t idx = cells[0];
457383b03bd4Skettenis 	uint32_t bit, mask, reg;
4574e3f8cdb8Skettenis 
457583b03bd4Skettenis 	switch (idx) {
45769efac0bfSkettenis 	case RK3588_SRST_P_TSADC:
45779efac0bfSkettenis 		reg = RK3588_CRU_SOFTRST_CON(12);
45789efac0bfSkettenis 		bit = 0;
45799efac0bfSkettenis 		break;
45809efac0bfSkettenis 	case RK3588_SRST_TSADC:
45819efac0bfSkettenis 		reg = RK3588_CRU_SOFTRST_CON(12);
45829efac0bfSkettenis 		bit = 1;
45839efac0bfSkettenis 		break;
458413600422Spatrick 	case RK3588_SRST_H_EMMC:
458513600422Spatrick 		reg = RK3588_CRU_SOFTRST_CON(31);
458613600422Spatrick 		bit = 4;
458713600422Spatrick 		break;
458813600422Spatrick 	case RK3588_SRST_A_EMMC:
458913600422Spatrick 		reg = RK3588_CRU_SOFTRST_CON(31);
459013600422Spatrick 		bit = 5;
459113600422Spatrick 		break;
459213600422Spatrick 	case RK3588_SRST_C_EMMC:
459313600422Spatrick 		reg = RK3588_CRU_SOFTRST_CON(31);
459413600422Spatrick 		bit = 6;
459513600422Spatrick 		break;
459613600422Spatrick 	case RK3588_SRST_B_EMMC:
459713600422Spatrick 		reg = RK3588_CRU_SOFTRST_CON(31);
459813600422Spatrick 		bit = 7;
459913600422Spatrick 		break;
460013600422Spatrick 	case RK3588_SRST_T_EMMC:
460113600422Spatrick 		reg = RK3588_CRU_SOFTRST_CON(31);
460213600422Spatrick 		bit = 8;
460313600422Spatrick 		break;
4604761126a9Skettenis 	case RK3588_SRST_A_GMAC0:
4605761126a9Skettenis 		reg = RK3588_CRU_SOFTRST_CON(32);
4606761126a9Skettenis 		bit = 10;
4607761126a9Skettenis 		break;
4608761126a9Skettenis 	case RK3588_SRST_A_GMAC1:
4609761126a9Skettenis 		reg = RK3588_CRU_SOFTRST_CON(32);
4610761126a9Skettenis 		bit = 11;
4611761126a9Skettenis 		break;
461273d3ec9fSpatrick 	case RK3588_SRST_PCIE0_POWER_UP:
461373d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(32);
461473d3ec9fSpatrick 		bit = 13;
461573d3ec9fSpatrick 		break;
461673d3ec9fSpatrick 	case RK3588_SRST_PCIE1_POWER_UP:
461773d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(32);
461873d3ec9fSpatrick 		bit = 14;
461973d3ec9fSpatrick 		break;
462073d3ec9fSpatrick 	case RK3588_SRST_PCIE2_POWER_UP:
462173d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(32);
462273d3ec9fSpatrick 		bit = 15;
462373d3ec9fSpatrick 		break;
462473d3ec9fSpatrick 	case RK3588_SRST_PCIE3_POWER_UP:
462573d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(33);
462673d3ec9fSpatrick 		bit = 0;
462773d3ec9fSpatrick 		break;
462883b03bd4Skettenis 	case RK3588_SRST_PCIE4_POWER_UP:
462983b03bd4Skettenis 		reg = RK3588_CRU_SOFTRST_CON(33);
463083b03bd4Skettenis 		bit = 1;
463183b03bd4Skettenis 		break;
463273d3ec9fSpatrick 	case RK3588_SRST_P_PCIE0:
463373d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(33);
463473d3ec9fSpatrick 		bit = 12;
463573d3ec9fSpatrick 		break;
463673d3ec9fSpatrick 	case RK3588_SRST_P_PCIE1:
463773d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(33);
463873d3ec9fSpatrick 		bit = 13;
463973d3ec9fSpatrick 		break;
464073d3ec9fSpatrick 	case RK3588_SRST_P_PCIE2:
464173d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(33);
464273d3ec9fSpatrick 		bit = 14;
464373d3ec9fSpatrick 		break;
464473d3ec9fSpatrick 	case RK3588_SRST_P_PCIE3:
464573d3ec9fSpatrick 		reg = RK3588_CRU_SOFTRST_CON(33);
464673d3ec9fSpatrick 		bit = 15;
464773d3ec9fSpatrick 		break;
46481897c8c3Skettenis 	case RK3588_SRST_P_PCIE4:
46491897c8c3Skettenis 		reg = RK3588_CRU_SOFTRST_CON(34);
46501897c8c3Skettenis 		bit = 0;
46511897c8c3Skettenis 		break;
46520dbfb11eSkettenis 	case RK3588_SRST_A_USB3OTG2:
46530dbfb11eSkettenis 		reg = RK3588_CRU_SOFTRST_CON(35);
46540dbfb11eSkettenis 		bit = 7;
46550dbfb11eSkettenis 		break;
4656e77f74d5Spatrick 	case RK3588_SRST_A_USB3OTG0:
4657e77f74d5Spatrick 		reg = RK3588_CRU_SOFTRST_CON(42);
4658e77f74d5Spatrick 		bit = 4;
4659e77f74d5Spatrick 		break;
4660e77f74d5Spatrick 	case RK3588_SRST_A_USB3OTG1:
4661e77f74d5Spatrick 		reg = RK3588_CRU_SOFTRST_CON(42);
4662e77f74d5Spatrick 		bit = 7;
4663e77f74d5Spatrick 		break;
466483b03bd4Skettenis 	case RK3588_SRST_REF_PIPE_PHY0:
466583b03bd4Skettenis 		reg = RK3588_CRU_SOFTRST_CON(77);
466683b03bd4Skettenis 		bit = 6;
466783b03bd4Skettenis 		break;
466820dcd189Spatrick 	case RK3588_SRST_REF_PIPE_PHY1:
466920dcd189Spatrick 		reg = RK3588_CRU_SOFTRST_CON(77);
467020dcd189Spatrick 		bit = 7;
467120dcd189Spatrick 		break;
467220dcd189Spatrick 	case RK3588_SRST_REF_PIPE_PHY2:
467320dcd189Spatrick 		reg = RK3588_CRU_SOFTRST_CON(77);
467420dcd189Spatrick 		bit = 8;
467520dcd189Spatrick 		break;
467683b03bd4Skettenis 	case RK3588_SRST_P_PCIE2_PHY0:
467783b03bd4Skettenis 		reg = RK3588_PHPTOPCRU_SOFTRST_CON(0);
467883b03bd4Skettenis 		bit = 5;
467983b03bd4Skettenis 		break;
468020dcd189Spatrick 	case RK3588_SRST_P_PCIE2_PHY1:
468120dcd189Spatrick 		reg = RK3588_PHPTOPCRU_SOFTRST_CON(0);
468220dcd189Spatrick 		bit = 6;
468320dcd189Spatrick 		break;
468420dcd189Spatrick 	case RK3588_SRST_P_PCIE2_PHY2:
468520dcd189Spatrick 		reg = RK3588_PHPTOPCRU_SOFTRST_CON(0);
468620dcd189Spatrick 		bit = 7;
468720dcd189Spatrick 		break;
468873d3ec9fSpatrick 	case RK3588_SRST_PCIE30_PHY:
468973d3ec9fSpatrick 		reg = RK3588_PHPTOPCRU_SOFTRST_CON(0);
469073d3ec9fSpatrick 		bit = 10;
469173d3ec9fSpatrick 		break;
469283b03bd4Skettenis 	default:
4693e3f8cdb8Skettenis 		printf("%s: 0x%08x\n", __func__, idx);
469483b03bd4Skettenis 		return;
469583b03bd4Skettenis 	}
469683b03bd4Skettenis 
469783b03bd4Skettenis 	mask = (1 << bit);
469883b03bd4Skettenis 	HWRITE4(sc, reg, mask << 16 | (on ? mask : 0));
4699e3f8cdb8Skettenis }
4700