1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2166097e8SThomas Abraham /*
3166097e8SThomas Abraham * Samsung Exynos7420 clock driver.
4166097e8SThomas Abraham * Copyright (C) 2016 Samsung Electronics
5166097e8SThomas Abraham * Thomas Abraham <thomas.ab@samsung.com>
6166097e8SThomas Abraham */
7166097e8SThomas Abraham
8166097e8SThomas Abraham #include <common.h>
9166097e8SThomas Abraham #include <dm.h>
10166097e8SThomas Abraham #include <errno.h>
11135aa950SStephen Warren #include <clk-uclass.h>
12166097e8SThomas Abraham #include <asm/io.h>
13166097e8SThomas Abraham #include <dt-bindings/clock/exynos7420-clk.h>
14166097e8SThomas Abraham #include "clk-pll.h"
15166097e8SThomas Abraham
16166097e8SThomas Abraham #define DIVIDER(reg, shift, mask) \
17166097e8SThomas Abraham (((readl(reg) >> shift) & mask) + 1)
18166097e8SThomas Abraham
19166097e8SThomas Abraham /* CMU TOPC block device structure */
20166097e8SThomas Abraham struct exynos7420_clk_cmu_topc {
21166097e8SThomas Abraham unsigned int rsvd1[68];
22166097e8SThomas Abraham unsigned int bus0_pll_con[2];
23166097e8SThomas Abraham unsigned int rsvd2[2];
24166097e8SThomas Abraham unsigned int bus1_pll_con[2];
25166097e8SThomas Abraham unsigned int rsvd3[54];
26166097e8SThomas Abraham unsigned int mux_sel[6];
27166097e8SThomas Abraham unsigned int rsvd4[250];
28166097e8SThomas Abraham unsigned int div[4];
29166097e8SThomas Abraham };
30166097e8SThomas Abraham
31166097e8SThomas Abraham /* CMU TOP0 block device structure */
32166097e8SThomas Abraham struct exynos7420_clk_cmu_top0 {
33166097e8SThomas Abraham unsigned int rsvd0[128];
34166097e8SThomas Abraham unsigned int mux_sel[7];
35166097e8SThomas Abraham unsigned int rsvd1[261];
36166097e8SThomas Abraham unsigned int div_peric[5];
37166097e8SThomas Abraham };
38166097e8SThomas Abraham
39166097e8SThomas Abraham /**
40166097e8SThomas Abraham * struct exynos7420_clk_topc_priv - private data for CMU topc clock driver.
41166097e8SThomas Abraham *
42166097e8SThomas Abraham * @topc: base address of the memory mapped CMU TOPC controller.
43166097e8SThomas Abraham * @fin_freq: frequency of the Oscillator clock.
44166097e8SThomas Abraham * @sclk_bus0_pll_a: frequency of sclk_bus0_pll_a clock.
45166097e8SThomas Abraham * @sclk_bus1_pll_a: frequency of sclk_bus1_pll_a clock.
46166097e8SThomas Abraham */
47166097e8SThomas Abraham struct exynos7420_clk_topc_priv {
48166097e8SThomas Abraham struct exynos7420_clk_cmu_topc *topc;
49166097e8SThomas Abraham unsigned long fin_freq;
50166097e8SThomas Abraham unsigned long sclk_bus0_pll_a;
51166097e8SThomas Abraham unsigned long sclk_bus1_pll_a;
52166097e8SThomas Abraham };
53166097e8SThomas Abraham
54166097e8SThomas Abraham /**
55166097e8SThomas Abraham * struct exynos7420_clk_top0_priv - private data for CMU top0 clock driver.
56166097e8SThomas Abraham *
57166097e8SThomas Abraham * @top0: base address of the memory mapped CMU TOP0 controller.
58166097e8SThomas Abraham * @mout_top0_bus0_pll_half: frequency of mout_top0_bus0_pll_half clock
59166097e8SThomas Abraham * @sclk_uart2: frequency of sclk_uart2 clock.
60166097e8SThomas Abraham */
61166097e8SThomas Abraham struct exynos7420_clk_top0_priv {
62166097e8SThomas Abraham struct exynos7420_clk_cmu_top0 *top0;
63166097e8SThomas Abraham unsigned long mout_top0_bus0_pll_half;
64166097e8SThomas Abraham unsigned long sclk_uart2;
65166097e8SThomas Abraham };
66166097e8SThomas Abraham
exynos7420_topc_get_rate(struct clk * clk)67135aa950SStephen Warren static ulong exynos7420_topc_get_rate(struct clk *clk)
68166097e8SThomas Abraham {
69135aa950SStephen Warren struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
70166097e8SThomas Abraham
71135aa950SStephen Warren switch (clk->id) {
72166097e8SThomas Abraham case DOUT_SCLK_BUS0_PLL:
73166097e8SThomas Abraham case SCLK_BUS0_PLL_A:
74166097e8SThomas Abraham case SCLK_BUS0_PLL_B:
75166097e8SThomas Abraham return priv->sclk_bus0_pll_a;
76166097e8SThomas Abraham case DOUT_SCLK_BUS1_PLL:
77166097e8SThomas Abraham case SCLK_BUS1_PLL_A:
78166097e8SThomas Abraham case SCLK_BUS1_PLL_B:
79166097e8SThomas Abraham return priv->sclk_bus1_pll_a;
80166097e8SThomas Abraham default:
81166097e8SThomas Abraham return 0;
82166097e8SThomas Abraham }
83166097e8SThomas Abraham }
84166097e8SThomas Abraham
85166097e8SThomas Abraham static struct clk_ops exynos7420_clk_topc_ops = {
86135aa950SStephen Warren .get_rate = exynos7420_topc_get_rate,
87166097e8SThomas Abraham };
88166097e8SThomas Abraham
exynos7420_clk_topc_probe(struct udevice * dev)89166097e8SThomas Abraham static int exynos7420_clk_topc_probe(struct udevice *dev)
90166097e8SThomas Abraham {
91166097e8SThomas Abraham struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
92166097e8SThomas Abraham struct exynos7420_clk_cmu_topc *topc;
93135aa950SStephen Warren struct clk in_clk;
94166097e8SThomas Abraham unsigned long rate;
95166097e8SThomas Abraham fdt_addr_t base;
96166097e8SThomas Abraham int ret;
97166097e8SThomas Abraham
98a821c4afSSimon Glass base = devfdt_get_addr(dev);
99166097e8SThomas Abraham if (base == FDT_ADDR_T_NONE)
100166097e8SThomas Abraham return -EINVAL;
101166097e8SThomas Abraham
102166097e8SThomas Abraham topc = (struct exynos7420_clk_cmu_topc *)base;
103166097e8SThomas Abraham priv->topc = topc;
104166097e8SThomas Abraham
105135aa950SStephen Warren ret = clk_get_by_index(dev, 0, &in_clk);
106166097e8SThomas Abraham if (ret >= 0)
107135aa950SStephen Warren priv->fin_freq = clk_get_rate(&in_clk);
108166097e8SThomas Abraham
109166097e8SThomas Abraham rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq);
110166097e8SThomas Abraham if (readl(&topc->mux_sel[1]) & (1 << 16))
111166097e8SThomas Abraham rate >>= 1;
112166097e8SThomas Abraham rate /= DIVIDER(&topc->div[3], 0, 0xf);
113166097e8SThomas Abraham priv->sclk_bus0_pll_a = rate;
114166097e8SThomas Abraham
115166097e8SThomas Abraham rate = pll145x_get_rate(&topc->bus1_pll_con[0], priv->fin_freq) /
116166097e8SThomas Abraham DIVIDER(&topc->div[3], 8, 0xf);
117166097e8SThomas Abraham priv->sclk_bus1_pll_a = rate;
118166097e8SThomas Abraham
119166097e8SThomas Abraham return 0;
120166097e8SThomas Abraham }
121166097e8SThomas Abraham
exynos7420_top0_get_rate(struct clk * clk)122135aa950SStephen Warren static ulong exynos7420_top0_get_rate(struct clk *clk)
123166097e8SThomas Abraham {
124135aa950SStephen Warren struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
125166097e8SThomas Abraham struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
126166097e8SThomas Abraham
127135aa950SStephen Warren switch (clk->id) {
128166097e8SThomas Abraham case CLK_SCLK_UART2:
129166097e8SThomas Abraham return priv->mout_top0_bus0_pll_half /
130166097e8SThomas Abraham DIVIDER(&top0->div_peric[3], 8, 0xf);
131166097e8SThomas Abraham default:
132166097e8SThomas Abraham return 0;
133166097e8SThomas Abraham }
134166097e8SThomas Abraham }
135166097e8SThomas Abraham
136166097e8SThomas Abraham static struct clk_ops exynos7420_clk_top0_ops = {
137135aa950SStephen Warren .get_rate = exynos7420_top0_get_rate,
138166097e8SThomas Abraham };
139166097e8SThomas Abraham
exynos7420_clk_top0_probe(struct udevice * dev)140166097e8SThomas Abraham static int exynos7420_clk_top0_probe(struct udevice *dev)
141166097e8SThomas Abraham {
142166097e8SThomas Abraham struct exynos7420_clk_top0_priv *priv;
143166097e8SThomas Abraham struct exynos7420_clk_cmu_top0 *top0;
144135aa950SStephen Warren struct clk in_clk;
145166097e8SThomas Abraham fdt_addr_t base;
146166097e8SThomas Abraham int ret;
147166097e8SThomas Abraham
148166097e8SThomas Abraham priv = dev_get_priv(dev);
149166097e8SThomas Abraham if (!priv)
150166097e8SThomas Abraham return -EINVAL;
151166097e8SThomas Abraham
152a821c4afSSimon Glass base = devfdt_get_addr(dev);
153166097e8SThomas Abraham if (base == FDT_ADDR_T_NONE)
154166097e8SThomas Abraham return -EINVAL;
155166097e8SThomas Abraham
156166097e8SThomas Abraham top0 = (struct exynos7420_clk_cmu_top0 *)base;
157166097e8SThomas Abraham priv->top0 = top0;
158166097e8SThomas Abraham
159135aa950SStephen Warren ret = clk_get_by_index(dev, 1, &in_clk);
160166097e8SThomas Abraham if (ret >= 0) {
161166097e8SThomas Abraham priv->mout_top0_bus0_pll_half =
162135aa950SStephen Warren clk_get_rate(&in_clk);
163166097e8SThomas Abraham if (readl(&top0->mux_sel[1]) & (1 << 16))
164166097e8SThomas Abraham priv->mout_top0_bus0_pll_half >>= 1;
165166097e8SThomas Abraham }
166166097e8SThomas Abraham
167166097e8SThomas Abraham return 0;
168166097e8SThomas Abraham }
169166097e8SThomas Abraham
exynos7420_peric1_get_rate(struct clk * clk)170135aa950SStephen Warren static ulong exynos7420_peric1_get_rate(struct clk *clk)
171166097e8SThomas Abraham {
172135aa950SStephen Warren struct clk in_clk;
173166097e8SThomas Abraham unsigned int ret;
174166097e8SThomas Abraham unsigned long freq = 0;
175166097e8SThomas Abraham
176135aa950SStephen Warren switch (clk->id) {
177166097e8SThomas Abraham case SCLK_UART2:
178135aa950SStephen Warren ret = clk_get_by_index(clk->dev, 3, &in_clk);
179166097e8SThomas Abraham if (ret < 0)
180166097e8SThomas Abraham return ret;
181135aa950SStephen Warren freq = clk_get_rate(&in_clk);
182166097e8SThomas Abraham break;
183166097e8SThomas Abraham }
184166097e8SThomas Abraham
185166097e8SThomas Abraham return freq;
186166097e8SThomas Abraham }
187166097e8SThomas Abraham
188166097e8SThomas Abraham static struct clk_ops exynos7420_clk_peric1_ops = {
189135aa950SStephen Warren .get_rate = exynos7420_peric1_get_rate,
190166097e8SThomas Abraham };
191166097e8SThomas Abraham
192166097e8SThomas Abraham static const struct udevice_id exynos7420_clk_topc_compat[] = {
193166097e8SThomas Abraham { .compatible = "samsung,exynos7-clock-topc" },
194166097e8SThomas Abraham { }
195166097e8SThomas Abraham };
196166097e8SThomas Abraham
197166097e8SThomas Abraham U_BOOT_DRIVER(exynos7420_clk_topc) = {
198166097e8SThomas Abraham .name = "exynos7420-clock-topc",
199166097e8SThomas Abraham .id = UCLASS_CLK,
200166097e8SThomas Abraham .of_match = exynos7420_clk_topc_compat,
201166097e8SThomas Abraham .probe = exynos7420_clk_topc_probe,
202166097e8SThomas Abraham .priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv),
203166097e8SThomas Abraham .ops = &exynos7420_clk_topc_ops,
204166097e8SThomas Abraham };
205166097e8SThomas Abraham
206166097e8SThomas Abraham static const struct udevice_id exynos7420_clk_top0_compat[] = {
207166097e8SThomas Abraham { .compatible = "samsung,exynos7-clock-top0" },
208166097e8SThomas Abraham { }
209166097e8SThomas Abraham };
210166097e8SThomas Abraham
211166097e8SThomas Abraham U_BOOT_DRIVER(exynos7420_clk_top0) = {
212166097e8SThomas Abraham .name = "exynos7420-clock-top0",
213166097e8SThomas Abraham .id = UCLASS_CLK,
214166097e8SThomas Abraham .of_match = exynos7420_clk_top0_compat,
215166097e8SThomas Abraham .probe = exynos7420_clk_top0_probe,
216166097e8SThomas Abraham .priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv),
217166097e8SThomas Abraham .ops = &exynos7420_clk_top0_ops,
218166097e8SThomas Abraham };
219166097e8SThomas Abraham
220166097e8SThomas Abraham static const struct udevice_id exynos7420_clk_peric1_compat[] = {
221166097e8SThomas Abraham { .compatible = "samsung,exynos7-clock-peric1" },
222166097e8SThomas Abraham { }
223166097e8SThomas Abraham };
224166097e8SThomas Abraham
225166097e8SThomas Abraham U_BOOT_DRIVER(exynos7420_clk_peric1) = {
226166097e8SThomas Abraham .name = "exynos7420-clock-peric1",
227166097e8SThomas Abraham .id = UCLASS_CLK,
228166097e8SThomas Abraham .of_match = exynos7420_clk_peric1_compat,
229166097e8SThomas Abraham .ops = &exynos7420_clk_peric1_ops,
230166097e8SThomas Abraham };
231