1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016 Icenowy Zheng <icenowy@aosc.xyz> |
4 | */ |
5 | |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/module.h> |
8 | #include <linux/of.h> |
9 | #include <linux/platform_device.h> |
10 | |
11 | #include "ccu_common.h" |
12 | #include "ccu_reset.h" |
13 | |
14 | #include "ccu_div.h" |
15 | #include "ccu_gate.h" |
16 | #include "ccu_mp.h" |
17 | #include "ccu_nm.h" |
18 | |
19 | #include "ccu-sun8i-r.h" |
20 | |
21 | static const struct clk_parent_data ar100_parents[] = { |
22 | { .fw_name = "losc" }, |
23 | { .fw_name = "hosc" }, |
24 | { .fw_name = "pll-periph" }, |
25 | { .fw_name = "iosc" }, |
26 | }; |
27 | |
28 | static const struct ccu_mux_var_prediv ar100_predivs[] = { |
29 | { .index = 2, .shift = 8, .width = 5 }, |
30 | }; |
31 | |
32 | static struct ccu_div ar100_clk = { |
33 | .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), |
34 | |
35 | .mux = { |
36 | .shift = 16, |
37 | .width = 2, |
38 | |
39 | .var_predivs = ar100_predivs, |
40 | .n_var_predivs = ARRAY_SIZE(ar100_predivs), |
41 | }, |
42 | |
43 | .common = { |
44 | .reg = 0x00, |
45 | .features = CCU_FEATURE_VARIABLE_PREDIV, |
46 | .hw.init = CLK_HW_INIT_PARENTS_DATA("ar100" , |
47 | ar100_parents, |
48 | &ccu_div_ops, |
49 | 0), |
50 | }, |
51 | }; |
52 | |
53 | static CLK_FIXED_FACTOR_HW(ahb0_clk, "ahb0" , &ar100_clk.common.hw, 1, 1, 0); |
54 | |
55 | static SUNXI_CCU_M(apb0_clk, "apb0" , "ahb0" , 0x0c, 0, 2, 0); |
56 | |
57 | /* |
58 | * Define the parent as an array that can be reused to save space |
59 | * instead of having compound literals for each gate. Also have it |
60 | * non-const so we can change it on the A83T. |
61 | */ |
62 | static const struct clk_hw *apb0_gate_parent[] = { &apb0_clk.common.hw }; |
63 | static SUNXI_CCU_GATE_HWS(apb0_pio_clk, "apb0-pio" , |
64 | apb0_gate_parent, 0x28, BIT(0), 0); |
65 | static SUNXI_CCU_GATE_HWS(apb0_ir_clk, "apb0-ir" , |
66 | apb0_gate_parent, 0x28, BIT(1), 0); |
67 | static SUNXI_CCU_GATE_HWS(apb0_timer_clk, "apb0-timer" , |
68 | apb0_gate_parent, 0x28, BIT(2), 0); |
69 | static SUNXI_CCU_GATE_HWS(apb0_rsb_clk, "apb0-rsb" , |
70 | apb0_gate_parent, 0x28, BIT(3), 0); |
71 | static SUNXI_CCU_GATE_HWS(apb0_uart_clk, "apb0-uart" , |
72 | apb0_gate_parent, 0x28, BIT(4), 0); |
73 | static SUNXI_CCU_GATE_HWS(apb0_i2c_clk, "apb0-i2c" , |
74 | apb0_gate_parent, 0x28, BIT(6), 0); |
75 | static SUNXI_CCU_GATE_HWS(apb0_twd_clk, "apb0-twd" , |
76 | apb0_gate_parent, 0x28, BIT(7), 0); |
77 | |
78 | static const char * const r_mod0_default_parents[] = { "osc32k" , "osc24M" }; |
79 | static SUNXI_CCU_MP_WITH_MUX_GATE(ir_clk, "ir" , |
80 | r_mod0_default_parents, 0x54, |
81 | 0, 4, /* M */ |
82 | 16, 2, /* P */ |
83 | 24, 2, /* mux */ |
84 | BIT(31), /* gate */ |
85 | 0); |
86 | |
87 | static const struct clk_parent_data a83t_r_mod0_parents[] = { |
88 | { .fw_name = "iosc" }, |
89 | { .fw_name = "hosc" }, |
90 | }; |
91 | static const struct ccu_mux_fixed_prediv a83t_ir_predivs[] = { |
92 | { .index = 0, .div = 16 }, |
93 | }; |
94 | static struct ccu_mp a83t_ir_clk = { |
95 | .enable = BIT(31), |
96 | |
97 | .m = _SUNXI_CCU_DIV(0, 4), |
98 | .p = _SUNXI_CCU_DIV(16, 2), |
99 | |
100 | .mux = { |
101 | .shift = 24, |
102 | .width = 2, |
103 | .fixed_predivs = a83t_ir_predivs, |
104 | .n_predivs = ARRAY_SIZE(a83t_ir_predivs), |
105 | }, |
106 | |
107 | .common = { |
108 | .reg = 0x54, |
109 | .features = CCU_FEATURE_VARIABLE_PREDIV, |
110 | .hw.init = CLK_HW_INIT_PARENTS_DATA("ir" , |
111 | a83t_r_mod0_parents, |
112 | &ccu_mp_ops, |
113 | 0), |
114 | }, |
115 | }; |
116 | |
117 | static struct ccu_common *sun8i_r_ccu_clks[] = { |
118 | &ar100_clk.common, |
119 | &apb0_clk.common, |
120 | &apb0_pio_clk.common, |
121 | &apb0_ir_clk.common, |
122 | &apb0_timer_clk.common, |
123 | &apb0_rsb_clk.common, |
124 | &apb0_uart_clk.common, |
125 | &apb0_i2c_clk.common, |
126 | &apb0_twd_clk.common, |
127 | &ir_clk.common, |
128 | &a83t_ir_clk.common, |
129 | }; |
130 | |
131 | static struct clk_hw_onecell_data sun8i_a83t_r_hw_clks = { |
132 | .hws = { |
133 | [CLK_AR100] = &ar100_clk.common.hw, |
134 | [CLK_AHB0] = &ahb0_clk.hw, |
135 | [CLK_APB0] = &apb0_clk.common.hw, |
136 | [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, |
137 | [CLK_APB0_IR] = &apb0_ir_clk.common.hw, |
138 | [CLK_APB0_TIMER] = &apb0_timer_clk.common.hw, |
139 | [CLK_APB0_RSB] = &apb0_rsb_clk.common.hw, |
140 | [CLK_APB0_UART] = &apb0_uart_clk.common.hw, |
141 | [CLK_APB0_I2C] = &apb0_i2c_clk.common.hw, |
142 | [CLK_APB0_TWD] = &apb0_twd_clk.common.hw, |
143 | [CLK_IR] = &a83t_ir_clk.common.hw, |
144 | }, |
145 | .num = CLK_NUMBER, |
146 | }; |
147 | |
148 | static struct clk_hw_onecell_data sun8i_h3_r_hw_clks = { |
149 | .hws = { |
150 | [CLK_AR100] = &ar100_clk.common.hw, |
151 | [CLK_AHB0] = &ahb0_clk.hw, |
152 | [CLK_APB0] = &apb0_clk.common.hw, |
153 | [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, |
154 | [CLK_APB0_IR] = &apb0_ir_clk.common.hw, |
155 | [CLK_APB0_TIMER] = &apb0_timer_clk.common.hw, |
156 | [CLK_APB0_UART] = &apb0_uart_clk.common.hw, |
157 | [CLK_APB0_I2C] = &apb0_i2c_clk.common.hw, |
158 | [CLK_APB0_TWD] = &apb0_twd_clk.common.hw, |
159 | [CLK_IR] = &ir_clk.common.hw, |
160 | }, |
161 | .num = CLK_NUMBER, |
162 | }; |
163 | |
164 | static struct clk_hw_onecell_data sun50i_a64_r_hw_clks = { |
165 | .hws = { |
166 | [CLK_AR100] = &ar100_clk.common.hw, |
167 | [CLK_AHB0] = &ahb0_clk.hw, |
168 | [CLK_APB0] = &apb0_clk.common.hw, |
169 | [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, |
170 | [CLK_APB0_IR] = &apb0_ir_clk.common.hw, |
171 | [CLK_APB0_TIMER] = &apb0_timer_clk.common.hw, |
172 | [CLK_APB0_RSB] = &apb0_rsb_clk.common.hw, |
173 | [CLK_APB0_UART] = &apb0_uart_clk.common.hw, |
174 | [CLK_APB0_I2C] = &apb0_i2c_clk.common.hw, |
175 | [CLK_APB0_TWD] = &apb0_twd_clk.common.hw, |
176 | [CLK_IR] = &ir_clk.common.hw, |
177 | }, |
178 | .num = CLK_NUMBER, |
179 | }; |
180 | |
181 | static struct ccu_reset_map sun8i_a83t_r_ccu_resets[] = { |
182 | [RST_APB0_IR] = { 0xb0, BIT(1) }, |
183 | [RST_APB0_TIMER] = { 0xb0, BIT(2) }, |
184 | [RST_APB0_RSB] = { 0xb0, BIT(3) }, |
185 | [RST_APB0_UART] = { 0xb0, BIT(4) }, |
186 | [RST_APB0_I2C] = { 0xb0, BIT(6) }, |
187 | }; |
188 | |
189 | static struct ccu_reset_map sun8i_h3_r_ccu_resets[] = { |
190 | [RST_APB0_IR] = { 0xb0, BIT(1) }, |
191 | [RST_APB0_TIMER] = { 0xb0, BIT(2) }, |
192 | [RST_APB0_UART] = { 0xb0, BIT(4) }, |
193 | [RST_APB0_I2C] = { 0xb0, BIT(6) }, |
194 | }; |
195 | |
196 | static struct ccu_reset_map sun50i_a64_r_ccu_resets[] = { |
197 | [RST_APB0_IR] = { 0xb0, BIT(1) }, |
198 | [RST_APB0_TIMER] = { 0xb0, BIT(2) }, |
199 | [RST_APB0_RSB] = { 0xb0, BIT(3) }, |
200 | [RST_APB0_UART] = { 0xb0, BIT(4) }, |
201 | [RST_APB0_I2C] = { 0xb0, BIT(6) }, |
202 | }; |
203 | |
204 | static const struct sunxi_ccu_desc sun8i_a83t_r_ccu_desc = { |
205 | .ccu_clks = sun8i_r_ccu_clks, |
206 | .num_ccu_clks = ARRAY_SIZE(sun8i_r_ccu_clks), |
207 | |
208 | .hw_clks = &sun8i_a83t_r_hw_clks, |
209 | |
210 | .resets = sun8i_a83t_r_ccu_resets, |
211 | .num_resets = ARRAY_SIZE(sun8i_a83t_r_ccu_resets), |
212 | }; |
213 | |
214 | static const struct sunxi_ccu_desc sun8i_h3_r_ccu_desc = { |
215 | .ccu_clks = sun8i_r_ccu_clks, |
216 | .num_ccu_clks = ARRAY_SIZE(sun8i_r_ccu_clks), |
217 | |
218 | .hw_clks = &sun8i_h3_r_hw_clks, |
219 | |
220 | .resets = sun8i_h3_r_ccu_resets, |
221 | .num_resets = ARRAY_SIZE(sun8i_h3_r_ccu_resets), |
222 | }; |
223 | |
224 | static const struct sunxi_ccu_desc sun50i_a64_r_ccu_desc = { |
225 | .ccu_clks = sun8i_r_ccu_clks, |
226 | .num_ccu_clks = ARRAY_SIZE(sun8i_r_ccu_clks), |
227 | |
228 | .hw_clks = &sun50i_a64_r_hw_clks, |
229 | |
230 | .resets = sun50i_a64_r_ccu_resets, |
231 | .num_resets = ARRAY_SIZE(sun50i_a64_r_ccu_resets), |
232 | }; |
233 | |
234 | static int sun8i_r_ccu_probe(struct platform_device *pdev) |
235 | { |
236 | const struct sunxi_ccu_desc *desc; |
237 | void __iomem *reg; |
238 | |
239 | desc = of_device_get_match_data(dev: &pdev->dev); |
240 | if (!desc) |
241 | return -EINVAL; |
242 | |
243 | reg = devm_platform_ioremap_resource(pdev, index: 0); |
244 | if (IS_ERR(ptr: reg)) |
245 | return PTR_ERR(ptr: reg); |
246 | |
247 | return devm_sunxi_ccu_probe(dev: &pdev->dev, reg, desc); |
248 | } |
249 | |
250 | static const struct of_device_id sun8i_r_ccu_ids[] = { |
251 | { |
252 | .compatible = "allwinner,sun8i-a83t-r-ccu" , |
253 | .data = &sun8i_a83t_r_ccu_desc, |
254 | }, |
255 | { |
256 | .compatible = "allwinner,sun8i-h3-r-ccu" , |
257 | .data = &sun8i_h3_r_ccu_desc, |
258 | }, |
259 | { |
260 | .compatible = "allwinner,sun50i-a64-r-ccu" , |
261 | .data = &sun50i_a64_r_ccu_desc, |
262 | }, |
263 | { } |
264 | }; |
265 | |
266 | static struct platform_driver sun8i_r_ccu_driver = { |
267 | .probe = sun8i_r_ccu_probe, |
268 | .driver = { |
269 | .name = "sun8i-r-ccu" , |
270 | .suppress_bind_attrs = true, |
271 | .of_match_table = sun8i_r_ccu_ids, |
272 | }, |
273 | }; |
274 | module_platform_driver(sun8i_r_ccu_driver); |
275 | |
276 | MODULE_IMPORT_NS(SUNXI_CCU); |
277 | MODULE_LICENSE("GPL" ); |
278 | |