xref: /linux/drivers/clk/sunxi-ng/ccu-sun8i-de2.c (revision 9f32a03e3e0d372c520d829dd4da6022fe88832a)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io>
4  */
5 
6 #include <linux/clk.h>
7 #include <linux/clk-provider.h>
8 #include <linux/io.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/platform_device.h>
12 #include <linux/reset.h>
13 
14 #include "ccu_common.h"
15 #include "ccu_div.h"
16 #include "ccu_gate.h"
17 #include "ccu_reset.h"
18 
19 #include "ccu-sun8i-de2.h"
20 
21 static SUNXI_CCU_GATE(bus_mixer0_clk,	"bus-mixer0",	"bus-de",
22 		      0x04, BIT(0), 0);
23 static SUNXI_CCU_GATE(bus_mixer1_clk,	"bus-mixer1",	"bus-de",
24 		      0x04, BIT(1), 0);
25 static SUNXI_CCU_GATE(bus_wb_clk,	"bus-wb",	"bus-de",
26 		      0x04, BIT(2), 0);
27 static SUNXI_CCU_GATE(bus_rot_clk,	"bus-rot",	"bus-de",
28 		      0x04, BIT(3), 0);
29 
30 static SUNXI_CCU_GATE(mixer0_clk,	"mixer0",	"mixer0-div",
31 		      0x00, BIT(0), CLK_SET_RATE_PARENT);
32 static SUNXI_CCU_GATE(mixer1_clk,	"mixer1",	"mixer1-div",
33 		      0x00, BIT(1), CLK_SET_RATE_PARENT);
34 static SUNXI_CCU_GATE(wb_clk,		"wb",		"wb-div",
35 		      0x00, BIT(2), CLK_SET_RATE_PARENT);
36 static SUNXI_CCU_GATE(rot_clk,		"rot",		"rot-div",
37 		      0x00, BIT(3), CLK_SET_RATE_PARENT);
38 
39 static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
40 		   CLK_SET_RATE_PARENT);
41 static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
42 		   CLK_SET_RATE_PARENT);
43 static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
44 		   CLK_SET_RATE_PARENT);
45 static SUNXI_CCU_M(rot_div_clk, "rot-div", "de", 0x0c, 0x0c, 4,
46 		   CLK_SET_RATE_PARENT);
47 
48 static SUNXI_CCU_M(mixer0_div_a83_clk, "mixer0-div", "pll-de", 0x0c, 0, 4,
49 		   CLK_SET_RATE_PARENT);
50 static SUNXI_CCU_M(mixer1_div_a83_clk, "mixer1-div", "pll-de", 0x0c, 4, 4,
51 		   CLK_SET_RATE_PARENT);
52 static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4,
53 		   CLK_SET_RATE_PARENT);
54 static SUNXI_CCU_M(rot_div_a83_clk, "rot-div", "pll-de", 0x0c, 0x0c, 4,
55 		   CLK_SET_RATE_PARENT);
56 
57 static struct ccu_common *sun8i_de2_ccu_clks[] = {
58 	&mixer0_clk.common,
59 	&mixer1_clk.common,
60 	&wb_clk.common,
61 	&rot_clk.common,
62 
63 	&bus_mixer0_clk.common,
64 	&bus_mixer1_clk.common,
65 	&bus_wb_clk.common,
66 	&bus_rot_clk.common,
67 
68 	&mixer0_div_clk.common,
69 	&mixer1_div_clk.common,
70 	&wb_div_clk.common,
71 	&rot_div_clk.common,
72 
73 	&mixer0_div_a83_clk.common,
74 	&mixer1_div_a83_clk.common,
75 	&wb_div_a83_clk.common,
76 	&rot_div_a83_clk.common,
77 };
78 
79 static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = {
80 	.hws	= {
81 		[CLK_MIXER0]		= &mixer0_clk.common.hw,
82 		[CLK_MIXER1]		= &mixer1_clk.common.hw,
83 		[CLK_WB]		= &wb_clk.common.hw,
84 		[CLK_ROT]		= &rot_clk.common.hw,
85 
86 		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
87 		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
88 		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
89 		[CLK_BUS_ROT]		= &bus_rot_clk.common.hw,
90 
91 		[CLK_MIXER0_DIV]	= &mixer0_div_a83_clk.common.hw,
92 		[CLK_MIXER1_DIV]	= &mixer1_div_a83_clk.common.hw,
93 		[CLK_WB_DIV]		= &wb_div_a83_clk.common.hw,
94 		[CLK_ROT_DIV]		= &rot_div_a83_clk.common.hw,
95 	},
96 	.num	= CLK_NUMBER_WITH_ROT,
97 };
98 
99 static struct clk_hw_onecell_data sun8i_h3_de2_hw_clks = {
100 	.hws	= {
101 		[CLK_MIXER0]		= &mixer0_clk.common.hw,
102 		[CLK_MIXER1]		= &mixer1_clk.common.hw,
103 		[CLK_WB]		= &wb_clk.common.hw,
104 
105 		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
106 		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
107 		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
108 
109 		[CLK_MIXER0_DIV]	= &mixer0_div_clk.common.hw,
110 		[CLK_MIXER1_DIV]	= &mixer1_div_clk.common.hw,
111 		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
112 	},
113 	.num	= CLK_NUMBER_WITHOUT_ROT,
114 };
115 
116 static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = {
117 	.hws	= {
118 		[CLK_MIXER0]		= &mixer0_clk.common.hw,
119 		[CLK_WB]		= &wb_clk.common.hw,
120 
121 		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
122 		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
123 
124 		[CLK_MIXER0_DIV]	= &mixer0_div_clk.common.hw,
125 		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
126 	},
127 	.num	= CLK_NUMBER_WITHOUT_ROT,
128 };
129 
130 static struct clk_hw_onecell_data sun50i_a64_de2_hw_clks = {
131 	.hws	= {
132 		[CLK_MIXER0]		= &mixer0_clk.common.hw,
133 		[CLK_MIXER1]		= &mixer1_clk.common.hw,
134 		[CLK_WB]		= &wb_clk.common.hw,
135 		[CLK_ROT]		= &rot_clk.common.hw,
136 
137 		[CLK_BUS_MIXER0]	= &bus_mixer0_clk.common.hw,
138 		[CLK_BUS_MIXER1]	= &bus_mixer1_clk.common.hw,
139 		[CLK_BUS_WB]		= &bus_wb_clk.common.hw,
140 		[CLK_BUS_ROT]		= &bus_rot_clk.common.hw,
141 
142 		[CLK_MIXER0_DIV]	= &mixer0_div_clk.common.hw,
143 		[CLK_MIXER1_DIV]	= &mixer1_div_clk.common.hw,
144 		[CLK_WB_DIV]		= &wb_div_clk.common.hw,
145 		[CLK_ROT_DIV]		= &rot_div_clk.common.hw,
146 	},
147 	.num	= CLK_NUMBER_WITH_ROT,
148 };
149 
150 static const struct ccu_reset_map sun8i_a83t_de2_resets[] = {
151 	[RST_MIXER0]	= { 0x08, BIT(0) },
152 	/*
153 	 * Mixer1 reset line is shared with wb, so only RST_WB is
154 	 * exported here.
155 	 */
156 	[RST_WB]	= { 0x08, BIT(2) },
157 	[RST_ROT]	= { 0x08, BIT(3) },
158 };
159 
160 static const struct ccu_reset_map sun8i_h3_de2_resets[] = {
161 	[RST_MIXER0]	= { 0x08, BIT(0) },
162 	/*
163 	 * Mixer1 reset line is shared with wb, so only RST_WB is
164 	 * exported here.
165 	 * V3s doesn't have mixer1, so it also shares this struct.
166 	 */
167 	[RST_WB]	= { 0x08, BIT(2) },
168 };
169 
170 static const struct ccu_reset_map sun50i_a64_de2_resets[] = {
171 	[RST_MIXER0]	= { 0x08, BIT(0) },
172 	[RST_MIXER1]	= { 0x08, BIT(1) },
173 	[RST_WB]	= { 0x08, BIT(2) },
174 	[RST_ROT]	= { 0x08, BIT(3) },
175 };
176 
177 static const struct ccu_reset_map sun50i_h5_de2_resets[] = {
178 	[RST_MIXER0]	= { 0x08, BIT(0) },
179 	[RST_MIXER1]	= { 0x08, BIT(1) },
180 	[RST_WB]	= { 0x08, BIT(2) },
181 };
182 
183 static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
184 	.ccu_clks	= sun8i_de2_ccu_clks,
185 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
186 
187 	.hw_clks	= &sun8i_a83t_de2_hw_clks,
188 
189 	.resets		= sun8i_a83t_de2_resets,
190 	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
191 };
192 
193 static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = {
194 	.ccu_clks	= sun8i_de2_ccu_clks,
195 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
196 
197 	.hw_clks	= &sun8i_h3_de2_hw_clks,
198 
199 	.resets		= sun8i_h3_de2_resets,
200 	.num_resets	= ARRAY_SIZE(sun8i_h3_de2_resets),
201 };
202 
203 static const struct sunxi_ccu_desc sun8i_r40_de2_clk_desc = {
204 	.ccu_clks	= sun8i_de2_ccu_clks,
205 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
206 
207 	.hw_clks	= &sun50i_a64_de2_hw_clks,
208 
209 	.resets		= sun8i_a83t_de2_resets,
210 	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
211 };
212 
213 static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = {
214 	.ccu_clks	= sun8i_de2_ccu_clks,
215 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
216 
217 	.hw_clks	= &sun8i_v3s_de2_hw_clks,
218 
219 	.resets		= sun8i_a83t_de2_resets,
220 	.num_resets	= ARRAY_SIZE(sun8i_a83t_de2_resets),
221 };
222 
223 static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
224 	.ccu_clks	= sun8i_de2_ccu_clks,
225 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
226 
227 	.hw_clks	= &sun50i_a64_de2_hw_clks,
228 
229 	.resets		= sun50i_a64_de2_resets,
230 	.num_resets	= ARRAY_SIZE(sun50i_a64_de2_resets),
231 };
232 
233 static const struct sunxi_ccu_desc sun50i_h5_de2_clk_desc = {
234 	.ccu_clks	= sun8i_de2_ccu_clks,
235 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
236 
237 	.hw_clks	= &sun8i_h3_de2_hw_clks,
238 
239 	.resets		= sun50i_h5_de2_resets,
240 	.num_resets	= ARRAY_SIZE(sun50i_h5_de2_resets),
241 };
242 
243 static const struct sunxi_ccu_desc sun50i_h616_de33_clk_desc = {
244 	.ccu_clks	= sun8i_de2_ccu_clks,
245 	.num_ccu_clks	= ARRAY_SIZE(sun8i_de2_ccu_clks),
246 
247 	.hw_clks	= &sun8i_h3_de2_hw_clks,
248 
249 	.resets		= sun50i_h5_de2_resets,
250 	.num_resets	= ARRAY_SIZE(sun50i_h5_de2_resets),
251 };
252 
sunxi_de2_clk_probe(struct platform_device * pdev)253 static int sunxi_de2_clk_probe(struct platform_device *pdev)
254 {
255 	struct clk *bus_clk, *mod_clk;
256 	struct reset_control *rstc;
257 	void __iomem *reg;
258 	const struct sunxi_ccu_desc *ccu_desc;
259 	int ret;
260 
261 	ccu_desc = of_device_get_match_data(&pdev->dev);
262 	if (!ccu_desc)
263 		return -EINVAL;
264 
265 	reg = devm_platform_ioremap_resource(pdev, 0);
266 	if (IS_ERR(reg))
267 		return PTR_ERR(reg);
268 
269 	bus_clk = devm_clk_get(&pdev->dev, "bus");
270 	if (IS_ERR(bus_clk))
271 		return dev_err_probe(&pdev->dev, PTR_ERR(bus_clk),
272 				     "Couldn't get bus clk\n");
273 
274 	mod_clk = devm_clk_get(&pdev->dev, "mod");
275 	if (IS_ERR(mod_clk))
276 		return dev_err_probe(&pdev->dev, PTR_ERR(mod_clk),
277 				     "Couldn't get mod clk\n");
278 
279 	rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
280 	if (IS_ERR(rstc))
281 		return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
282 				     "Couldn't get reset control\n");
283 
284 	/* The clocks need to be enabled for us to access the registers */
285 	ret = clk_prepare_enable(bus_clk);
286 	if (ret) {
287 		dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
288 		return ret;
289 	}
290 
291 	ret = clk_prepare_enable(mod_clk);
292 	if (ret) {
293 		dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret);
294 		goto err_disable_bus_clk;
295 	}
296 
297 	/* The reset control needs to be asserted for the controls to work */
298 	ret = reset_control_deassert(rstc);
299 	if (ret) {
300 		dev_err(&pdev->dev,
301 			"Couldn't deassert reset control: %d\n", ret);
302 		goto err_disable_mod_clk;
303 	}
304 
305 	/*
306 	 * The DE33 requires these additional (unknown) registers set
307 	 * during initialisation.
308 	 */
309 	if (of_device_is_compatible(pdev->dev.of_node,
310 				    "allwinner,sun50i-h616-de33-clk")) {
311 		writel(0, reg + 0x24);
312 		writel(0x0000a980, reg + 0x28);
313 	}
314 
315 	ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc);
316 	if (ret)
317 		goto err_assert_reset;
318 
319 	return 0;
320 
321 err_assert_reset:
322 	reset_control_assert(rstc);
323 err_disable_mod_clk:
324 	clk_disable_unprepare(mod_clk);
325 err_disable_bus_clk:
326 	clk_disable_unprepare(bus_clk);
327 	return ret;
328 }
329 
330 static const struct of_device_id sunxi_de2_clk_ids[] = {
331 	{
332 		.compatible = "allwinner,sun8i-a83t-de2-clk",
333 		.data = &sun8i_a83t_de2_clk_desc,
334 	},
335 	{
336 		.compatible = "allwinner,sun8i-h3-de2-clk",
337 		.data = &sun8i_h3_de2_clk_desc,
338 	},
339 	{
340 		.compatible = "allwinner,sun8i-r40-de2-clk",
341 		.data = &sun8i_r40_de2_clk_desc,
342 	},
343 	{
344 		.compatible = "allwinner,sun8i-v3s-de2-clk",
345 		.data = &sun8i_v3s_de2_clk_desc,
346 	},
347 	{
348 		.compatible = "allwinner,sun50i-a64-de2-clk",
349 		.data = &sun50i_a64_de2_clk_desc,
350 	},
351 	{
352 		.compatible = "allwinner,sun50i-h5-de2-clk",
353 		.data = &sun50i_h5_de2_clk_desc,
354 	},
355 	{
356 		.compatible = "allwinner,sun50i-h6-de3-clk",
357 		.data = &sun50i_h5_de2_clk_desc,
358 	},
359 	{
360 		.compatible = "allwinner,sun50i-h616-de33-clk",
361 		.data = &sun50i_h616_de33_clk_desc,
362 	},
363 	{ }
364 };
365 MODULE_DEVICE_TABLE(of, sunxi_de2_clk_ids);
366 
367 static struct platform_driver sunxi_de2_clk_driver = {
368 	.probe	= sunxi_de2_clk_probe,
369 	.driver	= {
370 		.name	= "sunxi-de2-clks",
371 		.of_match_table	= sunxi_de2_clk_ids,
372 	},
373 };
374 module_platform_driver(sunxi_de2_clk_driver);
375 
376 MODULE_IMPORT_NS("SUNXI_CCU");
377 MODULE_DESCRIPTION("Support for the Allwinner SoCs DE2 CCU");
378 MODULE_LICENSE("GPL");
379