1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0
2d1dcf852SAndy Yan /*
3d1dcf852SAndy Yan * (C) Copyright 2017 Rockchip Electronics Co., Ltd
4d1dcf852SAndy Yan * Author: Andy Yan <andy.yan@rock-chips.com>
5ddfe77dfSPhilipp Tomsich * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
6d1dcf852SAndy Yan */
7d1dcf852SAndy Yan
8d1dcf852SAndy Yan #include <common.h>
9d1dcf852SAndy Yan #include <clk-uclass.h>
10d1dcf852SAndy Yan #include <dm.h>
11bee61801SPhilipp Tomsich #include <dt-structs.h>
12d1dcf852SAndy Yan #include <errno.h>
13bee61801SPhilipp Tomsich #include <mapmem.h>
14d1dcf852SAndy Yan #include <syscon.h>
15615514c1SDavid Wu #include <bitfield.h>
16d1dcf852SAndy Yan #include <asm/arch/clock.h>
17d1dcf852SAndy Yan #include <asm/arch/cru_rk3368.h>
18d1dcf852SAndy Yan #include <asm/arch/hardware.h>
19d1dcf852SAndy Yan #include <asm/io.h>
20d1dcf852SAndy Yan #include <dm/lists.h>
21d1dcf852SAndy Yan #include <dt-bindings/clock/rk3368-cru.h>
22d1dcf852SAndy Yan
23bee61801SPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_PLATDATA)
24bee61801SPhilipp Tomsich struct rk3368_clk_plat {
25bee61801SPhilipp Tomsich struct dtd_rockchip_rk3368_cru dtd;
26bee61801SPhilipp Tomsich };
27bee61801SPhilipp Tomsich #endif
28bee61801SPhilipp Tomsich
29d1dcf852SAndy Yan struct pll_div {
30d1dcf852SAndy Yan u32 nr;
31d1dcf852SAndy Yan u32 nf;
32d1dcf852SAndy Yan u32 no;
33d1dcf852SAndy Yan };
34d1dcf852SAndy Yan
35d1dcf852SAndy Yan #define OSC_HZ (24 * 1000 * 1000)
36d1dcf852SAndy Yan #define APLL_L_HZ (800 * 1000 * 1000)
37d1dcf852SAndy Yan #define APLL_B_HZ (816 * 1000 * 1000)
38d1dcf852SAndy Yan #define GPLL_HZ (576 * 1000 * 1000)
39d1dcf852SAndy Yan #define CPLL_HZ (400 * 1000 * 1000)
40d1dcf852SAndy Yan
41d1dcf852SAndy Yan #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
42d1dcf852SAndy Yan
43d1dcf852SAndy Yan #define PLL_DIVISORS(hz, _nr, _no) { \
44d1dcf852SAndy Yan .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no}; \
45d1dcf852SAndy Yan _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
46d1dcf852SAndy Yan (_nr * _no) == hz, #hz "Hz cannot be hit with PLL " \
47d1dcf852SAndy Yan "divisors on line " __stringify(__LINE__));
48d1dcf852SAndy Yan
494bebf94eSPhilipp Tomsich #if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
50d1dcf852SAndy Yan static const struct pll_div apll_l_init_cfg = PLL_DIVISORS(APLL_L_HZ, 12, 2);
51d1dcf852SAndy Yan static const struct pll_div apll_b_init_cfg = PLL_DIVISORS(APLL_B_HZ, 1, 2);
524bebf94eSPhilipp Tomsich #if !defined(CONFIG_TPL_BUILD)
53d1dcf852SAndy Yan static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 2);
54d1dcf852SAndy Yan static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6);
554bebf94eSPhilipp Tomsich #endif
564bebf94eSPhilipp Tomsich #endif
57d1dcf852SAndy Yan
58f5a43295SPhilipp Tomsich static ulong rk3368_clk_get_rate(struct clk *clk);
59f5a43295SPhilipp Tomsich
60d1dcf852SAndy Yan /* Get pll rate by id */
rkclk_pll_get_rate(struct rk3368_cru * cru,enum rk3368_pll_id pll_id)61d1dcf852SAndy Yan static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru,
62d1dcf852SAndy Yan enum rk3368_pll_id pll_id)
63d1dcf852SAndy Yan {
64d1dcf852SAndy Yan uint32_t nr, no, nf;
65d1dcf852SAndy Yan uint32_t con;
66d1dcf852SAndy Yan struct rk3368_pll *pll = &cru->pll[pll_id];
67d1dcf852SAndy Yan
68d1dcf852SAndy Yan con = readl(&pll->con3);
69d1dcf852SAndy Yan
70d1dcf852SAndy Yan switch ((con & PLL_MODE_MASK) >> PLL_MODE_SHIFT) {
71d1dcf852SAndy Yan case PLL_MODE_SLOW:
72d1dcf852SAndy Yan return OSC_HZ;
73d1dcf852SAndy Yan case PLL_MODE_NORMAL:
74d1dcf852SAndy Yan con = readl(&pll->con0);
75d1dcf852SAndy Yan no = ((con & PLL_OD_MASK) >> PLL_OD_SHIFT) + 1;
76d1dcf852SAndy Yan nr = ((con & PLL_NR_MASK) >> PLL_NR_SHIFT) + 1;
77d1dcf852SAndy Yan con = readl(&pll->con1);
78d1dcf852SAndy Yan nf = ((con & PLL_NF_MASK) >> PLL_NF_SHIFT) + 1;
79d1dcf852SAndy Yan
80d1dcf852SAndy Yan return (24 * nf / (nr * no)) * 1000000;
81d1dcf852SAndy Yan case PLL_MODE_DEEP_SLOW:
82d1dcf852SAndy Yan default:
83d1dcf852SAndy Yan return 32768;
84d1dcf852SAndy Yan }
85d1dcf852SAndy Yan }
86d1dcf852SAndy Yan
874bebf94eSPhilipp Tomsich #if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
rkclk_set_pll(struct rk3368_cru * cru,enum rk3368_pll_id pll_id,const struct pll_div * div)88d1dcf852SAndy Yan static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id,
89ddfe77dfSPhilipp Tomsich const struct pll_div *div)
90d1dcf852SAndy Yan {
91d1dcf852SAndy Yan struct rk3368_pll *pll = &cru->pll[pll_id];
92d1dcf852SAndy Yan /* All PLLs have same VCO and output frequency range restrictions*/
93d1dcf852SAndy Yan uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
94d1dcf852SAndy Yan uint output_hz = vco_hz / div->no;
95d1dcf852SAndy Yan
96d1dcf852SAndy Yan debug("PLL at %p: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
97d1dcf852SAndy Yan pll, div->nf, div->nr, div->no, vco_hz, output_hz);
98d1dcf852SAndy Yan
99d1dcf852SAndy Yan /* enter slow mode and reset pll */
100d1dcf852SAndy Yan rk_clrsetreg(&pll->con3, PLL_MODE_MASK | PLL_RESET_MASK,
101d1dcf852SAndy Yan PLL_RESET << PLL_RESET_SHIFT);
102d1dcf852SAndy Yan
103d1dcf852SAndy Yan rk_clrsetreg(&pll->con0, PLL_NR_MASK | PLL_OD_MASK,
104d1dcf852SAndy Yan ((div->nr - 1) << PLL_NR_SHIFT) |
105d1dcf852SAndy Yan ((div->no - 1) << PLL_OD_SHIFT));
106d1dcf852SAndy Yan writel((div->nf - 1) << PLL_NF_SHIFT, &pll->con1);
107ddfe77dfSPhilipp Tomsich /*
108ddfe77dfSPhilipp Tomsich * BWADJ should be set to NF / 2 to ensure the nominal bandwidth.
109ddfe77dfSPhilipp Tomsich * Compare the RK3368 TRM, section "3.6.4 PLL Bandwidth Adjustment".
110ddfe77dfSPhilipp Tomsich */
111ddfe77dfSPhilipp Tomsich clrsetbits_le32(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
112ddfe77dfSPhilipp Tomsich
113d1dcf852SAndy Yan udelay(10);
114d1dcf852SAndy Yan
115d1dcf852SAndy Yan /* return from reset */
116d1dcf852SAndy Yan rk_clrreg(&pll->con3, PLL_RESET_MASK);
117d1dcf852SAndy Yan
118d1dcf852SAndy Yan /* waiting for pll lock */
119d1dcf852SAndy Yan while (!(readl(&pll->con1) & PLL_LOCK_STA))
120d1dcf852SAndy Yan udelay(1);
121d1dcf852SAndy Yan
122d1dcf852SAndy Yan rk_clrsetreg(&pll->con3, PLL_MODE_MASK,
123d1dcf852SAndy Yan PLL_MODE_NORMAL << PLL_MODE_SHIFT);
124d1dcf852SAndy Yan
125d1dcf852SAndy Yan return 0;
126d1dcf852SAndy Yan }
1274bebf94eSPhilipp Tomsich #endif
128d1dcf852SAndy Yan
1294bebf94eSPhilipp Tomsich #if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
rkclk_init(struct rk3368_cru * cru)130d1dcf852SAndy Yan static void rkclk_init(struct rk3368_cru *cru)
131d1dcf852SAndy Yan {
132d1dcf852SAndy Yan u32 apllb, aplll, dpll, cpll, gpll;
133d1dcf852SAndy Yan
134ddfe77dfSPhilipp Tomsich rkclk_set_pll(cru, APLLB, &apll_b_init_cfg);
135ddfe77dfSPhilipp Tomsich rkclk_set_pll(cru, APLLL, &apll_l_init_cfg);
1364bebf94eSPhilipp Tomsich #if !defined(CONFIG_TPL_BUILD)
1374bebf94eSPhilipp Tomsich /*
1384bebf94eSPhilipp Tomsich * If we plan to return to the boot ROM, we can't increase the
1394bebf94eSPhilipp Tomsich * GPLL rate from the SPL stage.
1404bebf94eSPhilipp Tomsich */
141ddfe77dfSPhilipp Tomsich rkclk_set_pll(cru, GPLL, &gpll_init_cfg);
142ddfe77dfSPhilipp Tomsich rkclk_set_pll(cru, CPLL, &cpll_init_cfg);
1434bebf94eSPhilipp Tomsich #endif
144d1dcf852SAndy Yan
145d1dcf852SAndy Yan apllb = rkclk_pll_get_rate(cru, APLLB);
146d1dcf852SAndy Yan aplll = rkclk_pll_get_rate(cru, APLLL);
147d1dcf852SAndy Yan dpll = rkclk_pll_get_rate(cru, DPLL);
148d1dcf852SAndy Yan cpll = rkclk_pll_get_rate(cru, CPLL);
149d1dcf852SAndy Yan gpll = rkclk_pll_get_rate(cru, GPLL);
150d1dcf852SAndy Yan
151d1dcf852SAndy Yan debug("%s apllb(%d) apll(%d) dpll(%d) cpll(%d) gpll(%d)\n",
152d1dcf852SAndy Yan __func__, apllb, aplll, dpll, cpll, gpll);
153d1dcf852SAndy Yan }
1544bebf94eSPhilipp Tomsich #endif
155d1dcf852SAndy Yan
156f5a43295SPhilipp Tomsich #if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
rk3368_mmc_get_clk(struct rk3368_cru * cru,uint clk_id)157d1dcf852SAndy Yan static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id)
158d1dcf852SAndy Yan {
159d1dcf852SAndy Yan u32 div, con, con_id, rate;
160d1dcf852SAndy Yan u32 pll_rate;
161d1dcf852SAndy Yan
162d1dcf852SAndy Yan switch (clk_id) {
163f5a43295SPhilipp Tomsich case HCLK_SDMMC:
164d1dcf852SAndy Yan con_id = 50;
165d1dcf852SAndy Yan break;
166f5a43295SPhilipp Tomsich case HCLK_EMMC:
167d1dcf852SAndy Yan con_id = 51;
168d1dcf852SAndy Yan break;
169d1dcf852SAndy Yan case SCLK_SDIO0:
170d1dcf852SAndy Yan con_id = 48;
171d1dcf852SAndy Yan break;
172d1dcf852SAndy Yan default:
173d1dcf852SAndy Yan return -EINVAL;
174d1dcf852SAndy Yan }
175d1dcf852SAndy Yan
176d1dcf852SAndy Yan con = readl(&cru->clksel_con[con_id]);
177f5a43295SPhilipp Tomsich switch (con & MMC_PLL_SEL_MASK) {
178d1dcf852SAndy Yan case MMC_PLL_SEL_GPLL:
179d1dcf852SAndy Yan pll_rate = rkclk_pll_get_rate(cru, GPLL);
180d1dcf852SAndy Yan break;
181d1dcf852SAndy Yan case MMC_PLL_SEL_24M:
182d1dcf852SAndy Yan pll_rate = OSC_HZ;
183d1dcf852SAndy Yan break;
184d1dcf852SAndy Yan case MMC_PLL_SEL_CPLL:
185f5a43295SPhilipp Tomsich pll_rate = rkclk_pll_get_rate(cru, CPLL);
186f5a43295SPhilipp Tomsich break;
187d1dcf852SAndy Yan case MMC_PLL_SEL_USBPHY_480M:
188d1dcf852SAndy Yan default:
189d1dcf852SAndy Yan return -EINVAL;
190d1dcf852SAndy Yan }
191d1dcf852SAndy Yan div = (con & MMC_CLK_DIV_MASK) >> MMC_CLK_DIV_SHIFT;
192d1dcf852SAndy Yan rate = DIV_TO_RATE(pll_rate, div);
193d1dcf852SAndy Yan
194f5a43295SPhilipp Tomsich debug("%s: raw rate %d (post-divide by 2)\n", __func__, rate);
195d1dcf852SAndy Yan return rate >> 1;
196d1dcf852SAndy Yan }
197d1dcf852SAndy Yan
rk3368_mmc_find_best_rate_and_parent(struct clk * clk,ulong rate,u32 * best_mux,u32 * best_div)198f5a43295SPhilipp Tomsich static ulong rk3368_mmc_find_best_rate_and_parent(struct clk *clk,
199f5a43295SPhilipp Tomsich ulong rate,
200f5a43295SPhilipp Tomsich u32 *best_mux,
201f5a43295SPhilipp Tomsich u32 *best_div)
202d1dcf852SAndy Yan {
203f5a43295SPhilipp Tomsich int i;
204f5a43295SPhilipp Tomsich ulong best_rate = 0;
205f5a43295SPhilipp Tomsich const ulong MHz = 1000000;
206f5a43295SPhilipp Tomsich const struct {
207f5a43295SPhilipp Tomsich u32 mux;
208f5a43295SPhilipp Tomsich ulong rate;
209f5a43295SPhilipp Tomsich } parents[] = {
210f5a43295SPhilipp Tomsich { .mux = MMC_PLL_SEL_CPLL, .rate = CPLL_HZ },
211f5a43295SPhilipp Tomsich { .mux = MMC_PLL_SEL_GPLL, .rate = GPLL_HZ },
212f5a43295SPhilipp Tomsich { .mux = MMC_PLL_SEL_24M, .rate = 24 * MHz }
213f5a43295SPhilipp Tomsich };
214d1dcf852SAndy Yan
215f5a43295SPhilipp Tomsich debug("%s: target rate %ld\n", __func__, rate);
216f5a43295SPhilipp Tomsich for (i = 0; i < ARRAY_SIZE(parents); ++i) {
217f5a43295SPhilipp Tomsich /*
218f5a43295SPhilipp Tomsich * Find the largest rate no larger than the target-rate for
219f5a43295SPhilipp Tomsich * the current parent.
220f5a43295SPhilipp Tomsich */
221f5a43295SPhilipp Tomsich ulong parent_rate = parents[i].rate;
222f5a43295SPhilipp Tomsich u32 div = DIV_ROUND_UP(parent_rate, rate);
223f5a43295SPhilipp Tomsich u32 adj_div = div;
224f5a43295SPhilipp Tomsich ulong new_rate = parent_rate / adj_div;
225f5a43295SPhilipp Tomsich
226f5a43295SPhilipp Tomsich debug("%s: rate %ld, parent-mux %d, parent-rate %ld, div %d\n",
227f5a43295SPhilipp Tomsich __func__, rate, parents[i].mux, parents[i].rate, div);
228f5a43295SPhilipp Tomsich
229f5a43295SPhilipp Tomsich /* Skip, if not representable */
230f5a43295SPhilipp Tomsich if ((div - 1) > MMC_CLK_DIV_MASK)
231f5a43295SPhilipp Tomsich continue;
232f5a43295SPhilipp Tomsich
233f5a43295SPhilipp Tomsich /* Skip, if we already have a better (or equal) solution */
234f5a43295SPhilipp Tomsich if (new_rate <= best_rate)
235f5a43295SPhilipp Tomsich continue;
236f5a43295SPhilipp Tomsich
237f5a43295SPhilipp Tomsich /* This is our new best rate. */
238f5a43295SPhilipp Tomsich best_rate = new_rate;
239f5a43295SPhilipp Tomsich *best_mux = parents[i].mux;
240f5a43295SPhilipp Tomsich *best_div = div - 1;
241f5a43295SPhilipp Tomsich }
242f5a43295SPhilipp Tomsich
243f5a43295SPhilipp Tomsich debug("%s: best_mux = %x, best_div = %d, best_rate = %ld\n",
244f5a43295SPhilipp Tomsich __func__, *best_mux, *best_div, best_rate);
245f5a43295SPhilipp Tomsich
246f5a43295SPhilipp Tomsich return best_rate;
247f5a43295SPhilipp Tomsich }
248f5a43295SPhilipp Tomsich
rk3368_mmc_set_clk(struct clk * clk,ulong rate)249f5a43295SPhilipp Tomsich static ulong rk3368_mmc_set_clk(struct clk *clk, ulong rate)
250f5a43295SPhilipp Tomsich {
251f5a43295SPhilipp Tomsich struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
252f5a43295SPhilipp Tomsich struct rk3368_cru *cru = priv->cru;
253f5a43295SPhilipp Tomsich ulong clk_id = clk->id;
254f5a43295SPhilipp Tomsich u32 con_id, mux = 0, div = 0;
255f5a43295SPhilipp Tomsich
256f5a43295SPhilipp Tomsich /* Find the best parent and rate */
257f5a43295SPhilipp Tomsich rk3368_mmc_find_best_rate_and_parent(clk, rate << 1, &mux, &div);
258d1dcf852SAndy Yan
259d1dcf852SAndy Yan switch (clk_id) {
260f5a43295SPhilipp Tomsich case HCLK_SDMMC:
261d1dcf852SAndy Yan con_id = 50;
262d1dcf852SAndy Yan break;
263f5a43295SPhilipp Tomsich case HCLK_EMMC:
264d1dcf852SAndy Yan con_id = 51;
265d1dcf852SAndy Yan break;
266d1dcf852SAndy Yan case SCLK_SDIO0:
267d1dcf852SAndy Yan con_id = 48;
268d1dcf852SAndy Yan break;
269d1dcf852SAndy Yan default:
270d1dcf852SAndy Yan return -EINVAL;
271d1dcf852SAndy Yan }
272d1dcf852SAndy Yan
273d1dcf852SAndy Yan rk_clrsetreg(&cru->clksel_con[con_id],
274d1dcf852SAndy Yan MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK,
275f5a43295SPhilipp Tomsich mux | div);
276d1dcf852SAndy Yan
277d1dcf852SAndy Yan return rk3368_mmc_get_clk(cru, clk_id);
278d1dcf852SAndy Yan }
279f5a43295SPhilipp Tomsich #endif
280d1dcf852SAndy Yan
28162924690SPhilipp Tomsich #if IS_ENABLED(CONFIG_TPL_BUILD)
rk3368_ddr_set_clk(struct rk3368_cru * cru,ulong set_rate)282a00dfa04SPhilipp Tomsich static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate)
283a00dfa04SPhilipp Tomsich {
284a00dfa04SPhilipp Tomsich const struct pll_div *dpll_cfg = NULL;
285a00dfa04SPhilipp Tomsich const ulong MHz = 1000000;
286a00dfa04SPhilipp Tomsich
287a00dfa04SPhilipp Tomsich /* Fout = ((Fin /NR) * NF )/ NO */
28862924690SPhilipp Tomsich static const struct pll_div dpll_1200 = PLL_DIVISORS(1200 * MHz, 1, 1);
28962924690SPhilipp Tomsich static const struct pll_div dpll_1332 = PLL_DIVISORS(1332 * MHz, 2, 1);
29062924690SPhilipp Tomsich static const struct pll_div dpll_1600 = PLL_DIVISORS(1600 * MHz, 3, 2);
291a00dfa04SPhilipp Tomsich
292a00dfa04SPhilipp Tomsich switch (set_rate) {
293a00dfa04SPhilipp Tomsich case 1200*MHz:
294a00dfa04SPhilipp Tomsich dpll_cfg = &dpll_1200;
295a00dfa04SPhilipp Tomsich break;
296a00dfa04SPhilipp Tomsich case 1332*MHz:
297a00dfa04SPhilipp Tomsich dpll_cfg = &dpll_1332;
298a00dfa04SPhilipp Tomsich break;
299a00dfa04SPhilipp Tomsich case 1600*MHz:
300a00dfa04SPhilipp Tomsich dpll_cfg = &dpll_1600;
301a00dfa04SPhilipp Tomsich break;
302a00dfa04SPhilipp Tomsich default:
3039b643e31SMasahiro Yamada pr_err("Unsupported SDRAM frequency!,%ld\n", set_rate);
304a00dfa04SPhilipp Tomsich }
305a00dfa04SPhilipp Tomsich rkclk_set_pll(cru, DPLL, dpll_cfg);
306a00dfa04SPhilipp Tomsich
307a00dfa04SPhilipp Tomsich return set_rate;
308a00dfa04SPhilipp Tomsich }
30962924690SPhilipp Tomsich #endif
310a00dfa04SPhilipp Tomsich
311df0ae000SPhilipp Tomsich #if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
rk3368_gmac_set_clk(struct rk3368_cru * cru,ulong set_rate)31264a12202SDavid Wu static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru, ulong set_rate)
313df0ae000SPhilipp Tomsich {
31464a12202SDavid Wu ulong ret;
31564a12202SDavid Wu
316df0ae000SPhilipp Tomsich /*
31764a12202SDavid Wu * The gmac clock can be derived either from an external clock
31864a12202SDavid Wu * or can be generated from internally by a divider from SCLK_MAC.
319df0ae000SPhilipp Tomsich */
32064a12202SDavid Wu if (readl(&cru->clksel_con[43]) & GMAC_MUX_SEL_EXTCLK) {
32164a12202SDavid Wu /* An external clock will always generate the right rate... */
32264a12202SDavid Wu ret = set_rate;
32364a12202SDavid Wu } else {
32464a12202SDavid Wu u32 con = readl(&cru->clksel_con[43]);
32564a12202SDavid Wu ulong pll_rate;
32664a12202SDavid Wu u8 div;
32764a12202SDavid Wu
32864a12202SDavid Wu if (((con >> GMAC_PLL_SHIFT) & GMAC_PLL_MASK) ==
32964a12202SDavid Wu GMAC_PLL_SELECT_GENERAL)
33064a12202SDavid Wu pll_rate = GPLL_HZ;
33164a12202SDavid Wu else if (((con >> GMAC_PLL_SHIFT) & GMAC_PLL_MASK) ==
33264a12202SDavid Wu GMAC_PLL_SELECT_CODEC)
33364a12202SDavid Wu pll_rate = CPLL_HZ;
33464a12202SDavid Wu else
33564a12202SDavid Wu /* CPLL is not set */
33664a12202SDavid Wu return -EPERM;
33764a12202SDavid Wu
33864a12202SDavid Wu div = DIV_ROUND_UP(pll_rate, set_rate) - 1;
33964a12202SDavid Wu if (div <= 0x1f)
34064a12202SDavid Wu rk_clrsetreg(&cru->clksel_con[43], GMAC_DIV_CON_MASK,
34164a12202SDavid Wu div << GMAC_DIV_CON_SHIFT);
34264a12202SDavid Wu else
34364a12202SDavid Wu debug("Unsupported div for gmac:%d\n", div);
34464a12202SDavid Wu
34564a12202SDavid Wu return DIV_TO_RATE(pll_rate, div);
34664a12202SDavid Wu }
34764a12202SDavid Wu
34864a12202SDavid Wu return ret;
349df0ae000SPhilipp Tomsich }
350df0ae000SPhilipp Tomsich #endif
351df0ae000SPhilipp Tomsich
352cf8aceb1SPhilipp Tomsich /*
353cf8aceb1SPhilipp Tomsich * RK3368 SPI clocks have a common divider-width (7 bits) and a single bit
354cf8aceb1SPhilipp Tomsich * to select either CPLL or GPLL as the clock-parent. The location within
355cf8aceb1SPhilipp Tomsich * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable.
356cf8aceb1SPhilipp Tomsich */
357cf8aceb1SPhilipp Tomsich
358cf8aceb1SPhilipp Tomsich struct spi_clkreg {
359cf8aceb1SPhilipp Tomsich uint8_t reg; /* CLKSEL_CON[reg] register in CRU */
360cf8aceb1SPhilipp Tomsich uint8_t div_shift;
361cf8aceb1SPhilipp Tomsich uint8_t sel_shift;
362cf8aceb1SPhilipp Tomsich };
363cf8aceb1SPhilipp Tomsich
364cf8aceb1SPhilipp Tomsich /*
365cf8aceb1SPhilipp Tomsich * The entries are numbered relative to their offset from SCLK_SPI0.
366cf8aceb1SPhilipp Tomsich */
367cf8aceb1SPhilipp Tomsich static const struct spi_clkreg spi_clkregs[] = {
368cf8aceb1SPhilipp Tomsich [0] = { .reg = 45, .div_shift = 0, .sel_shift = 7, },
369cf8aceb1SPhilipp Tomsich [1] = { .reg = 45, .div_shift = 8, .sel_shift = 15, },
370cf8aceb1SPhilipp Tomsich [2] = { .reg = 46, .div_shift = 8, .sel_shift = 15, },
371cf8aceb1SPhilipp Tomsich };
372cf8aceb1SPhilipp Tomsich
extract_bits(u32 val,unsigned width,unsigned shift)373cf8aceb1SPhilipp Tomsich static inline u32 extract_bits(u32 val, unsigned width, unsigned shift)
374cf8aceb1SPhilipp Tomsich {
375cf8aceb1SPhilipp Tomsich return (val >> shift) & ((1 << width) - 1);
376cf8aceb1SPhilipp Tomsich }
377cf8aceb1SPhilipp Tomsich
rk3368_spi_get_clk(struct rk3368_cru * cru,ulong clk_id)378cf8aceb1SPhilipp Tomsich static ulong rk3368_spi_get_clk(struct rk3368_cru *cru, ulong clk_id)
379cf8aceb1SPhilipp Tomsich {
380cf8aceb1SPhilipp Tomsich const struct spi_clkreg *spiclk = NULL;
381cf8aceb1SPhilipp Tomsich u32 div, val;
382cf8aceb1SPhilipp Tomsich
383cf8aceb1SPhilipp Tomsich switch (clk_id) {
384cf8aceb1SPhilipp Tomsich case SCLK_SPI0 ... SCLK_SPI2:
385cf8aceb1SPhilipp Tomsich spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
386cf8aceb1SPhilipp Tomsich break;
387cf8aceb1SPhilipp Tomsich
388cf8aceb1SPhilipp Tomsich default:
3899b643e31SMasahiro Yamada pr_err("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
390cf8aceb1SPhilipp Tomsich return -EINVAL;
391cf8aceb1SPhilipp Tomsich }
392cf8aceb1SPhilipp Tomsich
393cf8aceb1SPhilipp Tomsich val = readl(&cru->clksel_con[spiclk->reg]);
394cf8aceb1SPhilipp Tomsich div = extract_bits(val, 7, spiclk->div_shift);
395cf8aceb1SPhilipp Tomsich
396cf8aceb1SPhilipp Tomsich debug("%s: div 0x%x\n", __func__, div);
397cf8aceb1SPhilipp Tomsich return DIV_TO_RATE(GPLL_HZ, div);
398cf8aceb1SPhilipp Tomsich }
399cf8aceb1SPhilipp Tomsich
rk3368_spi_set_clk(struct rk3368_cru * cru,ulong clk_id,uint hz)400cf8aceb1SPhilipp Tomsich static ulong rk3368_spi_set_clk(struct rk3368_cru *cru, ulong clk_id, uint hz)
401cf8aceb1SPhilipp Tomsich {
402cf8aceb1SPhilipp Tomsich const struct spi_clkreg *spiclk = NULL;
403cf8aceb1SPhilipp Tomsich int src_clk_div;
404cf8aceb1SPhilipp Tomsich
405cf8aceb1SPhilipp Tomsich src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz);
406cf8aceb1SPhilipp Tomsich assert(src_clk_div < 127);
407cf8aceb1SPhilipp Tomsich
408cf8aceb1SPhilipp Tomsich switch (clk_id) {
409cf8aceb1SPhilipp Tomsich case SCLK_SPI0 ... SCLK_SPI2:
410cf8aceb1SPhilipp Tomsich spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
411cf8aceb1SPhilipp Tomsich break;
412cf8aceb1SPhilipp Tomsich
413cf8aceb1SPhilipp Tomsich default:
4149b643e31SMasahiro Yamada pr_err("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
415cf8aceb1SPhilipp Tomsich return -EINVAL;
416cf8aceb1SPhilipp Tomsich }
417cf8aceb1SPhilipp Tomsich
418cf8aceb1SPhilipp Tomsich rk_clrsetreg(&cru->clksel_con[spiclk->reg],
419cf8aceb1SPhilipp Tomsich ((0x7f << spiclk->div_shift) |
420cf8aceb1SPhilipp Tomsich (0x1 << spiclk->sel_shift)),
421cf8aceb1SPhilipp Tomsich ((src_clk_div << spiclk->div_shift) |
422cf8aceb1SPhilipp Tomsich (1 << spiclk->sel_shift)));
423cf8aceb1SPhilipp Tomsich
424cf8aceb1SPhilipp Tomsich return rk3368_spi_get_clk(cru, clk_id);
425cf8aceb1SPhilipp Tomsich }
426cf8aceb1SPhilipp Tomsich
rk3368_saradc_get_clk(struct rk3368_cru * cru)427615514c1SDavid Wu static ulong rk3368_saradc_get_clk(struct rk3368_cru *cru)
428615514c1SDavid Wu {
429615514c1SDavid Wu u32 div, val;
430615514c1SDavid Wu
431615514c1SDavid Wu val = readl(&cru->clksel_con[25]);
432615514c1SDavid Wu div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
433615514c1SDavid Wu CLK_SARADC_DIV_CON_WIDTH);
434615514c1SDavid Wu
435615514c1SDavid Wu return DIV_TO_RATE(OSC_HZ, div);
436615514c1SDavid Wu }
437615514c1SDavid Wu
rk3368_saradc_set_clk(struct rk3368_cru * cru,uint hz)438615514c1SDavid Wu static ulong rk3368_saradc_set_clk(struct rk3368_cru *cru, uint hz)
439615514c1SDavid Wu {
440615514c1SDavid Wu int src_clk_div;
441615514c1SDavid Wu
442615514c1SDavid Wu src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
443615514c1SDavid Wu assert(src_clk_div < 128);
444615514c1SDavid Wu
445615514c1SDavid Wu rk_clrsetreg(&cru->clksel_con[25],
446615514c1SDavid Wu CLK_SARADC_DIV_CON_MASK,
447615514c1SDavid Wu src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
448615514c1SDavid Wu
449615514c1SDavid Wu return rk3368_saradc_get_clk(cru);
450615514c1SDavid Wu }
451615514c1SDavid Wu
rk3368_clk_get_rate(struct clk * clk)452cf8aceb1SPhilipp Tomsich static ulong rk3368_clk_get_rate(struct clk *clk)
453cf8aceb1SPhilipp Tomsich {
454cf8aceb1SPhilipp Tomsich struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
455cf8aceb1SPhilipp Tomsich ulong rate = 0;
456cf8aceb1SPhilipp Tomsich
457cf8aceb1SPhilipp Tomsich debug("%s: id %ld\n", __func__, clk->id);
458cf8aceb1SPhilipp Tomsich switch (clk->id) {
459cf8aceb1SPhilipp Tomsich case PLL_CPLL:
460cf8aceb1SPhilipp Tomsich rate = rkclk_pll_get_rate(priv->cru, CPLL);
461cf8aceb1SPhilipp Tomsich break;
462cf8aceb1SPhilipp Tomsich case PLL_GPLL:
463cf8aceb1SPhilipp Tomsich rate = rkclk_pll_get_rate(priv->cru, GPLL);
464cf8aceb1SPhilipp Tomsich break;
465cf8aceb1SPhilipp Tomsich case SCLK_SPI0 ... SCLK_SPI2:
466cf8aceb1SPhilipp Tomsich rate = rk3368_spi_get_clk(priv->cru, clk->id);
467cf8aceb1SPhilipp Tomsich break;
468cf8aceb1SPhilipp Tomsich #if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
469cf8aceb1SPhilipp Tomsich case HCLK_SDMMC:
470cf8aceb1SPhilipp Tomsich case HCLK_EMMC:
471cf8aceb1SPhilipp Tomsich rate = rk3368_mmc_get_clk(priv->cru, clk->id);
472cf8aceb1SPhilipp Tomsich break;
473cf8aceb1SPhilipp Tomsich #endif
474615514c1SDavid Wu case SCLK_SARADC:
475615514c1SDavid Wu rate = rk3368_saradc_get_clk(priv->cru);
476615514c1SDavid Wu break;
477cf8aceb1SPhilipp Tomsich default:
478cf8aceb1SPhilipp Tomsich return -ENOENT;
479cf8aceb1SPhilipp Tomsich }
480cf8aceb1SPhilipp Tomsich
481cf8aceb1SPhilipp Tomsich return rate;
482cf8aceb1SPhilipp Tomsich }
483cf8aceb1SPhilipp Tomsich
rk3368_clk_set_rate(struct clk * clk,ulong rate)484d1dcf852SAndy Yan static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
485d1dcf852SAndy Yan {
4864e4c40dfSPhilipp Tomsich __maybe_unused struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
487d1dcf852SAndy Yan ulong ret = 0;
488d1dcf852SAndy Yan
489d1dcf852SAndy Yan debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate);
490d1dcf852SAndy Yan switch (clk->id) {
491cf8aceb1SPhilipp Tomsich case SCLK_SPI0 ... SCLK_SPI2:
492cf8aceb1SPhilipp Tomsich ret = rk3368_spi_set_clk(priv->cru, clk->id, rate);
493cf8aceb1SPhilipp Tomsich break;
49462924690SPhilipp Tomsich #if IS_ENABLED(CONFIG_TPL_BUILD)
495a00dfa04SPhilipp Tomsich case CLK_DDR:
496a00dfa04SPhilipp Tomsich ret = rk3368_ddr_set_clk(priv->cru, rate);
497a00dfa04SPhilipp Tomsich break;
49862924690SPhilipp Tomsich #endif
499f5a43295SPhilipp Tomsich #if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT)
500f5a43295SPhilipp Tomsich case HCLK_SDMMC:
501f5a43295SPhilipp Tomsich case HCLK_EMMC:
502f5a43295SPhilipp Tomsich ret = rk3368_mmc_set_clk(clk, rate);
503f5a43295SPhilipp Tomsich break;
504f5a43295SPhilipp Tomsich #endif
505df0ae000SPhilipp Tomsich #if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
506f5a43295SPhilipp Tomsich case SCLK_MAC:
507df0ae000SPhilipp Tomsich /* select the external clock */
50864a12202SDavid Wu ret = rk3368_gmac_set_clk(priv->cru, rate);
509d1dcf852SAndy Yan break;
510df0ae000SPhilipp Tomsich #endif
511615514c1SDavid Wu case SCLK_SARADC:
512615514c1SDavid Wu ret = rk3368_saradc_set_clk(priv->cru, rate);
513615514c1SDavid Wu break;
514d1dcf852SAndy Yan default:
515d1dcf852SAndy Yan return -ENOENT;
516d1dcf852SAndy Yan }
517d1dcf852SAndy Yan
518d1dcf852SAndy Yan return ret;
519d1dcf852SAndy Yan }
520d1dcf852SAndy Yan
rk3368_gmac_set_parent(struct clk * clk,struct clk * parent)52175b381aaSPhilipp Tomsich static int __maybe_unused rk3368_gmac_set_parent(struct clk *clk, struct clk *parent)
52264a12202SDavid Wu {
52364a12202SDavid Wu struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
52464a12202SDavid Wu struct rk3368_cru *cru = priv->cru;
52564a12202SDavid Wu const char *clock_output_name;
52664a12202SDavid Wu int ret;
52764a12202SDavid Wu
52864a12202SDavid Wu /*
52964a12202SDavid Wu * If the requested parent is in the same clock-controller and
53064a12202SDavid Wu * the id is SCLK_MAC ("sclk_mac"), switch to the internal
53164a12202SDavid Wu * clock.
53264a12202SDavid Wu */
53364a12202SDavid Wu if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC)) {
53464a12202SDavid Wu debug("%s: switching GAMC to SCLK_MAC\n", __func__);
53564a12202SDavid Wu rk_clrreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK);
53664a12202SDavid Wu return 0;
53764a12202SDavid Wu }
53864a12202SDavid Wu
53964a12202SDavid Wu /*
54064a12202SDavid Wu * Otherwise, we need to check the clock-output-names of the
54164a12202SDavid Wu * requested parent to see if the requested id is "ext_gmac".
54264a12202SDavid Wu */
54364a12202SDavid Wu ret = dev_read_string_index(parent->dev, "clock-output-names",
54464a12202SDavid Wu parent->id, &clock_output_name);
54564a12202SDavid Wu if (ret < 0)
54664a12202SDavid Wu return -ENODATA;
54764a12202SDavid Wu
54864a12202SDavid Wu /* If this is "ext_gmac", switch to the external clock input */
54964a12202SDavid Wu if (!strcmp(clock_output_name, "ext_gmac")) {
55064a12202SDavid Wu debug("%s: switching GMAC to external clock\n", __func__);
55164a12202SDavid Wu rk_setreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK);
55264a12202SDavid Wu return 0;
55364a12202SDavid Wu }
55464a12202SDavid Wu
55564a12202SDavid Wu return -EINVAL;
55664a12202SDavid Wu }
55764a12202SDavid Wu
rk3368_clk_set_parent(struct clk * clk,struct clk * parent)55875b381aaSPhilipp Tomsich static int __maybe_unused rk3368_clk_set_parent(struct clk *clk, struct clk *parent)
55964a12202SDavid Wu {
56064a12202SDavid Wu switch (clk->id) {
56164a12202SDavid Wu case SCLK_MAC:
56264a12202SDavid Wu return rk3368_gmac_set_parent(clk, parent);
56364a12202SDavid Wu }
56464a12202SDavid Wu
56564a12202SDavid Wu debug("%s: unsupported clk %ld\n", __func__, clk->id);
56664a12202SDavid Wu return -ENOENT;
56764a12202SDavid Wu }
56864a12202SDavid Wu
rk3368_clk_enable(struct clk * clk)56935a69a3bSPhilipp Tomsich static int rk3368_clk_enable(struct clk *clk)
57035a69a3bSPhilipp Tomsich {
57135a69a3bSPhilipp Tomsich switch (clk->id) {
57235a69a3bSPhilipp Tomsich case SCLK_MAC:
57335a69a3bSPhilipp Tomsich case SCLK_MAC_RX:
57435a69a3bSPhilipp Tomsich case SCLK_MAC_TX:
57535a69a3bSPhilipp Tomsich case SCLK_MACREF:
57635a69a3bSPhilipp Tomsich case SCLK_MACREF_OUT:
57735a69a3bSPhilipp Tomsich case ACLK_GMAC:
57835a69a3bSPhilipp Tomsich case PCLK_GMAC:
57935a69a3bSPhilipp Tomsich /* Required to successfully probe the Designware GMAC driver */
58035a69a3bSPhilipp Tomsich return 0;
58135a69a3bSPhilipp Tomsich }
58235a69a3bSPhilipp Tomsich
58335a69a3bSPhilipp Tomsich debug("%s: unsupported clk %ld\n", __func__, clk->id);
58435a69a3bSPhilipp Tomsich return -ENOENT;
58535a69a3bSPhilipp Tomsich }
58635a69a3bSPhilipp Tomsich
587d1dcf852SAndy Yan static struct clk_ops rk3368_clk_ops = {
588d1dcf852SAndy Yan .get_rate = rk3368_clk_get_rate,
589d1dcf852SAndy Yan .set_rate = rk3368_clk_set_rate,
59075b381aaSPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
59164a12202SDavid Wu .set_parent = rk3368_clk_set_parent,
59275b381aaSPhilipp Tomsich #endif
59335a69a3bSPhilipp Tomsich .enable = rk3368_clk_enable,
594d1dcf852SAndy Yan };
595d1dcf852SAndy Yan
rk3368_clk_probe(struct udevice * dev)596d1dcf852SAndy Yan static int rk3368_clk_probe(struct udevice *dev)
597d1dcf852SAndy Yan {
5984bebf94eSPhilipp Tomsich struct rk3368_clk_priv __maybe_unused *priv = dev_get_priv(dev);
599bee61801SPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_PLATDATA)
600bee61801SPhilipp Tomsich struct rk3368_clk_plat *plat = dev_get_platdata(dev);
601d1dcf852SAndy Yan
602c20ee0edSSimon Glass priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
603bee61801SPhilipp Tomsich #endif
6044bebf94eSPhilipp Tomsich #if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD)
605d1dcf852SAndy Yan rkclk_init(priv->cru);
6064bebf94eSPhilipp Tomsich #endif
607d1dcf852SAndy Yan
608d1dcf852SAndy Yan return 0;
609d1dcf852SAndy Yan }
610d1dcf852SAndy Yan
rk3368_clk_ofdata_to_platdata(struct udevice * dev)611d1dcf852SAndy Yan static int rk3368_clk_ofdata_to_platdata(struct udevice *dev)
612d1dcf852SAndy Yan {
613bee61801SPhilipp Tomsich #if !CONFIG_IS_ENABLED(OF_PLATDATA)
614d1dcf852SAndy Yan struct rk3368_clk_priv *priv = dev_get_priv(dev);
615d1dcf852SAndy Yan
6169a342f48SPhilipp Tomsich priv->cru = dev_read_addr_ptr(dev);
617bee61801SPhilipp Tomsich #endif
618d1dcf852SAndy Yan
619d1dcf852SAndy Yan return 0;
620d1dcf852SAndy Yan }
621d1dcf852SAndy Yan
rk3368_clk_bind(struct udevice * dev)622d1dcf852SAndy Yan static int rk3368_clk_bind(struct udevice *dev)
623d1dcf852SAndy Yan {
624d1dcf852SAndy Yan int ret;
625f24e36daSKever Yang struct udevice *sys_child;
626f24e36daSKever Yang struct sysreset_reg *priv;
627d1dcf852SAndy Yan
628d1dcf852SAndy Yan /* The reset driver does not have a device node, so bind it here */
629f24e36daSKever Yang ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
630f24e36daSKever Yang &sys_child);
631f24e36daSKever Yang if (ret) {
632f24e36daSKever Yang debug("Warning: No sysreset driver: ret=%d\n", ret);
633f24e36daSKever Yang } else {
634f24e36daSKever Yang priv = malloc(sizeof(struct sysreset_reg));
635f24e36daSKever Yang priv->glb_srst_fst_value = offsetof(struct rk3368_cru,
636f24e36daSKever Yang glb_srst_fst_val);
637f24e36daSKever Yang priv->glb_srst_snd_value = offsetof(struct rk3368_cru,
638f24e36daSKever Yang glb_srst_snd_val);
639f24e36daSKever Yang sys_child->priv = priv;
640f24e36daSKever Yang }
641d1dcf852SAndy Yan
642538f67c3SElaine Zhang #if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP)
643538f67c3SElaine Zhang ret = offsetof(struct rk3368_cru, softrst_con[0]);
644538f67c3SElaine Zhang ret = rockchip_reset_bind(dev, ret, 15);
645538f67c3SElaine Zhang if (ret)
646538f67c3SElaine Zhang debug("Warning: software reset driver bind faile\n");
647538f67c3SElaine Zhang #endif
648538f67c3SElaine Zhang
649d1dcf852SAndy Yan return ret;
650d1dcf852SAndy Yan }
651d1dcf852SAndy Yan
652d1dcf852SAndy Yan static const struct udevice_id rk3368_clk_ids[] = {
653d1dcf852SAndy Yan { .compatible = "rockchip,rk3368-cru" },
654d1dcf852SAndy Yan { }
655d1dcf852SAndy Yan };
656d1dcf852SAndy Yan
657d1dcf852SAndy Yan U_BOOT_DRIVER(rockchip_rk3368_cru) = {
658d1dcf852SAndy Yan .name = "rockchip_rk3368_cru",
659d1dcf852SAndy Yan .id = UCLASS_CLK,
660d1dcf852SAndy Yan .of_match = rk3368_clk_ids,
661cdc6080aSPhilipp Tomsich .priv_auto_alloc_size = sizeof(struct rk3368_clk_priv),
662bee61801SPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_PLATDATA)
663bee61801SPhilipp Tomsich .platdata_auto_alloc_size = sizeof(struct rk3368_clk_plat),
664bee61801SPhilipp Tomsich #endif
665d1dcf852SAndy Yan .ofdata_to_platdata = rk3368_clk_ofdata_to_platdata,
666d1dcf852SAndy Yan .ops = &rk3368_clk_ops,
667d1dcf852SAndy Yan .bind = rk3368_clk_bind,
668d1dcf852SAndy Yan .probe = rk3368_clk_probe,
669d1dcf852SAndy Yan };
670