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