blob: e34945dbbcc0a74cfaa3ae1bc7b6c2cc0688e757 [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
17DECLARE_GLOBAL_DATA_PTR;
18
19#define DIVIDER(reg, shift, mask) \
20 (((readl(reg) >> shift) & mask) + 1)
21
22/* CMU TOPC block device structure */
23struct 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 */
35struct 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 */
50struct 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 */
64struct 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
Stephen Warren135aa952016-06-17 09:44:00 -060070static ulong exynos7420_topc_get_rate(struct clk *clk)
Thomas Abraham166097e2016-04-23 22:18:09 +053071{
Stephen Warren135aa952016-06-17 09:44:00 -060072 struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
Thomas Abraham166097e2016-04-23 22:18:09 +053073
Stephen Warren135aa952016-06-17 09:44:00 -060074 switch (clk->id) {
Thomas Abraham166097e2016-04-23 22:18:09 +053075 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
88static struct clk_ops exynos7420_clk_topc_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -060089 .get_rate = exynos7420_topc_get_rate,
Thomas Abraham166097e2016-04-23 22:18:09 +053090};
91
92static 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;
Stephen Warren135aa952016-06-17 09:44:00 -060096 struct clk in_clk;
Thomas Abraham166097e2016-04-23 22:18:09 +053097 unsigned long rate;
98 fdt_addr_t base;
99 int ret;
100
Simon Glassa821c4a2017-05-17 17:18:05 -0600101 base = devfdt_get_addr(dev);
Thomas Abraham166097e2016-04-23 22:18:09 +0530102 if (base == FDT_ADDR_T_NONE)
103 return -EINVAL;
104
105 topc = (struct exynos7420_clk_cmu_topc *)base;
106 priv->topc = topc;
107
Stephen Warren135aa952016-06-17 09:44:00 -0600108 ret = clk_get_by_index(dev, 0, &in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530109 if (ret >= 0)
Stephen Warren135aa952016-06-17 09:44:00 -0600110 priv->fin_freq = clk_get_rate(&in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530111
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
Stephen Warren135aa952016-06-17 09:44:00 -0600125static ulong exynos7420_top0_get_rate(struct clk *clk)
Thomas Abraham166097e2016-04-23 22:18:09 +0530126{
Stephen Warren135aa952016-06-17 09:44:00 -0600127 struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
Thomas Abraham166097e2016-04-23 22:18:09 +0530128 struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
129
Stephen Warren135aa952016-06-17 09:44:00 -0600130 switch (clk->id) {
Thomas Abraham166097e2016-04-23 22:18:09 +0530131 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
139static struct clk_ops exynos7420_clk_top0_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -0600140 .get_rate = exynos7420_top0_get_rate,
Thomas Abraham166097e2016-04-23 22:18:09 +0530141};
142
143static int exynos7420_clk_top0_probe(struct udevice *dev)
144{
145 struct exynos7420_clk_top0_priv *priv;
146 struct exynos7420_clk_cmu_top0 *top0;
Stephen Warren135aa952016-06-17 09:44:00 -0600147 struct clk in_clk;
Thomas Abraham166097e2016-04-23 22:18:09 +0530148 fdt_addr_t base;
149 int ret;
150
151 priv = dev_get_priv(dev);
152 if (!priv)
153 return -EINVAL;
154
Simon Glassa821c4a2017-05-17 17:18:05 -0600155 base = devfdt_get_addr(dev);
Thomas Abraham166097e2016-04-23 22:18:09 +0530156 if (base == FDT_ADDR_T_NONE)
157 return -EINVAL;
158
159 top0 = (struct exynos7420_clk_cmu_top0 *)base;
160 priv->top0 = top0;
161
Stephen Warren135aa952016-06-17 09:44:00 -0600162 ret = clk_get_by_index(dev, 1, &in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530163 if (ret >= 0) {
164 priv->mout_top0_bus0_pll_half =
Stephen Warren135aa952016-06-17 09:44:00 -0600165 clk_get_rate(&in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530166 if (readl(&top0->mux_sel[1]) & (1 << 16))
167 priv->mout_top0_bus0_pll_half >>= 1;
168 }
169
170 return 0;
171}
172
Stephen Warren135aa952016-06-17 09:44:00 -0600173static ulong exynos7420_peric1_get_rate(struct clk *clk)
Thomas Abraham166097e2016-04-23 22:18:09 +0530174{
Stephen Warren135aa952016-06-17 09:44:00 -0600175 struct clk in_clk;
Thomas Abraham166097e2016-04-23 22:18:09 +0530176 unsigned int ret;
177 unsigned long freq = 0;
178
Stephen Warren135aa952016-06-17 09:44:00 -0600179 switch (clk->id) {
Thomas Abraham166097e2016-04-23 22:18:09 +0530180 case SCLK_UART2:
Stephen Warren135aa952016-06-17 09:44:00 -0600181 ret = clk_get_by_index(clk->dev, 3, &in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530182 if (ret < 0)
183 return ret;
Stephen Warren135aa952016-06-17 09:44:00 -0600184 freq = clk_get_rate(&in_clk);
Thomas Abraham166097e2016-04-23 22:18:09 +0530185 break;
186 }
187
188 return freq;
189}
190
191static struct clk_ops exynos7420_clk_peric1_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -0600192 .get_rate = exynos7420_peric1_get_rate,
Thomas Abraham166097e2016-04-23 22:18:09 +0530193};
194
195static const struct udevice_id exynos7420_clk_topc_compat[] = {
196 { .compatible = "samsung,exynos7-clock-topc" },
197 { }
198};
199
200U_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
210static const struct udevice_id exynos7420_clk_top0_compat[] = {
211 { .compatible = "samsung,exynos7-clock-top0" },
212 { }
213};
214
215U_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
225static const struct udevice_id exynos7420_clk_peric1_compat[] = {
226 { .compatible = "samsung,exynos7-clock-peric1" },
227 { }
228};
229
230U_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};