xref: /openbmc/u-boot/drivers/clk/exynos/clk-exynos7420.c (revision 1d6edcbfed2af33c748f2beb399810a0441888da)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2166097e8SThomas Abraham /*
3166097e8SThomas Abraham  * Samsung Exynos7420 clock driver.
4166097e8SThomas Abraham  * Copyright (C) 2016 Samsung Electronics
5166097e8SThomas Abraham  * Thomas Abraham <thomas.ab@samsung.com>
6166097e8SThomas Abraham  */
7166097e8SThomas Abraham 
8166097e8SThomas Abraham #include <common.h>
9166097e8SThomas Abraham #include <dm.h>
10166097e8SThomas Abraham #include <errno.h>
11135aa950SStephen Warren #include <clk-uclass.h>
12166097e8SThomas Abraham #include <asm/io.h>
13166097e8SThomas Abraham #include <dt-bindings/clock/exynos7420-clk.h>
14166097e8SThomas Abraham #include "clk-pll.h"
15166097e8SThomas Abraham 
16166097e8SThomas Abraham #define DIVIDER(reg, shift, mask)	\
17166097e8SThomas Abraham 	(((readl(reg) >> shift) & mask) + 1)
18166097e8SThomas Abraham 
19166097e8SThomas Abraham /* CMU TOPC block device structure */
20166097e8SThomas Abraham struct exynos7420_clk_cmu_topc {
21166097e8SThomas Abraham 	unsigned int	rsvd1[68];
22166097e8SThomas Abraham 	unsigned int	bus0_pll_con[2];
23166097e8SThomas Abraham 	unsigned int	rsvd2[2];
24166097e8SThomas Abraham 	unsigned int	bus1_pll_con[2];
25166097e8SThomas Abraham 	unsigned int	rsvd3[54];
26166097e8SThomas Abraham 	unsigned int	mux_sel[6];
27166097e8SThomas Abraham 	unsigned int	rsvd4[250];
28166097e8SThomas Abraham 	unsigned int	div[4];
29166097e8SThomas Abraham };
30166097e8SThomas Abraham 
31166097e8SThomas Abraham /* CMU TOP0 block device structure */
32166097e8SThomas Abraham struct exynos7420_clk_cmu_top0 {
33166097e8SThomas Abraham 	unsigned int	rsvd0[128];
34166097e8SThomas Abraham 	unsigned int	mux_sel[7];
35166097e8SThomas Abraham 	unsigned int	rsvd1[261];
36166097e8SThomas Abraham 	unsigned int	div_peric[5];
37166097e8SThomas Abraham };
38166097e8SThomas Abraham 
39166097e8SThomas Abraham /**
40166097e8SThomas Abraham  * struct exynos7420_clk_topc_priv - private data for CMU topc clock driver.
41166097e8SThomas Abraham  *
42166097e8SThomas Abraham  * @topc: base address of the memory mapped CMU TOPC controller.
43166097e8SThomas Abraham  * @fin_freq: frequency of the Oscillator clock.
44166097e8SThomas Abraham  * @sclk_bus0_pll_a: frequency of sclk_bus0_pll_a clock.
45166097e8SThomas Abraham  * @sclk_bus1_pll_a: frequency of sclk_bus1_pll_a clock.
46166097e8SThomas Abraham  */
47166097e8SThomas Abraham struct exynos7420_clk_topc_priv {
48166097e8SThomas Abraham 	struct exynos7420_clk_cmu_topc *topc;
49166097e8SThomas Abraham 	unsigned long fin_freq;
50166097e8SThomas Abraham 	unsigned long sclk_bus0_pll_a;
51166097e8SThomas Abraham 	unsigned long sclk_bus1_pll_a;
52166097e8SThomas Abraham };
53166097e8SThomas Abraham 
54166097e8SThomas Abraham /**
55166097e8SThomas Abraham  * struct exynos7420_clk_top0_priv - private data for CMU top0 clock driver.
56166097e8SThomas Abraham  *
57166097e8SThomas Abraham  * @top0: base address of the memory mapped CMU TOP0 controller.
58166097e8SThomas Abraham  * @mout_top0_bus0_pll_half: frequency of mout_top0_bus0_pll_half clock
59166097e8SThomas Abraham  * @sclk_uart2: frequency of sclk_uart2 clock.
60166097e8SThomas Abraham  */
61166097e8SThomas Abraham struct exynos7420_clk_top0_priv {
62166097e8SThomas Abraham 	struct exynos7420_clk_cmu_top0 *top0;
63166097e8SThomas Abraham 	unsigned long mout_top0_bus0_pll_half;
64166097e8SThomas Abraham 	unsigned long sclk_uart2;
65166097e8SThomas Abraham };
66166097e8SThomas Abraham 
exynos7420_topc_get_rate(struct clk * clk)67135aa950SStephen Warren static ulong exynos7420_topc_get_rate(struct clk *clk)
68166097e8SThomas Abraham {
69135aa950SStephen Warren 	struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
70166097e8SThomas Abraham 
71135aa950SStephen Warren 	switch (clk->id) {
72166097e8SThomas Abraham 	case DOUT_SCLK_BUS0_PLL:
73166097e8SThomas Abraham 	case SCLK_BUS0_PLL_A:
74166097e8SThomas Abraham 	case SCLK_BUS0_PLL_B:
75166097e8SThomas Abraham 		return priv->sclk_bus0_pll_a;
76166097e8SThomas Abraham 	case DOUT_SCLK_BUS1_PLL:
77166097e8SThomas Abraham 	case SCLK_BUS1_PLL_A:
78166097e8SThomas Abraham 	case SCLK_BUS1_PLL_B:
79166097e8SThomas Abraham 		return priv->sclk_bus1_pll_a;
80166097e8SThomas Abraham 	default:
81166097e8SThomas Abraham 		return 0;
82166097e8SThomas Abraham 	}
83166097e8SThomas Abraham }
84166097e8SThomas Abraham 
85166097e8SThomas Abraham static struct clk_ops exynos7420_clk_topc_ops = {
86135aa950SStephen Warren 	.get_rate	= exynos7420_topc_get_rate,
87166097e8SThomas Abraham };
88166097e8SThomas Abraham 
exynos7420_clk_topc_probe(struct udevice * dev)89166097e8SThomas Abraham static int exynos7420_clk_topc_probe(struct udevice *dev)
90166097e8SThomas Abraham {
91166097e8SThomas Abraham 	struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
92166097e8SThomas Abraham 	struct exynos7420_clk_cmu_topc *topc;
93135aa950SStephen Warren 	struct clk in_clk;
94166097e8SThomas Abraham 	unsigned long rate;
95166097e8SThomas Abraham 	fdt_addr_t base;
96166097e8SThomas Abraham 	int ret;
97166097e8SThomas Abraham 
98a821c4afSSimon Glass 	base = devfdt_get_addr(dev);
99166097e8SThomas Abraham 	if (base == FDT_ADDR_T_NONE)
100166097e8SThomas Abraham 		return -EINVAL;
101166097e8SThomas Abraham 
102166097e8SThomas Abraham 	topc = (struct exynos7420_clk_cmu_topc *)base;
103166097e8SThomas Abraham 	priv->topc = topc;
104166097e8SThomas Abraham 
105135aa950SStephen Warren 	ret = clk_get_by_index(dev, 0, &in_clk);
106166097e8SThomas Abraham 	if (ret >= 0)
107135aa950SStephen Warren 		priv->fin_freq = clk_get_rate(&in_clk);
108166097e8SThomas Abraham 
109166097e8SThomas Abraham 	rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq);
110166097e8SThomas Abraham 	if (readl(&topc->mux_sel[1]) & (1 << 16))
111166097e8SThomas Abraham 		rate >>= 1;
112166097e8SThomas Abraham 	rate /= DIVIDER(&topc->div[3], 0, 0xf);
113166097e8SThomas Abraham 	priv->sclk_bus0_pll_a = rate;
114166097e8SThomas Abraham 
115166097e8SThomas Abraham 	rate = pll145x_get_rate(&topc->bus1_pll_con[0], priv->fin_freq) /
116166097e8SThomas Abraham 			DIVIDER(&topc->div[3], 8, 0xf);
117166097e8SThomas Abraham 	priv->sclk_bus1_pll_a = rate;
118166097e8SThomas Abraham 
119166097e8SThomas Abraham 	return 0;
120166097e8SThomas Abraham }
121166097e8SThomas Abraham 
exynos7420_top0_get_rate(struct clk * clk)122135aa950SStephen Warren static ulong exynos7420_top0_get_rate(struct clk *clk)
123166097e8SThomas Abraham {
124135aa950SStephen Warren 	struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
125166097e8SThomas Abraham 	struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
126166097e8SThomas Abraham 
127135aa950SStephen Warren 	switch (clk->id) {
128166097e8SThomas Abraham 	case CLK_SCLK_UART2:
129166097e8SThomas Abraham 		return priv->mout_top0_bus0_pll_half /
130166097e8SThomas Abraham 			DIVIDER(&top0->div_peric[3], 8, 0xf);
131166097e8SThomas Abraham 	default:
132166097e8SThomas Abraham 		return 0;
133166097e8SThomas Abraham 	}
134166097e8SThomas Abraham }
135166097e8SThomas Abraham 
136166097e8SThomas Abraham static struct clk_ops exynos7420_clk_top0_ops = {
137135aa950SStephen Warren 	.get_rate	= exynos7420_top0_get_rate,
138166097e8SThomas Abraham };
139166097e8SThomas Abraham 
exynos7420_clk_top0_probe(struct udevice * dev)140166097e8SThomas Abraham static int exynos7420_clk_top0_probe(struct udevice *dev)
141166097e8SThomas Abraham {
142166097e8SThomas Abraham 	struct exynos7420_clk_top0_priv *priv;
143166097e8SThomas Abraham 	struct exynos7420_clk_cmu_top0 *top0;
144135aa950SStephen Warren 	struct clk in_clk;
145166097e8SThomas Abraham 	fdt_addr_t base;
146166097e8SThomas Abraham 	int ret;
147166097e8SThomas Abraham 
148166097e8SThomas Abraham 	priv = dev_get_priv(dev);
149166097e8SThomas Abraham 	if (!priv)
150166097e8SThomas Abraham 		return -EINVAL;
151166097e8SThomas Abraham 
152a821c4afSSimon Glass 	base = devfdt_get_addr(dev);
153166097e8SThomas Abraham 	if (base == FDT_ADDR_T_NONE)
154166097e8SThomas Abraham 		return -EINVAL;
155166097e8SThomas Abraham 
156166097e8SThomas Abraham 	top0 = (struct exynos7420_clk_cmu_top0 *)base;
157166097e8SThomas Abraham 	priv->top0 = top0;
158166097e8SThomas Abraham 
159135aa950SStephen Warren 	ret = clk_get_by_index(dev, 1, &in_clk);
160166097e8SThomas Abraham 	if (ret >= 0) {
161166097e8SThomas Abraham 		priv->mout_top0_bus0_pll_half =
162135aa950SStephen Warren 			clk_get_rate(&in_clk);
163166097e8SThomas Abraham 		if (readl(&top0->mux_sel[1]) & (1 << 16))
164166097e8SThomas Abraham 			priv->mout_top0_bus0_pll_half >>= 1;
165166097e8SThomas Abraham 	}
166166097e8SThomas Abraham 
167166097e8SThomas Abraham 	return 0;
168166097e8SThomas Abraham }
169166097e8SThomas Abraham 
exynos7420_peric1_get_rate(struct clk * clk)170135aa950SStephen Warren static ulong exynos7420_peric1_get_rate(struct clk *clk)
171166097e8SThomas Abraham {
172135aa950SStephen Warren 	struct clk in_clk;
173166097e8SThomas Abraham 	unsigned int ret;
174166097e8SThomas Abraham 	unsigned long freq = 0;
175166097e8SThomas Abraham 
176135aa950SStephen Warren 	switch (clk->id) {
177166097e8SThomas Abraham 	case SCLK_UART2:
178135aa950SStephen Warren 		ret = clk_get_by_index(clk->dev, 3, &in_clk);
179166097e8SThomas Abraham 		if (ret < 0)
180166097e8SThomas Abraham 			return ret;
181135aa950SStephen Warren 		freq = clk_get_rate(&in_clk);
182166097e8SThomas Abraham 		break;
183166097e8SThomas Abraham 	}
184166097e8SThomas Abraham 
185166097e8SThomas Abraham 	return freq;
186166097e8SThomas Abraham }
187166097e8SThomas Abraham 
188166097e8SThomas Abraham static struct clk_ops exynos7420_clk_peric1_ops = {
189135aa950SStephen Warren 	.get_rate	= exynos7420_peric1_get_rate,
190166097e8SThomas Abraham };
191166097e8SThomas Abraham 
192166097e8SThomas Abraham static const struct udevice_id exynos7420_clk_topc_compat[] = {
193166097e8SThomas Abraham 	{ .compatible = "samsung,exynos7-clock-topc" },
194166097e8SThomas Abraham 	{ }
195166097e8SThomas Abraham };
196166097e8SThomas Abraham 
197166097e8SThomas Abraham U_BOOT_DRIVER(exynos7420_clk_topc) = {
198166097e8SThomas Abraham 	.name = "exynos7420-clock-topc",
199166097e8SThomas Abraham 	.id = UCLASS_CLK,
200166097e8SThomas Abraham 	.of_match = exynos7420_clk_topc_compat,
201166097e8SThomas Abraham 	.probe = exynos7420_clk_topc_probe,
202166097e8SThomas Abraham 	.priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv),
203166097e8SThomas Abraham 	.ops = &exynos7420_clk_topc_ops,
204166097e8SThomas Abraham };
205166097e8SThomas Abraham 
206166097e8SThomas Abraham static const struct udevice_id exynos7420_clk_top0_compat[] = {
207166097e8SThomas Abraham 	{ .compatible = "samsung,exynos7-clock-top0" },
208166097e8SThomas Abraham 	{ }
209166097e8SThomas Abraham };
210166097e8SThomas Abraham 
211166097e8SThomas Abraham U_BOOT_DRIVER(exynos7420_clk_top0) = {
212166097e8SThomas Abraham 	.name = "exynos7420-clock-top0",
213166097e8SThomas Abraham 	.id = UCLASS_CLK,
214166097e8SThomas Abraham 	.of_match = exynos7420_clk_top0_compat,
215166097e8SThomas Abraham 	.probe = exynos7420_clk_top0_probe,
216166097e8SThomas Abraham 	.priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv),
217166097e8SThomas Abraham 	.ops = &exynos7420_clk_top0_ops,
218166097e8SThomas Abraham };
219166097e8SThomas Abraham 
220166097e8SThomas Abraham static const struct udevice_id exynos7420_clk_peric1_compat[] = {
221166097e8SThomas Abraham 	{ .compatible = "samsung,exynos7-clock-peric1" },
222166097e8SThomas Abraham 	{ }
223166097e8SThomas Abraham };
224166097e8SThomas Abraham 
225166097e8SThomas Abraham U_BOOT_DRIVER(exynos7420_clk_peric1) = {
226166097e8SThomas Abraham 	.name = "exynos7420-clock-peric1",
227166097e8SThomas Abraham 	.id = UCLASS_CLK,
228166097e8SThomas Abraham 	.of_match = exynos7420_clk_peric1_compat,
229166097e8SThomas Abraham 	.ops = &exynos7420_clk_peric1_ops,
230166097e8SThomas Abraham };
231