1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Hi3519 Clock Driver |
4 | * |
5 | * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. |
6 | */ |
7 | |
8 | #include <dt-bindings/clock/hi3519-clock.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include "clk.h" |
13 | #include "reset.h" |
14 | |
15 | #define HI3519_INNER_CLK_OFFSET 64 |
16 | #define HI3519_FIXED_24M 65 |
17 | #define HI3519_FIXED_50M 66 |
18 | #define HI3519_FIXED_75M 67 |
19 | #define HI3519_FIXED_125M 68 |
20 | #define HI3519_FIXED_150M 69 |
21 | #define HI3519_FIXED_200M 70 |
22 | #define HI3519_FIXED_250M 71 |
23 | #define HI3519_FIXED_300M 72 |
24 | #define HI3519_FIXED_400M 73 |
25 | #define HI3519_FMC_MUX 74 |
26 | |
27 | #define HI3519_NR_CLKS 128 |
28 | |
29 | struct hi3519_crg_data { |
30 | struct hisi_clock_data *clk_data; |
31 | struct hisi_reset_controller *rstc; |
32 | }; |
33 | |
34 | static const struct hisi_fixed_rate_clock hi3519_fixed_rate_clks[] = { |
35 | { HI3519_FIXED_24M, "24m" , NULL, 0, 24000000, }, |
36 | { HI3519_FIXED_50M, "50m" , NULL, 0, 50000000, }, |
37 | { HI3519_FIXED_75M, "75m" , NULL, 0, 75000000, }, |
38 | { HI3519_FIXED_125M, "125m" , NULL, 0, 125000000, }, |
39 | { HI3519_FIXED_150M, "150m" , NULL, 0, 150000000, }, |
40 | { HI3519_FIXED_200M, "200m" , NULL, 0, 200000000, }, |
41 | { HI3519_FIXED_250M, "250m" , NULL, 0, 250000000, }, |
42 | { HI3519_FIXED_300M, "300m" , NULL, 0, 300000000, }, |
43 | { HI3519_FIXED_400M, "400m" , NULL, 0, 400000000, }, |
44 | }; |
45 | |
46 | static const char *const fmc_mux_p[] = { |
47 | "24m" , "75m" , "125m" , "150m" , "200m" , "250m" , "300m" , "400m" , }; |
48 | static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; |
49 | |
50 | static const struct hisi_mux_clock hi3519_mux_clks[] = { |
51 | { HI3519_FMC_MUX, "fmc_mux" , fmc_mux_p, ARRAY_SIZE(fmc_mux_p), |
52 | CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, |
53 | }; |
54 | |
55 | static const struct hisi_gate_clock hi3519_gate_clks[] = { |
56 | { HI3519_FMC_CLK, "clk_fmc" , "fmc_mux" , |
57 | CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, |
58 | { HI3519_UART0_CLK, "clk_uart0" , "24m" , |
59 | CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, |
60 | { HI3519_UART1_CLK, "clk_uart1" , "24m" , |
61 | CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, |
62 | { HI3519_UART2_CLK, "clk_uart2" , "24m" , |
63 | CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, |
64 | { HI3519_UART3_CLK, "clk_uart3" , "24m" , |
65 | CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, |
66 | { HI3519_UART4_CLK, "clk_uart4" , "24m" , |
67 | CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, |
68 | { HI3519_SPI0_CLK, "clk_spi0" , "50m" , |
69 | CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, |
70 | { HI3519_SPI1_CLK, "clk_spi1" , "50m" , |
71 | CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, |
72 | { HI3519_SPI2_CLK, "clk_spi2" , "50m" , |
73 | CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, |
74 | }; |
75 | |
76 | static struct hisi_clock_data *hi3519_clk_register(struct platform_device *pdev) |
77 | { |
78 | struct hisi_clock_data *clk_data; |
79 | int ret; |
80 | |
81 | clk_data = hisi_clk_alloc(pdev, HI3519_NR_CLKS); |
82 | if (!clk_data) |
83 | return ERR_PTR(error: -ENOMEM); |
84 | |
85 | ret = hisi_clk_register_fixed_rate(hi3519_fixed_rate_clks, |
86 | ARRAY_SIZE(hi3519_fixed_rate_clks), |
87 | clk_data); |
88 | if (ret) |
89 | return ERR_PTR(error: ret); |
90 | |
91 | ret = hisi_clk_register_mux(hi3519_mux_clks, |
92 | ARRAY_SIZE(hi3519_mux_clks), |
93 | clk_data); |
94 | if (ret) |
95 | goto unregister_fixed_rate; |
96 | |
97 | ret = hisi_clk_register_gate(hi3519_gate_clks, |
98 | ARRAY_SIZE(hi3519_gate_clks), |
99 | clk_data); |
100 | if (ret) |
101 | goto unregister_mux; |
102 | |
103 | ret = of_clk_add_provider(np: pdev->dev.of_node, |
104 | clk_src_get: of_clk_src_onecell_get, data: &clk_data->clk_data); |
105 | if (ret) |
106 | goto unregister_gate; |
107 | |
108 | return clk_data; |
109 | |
110 | unregister_fixed_rate: |
111 | hisi_clk_unregister_fixed_rate(clks: hi3519_fixed_rate_clks, |
112 | ARRAY_SIZE(hi3519_fixed_rate_clks), |
113 | data: clk_data); |
114 | |
115 | unregister_mux: |
116 | hisi_clk_unregister_mux(clks: hi3519_mux_clks, |
117 | ARRAY_SIZE(hi3519_mux_clks), |
118 | data: clk_data); |
119 | unregister_gate: |
120 | hisi_clk_unregister_gate(clks: hi3519_gate_clks, |
121 | ARRAY_SIZE(hi3519_gate_clks), |
122 | data: clk_data); |
123 | return ERR_PTR(error: ret); |
124 | } |
125 | |
126 | static void hi3519_clk_unregister(struct platform_device *pdev) |
127 | { |
128 | struct hi3519_crg_data *crg = platform_get_drvdata(pdev); |
129 | |
130 | of_clk_del_provider(np: pdev->dev.of_node); |
131 | |
132 | hisi_clk_unregister_gate(clks: hi3519_gate_clks, |
133 | ARRAY_SIZE(hi3519_mux_clks), |
134 | data: crg->clk_data); |
135 | hisi_clk_unregister_mux(clks: hi3519_mux_clks, |
136 | ARRAY_SIZE(hi3519_mux_clks), |
137 | data: crg->clk_data); |
138 | hisi_clk_unregister_fixed_rate(clks: hi3519_fixed_rate_clks, |
139 | ARRAY_SIZE(hi3519_fixed_rate_clks), |
140 | data: crg->clk_data); |
141 | } |
142 | |
143 | static int hi3519_clk_probe(struct platform_device *pdev) |
144 | { |
145 | struct hi3519_crg_data *crg; |
146 | |
147 | crg = devm_kmalloc(dev: &pdev->dev, size: sizeof(*crg), GFP_KERNEL); |
148 | if (!crg) |
149 | return -ENOMEM; |
150 | |
151 | crg->rstc = hisi_reset_init(pdev); |
152 | if (!crg->rstc) |
153 | return -ENOMEM; |
154 | |
155 | crg->clk_data = hi3519_clk_register(pdev); |
156 | if (IS_ERR(ptr: crg->clk_data)) { |
157 | hisi_reset_exit(rstc: crg->rstc); |
158 | return PTR_ERR(ptr: crg->clk_data); |
159 | } |
160 | |
161 | platform_set_drvdata(pdev, data: crg); |
162 | return 0; |
163 | } |
164 | |
165 | static void hi3519_clk_remove(struct platform_device *pdev) |
166 | { |
167 | struct hi3519_crg_data *crg = platform_get_drvdata(pdev); |
168 | |
169 | hisi_reset_exit(rstc: crg->rstc); |
170 | hi3519_clk_unregister(pdev); |
171 | } |
172 | |
173 | |
174 | static const struct of_device_id hi3519_clk_match_table[] = { |
175 | { .compatible = "hisilicon,hi3519-crg" }, |
176 | { } |
177 | }; |
178 | MODULE_DEVICE_TABLE(of, hi3519_clk_match_table); |
179 | |
180 | static struct platform_driver hi3519_clk_driver = { |
181 | .probe = hi3519_clk_probe, |
182 | .remove_new = hi3519_clk_remove, |
183 | .driver = { |
184 | .name = "hi3519-clk" , |
185 | .of_match_table = hi3519_clk_match_table, |
186 | }, |
187 | }; |
188 | |
189 | static int __init hi3519_clk_init(void) |
190 | { |
191 | return platform_driver_register(&hi3519_clk_driver); |
192 | } |
193 | core_initcall(hi3519_clk_init); |
194 | |
195 | static void __exit hi3519_clk_exit(void) |
196 | { |
197 | platform_driver_unregister(&hi3519_clk_driver); |
198 | } |
199 | module_exit(hi3519_clk_exit); |
200 | |
201 | MODULE_LICENSE("GPL v2" ); |
202 | MODULE_DESCRIPTION("HiSilicon Hi3519 Clock Driver" ); |
203 | |