summaryrefslogtreecommitdiffstats
path: root/drivers/clk/sunxi-ng
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2017-01-28 07:22:36 -0500
committerMaxime Ripard <maxime.ripard@free-electrons.com>2017-01-30 02:38:30 -0500
commit783ab76ae553abc23f80ef7511052d055697531b (patch)
tree739dab1483f6990a791043dd6b06392082f01276 /drivers/clk/sunxi-ng
parent439b65c4bb66564e46a8df38c06863ee7cecb4e4 (diff)
clk: sunxi-ng: Add A80 Display Engine CCU
With the A80 SoC, Allwinner grouped and moved some subsystem specific clock controls to a separate address space, and possibly separate hardware block. One such subsystem is the display engine. The main clock control unit now only has 1 set of bus gate, dram gate, module clock, and reset control for the entire display subsystem. These feed into a secondary clock control unit, which has controls for each individual module of the display pipeline. This block is not documented in the user manual. Allwinner's kernel was used as the reference. Add support for the display engine clock controls found on the A80. Signed-off-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Diffstat (limited to 'drivers/clk/sunxi-ng')
-rw-r--r--drivers/clk/sunxi-ng/Makefile1
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c283
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h33
3 files changed, 317 insertions, 0 deletions
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 8f37ef7fb67d..6feaac0c5600 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -26,4 +26,5 @@ obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o
26obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o 26obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o
27obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o 27obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o
28obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o 28obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o
29obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o
29obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o 30obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
new file mode 100644
index 000000000000..3fc27db0a49a
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
@@ -0,0 +1,283 @@
1/*
2 * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/clk.h>
15#include <linux/clk-provider.h>
16#include <linux/of_address.h>
17#include <linux/platform_device.h>
18#include <linux/reset.h>
19
20#include "ccu_common.h"
21#include "ccu_div.h"
22#include "ccu_gate.h"
23#include "ccu_reset.h"
24
25#include "ccu-sun9i-a80-de.h"
26
27static SUNXI_CCU_GATE(fe0_clk, "fe0", "fe0-div",
28 0x00, BIT(0), 0);
29static SUNXI_CCU_GATE(fe1_clk, "fe1", "fe1-div",
30 0x00, BIT(1), 0);
31static SUNXI_CCU_GATE(fe2_clk, "fe2", "fe2-div",
32 0x00, BIT(2), 0);
33static SUNXI_CCU_GATE(iep_deu0_clk, "iep-deu0", "de",
34 0x00, BIT(4), 0);
35static SUNXI_CCU_GATE(iep_deu1_clk, "iep-deu1", "de",
36 0x00, BIT(5), 0);
37static SUNXI_CCU_GATE(be0_clk, "be0", "be0-div",
38 0x00, BIT(8), 0);
39static SUNXI_CCU_GATE(be1_clk, "be1", "be1-div",
40 0x00, BIT(9), 0);
41static SUNXI_CCU_GATE(be2_clk, "be2", "be2-div",
42 0x00, BIT(10), 0);
43static SUNXI_CCU_GATE(iep_drc0_clk, "iep-drc0", "de",
44 0x00, BIT(12), 0);
45static SUNXI_CCU_GATE(iep_drc1_clk, "iep-drc1", "de",
46 0x00, BIT(13), 0);
47static SUNXI_CCU_GATE(merge_clk, "merge", "de",
48 0x00, BIT(20), 0);
49
50static SUNXI_CCU_GATE(dram_fe0_clk, "dram-fe0", "sdram",
51 0x04, BIT(0), 0);
52static SUNXI_CCU_GATE(dram_fe1_clk, "dram-fe1", "sdram",
53 0x04, BIT(1), 0);
54static SUNXI_CCU_GATE(dram_fe2_clk, "dram-fe2", "sdram",
55 0x04, BIT(2), 0);
56static SUNXI_CCU_GATE(dram_deu0_clk, "dram-deu0", "sdram",
57 0x04, BIT(4), 0);
58static SUNXI_CCU_GATE(dram_deu1_clk, "dram-deu1", "sdram",
59 0x04, BIT(5), 0);
60static SUNXI_CCU_GATE(dram_be0_clk, "dram-be0", "sdram",
61 0x04, BIT(8), 0);
62static SUNXI_CCU_GATE(dram_be1_clk, "dram-be1", "sdram",
63 0x04, BIT(9), 0);
64static SUNXI_CCU_GATE(dram_be2_clk, "dram-be2", "sdram",
65 0x04, BIT(10), 0);
66static SUNXI_CCU_GATE(dram_drc0_clk, "dram-drc0", "sdram",
67 0x04, BIT(12), 0);
68static SUNXI_CCU_GATE(dram_drc1_clk, "dram-drc1", "sdram",
69 0x04, BIT(13), 0);
70
71static SUNXI_CCU_GATE(bus_fe0_clk, "bus-fe0", "bus-de",
72 0x08, BIT(0), 0);
73static SUNXI_CCU_GATE(bus_fe1_clk, "bus-fe1", "bus-de",
74 0x08, BIT(1), 0);
75static SUNXI_CCU_GATE(bus_fe2_clk, "bus-fe2", "bus-de",
76 0x08, BIT(2), 0);
77static SUNXI_CCU_GATE(bus_deu0_clk, "bus-deu0", "bus-de",
78 0x08, BIT(4), 0);
79static SUNXI_CCU_GATE(bus_deu1_clk, "bus-deu1", "bus-de",
80 0x08, BIT(5), 0);
81static SUNXI_CCU_GATE(bus_be0_clk, "bus-be0", "bus-de",
82 0x08, BIT(8), 0);
83static SUNXI_CCU_GATE(bus_be1_clk, "bus-be1", "bus-de",
84 0x08, BIT(9), 0);
85static SUNXI_CCU_GATE(bus_be2_clk, "bus-be2", "bus-de",
86 0x08, BIT(10), 0);
87static SUNXI_CCU_GATE(bus_drc0_clk, "bus-drc0", "bus-de",
88 0x08, BIT(12), 0);
89static SUNXI_CCU_GATE(bus_drc1_clk, "bus-drc1", "bus-de",
90 0x08, BIT(13), 0);
91
92static SUNXI_CCU_M(fe0_div_clk, "fe0-div", "de", 0x20, 0, 4, 0);
93static SUNXI_CCU_M(fe1_div_clk, "fe1-div", "de", 0x20, 4, 4, 0);
94static SUNXI_CCU_M(fe2_div_clk, "fe2-div", "de", 0x20, 8, 4, 0);
95static SUNXI_CCU_M(be0_div_clk, "be0-div", "de", 0x20, 16, 4, 0);
96static SUNXI_CCU_M(be1_div_clk, "be1-div", "de", 0x20, 20, 4, 0);
97static SUNXI_CCU_M(be2_div_clk, "be2-div", "de", 0x20, 24, 4, 0);
98
99static struct ccu_common *sun9i_a80_de_clks[] = {
100 &fe0_clk.common,
101 &fe1_clk.common,
102 &fe2_clk.common,
103 &iep_deu0_clk.common,
104 &iep_deu1_clk.common,
105 &be0_clk.common,
106 &be1_clk.common,
107 &be2_clk.common,
108 &iep_drc0_clk.common,
109 &iep_drc1_clk.common,
110 &merge_clk.common,
111
112 &dram_fe0_clk.common,
113 &dram_fe1_clk.common,
114 &dram_fe2_clk.common,
115 &dram_deu0_clk.common,
116 &dram_deu1_clk.common,
117 &dram_be0_clk.common,
118 &dram_be1_clk.common,
119 &dram_be2_clk.common,
120 &dram_drc0_clk.common,
121 &dram_drc1_clk.common,
122
123 &bus_fe0_clk.common,
124 &bus_fe1_clk.common,
125 &bus_fe2_clk.common,
126 &bus_deu0_clk.common,
127 &bus_deu1_clk.common,
128 &bus_be0_clk.common,
129 &bus_be1_clk.common,
130 &bus_be2_clk.common,
131 &bus_drc0_clk.common,
132 &bus_drc1_clk.common,
133
134 &fe0_div_clk.common,
135 &fe1_div_clk.common,
136 &fe2_div_clk.common,
137 &be0_div_clk.common,
138 &be1_div_clk.common,
139 &be2_div_clk.common,
140};
141
142static struct clk_hw_onecell_data sun9i_a80_de_hw_clks = {
143 .hws = {
144 [CLK_FE0] = &fe0_clk.common.hw,
145 [CLK_FE1] = &fe1_clk.common.hw,
146 [CLK_FE2] = &fe2_clk.common.hw,
147 [CLK_IEP_DEU0] = &iep_deu0_clk.common.hw,
148 [CLK_IEP_DEU1] = &iep_deu1_clk.common.hw,
149 [CLK_BE0] = &be0_clk.common.hw,
150 [CLK_BE1] = &be1_clk.common.hw,
151 [CLK_BE2] = &be2_clk.common.hw,
152 [CLK_IEP_DRC0] = &iep_drc0_clk.common.hw,
153 [CLK_IEP_DRC1] = &iep_drc1_clk.common.hw,
154 [CLK_MERGE] = &merge_clk.common.hw,
155
156 [CLK_DRAM_FE0] = &dram_fe0_clk.common.hw,
157 [CLK_DRAM_FE1] = &dram_fe1_clk.common.hw,
158 [CLK_DRAM_FE2] = &dram_fe2_clk.common.hw,
159 [CLK_DRAM_DEU0] = &dram_deu0_clk.common.hw,
160 [CLK_DRAM_DEU1] = &dram_deu1_clk.common.hw,
161 [CLK_DRAM_BE0] = &dram_be0_clk.common.hw,
162 [CLK_DRAM_BE1] = &dram_be1_clk.common.hw,
163 [CLK_DRAM_BE2] = &dram_be2_clk.common.hw,
164 [CLK_DRAM_DRC0] = &dram_drc0_clk.common.hw,
165 [CLK_DRAM_DRC1] = &dram_drc1_clk.common.hw,
166
167 [CLK_BUS_FE0] = &bus_fe0_clk.common.hw,
168 [CLK_BUS_FE1] = &bus_fe1_clk.common.hw,
169 [CLK_BUS_FE2] = &bus_fe2_clk.common.hw,
170 [CLK_BUS_DEU0] = &bus_deu0_clk.common.hw,
171 [CLK_BUS_DEU1] = &bus_deu1_clk.common.hw,
172 [CLK_BUS_BE0] = &bus_be0_clk.common.hw,
173 [CLK_BUS_BE1] = &bus_be1_clk.common.hw,
174 [CLK_BUS_BE2] = &bus_be2_clk.common.hw,
175 [CLK_BUS_DRC0] = &bus_drc0_clk.common.hw,
176 [CLK_BUS_DRC1] = &bus_drc1_clk.common.hw,
177
178 [CLK_FE0_DIV] = &fe0_div_clk.common.hw,
179 [CLK_FE1_DIV] = &fe1_div_clk.common.hw,
180 [CLK_FE2_DIV] = &fe2_div_clk.common.hw,
181 [CLK_BE0_DIV] = &be0_div_clk.common.hw,
182 [CLK_BE1_DIV] = &be1_div_clk.common.hw,
183 [CLK_BE2_DIV] = &be2_div_clk.common.hw,
184 },
185 .num = CLK_NUMBER,
186};
187
188static struct ccu_reset_map sun9i_a80_de_resets[] = {
189 [RST_FE0] = { 0x0c, BIT(0) },
190 [RST_FE1] = { 0x0c, BIT(1) },
191 [RST_FE2] = { 0x0c, BIT(2) },
192 [RST_DEU0] = { 0x0c, BIT(4) },
193 [RST_DEU1] = { 0x0c, BIT(5) },
194 [RST_BE0] = { 0x0c, BIT(8) },
195 [RST_BE1] = { 0x0c, BIT(9) },
196 [RST_BE2] = { 0x0c, BIT(10) },
197 [RST_DRC0] = { 0x0c, BIT(12) },
198 [RST_DRC1] = { 0x0c, BIT(13) },
199 [RST_MERGE] = { 0x0c, BIT(20) },
200};
201
202static const struct sunxi_ccu_desc sun9i_a80_de_clk_desc = {
203 .ccu_clks = sun9i_a80_de_clks,
204 .num_ccu_clks = ARRAY_SIZE(sun9i_a80_de_clks),
205
206 .hw_clks = &sun9i_a80_de_hw_clks,
207
208 .resets = sun9i_a80_de_resets,
209 .num_resets = ARRAY_SIZE(sun9i_a80_de_resets),
210};
211
212static int sun9i_a80_de_clk_probe(struct platform_device *pdev)
213{
214 struct resource *res;
215 struct clk *bus_clk;
216 struct reset_control *rstc;
217 void __iomem *reg;
218 int ret;
219
220 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
221 reg = devm_ioremap_resource(&pdev->dev, res);
222 if (IS_ERR(reg))
223 return PTR_ERR(reg);
224
225 bus_clk = devm_clk_get(&pdev->dev, "bus");
226 if (IS_ERR(bus_clk)) {
227 ret = PTR_ERR(bus_clk);
228 if (ret != -EPROBE_DEFER)
229 dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret);
230 return ret;
231 }
232
233 rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
234 if (IS_ERR(rstc)) {
235 ret = PTR_ERR(bus_clk);
236 if (ret != -EPROBE_DEFER)
237 dev_err(&pdev->dev,
238 "Couldn't get reset control: %d\n", ret);
239 return ret;
240 }
241
242 /* The bus clock needs to be enabled for us to access the registers */
243 ret = clk_prepare_enable(bus_clk);
244 if (ret) {
245 dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
246 return ret;
247 }
248
249 /* The reset control needs to be asserted for the controls to work */
250 ret = reset_control_deassert(rstc);
251 if (ret) {
252 dev_err(&pdev->dev,
253 "Couldn't deassert reset control: %d\n", ret);
254 goto err_disable_clk;
255 }
256
257 ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
258 &sun9i_a80_de_clk_desc);
259 if (ret)
260 goto err_assert_reset;
261
262 return 0;
263
264err_assert_reset:
265 reset_control_assert(rstc);
266err_disable_clk:
267 clk_disable_unprepare(bus_clk);
268 return ret;
269}
270
271static const struct of_device_id sun9i_a80_de_clk_ids[] = {
272 { .compatible = "allwinner,sun9i-a80-de-clks" },
273 { }
274};
275
276static struct platform_driver sun9i_a80_de_clk_driver = {
277 .probe = sun9i_a80_de_clk_probe,
278 .driver = {
279 .name = "sun9i-a80-de-clks",
280 .of_match_table = sun9i_a80_de_clk_ids,
281 },
282};
283builtin_platform_driver(sun9i_a80_de_clk_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h
new file mode 100644
index 000000000000..a4769041e40f
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h
@@ -0,0 +1,33 @@
1/*
2 * Copyright 2016 Chen-Yu Tsai
3 *
4 * Chen-Yu Tsai <wens@csie.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#ifndef _CCU_SUN9I_A80_DE_H_
18#define _CCU_SUN9I_A80_DE_H_
19
20#include <dt-bindings/clock/sun9i-a80-de.h>
21#include <dt-bindings/reset/sun9i-a80-de.h>
22
23/* Intermediary clock dividers are not exported */
24#define CLK_FE0_DIV 31
25#define CLK_FE1_DIV 32
26#define CLK_FE2_DIV 33
27#define CLK_BE0_DIV 34
28#define CLK_BE1_DIV 35
29#define CLK_BE2_DIV 36
30
31#define CLK_NUMBER (CLK_BE2_DIV + 1)
32
33#endif /* _CCU_SUN9I_A80_DE_H_ */