1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Samsung Exynos7420 clock driver.
4  * Copyright (C) 2016 Samsung Electronics
5  * Thomas Abraham <thomas.ab@samsung.com>
6  */
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <errno.h>
11 #include <clk-uclass.h>
12 #include <asm/io.h>
13 #include <dt-bindings/clock/exynos7420-clk.h>
14 #include "clk-pll.h"
15 
16 #define DIVIDER(reg, shift, mask)	\
17 	(((readl(reg) >> shift) & mask) + 1)
18 
19 /* CMU TOPC block device structure */
20 struct exynos7420_clk_cmu_topc {
21 	unsigned int	rsvd1[68];
22 	unsigned int	bus0_pll_con[2];
23 	unsigned int	rsvd2[2];
24 	unsigned int	bus1_pll_con[2];
25 	unsigned int	rsvd3[54];
26 	unsigned int	mux_sel[6];
27 	unsigned int	rsvd4[250];
28 	unsigned int	div[4];
29 };
30 
31 /* CMU TOP0 block device structure */
32 struct exynos7420_clk_cmu_top0 {
33 	unsigned int	rsvd0[128];
34 	unsigned int	mux_sel[7];
35 	unsigned int	rsvd1[261];
36 	unsigned int	div_peric[5];
37 };
38 
39 /**
40  * struct exynos7420_clk_topc_priv - private data for CMU topc clock driver.
41  *
42  * @topc: base address of the memory mapped CMU TOPC controller.
43  * @fin_freq: frequency of the Oscillator clock.
44  * @sclk_bus0_pll_a: frequency of sclk_bus0_pll_a clock.
45  * @sclk_bus1_pll_a: frequency of sclk_bus1_pll_a clock.
46  */
47 struct exynos7420_clk_topc_priv {
48 	struct exynos7420_clk_cmu_topc *topc;
49 	unsigned long fin_freq;
50 	unsigned long sclk_bus0_pll_a;
51 	unsigned long sclk_bus1_pll_a;
52 };
53 
54 /**
55  * struct exynos7420_clk_top0_priv - private data for CMU top0 clock driver.
56  *
57  * @top0: base address of the memory mapped CMU TOP0 controller.
58  * @mout_top0_bus0_pll_half: frequency of mout_top0_bus0_pll_half clock
59  * @sclk_uart2: frequency of sclk_uart2 clock.
60  */
61 struct exynos7420_clk_top0_priv {
62 	struct exynos7420_clk_cmu_top0 *top0;
63 	unsigned long mout_top0_bus0_pll_half;
64 	unsigned long sclk_uart2;
65 };
66 
67 static ulong exynos7420_topc_get_rate(struct clk *clk)
68 {
69 	struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
70 
71 	switch (clk->id) {
72 	case DOUT_SCLK_BUS0_PLL:
73 	case SCLK_BUS0_PLL_A:
74 	case SCLK_BUS0_PLL_B:
75 		return priv->sclk_bus0_pll_a;
76 	case DOUT_SCLK_BUS1_PLL:
77 	case SCLK_BUS1_PLL_A:
78 	case SCLK_BUS1_PLL_B:
79 		return priv->sclk_bus1_pll_a;
80 	default:
81 		return 0;
82 	}
83 }
84 
85 static struct clk_ops exynos7420_clk_topc_ops = {
86 	.get_rate	= exynos7420_topc_get_rate,
87 };
88 
89 static int exynos7420_clk_topc_probe(struct udevice *dev)
90 {
91 	struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
92 	struct exynos7420_clk_cmu_topc *topc;
93 	struct clk in_clk;
94 	unsigned long rate;
95 	fdt_addr_t base;
96 	int ret;
97 
98 	base = devfdt_get_addr(dev);
99 	if (base == FDT_ADDR_T_NONE)
100 		return -EINVAL;
101 
102 	topc = (struct exynos7420_clk_cmu_topc *)base;
103 	priv->topc = topc;
104 
105 	ret = clk_get_by_index(dev, 0, &in_clk);
106 	if (ret >= 0)
107 		priv->fin_freq = clk_get_rate(&in_clk);
108 
109 	rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq);
110 	if (readl(&topc->mux_sel[1]) & (1 << 16))
111 		rate >>= 1;
112 	rate /= DIVIDER(&topc->div[3], 0, 0xf);
113 	priv->sclk_bus0_pll_a = rate;
114 
115 	rate = pll145x_get_rate(&topc->bus1_pll_con[0], priv->fin_freq) /
116 			DIVIDER(&topc->div[3], 8, 0xf);
117 	priv->sclk_bus1_pll_a = rate;
118 
119 	return 0;
120 }
121 
122 static ulong exynos7420_top0_get_rate(struct clk *clk)
123 {
124 	struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
125 	struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
126 
127 	switch (clk->id) {
128 	case CLK_SCLK_UART2:
129 		return priv->mout_top0_bus0_pll_half /
130 			DIVIDER(&top0->div_peric[3], 8, 0xf);
131 	default:
132 		return 0;
133 	}
134 }
135 
136 static struct clk_ops exynos7420_clk_top0_ops = {
137 	.get_rate	= exynos7420_top0_get_rate,
138 };
139 
140 static int exynos7420_clk_top0_probe(struct udevice *dev)
141 {
142 	struct exynos7420_clk_top0_priv *priv;
143 	struct exynos7420_clk_cmu_top0 *top0;
144 	struct clk in_clk;
145 	fdt_addr_t base;
146 	int ret;
147 
148 	priv = dev_get_priv(dev);
149 	if (!priv)
150 		return -EINVAL;
151 
152 	base = devfdt_get_addr(dev);
153 	if (base == FDT_ADDR_T_NONE)
154 		return -EINVAL;
155 
156 	top0 = (struct exynos7420_clk_cmu_top0 *)base;
157 	priv->top0 = top0;
158 
159 	ret = clk_get_by_index(dev, 1, &in_clk);
160 	if (ret >= 0) {
161 		priv->mout_top0_bus0_pll_half =
162 			clk_get_rate(&in_clk);
163 		if (readl(&top0->mux_sel[1]) & (1 << 16))
164 			priv->mout_top0_bus0_pll_half >>= 1;
165 	}
166 
167 	return 0;
168 }
169 
170 static ulong exynos7420_peric1_get_rate(struct clk *clk)
171 {
172 	struct clk in_clk;
173 	unsigned int ret;
174 	unsigned long freq = 0;
175 
176 	switch (clk->id) {
177 	case SCLK_UART2:
178 		ret = clk_get_by_index(clk->dev, 3, &in_clk);
179 		if (ret < 0)
180 			return ret;
181 		freq = clk_get_rate(&in_clk);
182 		break;
183 	}
184 
185 	return freq;
186 }
187 
188 static struct clk_ops exynos7420_clk_peric1_ops = {
189 	.get_rate	= exynos7420_peric1_get_rate,
190 };
191 
192 static const struct udevice_id exynos7420_clk_topc_compat[] = {
193 	{ .compatible = "samsung,exynos7-clock-topc" },
194 	{ }
195 };
196 
197 U_BOOT_DRIVER(exynos7420_clk_topc) = {
198 	.name = "exynos7420-clock-topc",
199 	.id = UCLASS_CLK,
200 	.of_match = exynos7420_clk_topc_compat,
201 	.probe = exynos7420_clk_topc_probe,
202 	.priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv),
203 	.ops = &exynos7420_clk_topc_ops,
204 	.flags = DM_FLAG_PRE_RELOC,
205 };
206 
207 static const struct udevice_id exynos7420_clk_top0_compat[] = {
208 	{ .compatible = "samsung,exynos7-clock-top0" },
209 	{ }
210 };
211 
212 U_BOOT_DRIVER(exynos7420_clk_top0) = {
213 	.name = "exynos7420-clock-top0",
214 	.id = UCLASS_CLK,
215 	.of_match = exynos7420_clk_top0_compat,
216 	.probe = exynos7420_clk_top0_probe,
217 	.priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv),
218 	.ops = &exynos7420_clk_top0_ops,
219 	.flags = DM_FLAG_PRE_RELOC,
220 };
221 
222 static const struct udevice_id exynos7420_clk_peric1_compat[] = {
223 	{ .compatible = "samsung,exynos7-clock-peric1" },
224 	{ }
225 };
226 
227 U_BOOT_DRIVER(exynos7420_clk_peric1) = {
228 	.name = "exynos7420-clock-peric1",
229 	.id = UCLASS_CLK,
230 	.of_match = exynos7420_clk_peric1_compat,
231 	.ops = &exynos7420_clk_peric1_ops,
232 	.flags = DM_FLAG_PRE_RELOC,
233 };
234