1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Amarula Solutions.
4 * Author: Jagan Teki <jagan@amarulasolutions.com>
5 */
6
7#include <common.h>
8#include <clk-uclass.h>
9#include <dm.h>
10#include <errno.h>
11#include <log.h>
12#include <reset.h>
13#include <asm/io.h>
14#include <clk/sunxi.h>
15#include <dm/device-internal.h>
16#include <linux/bitops.h>
17#include <linux/log2.h>
18
19extern U_BOOT_DRIVER(sunxi_reset);
20
21static const struct ccu_clk_gate *plat_to_gate(struct ccu_plat *plat,
22					       unsigned long id)
23{
24	if (id >= plat->desc->num_gates)
25		return NULL;
26
27	return &plat->desc->gates[id];
28}
29
30static int sunxi_set_gate(struct clk *clk, bool on)
31{
32	struct ccu_plat *plat = dev_get_plat(clk->dev);
33	const struct ccu_clk_gate *gate = plat_to_gate(plat, clk->id);
34	u32 reg;
35
36	if (gate && (gate->flags & CCU_CLK_F_DUMMY_GATE))
37		return 0;
38
39	if (!gate || !(gate->flags & CCU_CLK_F_IS_VALID)) {
40		printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id);
41		return 0;
42	}
43
44	debug("%s: (CLK#%ld) off#0x%x, BIT(%d)\n", __func__,
45	      clk->id, gate->off, ilog2(gate->bit));
46
47	reg = readl(plat->base + gate->off);
48	if (on)
49		reg |= gate->bit;
50	else
51		reg &= ~gate->bit;
52
53	writel(reg, plat->base + gate->off);
54
55	return 0;
56}
57
58static int sunxi_clk_enable(struct clk *clk)
59{
60	return sunxi_set_gate(clk, true);
61}
62
63static int sunxi_clk_disable(struct clk *clk)
64{
65	return sunxi_set_gate(clk, false);
66}
67
68struct clk_ops sunxi_clk_ops = {
69	.enable = sunxi_clk_enable,
70	.disable = sunxi_clk_disable,
71};
72
73static int sunxi_clk_bind(struct udevice *dev)
74{
75	/* Reuse the platform data for the reset driver. */
76	return device_bind(dev, DM_DRIVER_REF(sunxi_reset), "reset",
77			   dev_get_plat(dev), dev_ofnode(dev), NULL);
78}
79
80static int sunxi_clk_probe(struct udevice *dev)
81{
82	struct clk_bulk clk_bulk;
83	struct reset_ctl_bulk rst_bulk;
84	int ret;
85
86	ret = clk_get_bulk(dev, &clk_bulk);
87	if (!ret)
88		clk_enable_bulk(&clk_bulk);
89
90	ret = reset_get_bulk(dev, &rst_bulk);
91	if (!ret)
92		reset_deassert_bulk(&rst_bulk);
93
94	return 0;
95}
96
97static int sunxi_clk_of_to_plat(struct udevice *dev)
98{
99	struct ccu_plat *plat = dev_get_plat(dev);
100
101	plat->base = dev_read_addr_ptr(dev);
102	if (!plat->base)
103		return -ENOMEM;
104
105	plat->desc = (const struct ccu_desc *)dev_get_driver_data(dev);
106	if (!plat->desc)
107		return -EINVAL;
108
109	return 0;
110}
111
112extern const struct ccu_desc a10_ccu_desc;
113extern const struct ccu_desc a10s_ccu_desc;
114extern const struct ccu_desc a23_ccu_desc;
115extern const struct ccu_desc a31_ccu_desc;
116extern const struct ccu_desc a31_r_ccu_desc;
117extern const struct ccu_desc a64_ccu_desc;
118extern const struct ccu_desc a80_ccu_desc;
119extern const struct ccu_desc a80_mmc_clk_desc;
120extern const struct ccu_desc a83t_ccu_desc;
121extern const struct ccu_desc d1_ccu_desc;
122extern const struct ccu_desc f1c100s_ccu_desc;
123extern const struct ccu_desc h3_ccu_desc;
124extern const struct ccu_desc h6_ccu_desc;
125extern const struct ccu_desc h616_ccu_desc;
126extern const struct ccu_desc h6_r_ccu_desc;
127extern const struct ccu_desc r40_ccu_desc;
128extern const struct ccu_desc v3s_ccu_desc;
129
130static const struct udevice_id sunxi_clk_ids[] = {
131#ifdef CONFIG_CLK_SUN4I_A10
132	{ .compatible = "allwinner,sun4i-a10-ccu",
133	  .data = (ulong)&a10_ccu_desc },
134#endif
135#ifdef CONFIG_CLK_SUN5I_A10S
136	{ .compatible = "allwinner,sun5i-a10s-ccu",
137	  .data = (ulong)&a10s_ccu_desc },
138	{ .compatible = "allwinner,sun5i-a13-ccu",
139	  .data = (ulong)&a10s_ccu_desc },
140#endif
141#ifdef CONFIG_CLK_SUN6I_A31
142	{ .compatible = "allwinner,sun6i-a31-ccu",
143	  .data = (ulong)&a31_ccu_desc },
144#endif
145#ifdef CONFIG_CLK_SUN4I_A10
146	{ .compatible = "allwinner,sun7i-a20-ccu",
147	  .data = (ulong)&a10_ccu_desc },
148#endif
149#ifdef CONFIG_CLK_SUN8I_A23
150	{ .compatible = "allwinner,sun8i-a23-ccu",
151	  .data = (ulong)&a23_ccu_desc },
152	{ .compatible = "allwinner,sun8i-a33-ccu",
153	  .data = (ulong)&a23_ccu_desc },
154#endif
155#ifdef CONFIG_CLK_SUN8I_A83T
156	{ .compatible = "allwinner,sun8i-a83t-ccu",
157	  .data = (ulong)&a83t_ccu_desc },
158#endif
159#ifdef CONFIG_CLK_SUN6I_A31_R
160	{ .compatible = "allwinner,sun8i-a83t-r-ccu",
161	  .data = (ulong)&a31_r_ccu_desc },
162#endif
163#ifdef CONFIG_CLK_SUN8I_H3
164	{ .compatible = "allwinner,sun8i-h3-ccu",
165	  .data = (ulong)&h3_ccu_desc },
166#endif
167#ifdef CONFIG_CLK_SUN6I_A31_R
168	{ .compatible = "allwinner,sun8i-h3-r-ccu",
169	  .data = (ulong)&a31_r_ccu_desc },
170#endif
171#ifdef CONFIG_CLK_SUN8I_R40
172	{ .compatible = "allwinner,sun8i-r40-ccu",
173	  .data = (ulong)&r40_ccu_desc },
174#endif
175#ifdef CONFIG_CLK_SUN8I_V3S
176	{ .compatible = "allwinner,sun8i-v3-ccu",
177	  .data = (ulong)&v3s_ccu_desc },
178	{ .compatible = "allwinner,sun8i-v3s-ccu",
179	  .data = (ulong)&v3s_ccu_desc },
180#endif
181#ifdef CONFIG_CLK_SUN9I_A80
182	{ .compatible = "allwinner,sun9i-a80-ccu",
183	  .data = (ulong)&a80_ccu_desc },
184	{ .compatible = "allwinner,sun9i-a80-mmc-config-clk",
185	  .data = (ulong)&a80_mmc_clk_desc },
186#endif
187#ifdef CONFIG_CLK_SUN50I_A64
188	{ .compatible = "allwinner,sun50i-a64-ccu",
189	  .data = (ulong)&a64_ccu_desc },
190#endif
191#ifdef CONFIG_CLK_SUN6I_A31_R
192	{ .compatible = "allwinner,sun50i-a64-r-ccu",
193	  .data = (ulong)&a31_r_ccu_desc },
194#endif
195#ifdef CONFIG_CLK_SUN8I_H3
196	{ .compatible = "allwinner,sun50i-h5-ccu",
197	  .data = (ulong)&h3_ccu_desc },
198#endif
199#ifdef CONFIG_CLK_SUN20I_D1
200	{ .compatible = "allwinner,sun20i-d1-ccu",
201	  .data = (ulong)&d1_ccu_desc },
202#endif
203#ifdef CONFIG_CLK_SUN50I_H6
204	{ .compatible = "allwinner,sun50i-h6-ccu",
205	  .data = (ulong)&h6_ccu_desc },
206#endif
207#ifdef CONFIG_CLK_SUN50I_H6_R
208	{ .compatible = "allwinner,sun50i-h6-r-ccu",
209	  .data = (ulong)&h6_r_ccu_desc },
210#endif
211#ifdef CONFIG_CLK_SUN50I_H616
212	{ .compatible = "allwinner,sun50i-h616-ccu",
213	  .data = (ulong)&h616_ccu_desc },
214#endif
215#ifdef CONFIG_CLK_SUN50I_H6_R
216	{ .compatible = "allwinner,sun50i-h616-r-ccu",
217	  .data = (ulong)&h6_r_ccu_desc },
218#endif
219#ifdef CONFIG_CLK_SUNIV_F1C100S
220	{ .compatible = "allwinner,suniv-f1c100s-ccu",
221	  .data = (ulong)&f1c100s_ccu_desc },
222#endif
223	{ }
224};
225
226U_BOOT_DRIVER(sunxi_clk) = {
227	.name		= "sunxi_clk",
228	.id		= UCLASS_CLK,
229	.of_match	= sunxi_clk_ids,
230	.bind		= sunxi_clk_bind,
231	.probe		= sunxi_clk_probe,
232	.of_to_plat	= sunxi_clk_of_to_plat,
233	.plat_auto	= sizeof(struct ccu_plat),
234	.ops		= &sunxi_clk_ops,
235};
236