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