1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Hi3516CV300 Clock and Reset Generator Driver |
4 | * |
5 | * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. |
6 | */ |
7 | |
8 | #include <dt-bindings/clock/hi3516cv300-clock.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include "clk.h" |
14 | #include "crg.h" |
15 | #include "reset.h" |
16 | |
17 | /* hi3516CV300 core CRG */ |
18 | #define HI3516CV300_INNER_CLK_OFFSET 64 |
19 | #define HI3516CV300_FIXED_3M 65 |
20 | #define HI3516CV300_FIXED_6M 66 |
21 | #define HI3516CV300_FIXED_24M 67 |
22 | #define HI3516CV300_FIXED_49P5 68 |
23 | #define HI3516CV300_FIXED_50M 69 |
24 | #define HI3516CV300_FIXED_83P3M 70 |
25 | #define HI3516CV300_FIXED_99M 71 |
26 | #define HI3516CV300_FIXED_100M 72 |
27 | #define HI3516CV300_FIXED_148P5M 73 |
28 | #define HI3516CV300_FIXED_198M 74 |
29 | #define HI3516CV300_FIXED_297M 75 |
30 | #define HI3516CV300_UART_MUX 76 |
31 | #define HI3516CV300_FMC_MUX 77 |
32 | #define HI3516CV300_MMC0_MUX 78 |
33 | #define HI3516CV300_MMC1_MUX 79 |
34 | #define HI3516CV300_MMC2_MUX 80 |
35 | #define HI3516CV300_MMC3_MUX 81 |
36 | #define HI3516CV300_PWM_MUX 82 |
37 | #define HI3516CV300_CRG_NR_CLKS 128 |
38 | |
39 | static const struct hisi_fixed_rate_clock hi3516cv300_fixed_rate_clks[] = { |
40 | { HI3516CV300_FIXED_3M, "3m" , NULL, 0, 3000000, }, |
41 | { HI3516CV300_FIXED_6M, "6m" , NULL, 0, 6000000, }, |
42 | { HI3516CV300_FIXED_24M, "24m" , NULL, 0, 24000000, }, |
43 | { HI3516CV300_FIXED_49P5, "49.5m" , NULL, 0, 49500000, }, |
44 | { HI3516CV300_FIXED_50M, "50m" , NULL, 0, 50000000, }, |
45 | { HI3516CV300_FIXED_83P3M, "83.3m" , NULL, 0, 83300000, }, |
46 | { HI3516CV300_FIXED_99M, "99m" , NULL, 0, 99000000, }, |
47 | { HI3516CV300_FIXED_100M, "100m" , NULL, 0, 100000000, }, |
48 | { HI3516CV300_FIXED_148P5M, "148.5m" , NULL, 0, 148500000, }, |
49 | { HI3516CV300_FIXED_198M, "198m" , NULL, 0, 198000000, }, |
50 | { HI3516CV300_FIXED_297M, "297m" , NULL, 0, 297000000, }, |
51 | { HI3516CV300_APB_CLK, "apb" , NULL, 0, 50000000, }, |
52 | }; |
53 | |
54 | static const char *const uart_mux_p[] = {"24m" , "6m" }; |
55 | static const char *const fmc_mux_p[] = { |
56 | "24m" , "83.3m" , "148.5m" , "198m" , "297m" |
57 | }; |
58 | static const char *const mmc_mux_p[] = {"49.5m" }; |
59 | static const char *const mmc2_mux_p[] = {"99m" , "49.5m" }; |
60 | static const char *const pwm_mux_p[] = {"3m" , "50m" , "24m" , "24m" }; |
61 | |
62 | static u32 uart_mux_table[] = {0, 1}; |
63 | static u32 fmc_mux_table[] = {0, 1, 2, 3, 4}; |
64 | static u32 mmc_mux_table[] = {0}; |
65 | static u32 mmc2_mux_table[] = {0, 2}; |
66 | static u32 pwm_mux_table[] = {0, 1, 2, 3}; |
67 | |
68 | static const struct hisi_mux_clock hi3516cv300_mux_clks[] = { |
69 | { HI3516CV300_UART_MUX, "uart_mux" , uart_mux_p, ARRAY_SIZE(uart_mux_p), |
70 | CLK_SET_RATE_PARENT, 0xe4, 19, 1, 0, uart_mux_table, }, |
71 | { HI3516CV300_FMC_MUX, "fmc_mux" , fmc_mux_p, ARRAY_SIZE(fmc_mux_p), |
72 | CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, |
73 | { HI3516CV300_MMC0_MUX, "mmc0_mux" , mmc_mux_p, ARRAY_SIZE(mmc_mux_p), |
74 | CLK_SET_RATE_PARENT, 0xc4, 4, 2, 0, mmc_mux_table, }, |
75 | { HI3516CV300_MMC1_MUX, "mmc1_mux" , mmc_mux_p, ARRAY_SIZE(mmc_mux_p), |
76 | CLK_SET_RATE_PARENT, 0xc4, 12, 2, 0, mmc_mux_table, }, |
77 | { HI3516CV300_MMC2_MUX, "mmc2_mux" , mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p), |
78 | CLK_SET_RATE_PARENT, 0xc4, 20, 2, 0, mmc2_mux_table, }, |
79 | { HI3516CV300_MMC3_MUX, "mmc3_mux" , mmc_mux_p, ARRAY_SIZE(mmc_mux_p), |
80 | CLK_SET_RATE_PARENT, 0xc8, 4, 2, 0, mmc_mux_table, }, |
81 | { HI3516CV300_PWM_MUX, "pwm_mux" , pwm_mux_p, ARRAY_SIZE(pwm_mux_p), |
82 | CLK_SET_RATE_PARENT, 0x38, 2, 2, 0, pwm_mux_table, }, |
83 | }; |
84 | |
85 | static const struct hisi_gate_clock hi3516cv300_gate_clks[] = { |
86 | |
87 | { HI3516CV300_UART0_CLK, "clk_uart0" , "uart_mux" , CLK_SET_RATE_PARENT, |
88 | 0xe4, 15, 0, }, |
89 | { HI3516CV300_UART1_CLK, "clk_uart1" , "uart_mux" , CLK_SET_RATE_PARENT, |
90 | 0xe4, 16, 0, }, |
91 | { HI3516CV300_UART2_CLK, "clk_uart2" , "uart_mux" , CLK_SET_RATE_PARENT, |
92 | 0xe4, 17, 0, }, |
93 | |
94 | { HI3516CV300_SPI0_CLK, "clk_spi0" , "100m" , CLK_SET_RATE_PARENT, |
95 | 0xe4, 13, 0, }, |
96 | { HI3516CV300_SPI1_CLK, "clk_spi1" , "100m" , CLK_SET_RATE_PARENT, |
97 | 0xe4, 14, 0, }, |
98 | |
99 | { HI3516CV300_FMC_CLK, "clk_fmc" , "fmc_mux" , CLK_SET_RATE_PARENT, |
100 | 0xc0, 1, 0, }, |
101 | { HI3516CV300_MMC0_CLK, "clk_mmc0" , "mmc0_mux" , CLK_SET_RATE_PARENT, |
102 | 0xc4, 1, 0, }, |
103 | { HI3516CV300_MMC1_CLK, "clk_mmc1" , "mmc1_mux" , CLK_SET_RATE_PARENT, |
104 | 0xc4, 9, 0, }, |
105 | { HI3516CV300_MMC2_CLK, "clk_mmc2" , "mmc2_mux" , CLK_SET_RATE_PARENT, |
106 | 0xc4, 17, 0, }, |
107 | { HI3516CV300_MMC3_CLK, "clk_mmc3" , "mmc3_mux" , CLK_SET_RATE_PARENT, |
108 | 0xc8, 1, 0, }, |
109 | |
110 | { HI3516CV300_ETH_CLK, "clk_eth" , NULL, 0, 0xec, 1, 0, }, |
111 | |
112 | { HI3516CV300_DMAC_CLK, "clk_dmac" , NULL, 0, 0xd8, 5, 0, }, |
113 | { HI3516CV300_PWM_CLK, "clk_pwm" , "pwm_mux" , CLK_SET_RATE_PARENT, |
114 | 0x38, 1, 0, }, |
115 | |
116 | { HI3516CV300_USB2_BUS_CLK, "clk_usb2_bus" , NULL, 0, 0xb8, 0, 0, }, |
117 | { HI3516CV300_USB2_OHCI48M_CLK, "clk_usb2_ohci48m" , NULL, 0, |
118 | 0xb8, 1, 0, }, |
119 | { HI3516CV300_USB2_OHCI12M_CLK, "clk_usb2_ohci12m" , NULL, 0, |
120 | 0xb8, 2, 0, }, |
121 | { HI3516CV300_USB2_OTG_UTMI_CLK, "clk_usb2_otg_utmi" , NULL, 0, |
122 | 0xb8, 3, 0, }, |
123 | { HI3516CV300_USB2_HST_PHY_CLK, "clk_usb2_hst_phy" , NULL, 0, |
124 | 0xb8, 4, 0, }, |
125 | { HI3516CV300_USB2_UTMI0_CLK, "clk_usb2_utmi0" , NULL, 0, 0xb8, 5, 0, }, |
126 | { HI3516CV300_USB2_PHY_CLK, "clk_usb2_phy" , NULL, 0, 0xb8, 7, 0, }, |
127 | }; |
128 | |
129 | static struct hisi_clock_data *hi3516cv300_clk_register( |
130 | struct platform_device *pdev) |
131 | { |
132 | struct hisi_clock_data *clk_data; |
133 | int ret; |
134 | |
135 | clk_data = hisi_clk_alloc(pdev, HI3516CV300_CRG_NR_CLKS); |
136 | if (!clk_data) |
137 | return ERR_PTR(error: -ENOMEM); |
138 | |
139 | ret = hisi_clk_register_fixed_rate(hi3516cv300_fixed_rate_clks, |
140 | ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data); |
141 | if (ret) |
142 | return ERR_PTR(error: ret); |
143 | |
144 | ret = hisi_clk_register_mux(hi3516cv300_mux_clks, |
145 | ARRAY_SIZE(hi3516cv300_mux_clks), clk_data); |
146 | if (ret) |
147 | goto unregister_fixed_rate; |
148 | |
149 | ret = hisi_clk_register_gate(hi3516cv300_gate_clks, |
150 | ARRAY_SIZE(hi3516cv300_gate_clks), clk_data); |
151 | if (ret) |
152 | goto unregister_mux; |
153 | |
154 | ret = of_clk_add_provider(np: pdev->dev.of_node, |
155 | clk_src_get: of_clk_src_onecell_get, data: &clk_data->clk_data); |
156 | if (ret) |
157 | goto unregister_gate; |
158 | |
159 | return clk_data; |
160 | |
161 | unregister_gate: |
162 | hisi_clk_unregister_gate(clks: hi3516cv300_gate_clks, |
163 | ARRAY_SIZE(hi3516cv300_gate_clks), data: clk_data); |
164 | unregister_mux: |
165 | hisi_clk_unregister_mux(clks: hi3516cv300_mux_clks, |
166 | ARRAY_SIZE(hi3516cv300_mux_clks), data: clk_data); |
167 | unregister_fixed_rate: |
168 | hisi_clk_unregister_fixed_rate(clks: hi3516cv300_fixed_rate_clks, |
169 | ARRAY_SIZE(hi3516cv300_fixed_rate_clks), data: clk_data); |
170 | return ERR_PTR(error: ret); |
171 | } |
172 | |
173 | static void hi3516cv300_clk_unregister(struct platform_device *pdev) |
174 | { |
175 | struct hisi_crg_dev *crg = platform_get_drvdata(pdev); |
176 | |
177 | of_clk_del_provider(np: pdev->dev.of_node); |
178 | |
179 | hisi_clk_unregister_gate(clks: hi3516cv300_gate_clks, |
180 | ARRAY_SIZE(hi3516cv300_gate_clks), data: crg->clk_data); |
181 | hisi_clk_unregister_mux(clks: hi3516cv300_mux_clks, |
182 | ARRAY_SIZE(hi3516cv300_mux_clks), data: crg->clk_data); |
183 | hisi_clk_unregister_fixed_rate(clks: hi3516cv300_fixed_rate_clks, |
184 | ARRAY_SIZE(hi3516cv300_fixed_rate_clks), data: crg->clk_data); |
185 | } |
186 | |
187 | static const struct hisi_crg_funcs hi3516cv300_crg_funcs = { |
188 | .register_clks = hi3516cv300_clk_register, |
189 | .unregister_clks = hi3516cv300_clk_unregister, |
190 | }; |
191 | |
192 | /* hi3516CV300 sysctrl CRG */ |
193 | #define HI3516CV300_SYSCTRL_NR_CLKS 16 |
194 | |
195 | static const char *const wdt_mux_p[] __initconst = { "3m" , "apb" }; |
196 | static u32 wdt_mux_table[] = {0, 1}; |
197 | |
198 | static const struct hisi_mux_clock hi3516cv300_sysctrl_mux_clks[] = { |
199 | { HI3516CV300_WDT_CLK, "wdt" , wdt_mux_p, ARRAY_SIZE(wdt_mux_p), |
200 | CLK_SET_RATE_PARENT, 0x0, 23, 1, 0, wdt_mux_table, }, |
201 | }; |
202 | |
203 | static struct hisi_clock_data *hi3516cv300_sysctrl_clk_register( |
204 | struct platform_device *pdev) |
205 | { |
206 | struct hisi_clock_data *clk_data; |
207 | int ret; |
208 | |
209 | clk_data = hisi_clk_alloc(pdev, HI3516CV300_SYSCTRL_NR_CLKS); |
210 | if (!clk_data) |
211 | return ERR_PTR(error: -ENOMEM); |
212 | |
213 | ret = hisi_clk_register_mux(hi3516cv300_sysctrl_mux_clks, |
214 | ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data); |
215 | if (ret) |
216 | return ERR_PTR(error: ret); |
217 | |
218 | |
219 | ret = of_clk_add_provider(np: pdev->dev.of_node, |
220 | clk_src_get: of_clk_src_onecell_get, data: &clk_data->clk_data); |
221 | if (ret) |
222 | goto unregister_mux; |
223 | |
224 | return clk_data; |
225 | |
226 | unregister_mux: |
227 | hisi_clk_unregister_mux(clks: hi3516cv300_sysctrl_mux_clks, |
228 | ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), data: clk_data); |
229 | return ERR_PTR(error: ret); |
230 | } |
231 | |
232 | static void hi3516cv300_sysctrl_clk_unregister(struct platform_device *pdev) |
233 | { |
234 | struct hisi_crg_dev *crg = platform_get_drvdata(pdev); |
235 | |
236 | of_clk_del_provider(np: pdev->dev.of_node); |
237 | |
238 | hisi_clk_unregister_mux(clks: hi3516cv300_sysctrl_mux_clks, |
239 | ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), |
240 | data: crg->clk_data); |
241 | } |
242 | |
243 | static const struct hisi_crg_funcs hi3516cv300_sysctrl_funcs = { |
244 | .register_clks = hi3516cv300_sysctrl_clk_register, |
245 | .unregister_clks = hi3516cv300_sysctrl_clk_unregister, |
246 | }; |
247 | |
248 | static const struct of_device_id hi3516cv300_crg_match_table[] = { |
249 | { |
250 | .compatible = "hisilicon,hi3516cv300-crg" , |
251 | .data = &hi3516cv300_crg_funcs |
252 | }, |
253 | { |
254 | .compatible = "hisilicon,hi3516cv300-sysctrl" , |
255 | .data = &hi3516cv300_sysctrl_funcs |
256 | }, |
257 | { } |
258 | }; |
259 | MODULE_DEVICE_TABLE(of, hi3516cv300_crg_match_table); |
260 | |
261 | static int hi3516cv300_crg_probe(struct platform_device *pdev) |
262 | { |
263 | struct hisi_crg_dev *crg; |
264 | |
265 | crg = devm_kmalloc(dev: &pdev->dev, size: sizeof(*crg), GFP_KERNEL); |
266 | if (!crg) |
267 | return -ENOMEM; |
268 | |
269 | crg->funcs = of_device_get_match_data(dev: &pdev->dev); |
270 | if (!crg->funcs) |
271 | return -ENOENT; |
272 | |
273 | crg->rstc = hisi_reset_init(pdev); |
274 | if (!crg->rstc) |
275 | return -ENOMEM; |
276 | |
277 | crg->clk_data = crg->funcs->register_clks(pdev); |
278 | if (IS_ERR(ptr: crg->clk_data)) { |
279 | hisi_reset_exit(rstc: crg->rstc); |
280 | return PTR_ERR(ptr: crg->clk_data); |
281 | } |
282 | |
283 | platform_set_drvdata(pdev, data: crg); |
284 | return 0; |
285 | } |
286 | |
287 | static void hi3516cv300_crg_remove(struct platform_device *pdev) |
288 | { |
289 | struct hisi_crg_dev *crg = platform_get_drvdata(pdev); |
290 | |
291 | hisi_reset_exit(rstc: crg->rstc); |
292 | crg->funcs->unregister_clks(pdev); |
293 | } |
294 | |
295 | static struct platform_driver hi3516cv300_crg_driver = { |
296 | .probe = hi3516cv300_crg_probe, |
297 | .remove_new = hi3516cv300_crg_remove, |
298 | .driver = { |
299 | .name = "hi3516cv300-crg" , |
300 | .of_match_table = hi3516cv300_crg_match_table, |
301 | }, |
302 | }; |
303 | |
304 | static int __init hi3516cv300_crg_init(void) |
305 | { |
306 | return platform_driver_register(&hi3516cv300_crg_driver); |
307 | } |
308 | core_initcall(hi3516cv300_crg_init); |
309 | |
310 | static void __exit hi3516cv300_crg_exit(void) |
311 | { |
312 | platform_driver_unregister(&hi3516cv300_crg_driver); |
313 | } |
314 | module_exit(hi3516cv300_crg_exit); |
315 | |
316 | MODULE_LICENSE("GPL v2" ); |
317 | MODULE_DESCRIPTION("HiSilicon Hi3516CV300 CRG Driver" ); |
318 | |