xref: /openbmc/linux/drivers/clk/clk-loongson2.c (revision 2a809ddc)
1acc0ccffSYinbo Zhu // SPDX-License-Identifier: GPL-2.0+
2acc0ccffSYinbo Zhu /*
3acc0ccffSYinbo Zhu  * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
4acc0ccffSYinbo Zhu  * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
5acc0ccffSYinbo Zhu  */
6acc0ccffSYinbo Zhu 
7acc0ccffSYinbo Zhu #include <linux/err.h>
8acc0ccffSYinbo Zhu #include <linux/init.h>
9acc0ccffSYinbo Zhu #include <linux/clk-provider.h>
10acc0ccffSYinbo Zhu #include <linux/slab.h>
11acc0ccffSYinbo Zhu #include <linux/module.h>
12acc0ccffSYinbo Zhu #include <linux/platform_device.h>
13acc0ccffSYinbo Zhu #include <linux/io-64-nonatomic-lo-hi.h>
14acc0ccffSYinbo Zhu #include <dt-bindings/clock/loongson,ls2k-clk.h>
15acc0ccffSYinbo Zhu 
16acc0ccffSYinbo Zhu #define LOONGSON2_PLL_MULT_SHIFT		32
17acc0ccffSYinbo Zhu #define LOONGSON2_PLL_MULT_WIDTH		10
18acc0ccffSYinbo Zhu #define LOONGSON2_PLL_DIV_SHIFT			26
19acc0ccffSYinbo Zhu #define LOONGSON2_PLL_DIV_WIDTH			6
20acc0ccffSYinbo Zhu #define LOONGSON2_APB_FREQSCALE_SHIFT		20
21acc0ccffSYinbo Zhu #define LOONGSON2_APB_FREQSCALE_WIDTH		3
22acc0ccffSYinbo Zhu #define LOONGSON2_USB_FREQSCALE_SHIFT		16
23acc0ccffSYinbo Zhu #define LOONGSON2_USB_FREQSCALE_WIDTH		3
24acc0ccffSYinbo Zhu #define LOONGSON2_SATA_FREQSCALE_SHIFT		12
25acc0ccffSYinbo Zhu #define LOONGSON2_SATA_FREQSCALE_WIDTH		3
26acc0ccffSYinbo Zhu #define LOONGSON2_BOOT_FREQSCALE_SHIFT		8
27acc0ccffSYinbo Zhu #define LOONGSON2_BOOT_FREQSCALE_WIDTH		3
28acc0ccffSYinbo Zhu 
29acc0ccffSYinbo Zhu static void __iomem *loongson2_pll_base;
30acc0ccffSYinbo Zhu 
31acc0ccffSYinbo Zhu static const struct clk_parent_data pdata[] = {
32acc0ccffSYinbo Zhu 	{ .fw_name = "ref_100m",},
33acc0ccffSYinbo Zhu };
34acc0ccffSYinbo Zhu 
loongson2_clk_register(struct device * dev,const char * name,const char * parent_name,const struct clk_ops * ops,unsigned long flags)35acc0ccffSYinbo Zhu static struct clk_hw *loongson2_clk_register(struct device *dev,
36acc0ccffSYinbo Zhu 					  const char *name,
37acc0ccffSYinbo Zhu 					  const char *parent_name,
38acc0ccffSYinbo Zhu 					  const struct clk_ops *ops,
39acc0ccffSYinbo Zhu 					  unsigned long flags)
40acc0ccffSYinbo Zhu {
41acc0ccffSYinbo Zhu 	int ret;
42acc0ccffSYinbo Zhu 	struct clk_hw *hw;
43*2a809ddcSBinbin Zhou 	struct clk_init_data init = { };
44acc0ccffSYinbo Zhu 
45acc0ccffSYinbo Zhu 	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
46acc0ccffSYinbo Zhu 	if (!hw)
47acc0ccffSYinbo Zhu 		return ERR_PTR(-ENOMEM);
48acc0ccffSYinbo Zhu 
49acc0ccffSYinbo Zhu 	init.name = name;
50acc0ccffSYinbo Zhu 	init.ops = ops;
51acc0ccffSYinbo Zhu 	init.flags = flags;
52acc0ccffSYinbo Zhu 	init.num_parents = 1;
53acc0ccffSYinbo Zhu 
54acc0ccffSYinbo Zhu 	if (!parent_name)
55acc0ccffSYinbo Zhu 		init.parent_data = pdata;
56acc0ccffSYinbo Zhu 	else
57acc0ccffSYinbo Zhu 		init.parent_names = &parent_name;
58acc0ccffSYinbo Zhu 
59acc0ccffSYinbo Zhu 	hw->init = &init;
60acc0ccffSYinbo Zhu 
61acc0ccffSYinbo Zhu 	ret = devm_clk_hw_register(dev, hw);
62acc0ccffSYinbo Zhu 	if (ret)
63acc0ccffSYinbo Zhu 		hw = ERR_PTR(ret);
64acc0ccffSYinbo Zhu 
65acc0ccffSYinbo Zhu 	return hw;
66acc0ccffSYinbo Zhu }
67acc0ccffSYinbo Zhu 
loongson2_calc_pll_rate(int offset,unsigned long rate)68acc0ccffSYinbo Zhu static unsigned long loongson2_calc_pll_rate(int offset, unsigned long rate)
69acc0ccffSYinbo Zhu {
70acc0ccffSYinbo Zhu 	u64 val;
71acc0ccffSYinbo Zhu 	u32 mult, div;
72acc0ccffSYinbo Zhu 
73acc0ccffSYinbo Zhu 	val = readq(loongson2_pll_base + offset);
74acc0ccffSYinbo Zhu 
75acc0ccffSYinbo Zhu 	mult = (val >> LOONGSON2_PLL_MULT_SHIFT) &
76acc0ccffSYinbo Zhu 			clk_div_mask(LOONGSON2_PLL_MULT_WIDTH);
77acc0ccffSYinbo Zhu 	div = (val >> LOONGSON2_PLL_DIV_SHIFT) &
78acc0ccffSYinbo Zhu 			clk_div_mask(LOONGSON2_PLL_DIV_WIDTH);
79acc0ccffSYinbo Zhu 
80acc0ccffSYinbo Zhu 	return div_u64((u64)rate * mult, div);
81acc0ccffSYinbo Zhu }
82acc0ccffSYinbo Zhu 
loongson2_node_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)83acc0ccffSYinbo Zhu static unsigned long loongson2_node_recalc_rate(struct clk_hw *hw,
84acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
85acc0ccffSYinbo Zhu {
86acc0ccffSYinbo Zhu 	return loongson2_calc_pll_rate(0x0, parent_rate);
87acc0ccffSYinbo Zhu }
88acc0ccffSYinbo Zhu 
89acc0ccffSYinbo Zhu static const struct clk_ops loongson2_node_clk_ops = {
90acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_node_recalc_rate,
91acc0ccffSYinbo Zhu };
92acc0ccffSYinbo Zhu 
loongson2_ddr_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)93acc0ccffSYinbo Zhu static unsigned long loongson2_ddr_recalc_rate(struct clk_hw *hw,
94acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
95acc0ccffSYinbo Zhu {
96acc0ccffSYinbo Zhu 	return loongson2_calc_pll_rate(0x10, parent_rate);
97acc0ccffSYinbo Zhu }
98acc0ccffSYinbo Zhu 
99acc0ccffSYinbo Zhu static const struct clk_ops loongson2_ddr_clk_ops = {
100acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_ddr_recalc_rate,
101acc0ccffSYinbo Zhu };
102acc0ccffSYinbo Zhu 
loongson2_dc_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)103acc0ccffSYinbo Zhu static unsigned long loongson2_dc_recalc_rate(struct clk_hw *hw,
104acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
105acc0ccffSYinbo Zhu {
106acc0ccffSYinbo Zhu 	return loongson2_calc_pll_rate(0x20, parent_rate);
107acc0ccffSYinbo Zhu }
108acc0ccffSYinbo Zhu 
109acc0ccffSYinbo Zhu static const struct clk_ops loongson2_dc_clk_ops = {
110acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_dc_recalc_rate,
111acc0ccffSYinbo Zhu };
112acc0ccffSYinbo Zhu 
loongson2_pix0_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)113acc0ccffSYinbo Zhu static unsigned long loongson2_pix0_recalc_rate(struct clk_hw *hw,
114acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
115acc0ccffSYinbo Zhu {
116acc0ccffSYinbo Zhu 	return loongson2_calc_pll_rate(0x30, parent_rate);
117acc0ccffSYinbo Zhu }
118acc0ccffSYinbo Zhu 
119acc0ccffSYinbo Zhu static const struct clk_ops loongson2_pix0_clk_ops = {
120acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_pix0_recalc_rate,
121acc0ccffSYinbo Zhu };
122acc0ccffSYinbo Zhu 
loongson2_pix1_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)123acc0ccffSYinbo Zhu static unsigned long loongson2_pix1_recalc_rate(struct clk_hw *hw,
124acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
125acc0ccffSYinbo Zhu {
126acc0ccffSYinbo Zhu 	return loongson2_calc_pll_rate(0x40, parent_rate);
127acc0ccffSYinbo Zhu }
128acc0ccffSYinbo Zhu 
129acc0ccffSYinbo Zhu static const struct clk_ops loongson2_pix1_clk_ops = {
130acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_pix1_recalc_rate,
131acc0ccffSYinbo Zhu };
132acc0ccffSYinbo Zhu 
loongson2_calc_rate(unsigned long rate,int shift,int width)133acc0ccffSYinbo Zhu static unsigned long loongson2_calc_rate(unsigned long rate,
134acc0ccffSYinbo Zhu 					 int shift, int width)
135acc0ccffSYinbo Zhu {
136acc0ccffSYinbo Zhu 	u64 val;
137acc0ccffSYinbo Zhu 	u32 mult;
138acc0ccffSYinbo Zhu 
139acc0ccffSYinbo Zhu 	val = readq(loongson2_pll_base + 0x50);
140acc0ccffSYinbo Zhu 
141acc0ccffSYinbo Zhu 	mult = (val >> shift) & clk_div_mask(width);
142acc0ccffSYinbo Zhu 
143acc0ccffSYinbo Zhu 	return div_u64((u64)rate * (mult + 1), 8);
144acc0ccffSYinbo Zhu }
145acc0ccffSYinbo Zhu 
loongson2_boot_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)146acc0ccffSYinbo Zhu static unsigned long loongson2_boot_recalc_rate(struct clk_hw *hw,
147acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
148acc0ccffSYinbo Zhu {
149acc0ccffSYinbo Zhu 	return loongson2_calc_rate(parent_rate,
150acc0ccffSYinbo Zhu 				   LOONGSON2_BOOT_FREQSCALE_SHIFT,
151acc0ccffSYinbo Zhu 				   LOONGSON2_BOOT_FREQSCALE_WIDTH);
152acc0ccffSYinbo Zhu }
153acc0ccffSYinbo Zhu 
154acc0ccffSYinbo Zhu static const struct clk_ops loongson2_boot_clk_ops = {
155acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_boot_recalc_rate,
156acc0ccffSYinbo Zhu };
157acc0ccffSYinbo Zhu 
loongson2_apb_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)158acc0ccffSYinbo Zhu static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
159acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
160acc0ccffSYinbo Zhu {
161acc0ccffSYinbo Zhu 	return loongson2_calc_rate(parent_rate,
162acc0ccffSYinbo Zhu 				   LOONGSON2_APB_FREQSCALE_SHIFT,
163acc0ccffSYinbo Zhu 				   LOONGSON2_APB_FREQSCALE_WIDTH);
164acc0ccffSYinbo Zhu }
165acc0ccffSYinbo Zhu 
166acc0ccffSYinbo Zhu static const struct clk_ops loongson2_apb_clk_ops = {
167acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_apb_recalc_rate,
168acc0ccffSYinbo Zhu };
169acc0ccffSYinbo Zhu 
loongson2_usb_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)170acc0ccffSYinbo Zhu static unsigned long loongson2_usb_recalc_rate(struct clk_hw *hw,
171acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
172acc0ccffSYinbo Zhu {
173acc0ccffSYinbo Zhu 	return loongson2_calc_rate(parent_rate,
174acc0ccffSYinbo Zhu 				   LOONGSON2_USB_FREQSCALE_SHIFT,
175acc0ccffSYinbo Zhu 				   LOONGSON2_USB_FREQSCALE_WIDTH);
176acc0ccffSYinbo Zhu }
177acc0ccffSYinbo Zhu 
178acc0ccffSYinbo Zhu static const struct clk_ops loongson2_usb_clk_ops = {
179acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_usb_recalc_rate,
180acc0ccffSYinbo Zhu };
181acc0ccffSYinbo Zhu 
loongson2_sata_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)182acc0ccffSYinbo Zhu static unsigned long loongson2_sata_recalc_rate(struct clk_hw *hw,
183acc0ccffSYinbo Zhu 					  unsigned long parent_rate)
184acc0ccffSYinbo Zhu {
185acc0ccffSYinbo Zhu 	return loongson2_calc_rate(parent_rate,
186acc0ccffSYinbo Zhu 				   LOONGSON2_SATA_FREQSCALE_SHIFT,
187acc0ccffSYinbo Zhu 				   LOONGSON2_SATA_FREQSCALE_WIDTH);
188acc0ccffSYinbo Zhu }
189acc0ccffSYinbo Zhu 
190acc0ccffSYinbo Zhu static const struct clk_ops loongson2_sata_clk_ops = {
191acc0ccffSYinbo Zhu 	.recalc_rate = loongson2_sata_recalc_rate,
192acc0ccffSYinbo Zhu };
193acc0ccffSYinbo Zhu 
loongson2_check_clk_hws(struct clk_hw * clks[],unsigned int count)194acc0ccffSYinbo Zhu static inline int loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
195acc0ccffSYinbo Zhu {
196acc0ccffSYinbo Zhu 	unsigned int i;
197acc0ccffSYinbo Zhu 
198acc0ccffSYinbo Zhu 	for (i = 0; i < count; i++)
199acc0ccffSYinbo Zhu 		if (IS_ERR(clks[i])) {
200acc0ccffSYinbo Zhu 			pr_err("Loongson2 clk %u: register failed with %ld\n",
201acc0ccffSYinbo Zhu 				i, PTR_ERR(clks[i]));
202acc0ccffSYinbo Zhu 			return PTR_ERR(clks[i]);
203acc0ccffSYinbo Zhu 		}
204acc0ccffSYinbo Zhu 
205acc0ccffSYinbo Zhu 	return 0;
206acc0ccffSYinbo Zhu }
207acc0ccffSYinbo Zhu 
loongson2_clk_probe(struct platform_device * pdev)208acc0ccffSYinbo Zhu static int loongson2_clk_probe(struct platform_device *pdev)
209acc0ccffSYinbo Zhu {
210acc0ccffSYinbo Zhu 	int ret;
211acc0ccffSYinbo Zhu 	struct clk_hw **hws;
212acc0ccffSYinbo Zhu 	struct clk_hw_onecell_data *clk_hw_data;
213acc0ccffSYinbo Zhu 	spinlock_t loongson2_clk_lock;
214acc0ccffSYinbo Zhu 	struct device *dev = &pdev->dev;
215acc0ccffSYinbo Zhu 
216acc0ccffSYinbo Zhu 	loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
217acc0ccffSYinbo Zhu 	if (IS_ERR(loongson2_pll_base))
218acc0ccffSYinbo Zhu 		return PTR_ERR(loongson2_pll_base);
219acc0ccffSYinbo Zhu 
220acc0ccffSYinbo Zhu 	clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
221acc0ccffSYinbo Zhu 					GFP_KERNEL);
222acc0ccffSYinbo Zhu 	if (WARN_ON(!clk_hw_data))
223acc0ccffSYinbo Zhu 		return -ENOMEM;
224acc0ccffSYinbo Zhu 
225acc0ccffSYinbo Zhu 	clk_hw_data->num = LOONGSON2_CLK_END;
226acc0ccffSYinbo Zhu 	hws = clk_hw_data->hws;
227acc0ccffSYinbo Zhu 
228acc0ccffSYinbo Zhu 	hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
229acc0ccffSYinbo Zhu 						NULL,
230acc0ccffSYinbo Zhu 						&loongson2_node_clk_ops, 0);
231acc0ccffSYinbo Zhu 
232acc0ccffSYinbo Zhu 	hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
233acc0ccffSYinbo Zhu 						NULL,
234acc0ccffSYinbo Zhu 						&loongson2_ddr_clk_ops, 0);
235acc0ccffSYinbo Zhu 
236acc0ccffSYinbo Zhu 	hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
237acc0ccffSYinbo Zhu 						NULL,
238acc0ccffSYinbo Zhu 						&loongson2_dc_clk_ops, 0);
239acc0ccffSYinbo Zhu 
240acc0ccffSYinbo Zhu 	hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
241acc0ccffSYinbo Zhu 						NULL,
242acc0ccffSYinbo Zhu 						&loongson2_pix0_clk_ops, 0);
243acc0ccffSYinbo Zhu 
244acc0ccffSYinbo Zhu 	hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
245acc0ccffSYinbo Zhu 						NULL,
246acc0ccffSYinbo Zhu 						&loongson2_pix1_clk_ops, 0);
247acc0ccffSYinbo Zhu 
248acc0ccffSYinbo Zhu 	hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
249acc0ccffSYinbo Zhu 						NULL,
250acc0ccffSYinbo Zhu 						&loongson2_boot_clk_ops, 0);
251acc0ccffSYinbo Zhu 
252acc0ccffSYinbo Zhu 	hws[LOONGSON2_NODE_CLK] = devm_clk_hw_register_divider(dev, "node",
253acc0ccffSYinbo Zhu 						"node_pll", 0,
254acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x8, 0,
255acc0ccffSYinbo Zhu 						6, CLK_DIVIDER_ONE_BASED,
256acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
257acc0ccffSYinbo Zhu 
258acc0ccffSYinbo Zhu 	/*
259acc0ccffSYinbo Zhu 	 * The hda clk divisor in the upper 32bits and the clk-prodiver
260acc0ccffSYinbo Zhu 	 * layer code doesn't support 64bit io operation thus a conversion
261acc0ccffSYinbo Zhu 	 * is required that subtract shift by 32 and add 4byte to the hda
262acc0ccffSYinbo Zhu 	 * address
263acc0ccffSYinbo Zhu 	 */
264acc0ccffSYinbo Zhu 	hws[LOONGSON2_HDA_CLK] = devm_clk_hw_register_divider(dev, "hda",
265acc0ccffSYinbo Zhu 						"ddr_pll", 0,
266acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x22, 12,
267acc0ccffSYinbo Zhu 						7, CLK_DIVIDER_ONE_BASED,
268acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
269acc0ccffSYinbo Zhu 
270acc0ccffSYinbo Zhu 	hws[LOONGSON2_GPU_CLK] = devm_clk_hw_register_divider(dev, "gpu",
271acc0ccffSYinbo Zhu 						"ddr_pll", 0,
272acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x18, 22,
273acc0ccffSYinbo Zhu 						6, CLK_DIVIDER_ONE_BASED,
274acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
275acc0ccffSYinbo Zhu 
276acc0ccffSYinbo Zhu 	hws[LOONGSON2_DDR_CLK] = devm_clk_hw_register_divider(dev, "ddr",
277acc0ccffSYinbo Zhu 						"ddr_pll", 0,
278acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x18, 0,
279acc0ccffSYinbo Zhu 						6, CLK_DIVIDER_ONE_BASED,
280acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
281acc0ccffSYinbo Zhu 
282acc0ccffSYinbo Zhu 	hws[LOONGSON2_GMAC_CLK] = devm_clk_hw_register_divider(dev, "gmac",
283acc0ccffSYinbo Zhu 						"dc_pll", 0,
284acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x28, 22,
285acc0ccffSYinbo Zhu 						6, CLK_DIVIDER_ONE_BASED,
286acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
287acc0ccffSYinbo Zhu 
288acc0ccffSYinbo Zhu 	hws[LOONGSON2_DC_CLK] = devm_clk_hw_register_divider(dev, "dc",
289acc0ccffSYinbo Zhu 						"dc_pll", 0,
290acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x28, 0,
291acc0ccffSYinbo Zhu 						6, CLK_DIVIDER_ONE_BASED,
292acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
293acc0ccffSYinbo Zhu 
294acc0ccffSYinbo Zhu 	hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
295acc0ccffSYinbo Zhu 						"gmac",
296acc0ccffSYinbo Zhu 						&loongson2_apb_clk_ops, 0);
297acc0ccffSYinbo Zhu 
298acc0ccffSYinbo Zhu 	hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
299acc0ccffSYinbo Zhu 						"gmac",
300acc0ccffSYinbo Zhu 						&loongson2_usb_clk_ops, 0);
301acc0ccffSYinbo Zhu 
302acc0ccffSYinbo Zhu 	hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
303acc0ccffSYinbo Zhu 						"gmac",
304acc0ccffSYinbo Zhu 						&loongson2_sata_clk_ops, 0);
305acc0ccffSYinbo Zhu 
306acc0ccffSYinbo Zhu 	hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
307acc0ccffSYinbo Zhu 						"pix0_pll", 0,
308acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x38, 0, 6,
309acc0ccffSYinbo Zhu 						CLK_DIVIDER_ONE_BASED,
310acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
311acc0ccffSYinbo Zhu 
312acc0ccffSYinbo Zhu 	hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
313acc0ccffSYinbo Zhu 						"pix1_pll", 0,
314acc0ccffSYinbo Zhu 						loongson2_pll_base + 0x48, 0, 6,
315acc0ccffSYinbo Zhu 						CLK_DIVIDER_ONE_BASED,
316acc0ccffSYinbo Zhu 						&loongson2_clk_lock);
317acc0ccffSYinbo Zhu 
318acc0ccffSYinbo Zhu 	ret = loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
319acc0ccffSYinbo Zhu 	if (ret)
320acc0ccffSYinbo Zhu 		return ret;
321acc0ccffSYinbo Zhu 
322acc0ccffSYinbo Zhu 	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
323acc0ccffSYinbo Zhu }
324acc0ccffSYinbo Zhu 
325acc0ccffSYinbo Zhu static const struct of_device_id loongson2_clk_match_table[] = {
326acc0ccffSYinbo Zhu 	{ .compatible = "loongson,ls2k-clk" },
327acc0ccffSYinbo Zhu 	{ }
328acc0ccffSYinbo Zhu };
329acc0ccffSYinbo Zhu MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
330acc0ccffSYinbo Zhu 
331acc0ccffSYinbo Zhu static struct platform_driver loongson2_clk_driver = {
332acc0ccffSYinbo Zhu 	.probe	= loongson2_clk_probe,
333acc0ccffSYinbo Zhu 	.driver	= {
334acc0ccffSYinbo Zhu 		.name	= "loongson2-clk",
335acc0ccffSYinbo Zhu 		.of_match_table	= loongson2_clk_match_table,
336acc0ccffSYinbo Zhu 	},
337acc0ccffSYinbo Zhu };
338acc0ccffSYinbo Zhu module_platform_driver(loongson2_clk_driver);
339acc0ccffSYinbo Zhu 
340acc0ccffSYinbo Zhu MODULE_DESCRIPTION("Loongson2 clock driver");
341acc0ccffSYinbo Zhu MODULE_LICENSE("GPL");
342