blob: 832b2d8765ebb24383906658d3ea07b4766a30e5 [file] [log] [blame]
Thomas Abraham166097e2016-04-23 22:18:09 +05301/*
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>
Stephen Warren135aa952016-06-17 09:44:00 -060012#include <clk-uclass.h>
Thomas Abraham166097e2016-04-23 22:18:09 +053013#include <asm/io.h>
14#include <dt-bindings/clock/exynos7420-clk.h>
15#include "clk-pll.h"
16
Thomas Abraham166097e2016-04-23 22:18:09 +053017#define DIVIDER(reg, shift, mask) \
18 (((readl(reg) >> shift) & mask) + 1)
19
20/* CMU TOPC block device structure */
21struct 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 */
33struct 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 */
48struct 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 */
62struct 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
Stephen Warren135aa952016-06-17 09:44:00 -060068static ulong exynos7420_topc_get_rate(struct clk *clk)
Thomas Abraham166097e2016-04-23 22:18:09 +053069{
Stephen Warren135aa952016-06-17 09:44:00 -060070 struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
Thomas Abraham166097e2016-04-23 22:18:09 +053071
Stephen Warren135aa952016-06-17 09:44:00 -060072 switch (clk->id) {
Thomas Abraham166097e2016-04-23 22:18:09 +053073 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
86static struct clk_ops exynos7420_clk_topc_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -060087 .get_rate = exynos7420_topc_get_rate,
Thomas Abraham166097e2016-04-23 22:18:09 +053088};
89
90static 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;
Stephen Warren135aa952016-06-17 09:44:00 -060094 struct clk in_clk;
Thomas Abraham166097e2016-04-23 22:18:09 +053095 unsigned long rate;
96 fdt_addr_t base;
97 int ret;
98
Simon Glassa821c4a2017-05-17 17:18:05 -060099 base = devfdt_get_addr(dev);
Thomas Abraham166097e2016-04-23 22:18:09 +0530100 if (base == FDT_ADDR_T_NONE)
101 return -EINVAL;
102
103 topc = (struct exynos7420_clk_cmu_topc *)base;
104 priv->topc = topc;
105
Stephen Warren135aa952016-06-17 09:44:00 -0600106 ret = clk_get_by_index(dev, 0, &in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530107 if (ret >= 0)
Stephen Warren135aa952016-06-17 09:44:00 -0600108 priv->fin_freq = clk_get_rate(&in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530109
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
Stephen Warren135aa952016-06-17 09:44:00 -0600123static ulong exynos7420_top0_get_rate(struct clk *clk)
Thomas Abraham166097e2016-04-23 22:18:09 +0530124{
Stephen Warren135aa952016-06-17 09:44:00 -0600125 struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
Thomas Abraham166097e2016-04-23 22:18:09 +0530126 struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
127
Stephen Warren135aa952016-06-17 09:44:00 -0600128 switch (clk->id) {
Thomas Abraham166097e2016-04-23 22:18:09 +0530129 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
137static struct clk_ops exynos7420_clk_top0_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -0600138 .get_rate = exynos7420_top0_get_rate,
Thomas Abraham166097e2016-04-23 22:18:09 +0530139};
140
141static int exynos7420_clk_top0_probe(struct udevice *dev)
142{
143 struct exynos7420_clk_top0_priv *priv;
144 struct exynos7420_clk_cmu_top0 *top0;
Stephen Warren135aa952016-06-17 09:44:00 -0600145 struct clk in_clk;
Thomas Abraham166097e2016-04-23 22:18:09 +0530146 fdt_addr_t base;
147 int ret;
148
149 priv = dev_get_priv(dev);
150 if (!priv)
151 return -EINVAL;
152
Simon Glassa821c4a2017-05-17 17:18:05 -0600153 base = devfdt_get_addr(dev);
Thomas Abraham166097e2016-04-23 22:18:09 +0530154 if (base == FDT_ADDR_T_NONE)
155 return -EINVAL;
156
157 top0 = (struct exynos7420_clk_cmu_top0 *)base;
158 priv->top0 = top0;
159
Stephen Warren135aa952016-06-17 09:44:00 -0600160 ret = clk_get_by_index(dev, 1, &in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530161 if (ret >= 0) {
162 priv->mout_top0_bus0_pll_half =
Stephen Warren135aa952016-06-17 09:44:00 -0600163 clk_get_rate(&in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530164 if (readl(&top0->mux_sel[1]) & (1 << 16))
165 priv->mout_top0_bus0_pll_half >>= 1;
166 }
167
168 return 0;
169}
170
Stephen Warren135aa952016-06-17 09:44:00 -0600171static ulong exynos7420_peric1_get_rate(struct clk *clk)
Thomas Abraham166097e2016-04-23 22:18:09 +0530172{
Stephen Warren135aa952016-06-17 09:44:00 -0600173 struct clk in_clk;
Thomas Abraham166097e2016-04-23 22:18:09 +0530174 unsigned int ret;
175 unsigned long freq = 0;
176
Stephen Warren135aa952016-06-17 09:44:00 -0600177 switch (clk->id) {
Thomas Abraham166097e2016-04-23 22:18:09 +0530178 case SCLK_UART2:
Stephen Warren135aa952016-06-17 09:44:00 -0600179 ret = clk_get_by_index(clk->dev, 3, &in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530180 if (ret < 0)
181 return ret;
Stephen Warren135aa952016-06-17 09:44:00 -0600182 freq = clk_get_rate(&in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530183 break;
184 }
185
186 return freq;
187}
188
189static struct clk_ops exynos7420_clk_peric1_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -0600190 .get_rate = exynos7420_peric1_get_rate,
Thomas Abraham166097e2016-04-23 22:18:09 +0530191};
192
193static const struct udevice_id exynos7420_clk_topc_compat[] = {
194 { .compatible = "samsung,exynos7-clock-topc" },
195 { }
196};
197
198U_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
208static const struct udevice_id exynos7420_clk_top0_compat[] = {
209 { .compatible = "samsung,exynos7-clock-top0" },
210 { }
211};
212
213U_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
223static const struct udevice_id exynos7420_clk_peric1_compat[] = {
224 { .compatible = "samsung,exynos7-clock-peric1" },
225 { }
226};
227
228U_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};