1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
4 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
5 */
6
7 #include <linux/err.h>
8 #include <linux/init.h>
9 #include <linux/clk-provider.h>
10 #include <linux/slab.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/io-64-nonatomic-lo-hi.h>
14 #include <dt-bindings/clock/loongson,ls2k-clk.h>
15
16 enum loongson2_clk_type {
17 CLK_TYPE_PLL,
18 CLK_TYPE_SCALE,
19 CLK_TYPE_DIVIDER,
20 CLK_TYPE_GATE,
21 CLK_TYPE_FIXED,
22 CLK_TYPE_NONE,
23 };
24
25 struct loongson2_clk_provider {
26 void __iomem *base;
27 struct device *dev;
28 spinlock_t clk_lock; /* protect access to DIV registers */
29
30 /* Must be last --ends in a flexible-array member. */
31 struct clk_hw_onecell_data clk_data;
32 };
33
34 struct loongson2_clk_data {
35 struct clk_hw hw;
36 void __iomem *reg;
37 u8 div_shift;
38 u8 div_width;
39 u8 mult_shift;
40 u8 mult_width;
41 u8 bit_idx;
42 };
43
44 struct loongson2_clk_board_info {
45 u8 id;
46 enum loongson2_clk_type type;
47 const char *name;
48 const char *parent_name;
49 unsigned long fixed_rate;
50 unsigned long flags;
51 u8 reg_offset;
52 u8 div_shift;
53 u8 div_width;
54 u8 mult_shift;
55 u8 mult_width;
56 u8 bit_idx;
57 };
58
59 #define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth) \
60 { \
61 .id = _id, \
62 .type = CLK_TYPE_DIVIDER, \
63 .name = _name, \
64 .parent_name = _pname, \
65 .reg_offset = _offset, \
66 .div_shift = _dshift, \
67 .div_width = _dwidth, \
68 }
69
70 #define CLK_PLL(_id, _name, _offset, _mshift, _mwidth, \
71 _dshift, _dwidth) \
72 { \
73 .id = _id, \
74 .type = CLK_TYPE_PLL, \
75 .name = _name, \
76 .parent_name = NULL, \
77 .reg_offset = _offset, \
78 .mult_shift = _mshift, \
79 .mult_width = _mwidth, \
80 .div_shift = _dshift, \
81 .div_width = _dwidth, \
82 }
83
84 #define CLK_SCALE(_id, _name, _pname, _offset, \
85 _dshift, _dwidth) \
86 { \
87 .id = _id, \
88 .type = CLK_TYPE_SCALE, \
89 .name = _name, \
90 .parent_name = _pname, \
91 .reg_offset = _offset, \
92 .div_shift = _dshift, \
93 .div_width = _dwidth, \
94 }
95
96 #define CLK_SCALE_MODE(_id, _name, _pname, _offset, \
97 _dshift, _dwidth, _midx) \
98 { \
99 .id = _id, \
100 .type = CLK_TYPE_SCALE, \
101 .name = _name, \
102 .parent_name = _pname, \
103 .reg_offset = _offset, \
104 .div_shift = _dshift, \
105 .div_width = _dwidth, \
106 .bit_idx = _midx + 1, \
107 }
108
109 #define CLK_GATE(_id, _name, _pname, _offset, _bidx) \
110 { \
111 .id = _id, \
112 .type = CLK_TYPE_GATE, \
113 .name = _name, \
114 .parent_name = _pname, \
115 .reg_offset = _offset, \
116 .bit_idx = _bidx, \
117 }
118
119 #define CLK_GATE_FLAGS(_id, _name, _pname, _offset, _bidx, \
120 _flags) \
121 { \
122 .id = _id, \
123 .type = CLK_TYPE_GATE, \
124 .name = _name, \
125 .parent_name = _pname, \
126 .reg_offset = _offset, \
127 .bit_idx = _bidx, \
128 .flags = _flags \
129 }
130
131 #define CLK_FIXED(_id, _name, _pname, _rate) \
132 { \
133 .id = _id, \
134 .type = CLK_TYPE_FIXED, \
135 .name = _name, \
136 .parent_name = _pname, \
137 .fixed_rate = _rate, \
138 }
139
140 static const struct loongson2_clk_board_info ls2k0300_clks[] = {
141 /* Reference Clock */
142 CLK_PLL(LS2K0300_NODE_PLL, "pll_node", 0x00, 15, 9, 8, 7),
143 CLK_PLL(LS2K0300_DDR_PLL, "pll_ddr", 0x08, 15, 9, 8, 7),
144 CLK_PLL(LS2K0300_PIX_PLL, "pll_pix", 0x10, 15, 9, 8, 7),
145 CLK_FIXED(LS2K0300_CLK_STABLE, "clk_stable", NULL, 100000000),
146 CLK_FIXED(LS2K0300_CLK_THSENS, "clk_thsens", NULL, 10000000),
147 /* Node PLL */
148 CLK_DIV(LS2K0300_CLK_NODE_DIV, "clk_node_div", "pll_node", 0x00, 24, 7),
149 CLK_DIV(LS2K0300_CLK_GMAC_DIV, "clk_gmac_div", "pll_node", 0x04, 0, 7),
150 CLK_DIV(LS2K0300_CLK_I2S_DIV, "clk_i2s_div", "pll_node", 0x04, 8, 7),
151 CLK_GATE(LS2K0300_CLK_NODE_PLL_GATE, "clk_node_pll_gate", "clk_node_div", 0x00, 0),
152 CLK_GATE(LS2K0300_CLK_GMAC_GATE, "clk_gmac_gate", "clk_gmac_div", 0x00, 1),
153 CLK_GATE(LS2K0300_CLK_I2S_GATE, "clk_i2s_gate", "clk_i2s_div", 0x00, 2),
154 CLK_GATE_FLAGS(LS2K0300_CLK_NODE_GATE, "clk_node_gate", "clk_node_scale", 0x24, 0,
155 CLK_IS_CRITICAL),
156 CLK_SCALE_MODE(LS2K0300_CLK_NODE_SCALE, "clk_node_scale", "clk_node_pll_gate", 0x20, 0, 3,
157 3),
158 /* DDR PLL */
159 CLK_DIV(LS2K0300_CLK_DDR_DIV, "clk_ddr_div", "pll_ddr", 0x08, 24, 7),
160 CLK_DIV(LS2K0300_CLK_NET_DIV, "clk_net_div", "pll_ddr", 0x0c, 0, 7),
161 CLK_DIV(LS2K0300_CLK_DEV_DIV, "clk_dev_div", "pll_ddr", 0x0c, 8, 7),
162 CLK_GATE(LS2K0300_CLK_NET_GATE, "clk_net_gate", "clk_net_div", 0x08, 1),
163 CLK_GATE(LS2K0300_CLK_DEV_GATE, "clk_dev_gate", "clk_dev_div", 0x08, 2),
164 CLK_GATE_FLAGS(LS2K0300_CLK_DDR_GATE, "clk_ddr_gate", "clk_ddr_div", 0x08, 0,
165 CLK_IS_CRITICAL),
166 /* PIX PLL */
167 CLK_DIV(LS2K0300_CLK_PIX_DIV, "clk_pix_div", "pll_pix", 0x10, 24, 7),
168 CLK_DIV(LS2K0300_CLK_GMACBP_DIV, "clk_gmacbp_div", "pll_pix", 0x14, 0, 7),
169 CLK_GATE(LS2K0300_CLK_PIX_PLL_GATE, "clk_pix_pll_gate", "clk_pix_div", 0x10, 0),
170 CLK_GATE(LS2K0300_CLK_PIX_GATE, "clk_pix_gate", "clk_pix_scale", 0x24, 6),
171 CLK_GATE(LS2K0300_CLK_GMACBP_GATE, "clk_gmacbp_gate", "clk_gmacbp_div", 0x10, 1),
172 CLK_SCALE_MODE(LS2K0300_CLK_PIX_SCALE, "clk_pix_scale", "clk_pix_pll_gate", 0x20, 4, 3, 7),
173 /* clk_dev_gate */
174 CLK_DIV(LS2K0300_CLK_SDIO_SCALE, "clk_sdio_scale", "clk_dev_gate", 0x20, 24, 4),
175 CLK_GATE(LS2K0300_CLK_USB_GATE, "clk_usb_gate", "clk_usb_scale", 0x24, 2),
176 CLK_GATE(LS2K0300_CLK_SDIO_GATE, "clk_sdio_gate", "clk_sdio_scale", 0x24, 4),
177 CLK_GATE(LS2K0300_CLK_APB_GATE, "clk_apb_gate", "clk_apb_scale", 0x24, 3),
178 CLK_GATE_FLAGS(LS2K0300_CLK_BOOT_GATE, "clk_boot_gate", "clk_boot_scale", 0x24, 1,
179 CLK_IS_CRITICAL),
180 CLK_SCALE_MODE(LS2K0300_CLK_USB_SCALE, "clk_usb_scale", "clk_dev_gate", 0x20, 12, 3, 15),
181 CLK_SCALE_MODE(LS2K0300_CLK_APB_SCALE, "clk_apb_scale", "clk_dev_gate", 0x20, 16, 3, 19),
182 CLK_SCALE_MODE(LS2K0300_CLK_BOOT_SCALE, "clk_boot_scale", "clk_dev_gate", 0x20, 8, 3, 11),
183 };
184
185 static const struct loongson2_clk_board_info ls2k0500_clks[] = {
186 CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 16, 8, 8, 6),
187 CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x8, 16, 8, 8, 6),
188 CLK_PLL(LOONGSON2_DC_PLL, "pll_soc", 0x10, 16, 8, 8, 6),
189 CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x18, 16, 8, 8, 6),
190 CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x20, 16, 8, 8, 6),
191 CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0, 24, 6),
192 CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x8, 24, 6),
193 CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0xc, 8, 6),
194 CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_soc", 0x10, 24, 6),
195 CLK_DIV(LOONGSON2_DC_CLK, "clk_sb", "pll_soc", 0x14, 0, 6),
196 CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_soc", 0x14, 8, 6),
197 CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x18, 24, 6),
198 CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x20, 24, 6),
199 CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb", 0x28, 8, 3),
200 CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb", 0x28, 12, 3),
201 CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_sb", 0x28, 16, 3),
202 CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_sb", 0x28, 20, 3),
203 { /* Sentinel */ },
204 };
205
206 static const struct loongson2_clk_board_info ls2k1000_clks[] = {
207 CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 32, 10, 26, 6),
208 CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x10, 32, 10, 26, 6),
209 CLK_PLL(LOONGSON2_DC_PLL, "pll_dc", 0x20, 32, 10, 26, 6),
210 CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 32, 10, 26, 6),
211 CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 32, 10, 26, 6),
212 CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0x8, 0, 6),
213 CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x18, 0, 6),
214 CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_ddr", 0x18, 22, 6),
215 /*
216 * The hda clk divisor in the upper 32bits and the clk-prodiver
217 * layer code doesn't support 64bit io operation thus a conversion
218 * is required that subtract shift by 32 and add 4byte to the hda
219 * address
220 */
221 CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0x22, 12, 7),
222 CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "pll_dc", 0x28, 0, 6),
223 CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_dc", 0x28, 22, 6),
224 CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x38, 0, 6),
225 CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x38, 0, 6),
226 CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 8, 3),
227 CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
228 CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_gmac", 0x50, 16, 3),
229 CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_gmac", 0x50, 20, 3),
230 { /* Sentinel */ },
231 };
232
233 static const struct loongson2_clk_board_info ls2k2000_clks[] = {
234 CLK_PLL(LOONGSON2_DC_PLL, "pll_0", 0, 21, 9, 32, 6),
235 CLK_PLL(LOONGSON2_DDR_PLL, "pll_1", 0x10, 21, 9, 32, 6),
236 CLK_PLL(LOONGSON2_NODE_PLL, "pll_2", 0x20, 21, 9, 32, 6),
237 CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 21, 9, 32, 6),
238 CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 21, 9, 32, 6),
239 CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0", 0, 40),
240 CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0", 0, 41),
241 CLK_GATE(LOONGSON2_RIO_GATE, "rio_gate", "pll_0", 0, 42),
242 CLK_GATE(LOONGSON2_DC_GATE, "dc_gate", "pll_1", 0x10, 40),
243 CLK_GATE(LOONGSON2_DDR_GATE, "ddr_gate", "pll_1", 0x10, 41),
244 CLK_GATE(LOONGSON2_GPU_GATE, "gpu_gate", "pll_1", 0x10, 42),
245 CLK_GATE(LOONGSON2_HDA_GATE, "hda_gate", "pll_2", 0x20, 40),
246 CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2", 0x20, 41),
247 CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2", 0x20, 42),
248 CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),
249 CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),
250 CLK_DIV(LOONGSON2_OUT0_CLK, "clk_out0", "out0_gate", 0, 0, 6),
251 CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "gmac_gate", 0, 7, 6),
252 CLK_DIV(LOONGSON2_RIO_CLK, "clk_rio", "rio_gate", 0, 14, 6),
253 CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "dc_gate", 0x10, 0, 6),
254 CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "gpu_gate", 0x10, 7, 6),
255 CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "ddr_gate", 0x10, 14, 6),
256 CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "hda_gate", 0x20, 0, 6),
257 CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "node_gate", 0x20, 7, 6),
258 CLK_DIV(LOONGSON2_EMMC_CLK, "clk_emmc", "emmc_gate", 0x20, 14, 6),
259 CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x30, 0, 6),
260 CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x40, 0, 6),
261 CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0", 0x50, 12, 3),
262 CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_out0", 0x50, 16, 3),
263 CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_node", 0x50, 20, 3),
264 CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 23, 3),
265 CLK_SCALE(LOONGSON2_DES_CLK, "clk_des", "clk_node", 0x50, 40, 3),
266 CLK_SCALE(LOONGSON2_I2S_CLK, "clk_i2s", "clk_node", 0x50, 44, 3),
267 CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),
268 { /* Sentinel */ },
269 };
270
to_loongson2_clk(struct clk_hw * hw)271 static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
272 {
273 return container_of(hw, struct loongson2_clk_data, hw);
274 }
275
loongson2_rate_part(u64 val,u8 shift,u8 width)276 static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
277 {
278 return (val & GENMASK(shift + width - 1, shift)) >> shift;
279 }
280
loongson2_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)281 static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
282 unsigned long parent_rate)
283 {
284 u64 val, mult, div;
285 struct loongson2_clk_data *clk = to_loongson2_clk(hw);
286
287 val = readq(clk->reg);
288 mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
289 div = loongson2_rate_part(val, clk->div_shift, clk->div_width);
290
291 return div_u64((u64)parent_rate * mult, div);
292 }
293
294 static const struct clk_ops loongson2_pll_recalc_ops = {
295 .recalc_rate = loongson2_pll_recalc_rate,
296 };
297
loongson2_freqscale_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)298 static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
299 unsigned long parent_rate)
300 {
301 u64 val, scale;
302 u32 mode = 0;
303 struct loongson2_clk_data *clk = to_loongson2_clk(hw);
304
305 val = readq(clk->reg);
306 scale = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
307
308 if (clk->bit_idx)
309 mode = val & BIT(clk->bit_idx - 1);
310
311 return mode == 0 ? div_u64((u64)parent_rate * scale, 8) :
312 div_u64((u64)parent_rate, scale);
313 }
314
315 static const struct clk_ops loongson2_freqscale_recalc_ops = {
316 .recalc_rate = loongson2_freqscale_recalc_rate,
317 };
318
loongson2_clk_register(const char * parent,struct loongson2_clk_provider * clp,const struct loongson2_clk_board_info * cld,const struct clk_ops * ops)319 static struct clk_hw *loongson2_clk_register(const char *parent,
320 struct loongson2_clk_provider *clp,
321 const struct loongson2_clk_board_info *cld,
322 const struct clk_ops *ops)
323 {
324 int ret;
325 struct clk_hw *hw;
326 struct loongson2_clk_data *clk;
327 struct clk_init_data init = { };
328
329 clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
330 if (!clk)
331 return ERR_PTR(-ENOMEM);
332
333 init.name = cld->name;
334 init.ops = ops;
335 init.flags = 0;
336 init.num_parents = 1;
337 init.parent_names = &parent;
338
339 clk->reg = clp->base + cld->reg_offset;
340 clk->div_shift = cld->div_shift;
341 clk->div_width = cld->div_width;
342 clk->mult_shift = cld->mult_shift;
343 clk->mult_width = cld->mult_width;
344 clk->bit_idx = cld->bit_idx;
345 clk->hw.init = &init;
346
347 hw = &clk->hw;
348 ret = devm_clk_hw_register(clp->dev, hw);
349 if (ret)
350 clk = ERR_PTR(ret);
351
352 return hw;
353 }
354
loongson2_clk_probe(struct platform_device * pdev)355 static int loongson2_clk_probe(struct platform_device *pdev)
356 {
357 int i, clks_num = 0;
358 struct clk_hw *hw;
359 struct device *dev = &pdev->dev;
360 struct loongson2_clk_provider *clp;
361 const struct loongson2_clk_board_info *p, *data;
362 const char *refclk_name, *parent_name;
363
364 data = device_get_match_data(dev);
365 if (!data)
366 return -EINVAL;
367
368 refclk_name = of_clk_get_parent_name(dev->of_node, 0);
369 if (IS_ERR(refclk_name))
370 return dev_err_probe(dev, PTR_ERR(refclk_name),
371 "failed to get refclk name\n");
372
373 for (p = data; p->name; p++)
374 clks_num = max(clks_num, p->id + 1);
375
376 clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
377 GFP_KERNEL);
378 if (!clp)
379 return -ENOMEM;
380
381 clp->base = devm_platform_ioremap_resource(pdev, 0);
382 if (IS_ERR(clp->base))
383 return PTR_ERR(clp->base);
384
385 spin_lock_init(&clp->clk_lock);
386 clp->clk_data.num = clks_num;
387 clp->dev = dev;
388
389 /* Avoid returning NULL for unused id */
390 memset_p((void **)clp->clk_data.hws, ERR_PTR(-ENOENT), clks_num);
391
392 for (i = 0; i < clks_num; i++) {
393 p = &data[i];
394 parent_name = p->parent_name ? p->parent_name : refclk_name;
395
396 switch (p->type) {
397 case CLK_TYPE_PLL:
398 hw = loongson2_clk_register(parent_name, clp, p,
399 &loongson2_pll_recalc_ops);
400 break;
401 case CLK_TYPE_SCALE:
402 hw = loongson2_clk_register(parent_name, clp, p,
403 &loongson2_freqscale_recalc_ops);
404 break;
405 case CLK_TYPE_DIVIDER:
406 hw = devm_clk_hw_register_divider(dev, p->name,
407 parent_name, 0,
408 clp->base + p->reg_offset,
409 p->div_shift, p->div_width,
410 CLK_DIVIDER_ONE_BASED |
411 CLK_DIVIDER_ALLOW_ZERO,
412 &clp->clk_lock);
413 break;
414 case CLK_TYPE_GATE:
415 hw = devm_clk_hw_register_gate(dev, p->name, parent_name,
416 p->flags,
417 clp->base + p->reg_offset,
418 p->bit_idx, 0,
419 &clp->clk_lock);
420 break;
421 case CLK_TYPE_FIXED:
422 hw = devm_clk_hw_register_fixed_rate(dev, p->name, parent_name,
423 0, p->fixed_rate);
424 break;
425 default:
426 return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
427 }
428
429 if (IS_ERR(hw))
430 return dev_err_probe(dev, PTR_ERR(hw),
431 "Register clk: %s, type: %u failed!\n",
432 p->name, p->type);
433
434 clp->clk_data.hws[p->id] = hw;
435 }
436
437 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
438 }
439
440 static const struct of_device_id loongson2_clk_match_table[] = {
441 { .compatible = "loongson,ls2k0300-clk", .data = &ls2k0300_clks },
442 { .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },
443 { .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
444 { .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },
445 { }
446 };
447 MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
448
449 static struct platform_driver loongson2_clk_driver = {
450 .probe = loongson2_clk_probe,
451 .driver = {
452 .name = "loongson2-clk",
453 .of_match_table = loongson2_clk_match_table,
454 },
455 };
456 module_platform_driver(loongson2_clk_driver);
457
458 MODULE_DESCRIPTION("Loongson2 clock driver");
459 MODULE_AUTHOR("Loongson Technology Corporation Limited");
460 MODULE_LICENSE("GPL");
461