Linux Audio

Check our new training course

Loading...
v6.13.7
  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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 16static const struct clk_parent_data pdata[] = {
 17	{ .fw_name = "ref_100m", },
 18};
 19
 20enum loongson2_clk_type {
 21	CLK_TYPE_PLL,
 22	CLK_TYPE_SCALE,
 23	CLK_TYPE_DIVIDER,
 24	CLK_TYPE_GATE,
 25	CLK_TYPE_FIXED,
 26	CLK_TYPE_NONE,
 27};
 28
 29struct loongson2_clk_provider {
 30	void __iomem *base;
 31	struct device *dev;
 32	spinlock_t clk_lock;	/* protect access to DIV registers */
 33
 34	/* Must be last --ends in a flexible-array member. */
 35	struct clk_hw_onecell_data clk_data;
 36};
 37
 38struct loongson2_clk_data {
 39	struct clk_hw hw;
 40	void __iomem *reg;
 41	u8 div_shift;
 42	u8 div_width;
 43	u8 mult_shift;
 44	u8 mult_width;
 45};
 46
 47struct loongson2_clk_board_info {
 48	u8 id;
 49	enum loongson2_clk_type type;
 50	const char *name;
 51	const char *parent_name;
 52	unsigned long fixed_rate;
 53	u8 reg_offset;
 54	u8 div_shift;
 55	u8 div_width;
 56	u8 mult_shift;
 57	u8 mult_width;
 58	u8 bit_idx;
 59};
 60
 61#define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth)	\
 62	{							\
 63		.id		= _id,				\
 64		.type		= CLK_TYPE_DIVIDER,		\
 65		.name		= _name,			\
 66		.parent_name	= _pname,			\
 67		.reg_offset	= _offset,			\
 68		.div_shift	= _dshift,			\
 69		.div_width	= _dwidth,			\
 70	}
 71
 72#define CLK_PLL(_id, _name, _offset, _mshift, _mwidth,		\
 73		_dshift, _dwidth)				\
 74	{							\
 75		.id		= _id,				\
 76		.type		= CLK_TYPE_PLL,			\
 77		.name		= _name,			\
 78		.parent_name	= NULL,				\
 79		.reg_offset	= _offset,			\
 80		.mult_shift	= _mshift,			\
 81		.mult_width	= _mwidth,			\
 82		.div_shift	= _dshift,			\
 83		.div_width	= _dwidth,			\
 84	}
 85
 86#define CLK_SCALE(_id, _name, _pname, _offset,			\
 87		  _dshift, _dwidth)				\
 88	{							\
 89		.id		= _id,				\
 90		.type		= CLK_TYPE_SCALE,		\
 91		.name		= _name,			\
 92		.parent_name	= _pname,			\
 93		.reg_offset	= _offset,			\
 94		.div_shift	= _dshift,			\
 95		.div_width	= _dwidth,			\
 96	}
 97
 98#define CLK_GATE(_id, _name, _pname, _offset, _bidx)		\
 99	{							\
100		.id		= _id,				\
101		.type		= CLK_TYPE_GATE,		\
102		.name		= _name,			\
103		.parent_name	= _pname,			\
104		.reg_offset	= _offset,			\
105		.bit_idx	= _bidx,			\
106	}
107
108#define CLK_FIXED(_id, _name, _pname, _rate)			\
109	{							\
110		.id		= _id,				\
111		.type		= CLK_TYPE_FIXED,		\
112		.name		= _name,			\
113		.parent_name	= _pname,			\
114		.fixed_rate	= _rate,			\
115	}
116
117static const struct loongson2_clk_board_info ls2k0500_clks[] = {
118	CLK_PLL(LOONGSON2_NODE_PLL,   "pll_node", 0,    16, 8, 8, 6),
119	CLK_PLL(LOONGSON2_DDR_PLL,    "pll_ddr",  0x8,  16, 8, 8, 6),
120	CLK_PLL(LOONGSON2_DC_PLL,     "pll_soc",  0x10, 16, 8, 8, 6),
121	CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x18, 16, 8, 8, 6),
122	CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x20, 16, 8, 8, 6),
123	CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "pll_node", 0,    24, 6),
124	CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "pll_ddr",  0x8,  24, 6),
125	CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "pll_ddr",  0xc,  8,  6),
126	CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "pll_soc",  0x10, 24, 6),
127	CLK_DIV(LOONGSON2_DC_CLK,     "clk_sb",   "pll_soc",  0x14, 0,  6),
128	CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "pll_soc",  0x14, 8,  6),
129	CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0", 0x18, 24, 6),
130	CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1", 0x20, 24, 6),
131	CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb",   0x28, 8,  3),
132	CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb",   0x28, 12, 3),
133	CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_sb",   0x28, 16, 3),
134	CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_sb",   0x28, 20, 3),
135	{ /* Sentinel */ },
136};
137
138static const struct loongson2_clk_board_info ls2k1000_clks[] = {
139	CLK_PLL(LOONGSON2_NODE_PLL,   "pll_node", 0,    32, 10, 26, 6),
140	CLK_PLL(LOONGSON2_DDR_PLL,    "pll_ddr",  0x10, 32, 10, 26, 6),
141	CLK_PLL(LOONGSON2_DC_PLL,     "pll_dc",   0x20, 32, 10, 26, 6),
142	CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x30, 32, 10, 26, 6),
143	CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x40, 32, 10, 26, 6),
144	CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "pll_node", 0x8,  0,  6),
145	CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "pll_ddr",  0x18, 0,  6),
146	CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "pll_ddr",  0x18, 22, 6),
147	/*
148	 * The hda clk divisor in the upper 32bits and the clk-prodiver
149	 * layer code doesn't support 64bit io operation thus a conversion
150	 * is required that subtract shift by 32 and add 4byte to the hda
151	 * address
152	 */
153	CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "pll_ddr",  0x22, 12, 7),
154	CLK_DIV(LOONGSON2_DC_CLK,     "clk_dc",   "pll_dc",   0x28, 0,  6),
155	CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "pll_dc",   0x28, 22, 6),
156	CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0", 0x38, 0,  6),
157	CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1", 0x38, 0,  6),
158	CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL,       0x50, 8,  3),
159	CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
160	CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_gmac", 0x50, 16, 3),
161	CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_gmac", 0x50, 20, 3),
162	{ /* Sentinel */ },
163};
164
165static const struct loongson2_clk_board_info ls2k2000_clks[] = {
166	CLK_PLL(LOONGSON2_DC_PLL,     "pll_0",    0,    21, 9, 32, 6),
167	CLK_PLL(LOONGSON2_DDR_PLL,    "pll_1",    0x10, 21, 9, 32, 6),
168	CLK_PLL(LOONGSON2_NODE_PLL,   "pll_2",    0x20, 21, 9, 32, 6),
169	CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x30, 21, 9, 32, 6),
170	CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x40, 21, 9, 32, 6),
171	CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0",    0,    40),
172	CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0",    0,    41),
173	CLK_GATE(LOONGSON2_RIO_GATE,  "rio_gate",  "pll_0",    0,    42),
174	CLK_GATE(LOONGSON2_DC_GATE,   "dc_gate",   "pll_1",    0x10, 40),
175	CLK_GATE(LOONGSON2_DDR_GATE,  "ddr_gate",  "pll_1",    0x10, 41),
176	CLK_GATE(LOONGSON2_GPU_GATE,  "gpu_gate",  "pll_1",    0x10, 42),
177	CLK_GATE(LOONGSON2_HDA_GATE,  "hda_gate",  "pll_2",    0x20, 40),
178	CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2",    0x20, 41),
179	CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2",    0x20, 42),
180	CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),
181	CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),
182	CLK_DIV(LOONGSON2_OUT0_CLK,   "clk_out0", "out0_gate", 0,    0,  6),
183	CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "gmac_gate", 0,    7,  6),
184	CLK_DIV(LOONGSON2_RIO_CLK,    "clk_rio",  "rio_gate",  0,    14, 6),
185	CLK_DIV(LOONGSON2_DC_CLK,     "clk_dc",   "dc_gate",   0x10, 0,  6),
186	CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "gpu_gate",  0x10, 7,  6),
187	CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "ddr_gate",  0x10, 14, 6),
188	CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "hda_gate",  0x20, 0,  6),
189	CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "node_gate", 0x20, 7,  6),
190	CLK_DIV(LOONGSON2_EMMC_CLK,   "clk_emmc", "emmc_gate", 0x20, 14, 6),
191	CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0",  0x30, 0,  6),
192	CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1",  0x40, 0,  6),
193	CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0",  0x50, 12, 3),
194	CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_out0",  0x50, 16, 3),
195	CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_node",  0x50, 20, 3),
196	CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL,        0x50, 23, 3),
197	CLK_SCALE(LOONGSON2_DES_CLK,  "clk_des",  "clk_node",  0x50, 40, 3),
198	CLK_SCALE(LOONGSON2_I2S_CLK,  "clk_i2s",  "clk_node",  0x50, 44, 3),
199	CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),
200	{ /* Sentinel */ },
201};
202
203static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
204{
205	return container_of(hw, struct loongson2_clk_data, hw);
206}
207
208static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
209{
210	return (val & GENMASK(shift + width - 1, shift)) >> shift;
211}
212
213static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
214					       unsigned long parent_rate)
215{
216	u64 val, mult, div;
217	struct loongson2_clk_data *clk = to_loongson2_clk(hw);
218
219	val  = readq(clk->reg);
220	mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
221	div  = loongson2_rate_part(val, clk->div_shift,  clk->div_width);
222
223	return div_u64((u64)parent_rate * mult, div);
224}
225
226static const struct clk_ops loongson2_pll_recalc_ops = {
227	.recalc_rate = loongson2_pll_recalc_rate,
228};
229
230static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
231						     unsigned long parent_rate)
232{
233	u64 val, mult;
234	struct loongson2_clk_data *clk = to_loongson2_clk(hw);
235
236	val  = readq(clk->reg);
237	mult = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
238
239	return div_u64((u64)parent_rate * mult, 8);
240}
241
242static const struct clk_ops loongson2_freqscale_recalc_ops = {
243	.recalc_rate = loongson2_freqscale_recalc_rate,
244};
245
246static struct clk_hw *loongson2_clk_register(struct loongson2_clk_provider *clp,
247					     const struct loongson2_clk_board_info *cld,
248					     const struct clk_ops *ops)
249{
250	int ret;
251	struct clk_hw *hw;
252	struct loongson2_clk_data *clk;
253	struct clk_init_data init = { };
254
255	clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
256	if (!clk)
257		return ERR_PTR(-ENOMEM);
258
259	init.name  = cld->name;
260	init.ops   = ops;
261	init.flags = 0;
262	init.num_parents = 1;
263
264	if (!cld->parent_name)
265		init.parent_data = pdata;
266	else
267		init.parent_names = &cld->parent_name;
268
269	clk->reg	= clp->base + cld->reg_offset;
270	clk->div_shift	= cld->div_shift;
271	clk->div_width	= cld->div_width;
272	clk->mult_shift	= cld->mult_shift;
273	clk->mult_width	= cld->mult_width;
274	clk->hw.init	= &init;
275
276	hw = &clk->hw;
277	ret = devm_clk_hw_register(clp->dev, hw);
278	if (ret)
279		clk = ERR_PTR(ret);
280
281	return hw;
282}
283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284static int loongson2_clk_probe(struct platform_device *pdev)
285{
286	int i, clks_num = 0;
287	struct clk_hw *hw;
 
 
288	struct device *dev = &pdev->dev;
289	struct loongson2_clk_provider *clp;
290	const struct loongson2_clk_board_info *p, *data;
291
292	data = device_get_match_data(dev);
293	if (!data)
294		return -EINVAL;
295
296	for (p = data; p->name; p++)
297		clks_num = max(clks_num, p->id + 1);
298
299	clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
300			   GFP_KERNEL);
301	if (!clp)
302		return -ENOMEM;
303
304	clp->base = devm_platform_ioremap_resource(pdev, 0);
305	if (IS_ERR(clp->base))
306		return PTR_ERR(clp->base);
307
308	spin_lock_init(&clp->clk_lock);
309	clp->clk_data.num = clks_num;
310	clp->dev = dev;
311
312	/* Avoid returning NULL for unused id */
313	memset_p((void **)clp->clk_data.hws, ERR_PTR(-ENOENT), clks_num);
314
315	for (i = 0; i < clks_num; i++) {
316		p = &data[i];
317		switch (p->type) {
318		case CLK_TYPE_PLL:
319			hw = loongson2_clk_register(clp, p,
320						    &loongson2_pll_recalc_ops);
321			break;
322		case CLK_TYPE_SCALE:
323			hw = loongson2_clk_register(clp, p,
324						    &loongson2_freqscale_recalc_ops);
325			break;
326		case CLK_TYPE_DIVIDER:
327			hw = devm_clk_hw_register_divider(dev, p->name,
328							  p->parent_name, 0,
329							  clp->base + p->reg_offset,
330							  p->div_shift, p->div_width,
331							  CLK_DIVIDER_ONE_BASED,
332							  &clp->clk_lock);
333			break;
334		case CLK_TYPE_GATE:
335			hw = devm_clk_hw_register_gate(dev, p->name, p->parent_name, 0,
336						       clp->base + p->reg_offset,
337						       p->bit_idx, 0,
338						       &clp->clk_lock);
339			break;
340		case CLK_TYPE_FIXED:
341			hw = clk_hw_register_fixed_rate_parent_data(dev, p->name, pdata,
342								    0, p->fixed_rate);
343			break;
344		default:
345			return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
346		}
347
348		if (IS_ERR(hw))
349			return dev_err_probe(dev, PTR_ERR(hw),
350					     "Register clk: %s, type: %u failed!\n",
351					     p->name, p->type);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
353		clp->clk_data.hws[p->id] = hw;
354	}
 
355
356	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
357}
358
359static const struct of_device_id loongson2_clk_match_table[] = {
360	{ .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },
361	{ .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
362	{ .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },
363	{ }
364};
365MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
366
367static struct platform_driver loongson2_clk_driver = {
368	.probe	= loongson2_clk_probe,
369	.driver	= {
370		.name	= "loongson2-clk",
371		.of_match_table	= loongson2_clk_match_table,
372	},
373};
374module_platform_driver(loongson2_clk_driver);
375
376MODULE_DESCRIPTION("Loongson2 clock driver");
377MODULE_AUTHOR("Loongson Technology Corporation Limited");
378MODULE_LICENSE("GPL");
v6.8
  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#define LOONGSON2_PLL_MULT_SHIFT		32
 17#define LOONGSON2_PLL_MULT_WIDTH		10
 18#define LOONGSON2_PLL_DIV_SHIFT			26
 19#define LOONGSON2_PLL_DIV_WIDTH			6
 20#define LOONGSON2_APB_FREQSCALE_SHIFT		20
 21#define LOONGSON2_APB_FREQSCALE_WIDTH		3
 22#define LOONGSON2_USB_FREQSCALE_SHIFT		16
 23#define LOONGSON2_USB_FREQSCALE_WIDTH		3
 24#define LOONGSON2_SATA_FREQSCALE_SHIFT		12
 25#define LOONGSON2_SATA_FREQSCALE_WIDTH		3
 26#define LOONGSON2_BOOT_FREQSCALE_SHIFT		8
 27#define LOONGSON2_BOOT_FREQSCALE_WIDTH		3
 28
 29static void __iomem *loongson2_pll_base;
 30
 31static const struct clk_parent_data pdata[] = {
 32	{ .fw_name = "ref_100m",},
 33};
 34
 35static struct clk_hw *loongson2_clk_register(struct device *dev,
 36					  const char *name,
 37					  const char *parent_name,
 38					  const struct clk_ops *ops,
 39					  unsigned long flags)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 40{
 41	int ret;
 42	struct clk_hw *hw;
 
 43	struct clk_init_data init = { };
 44
 45	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
 46	if (!hw)
 47		return ERR_PTR(-ENOMEM);
 48
 49	init.name = name;
 50	init.ops = ops;
 51	init.flags = flags;
 52	init.num_parents = 1;
 53
 54	if (!parent_name)
 55		init.parent_data = pdata;
 56	else
 57		init.parent_names = &parent_name;
 58
 59	hw->init = &init;
 
 
 
 
 
 60
 61	ret = devm_clk_hw_register(dev, hw);
 
 62	if (ret)
 63		hw = ERR_PTR(ret);
 64
 65	return hw;
 66}
 67
 68static unsigned long loongson2_calc_pll_rate(int offset, unsigned long rate)
 69{
 70	u64 val;
 71	u32 mult, div;
 72
 73	val = readq(loongson2_pll_base + offset);
 74
 75	mult = (val >> LOONGSON2_PLL_MULT_SHIFT) &
 76			clk_div_mask(LOONGSON2_PLL_MULT_WIDTH);
 77	div = (val >> LOONGSON2_PLL_DIV_SHIFT) &
 78			clk_div_mask(LOONGSON2_PLL_DIV_WIDTH);
 79
 80	return div_u64((u64)rate * mult, div);
 81}
 82
 83static unsigned long loongson2_node_recalc_rate(struct clk_hw *hw,
 84					  unsigned long parent_rate)
 85{
 86	return loongson2_calc_pll_rate(0x0, parent_rate);
 87}
 88
 89static const struct clk_ops loongson2_node_clk_ops = {
 90	.recalc_rate = loongson2_node_recalc_rate,
 91};
 92
 93static unsigned long loongson2_ddr_recalc_rate(struct clk_hw *hw,
 94					  unsigned long parent_rate)
 95{
 96	return loongson2_calc_pll_rate(0x10, parent_rate);
 97}
 98
 99static const struct clk_ops loongson2_ddr_clk_ops = {
100	.recalc_rate = loongson2_ddr_recalc_rate,
101};
102
103static unsigned long loongson2_dc_recalc_rate(struct clk_hw *hw,
104					  unsigned long parent_rate)
105{
106	return loongson2_calc_pll_rate(0x20, parent_rate);
107}
108
109static const struct clk_ops loongson2_dc_clk_ops = {
110	.recalc_rate = loongson2_dc_recalc_rate,
111};
112
113static unsigned long loongson2_pix0_recalc_rate(struct clk_hw *hw,
114					  unsigned long parent_rate)
115{
116	return loongson2_calc_pll_rate(0x30, parent_rate);
117}
118
119static const struct clk_ops loongson2_pix0_clk_ops = {
120	.recalc_rate = loongson2_pix0_recalc_rate,
121};
122
123static unsigned long loongson2_pix1_recalc_rate(struct clk_hw *hw,
124					  unsigned long parent_rate)
125{
126	return loongson2_calc_pll_rate(0x40, parent_rate);
127}
128
129static const struct clk_ops loongson2_pix1_clk_ops = {
130	.recalc_rate = loongson2_pix1_recalc_rate,
131};
132
133static unsigned long loongson2_calc_rate(unsigned long rate,
134					 int shift, int width)
135{
136	u64 val;
137	u32 mult;
138
139	val = readq(loongson2_pll_base + 0x50);
140
141	mult = (val >> shift) & clk_div_mask(width);
142
143	return div_u64((u64)rate * (mult + 1), 8);
144}
145
146static unsigned long loongson2_boot_recalc_rate(struct clk_hw *hw,
147					  unsigned long parent_rate)
148{
149	return loongson2_calc_rate(parent_rate,
150				   LOONGSON2_BOOT_FREQSCALE_SHIFT,
151				   LOONGSON2_BOOT_FREQSCALE_WIDTH);
152}
153
154static const struct clk_ops loongson2_boot_clk_ops = {
155	.recalc_rate = loongson2_boot_recalc_rate,
156};
157
158static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
159					  unsigned long parent_rate)
160{
161	return loongson2_calc_rate(parent_rate,
162				   LOONGSON2_APB_FREQSCALE_SHIFT,
163				   LOONGSON2_APB_FREQSCALE_WIDTH);
164}
165
166static const struct clk_ops loongson2_apb_clk_ops = {
167	.recalc_rate = loongson2_apb_recalc_rate,
168};
169
170static unsigned long loongson2_usb_recalc_rate(struct clk_hw *hw,
171					  unsigned long parent_rate)
172{
173	return loongson2_calc_rate(parent_rate,
174				   LOONGSON2_USB_FREQSCALE_SHIFT,
175				   LOONGSON2_USB_FREQSCALE_WIDTH);
176}
177
178static const struct clk_ops loongson2_usb_clk_ops = {
179	.recalc_rate = loongson2_usb_recalc_rate,
180};
181
182static unsigned long loongson2_sata_recalc_rate(struct clk_hw *hw,
183					  unsigned long parent_rate)
184{
185	return loongson2_calc_rate(parent_rate,
186				   LOONGSON2_SATA_FREQSCALE_SHIFT,
187				   LOONGSON2_SATA_FREQSCALE_WIDTH);
188}
189
190static const struct clk_ops loongson2_sata_clk_ops = {
191	.recalc_rate = loongson2_sata_recalc_rate,
192};
193
194static inline int loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
195{
196	unsigned int i;
197
198	for (i = 0; i < count; i++)
199		if (IS_ERR(clks[i])) {
200			pr_err("Loongson2 clk %u: register failed with %ld\n",
201				i, PTR_ERR(clks[i]));
202			return PTR_ERR(clks[i]);
203		}
204
205	return 0;
206}
207
208static int loongson2_clk_probe(struct platform_device *pdev)
209{
210	int ret;
211	struct clk_hw **hws;
212	struct clk_hw_onecell_data *clk_hw_data;
213	spinlock_t loongson2_clk_lock;
214	struct device *dev = &pdev->dev;
 
 
215
216	loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
217	if (IS_ERR(loongson2_pll_base))
218		return PTR_ERR(loongson2_pll_base);
219
220	clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
221					GFP_KERNEL);
222	if (WARN_ON(!clk_hw_data))
 
 
 
223		return -ENOMEM;
224
225	clk_hw_data->num = LOONGSON2_CLK_END;
226	hws = clk_hw_data->hws;
227
228	hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
229						NULL,
230						&loongson2_node_clk_ops, 0);
231
232	hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
233						NULL,
234						&loongson2_ddr_clk_ops, 0);
235
236	hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
237						NULL,
238						&loongson2_dc_clk_ops, 0);
239
240	hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
241						NULL,
242						&loongson2_pix0_clk_ops, 0);
243
244	hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
245						NULL,
246						&loongson2_pix1_clk_ops, 0);
247
248	hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
249						NULL,
250						&loongson2_boot_clk_ops, 0);
251
252	hws[LOONGSON2_NODE_CLK] = devm_clk_hw_register_divider(dev, "node",
253						"node_pll", 0,
254						loongson2_pll_base + 0x8, 0,
255						6, CLK_DIVIDER_ONE_BASED,
256						&loongson2_clk_lock);
 
 
 
 
 
 
 
 
 
 
 
257
258	/*
259	 * The hda clk divisor in the upper 32bits and the clk-prodiver
260	 * layer code doesn't support 64bit io operation thus a conversion
261	 * is required that subtract shift by 32 and add 4byte to the hda
262	 * address
263	 */
264	hws[LOONGSON2_HDA_CLK] = devm_clk_hw_register_divider(dev, "hda",
265						"ddr_pll", 0,
266						loongson2_pll_base + 0x22, 12,
267						7, CLK_DIVIDER_ONE_BASED,
268						&loongson2_clk_lock);
269
270	hws[LOONGSON2_GPU_CLK] = devm_clk_hw_register_divider(dev, "gpu",
271						"ddr_pll", 0,
272						loongson2_pll_base + 0x18, 22,
273						6, CLK_DIVIDER_ONE_BASED,
274						&loongson2_clk_lock);
275
276	hws[LOONGSON2_DDR_CLK] = devm_clk_hw_register_divider(dev, "ddr",
277						"ddr_pll", 0,
278						loongson2_pll_base + 0x18, 0,
279						6, CLK_DIVIDER_ONE_BASED,
280						&loongson2_clk_lock);
281
282	hws[LOONGSON2_GMAC_CLK] = devm_clk_hw_register_divider(dev, "gmac",
283						"dc_pll", 0,
284						loongson2_pll_base + 0x28, 22,
285						6, CLK_DIVIDER_ONE_BASED,
286						&loongson2_clk_lock);
287
288	hws[LOONGSON2_DC_CLK] = devm_clk_hw_register_divider(dev, "dc",
289						"dc_pll", 0,
290						loongson2_pll_base + 0x28, 0,
291						6, CLK_DIVIDER_ONE_BASED,
292						&loongson2_clk_lock);
293
294	hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
295						"gmac",
296						&loongson2_apb_clk_ops, 0);
297
298	hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
299						"gmac",
300						&loongson2_usb_clk_ops, 0);
301
302	hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
303						"gmac",
304						&loongson2_sata_clk_ops, 0);
305
306	hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
307						"pix0_pll", 0,
308						loongson2_pll_base + 0x38, 0, 6,
309						CLK_DIVIDER_ONE_BASED,
310						&loongson2_clk_lock);
311
312	hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
313						"pix1_pll", 0,
314						loongson2_pll_base + 0x48, 0, 6,
315						CLK_DIVIDER_ONE_BASED,
316						&loongson2_clk_lock);
317
318	ret = loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
319	if (ret)
320		return ret;
321
322	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
323}
324
325static const struct of_device_id loongson2_clk_match_table[] = {
326	{ .compatible = "loongson,ls2k-clk" },
 
 
327	{ }
328};
329MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
330
331static struct platform_driver loongson2_clk_driver = {
332	.probe	= loongson2_clk_probe,
333	.driver	= {
334		.name	= "loongson2-clk",
335		.of_match_table	= loongson2_clk_match_table,
336	},
337};
338module_platform_driver(loongson2_clk_driver);
339
340MODULE_DESCRIPTION("Loongson2 clock driver");
 
341MODULE_LICENSE("GPL");