2 * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io>
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.
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.
14 #include <linux/clk.h>
15 #include <linux/clk-provider.h>
16 #include <linux/of_address.h>
17 #include <linux/of_platform.h>
18 #include <linux/platform_device.h>
19 #include <linux/reset.h>
21 #include "ccu_common.h"
24 #include "ccu_reset.h"
26 #include "ccu-sun8i-de2.h"
28 static SUNXI_CCU_GATE(bus_mixer0_clk
, "bus-mixer0", "bus-de",
30 static SUNXI_CCU_GATE(bus_mixer1_clk
, "bus-mixer1", "bus-de",
32 static SUNXI_CCU_GATE(bus_wb_clk
, "bus-wb", "bus-de",
35 static SUNXI_CCU_GATE(mixer0_clk
, "mixer0", "mixer0-div",
36 0x00, BIT(0), CLK_SET_RATE_PARENT
);
37 static SUNXI_CCU_GATE(mixer1_clk
, "mixer1", "mixer1-div",
38 0x00, BIT(1), CLK_SET_RATE_PARENT
);
39 static SUNXI_CCU_GATE(wb_clk
, "wb", "wb-div",
40 0x00, BIT(2), CLK_SET_RATE_PARENT
);
42 static SUNXI_CCU_M(mixer0_div_clk
, "mixer0-div", "de", 0x0c, 0, 4,
44 static SUNXI_CCU_M(mixer1_div_clk
, "mixer1-div", "de", 0x0c, 4, 4,
46 static SUNXI_CCU_M(wb_div_clk
, "wb-div", "de", 0x0c, 8, 4,
49 static SUNXI_CCU_M(mixer0_div_a83_clk
, "mixer0-div", "pll-de", 0x0c, 0, 4,
51 static SUNXI_CCU_M(mixer1_div_a83_clk
, "mixer1-div", "pll-de", 0x0c, 4, 4,
53 static SUNXI_CCU_M(wb_div_a83_clk
, "wb-div", "pll-de", 0x0c, 8, 4,
56 static struct ccu_common
*sun8i_a83t_de2_clks
[] = {
61 &bus_mixer0_clk
.common
,
62 &bus_mixer1_clk
.common
,
65 &mixer0_div_a83_clk
.common
,
66 &mixer1_div_a83_clk
.common
,
67 &wb_div_a83_clk
.common
,
70 static struct ccu_common
*sun8i_h3_de2_clks
[] = {
75 &bus_mixer0_clk
.common
,
76 &bus_mixer1_clk
.common
,
79 &mixer0_div_clk
.common
,
80 &mixer1_div_clk
.common
,
84 static struct ccu_common
*sun8i_v3s_de2_clks
[] = {
88 &bus_mixer0_clk
.common
,
91 &mixer0_div_clk
.common
,
95 static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks
= {
97 [CLK_MIXER0
] = &mixer0_clk
.common
.hw
,
98 [CLK_MIXER1
] = &mixer1_clk
.common
.hw
,
99 [CLK_WB
] = &wb_clk
.common
.hw
,
101 [CLK_BUS_MIXER0
] = &bus_mixer0_clk
.common
.hw
,
102 [CLK_BUS_MIXER1
] = &bus_mixer1_clk
.common
.hw
,
103 [CLK_BUS_WB
] = &bus_wb_clk
.common
.hw
,
105 [CLK_MIXER0_DIV
] = &mixer0_div_a83_clk
.common
.hw
,
106 [CLK_MIXER1_DIV
] = &mixer1_div_a83_clk
.common
.hw
,
107 [CLK_WB_DIV
] = &wb_div_a83_clk
.common
.hw
,
112 static struct clk_hw_onecell_data sun8i_h3_de2_hw_clks
= {
114 [CLK_MIXER0
] = &mixer0_clk
.common
.hw
,
115 [CLK_MIXER1
] = &mixer1_clk
.common
.hw
,
116 [CLK_WB
] = &wb_clk
.common
.hw
,
118 [CLK_BUS_MIXER0
] = &bus_mixer0_clk
.common
.hw
,
119 [CLK_BUS_MIXER1
] = &bus_mixer1_clk
.common
.hw
,
120 [CLK_BUS_WB
] = &bus_wb_clk
.common
.hw
,
122 [CLK_MIXER0_DIV
] = &mixer0_div_clk
.common
.hw
,
123 [CLK_MIXER1_DIV
] = &mixer1_div_clk
.common
.hw
,
124 [CLK_WB_DIV
] = &wb_div_clk
.common
.hw
,
129 static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks
= {
131 [CLK_MIXER0
] = &mixer0_clk
.common
.hw
,
132 [CLK_WB
] = &wb_clk
.common
.hw
,
134 [CLK_BUS_MIXER0
] = &bus_mixer0_clk
.common
.hw
,
135 [CLK_BUS_WB
] = &bus_wb_clk
.common
.hw
,
137 [CLK_MIXER0_DIV
] = &mixer0_div_clk
.common
.hw
,
138 [CLK_WB_DIV
] = &wb_div_clk
.common
.hw
,
143 static struct ccu_reset_map sun8i_a83t_de2_resets
[] = {
144 [RST_MIXER0
] = { 0x08, BIT(0) },
146 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
147 * only RST_WB is exported here.
148 * For V3s there's just no mixer1, so it also shares this struct.
150 [RST_WB
] = { 0x08, BIT(2) },
153 static struct ccu_reset_map sun50i_a64_de2_resets
[] = {
154 [RST_MIXER0
] = { 0x08, BIT(0) },
155 [RST_MIXER1
] = { 0x08, BIT(1) },
156 [RST_WB
] = { 0x08, BIT(2) },
159 static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc
= {
160 .ccu_clks
= sun8i_a83t_de2_clks
,
161 .num_ccu_clks
= ARRAY_SIZE(sun8i_a83t_de2_clks
),
163 .hw_clks
= &sun8i_a83t_de2_hw_clks
,
165 .resets
= sun8i_a83t_de2_resets
,
166 .num_resets
= ARRAY_SIZE(sun8i_a83t_de2_resets
),
169 static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc
= {
170 .ccu_clks
= sun8i_h3_de2_clks
,
171 .num_ccu_clks
= ARRAY_SIZE(sun8i_h3_de2_clks
),
173 .hw_clks
= &sun8i_h3_de2_hw_clks
,
175 .resets
= sun8i_a83t_de2_resets
,
176 .num_resets
= ARRAY_SIZE(sun8i_a83t_de2_resets
),
179 static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc
= {
180 .ccu_clks
= sun8i_h3_de2_clks
,
181 .num_ccu_clks
= ARRAY_SIZE(sun8i_h3_de2_clks
),
183 .hw_clks
= &sun8i_h3_de2_hw_clks
,
185 .resets
= sun50i_a64_de2_resets
,
186 .num_resets
= ARRAY_SIZE(sun50i_a64_de2_resets
),
189 static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc
= {
190 .ccu_clks
= sun8i_v3s_de2_clks
,
191 .num_ccu_clks
= ARRAY_SIZE(sun8i_v3s_de2_clks
),
193 .hw_clks
= &sun8i_v3s_de2_hw_clks
,
195 .resets
= sun8i_a83t_de2_resets
,
196 .num_resets
= ARRAY_SIZE(sun8i_a83t_de2_resets
),
199 static int sunxi_de2_clk_probe(struct platform_device
*pdev
)
201 struct resource
*res
;
202 struct clk
*bus_clk
, *mod_clk
;
203 struct reset_control
*rstc
;
205 const struct sunxi_ccu_desc
*ccu_desc
;
208 ccu_desc
= of_device_get_match_data(&pdev
->dev
);
212 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
213 reg
= devm_ioremap_resource(&pdev
->dev
, res
);
217 bus_clk
= devm_clk_get(&pdev
->dev
, "bus");
218 if (IS_ERR(bus_clk
)) {
219 ret
= PTR_ERR(bus_clk
);
220 if (ret
!= -EPROBE_DEFER
)
221 dev_err(&pdev
->dev
, "Couldn't get bus clk: %d\n", ret
);
225 mod_clk
= devm_clk_get(&pdev
->dev
, "mod");
226 if (IS_ERR(mod_clk
)) {
227 ret
= PTR_ERR(mod_clk
);
228 if (ret
!= -EPROBE_DEFER
)
229 dev_err(&pdev
->dev
, "Couldn't get mod clk: %d\n", ret
);
233 rstc
= devm_reset_control_get_exclusive(&pdev
->dev
, NULL
);
236 if (ret
!= -EPROBE_DEFER
)
238 "Couldn't get reset control: %d\n", ret
);
242 /* The clocks need to be enabled for us to access the registers */
243 ret
= clk_prepare_enable(bus_clk
);
245 dev_err(&pdev
->dev
, "Couldn't enable bus clk: %d\n", ret
);
249 ret
= clk_prepare_enable(mod_clk
);
251 dev_err(&pdev
->dev
, "Couldn't enable mod clk: %d\n", ret
);
252 goto err_disable_bus_clk
;
255 /* The reset control needs to be asserted for the controls to work */
256 ret
= reset_control_deassert(rstc
);
259 "Couldn't deassert reset control: %d\n", ret
);
260 goto err_disable_mod_clk
;
263 ret
= sunxi_ccu_probe(pdev
->dev
.of_node
, reg
, ccu_desc
);
265 goto err_assert_reset
;
270 reset_control_assert(rstc
);
272 clk_disable_unprepare(mod_clk
);
274 clk_disable_unprepare(bus_clk
);
278 static const struct of_device_id sunxi_de2_clk_ids
[] = {
280 .compatible
= "allwinner,sun8i-a83t-de2-clk",
281 .data
= &sun8i_a83t_de2_clk_desc
,
284 .compatible
= "allwinner,sun8i-h3-de2-clk",
285 .data
= &sun8i_h3_de2_clk_desc
,
288 .compatible
= "allwinner,sun8i-v3s-de2-clk",
289 .data
= &sun8i_v3s_de2_clk_desc
,
292 .compatible
= "allwinner,sun50i-a64-de2-clk",
293 .data
= &sun50i_a64_de2_clk_desc
,
296 .compatible
= "allwinner,sun50i-h5-de2-clk",
297 .data
= &sun50i_a64_de2_clk_desc
,
302 static struct platform_driver sunxi_de2_clk_driver
= {
303 .probe
= sunxi_de2_clk_probe
,
305 .name
= "sunxi-de2-clks",
306 .of_match_table
= sunxi_de2_clk_ids
,
309 builtin_platform_driver(sunxi_de2_clk_driver
);