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 struct ccu_common
*sun8i_a83t_de2_clks
[] = {
54 &bus_mixer0_clk
.common
,
55 &bus_mixer1_clk
.common
,
58 &mixer0_div_clk
.common
,
59 &mixer1_div_clk
.common
,
63 static struct ccu_common
*sun8i_v3s_de2_clks
[] = {
67 &bus_mixer0_clk
.common
,
70 &mixer0_div_clk
.common
,
74 static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks
= {
76 [CLK_MIXER0
] = &mixer0_clk
.common
.hw
,
77 [CLK_MIXER1
] = &mixer1_clk
.common
.hw
,
78 [CLK_WB
] = &wb_clk
.common
.hw
,
80 [CLK_BUS_MIXER0
] = &bus_mixer0_clk
.common
.hw
,
81 [CLK_BUS_MIXER1
] = &bus_mixer1_clk
.common
.hw
,
82 [CLK_BUS_WB
] = &bus_wb_clk
.common
.hw
,
84 [CLK_MIXER0_DIV
] = &mixer0_div_clk
.common
.hw
,
85 [CLK_MIXER1_DIV
] = &mixer1_div_clk
.common
.hw
,
86 [CLK_WB_DIV
] = &wb_div_clk
.common
.hw
,
91 static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks
= {
93 [CLK_MIXER0
] = &mixer0_clk
.common
.hw
,
94 [CLK_WB
] = &wb_clk
.common
.hw
,
96 [CLK_BUS_MIXER0
] = &bus_mixer0_clk
.common
.hw
,
97 [CLK_BUS_WB
] = &bus_wb_clk
.common
.hw
,
99 [CLK_MIXER0_DIV
] = &mixer0_div_clk
.common
.hw
,
100 [CLK_WB_DIV
] = &wb_div_clk
.common
.hw
,
105 static struct ccu_reset_map sun8i_a83t_de2_resets
[] = {
106 [RST_MIXER0
] = { 0x08, BIT(0) },
108 * For A83T, H3 and R40, mixer1 reset line is shared with wb, so
109 * only RST_WB is exported here.
110 * For V3s there's just no mixer1, so it also shares this struct.
112 [RST_WB
] = { 0x08, BIT(2) },
115 static struct ccu_reset_map sun50i_a64_de2_resets
[] = {
116 [RST_MIXER0
] = { 0x08, BIT(0) },
117 [RST_MIXER1
] = { 0x08, BIT(1) },
118 [RST_WB
] = { 0x08, BIT(2) },
121 static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc
= {
122 .ccu_clks
= sun8i_a83t_de2_clks
,
123 .num_ccu_clks
= ARRAY_SIZE(sun8i_a83t_de2_clks
),
125 .hw_clks
= &sun8i_a83t_de2_hw_clks
,
127 .resets
= sun8i_a83t_de2_resets
,
128 .num_resets
= ARRAY_SIZE(sun8i_a83t_de2_resets
),
131 static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc
= {
132 .ccu_clks
= sun8i_a83t_de2_clks
,
133 .num_ccu_clks
= ARRAY_SIZE(sun8i_a83t_de2_clks
),
135 .hw_clks
= &sun8i_a83t_de2_hw_clks
,
137 .resets
= sun50i_a64_de2_resets
,
138 .num_resets
= ARRAY_SIZE(sun50i_a64_de2_resets
),
141 static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc
= {
142 .ccu_clks
= sun8i_v3s_de2_clks
,
143 .num_ccu_clks
= ARRAY_SIZE(sun8i_v3s_de2_clks
),
145 .hw_clks
= &sun8i_v3s_de2_hw_clks
,
147 .resets
= sun8i_a83t_de2_resets
,
148 .num_resets
= ARRAY_SIZE(sun8i_a83t_de2_resets
),
151 static int sunxi_de2_clk_probe(struct platform_device
*pdev
)
153 struct resource
*res
;
154 struct clk
*bus_clk
, *mod_clk
;
155 struct reset_control
*rstc
;
157 const struct sunxi_ccu_desc
*ccu_desc
;
160 ccu_desc
= of_device_get_match_data(&pdev
->dev
);
164 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
165 reg
= devm_ioremap_resource(&pdev
->dev
, res
);
169 bus_clk
= devm_clk_get(&pdev
->dev
, "bus");
170 if (IS_ERR(bus_clk
)) {
171 ret
= PTR_ERR(bus_clk
);
172 if (ret
!= -EPROBE_DEFER
)
173 dev_err(&pdev
->dev
, "Couldn't get bus clk: %d\n", ret
);
177 mod_clk
= devm_clk_get(&pdev
->dev
, "mod");
178 if (IS_ERR(mod_clk
)) {
179 ret
= PTR_ERR(mod_clk
);
180 if (ret
!= -EPROBE_DEFER
)
181 dev_err(&pdev
->dev
, "Couldn't get mod clk: %d\n", ret
);
185 rstc
= devm_reset_control_get_exclusive(&pdev
->dev
, NULL
);
188 if (ret
!= -EPROBE_DEFER
)
190 "Couldn't get reset control: %d\n", ret
);
194 /* The clocks need to be enabled for us to access the registers */
195 ret
= clk_prepare_enable(bus_clk
);
197 dev_err(&pdev
->dev
, "Couldn't enable bus clk: %d\n", ret
);
201 ret
= clk_prepare_enable(mod_clk
);
203 dev_err(&pdev
->dev
, "Couldn't enable mod clk: %d\n", ret
);
204 goto err_disable_bus_clk
;
207 /* The reset control needs to be asserted for the controls to work */
208 ret
= reset_control_deassert(rstc
);
211 "Couldn't deassert reset control: %d\n", ret
);
212 goto err_disable_mod_clk
;
215 ret
= sunxi_ccu_probe(pdev
->dev
.of_node
, reg
, ccu_desc
);
217 goto err_assert_reset
;
222 reset_control_assert(rstc
);
224 clk_disable_unprepare(mod_clk
);
226 clk_disable_unprepare(bus_clk
);
230 static const struct of_device_id sunxi_de2_clk_ids
[] = {
232 .compatible
= "allwinner,sun8i-a83t-de2-clk",
233 .data
= &sun8i_a83t_de2_clk_desc
,
236 .compatible
= "allwinner,sun8i-v3s-de2-clk",
237 .data
= &sun8i_v3s_de2_clk_desc
,
240 .compatible
= "allwinner,sun50i-h5-de2-clk",
241 .data
= &sun50i_a64_de2_clk_desc
,
244 * The Allwinner A64 SoC needs some bit to be poke in syscon to make
245 * DE2 really working.
246 * So there's currently no A64 compatible here.
247 * H5 shares the same reset line with A64, so here H5 is using the
248 * clock description of A64.
253 static struct platform_driver sunxi_de2_clk_driver
= {
254 .probe
= sunxi_de2_clk_probe
,
256 .name
= "sunxi-de2-clks",
257 .of_match_table
= sunxi_de2_clk_ids
,
260 builtin_platform_driver(sunxi_de2_clk_driver
);