xref: /linux/drivers/clk/clk-loongson2.c (revision 522ba450b56fff29f868b1552bdc2965f55de7ed)
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