1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * drivers/clk/clk-axm5516.c
4 *
5 * Provides clock implementations for three different types of clock devices on
6 * the Axxia device: PLL clock, a clock divider and a clock mux.
7 *
8 * Copyright (C) 2014 LSI Corporation
9 */
10#include <linux/module.h>
11#include <linux/kernel.h>
12#include <linux/slab.h>
13#include <linux/platform_device.h>
14#include <linux/of.h>
15#include <linux/of_address.h>
16#include <linux/clk-provider.h>
17#include <linux/regmap.h>
18#include <dt-bindings/clock/lsi,axm5516-clks.h>
19
20
21/**
22 * struct axxia_clk - Common struct to all Axxia clocks.
23 * @hw: clk_hw for the common clk framework
24 * @regmap: Regmap for the clock control registers
25 */
26struct axxia_clk {
27	struct clk_hw hw;
28	struct regmap *regmap;
29};
30#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)
31
32/**
33 * struct axxia_pllclk - Axxia PLL generated clock.
34 * @aclk: Common struct
35 * @reg: Offset into regmap for PLL control register
36 */
37struct axxia_pllclk {
38	struct axxia_clk aclk;
39	u32 reg;
40};
41#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)
42
43/**
44 * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the
45 * parent clock rate.
46 */
47static unsigned long
48axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)
49{
50	struct axxia_clk *aclk = to_axxia_clk(hw);
51	struct axxia_pllclk *pll = to_axxia_pllclk(aclk);
52	unsigned long rate, fbdiv, refdiv, postdiv;
53	u32 control;
54
55	regmap_read(aclk->regmap, pll->reg, &control);
56	postdiv = ((control >> 0) & 0xf) + 1;
57	fbdiv   = ((control >> 4) & 0xfff) + 3;
58	refdiv  = ((control >> 16) & 0x1f) + 1;
59	rate = (parent_rate / (refdiv * postdiv)) * fbdiv;
60
61	return rate;
62}
63
64static const struct clk_ops axxia_pllclk_ops = {
65	.recalc_rate = axxia_pllclk_recalc,
66};
67
68/**
69 * struct axxia_divclk - Axxia clock divider
70 * @aclk: Common struct
71 * @reg: Offset into regmap for PLL control register
72 * @shift: Bit position for divider value
73 * @width: Number of bits in divider value
74 */
75struct axxia_divclk {
76	struct axxia_clk aclk;
77	u32 reg;
78	u32 shift;
79	u32 width;
80};
81#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)
82
83/**
84 * axxia_divclk_recalc_rate - Calculate clock divider output rage
85 */
86static unsigned long
87axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
88{
89	struct axxia_clk *aclk = to_axxia_clk(hw);
90	struct axxia_divclk *divclk = to_axxia_divclk(aclk);
91	u32 ctrl, div;
92
93	regmap_read(aclk->regmap, divclk->reg, &ctrl);
94	div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));
95
96	return parent_rate / div;
97}
98
99static const struct clk_ops axxia_divclk_ops = {
100	.recalc_rate = axxia_divclk_recalc_rate,
101};
102
103/**
104 * struct axxia_clkmux - Axxia clock mux
105 * @aclk: Common struct
106 * @reg: Offset into regmap for PLL control register
107 * @shift: Bit position for selection value
108 * @width: Number of bits in selection value
109 */
110struct axxia_clkmux {
111	struct axxia_clk aclk;
112	u32 reg;
113	u32 shift;
114	u32 width;
115};
116#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)
117
118/**
119 * axxia_clkmux_get_parent - Return the index of selected parent clock
120 */
121static u8 axxia_clkmux_get_parent(struct clk_hw *hw)
122{
123	struct axxia_clk *aclk = to_axxia_clk(hw);
124	struct axxia_clkmux *mux = to_axxia_clkmux(aclk);
125	u32 ctrl, parent;
126
127	regmap_read(aclk->regmap, mux->reg, &ctrl);
128	parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);
129
130	return (u8) parent;
131}
132
133static const struct clk_ops axxia_clkmux_ops = {
134	.get_parent = axxia_clkmux_get_parent,
135};
136
137
138/*
139 * PLLs
140 */
141
142static struct axxia_pllclk clk_fab_pll = {
143	.aclk.hw.init = &(struct clk_init_data){
144		.name = "clk_fab_pll",
145		.parent_names = (const char *[]){
146			"clk_ref0"
147		},
148		.num_parents = 1,
149		.ops = &axxia_pllclk_ops,
150	},
151	.reg   = 0x01800,
152};
153
154static struct axxia_pllclk clk_cpu_pll = {
155	.aclk.hw.init = &(struct clk_init_data){
156		.name = "clk_cpu_pll",
157		.parent_names = (const char *[]){
158			"clk_ref0"
159		},
160		.num_parents = 1,
161		.ops = &axxia_pllclk_ops,
162	},
163	.reg   = 0x02000,
164};
165
166static struct axxia_pllclk clk_sys_pll = {
167	.aclk.hw.init = &(struct clk_init_data){
168		.name = "clk_sys_pll",
169		.parent_names = (const char *[]){
170			"clk_ref0"
171		},
172		.num_parents = 1,
173		.ops = &axxia_pllclk_ops,
174	},
175	.reg   = 0x02800,
176};
177
178static struct axxia_pllclk clk_sm0_pll = {
179	.aclk.hw.init = &(struct clk_init_data){
180		.name = "clk_sm0_pll",
181		.parent_names = (const char *[]){
182			"clk_ref2"
183		},
184		.num_parents = 1,
185		.ops = &axxia_pllclk_ops,
186	},
187	.reg   = 0x03000,
188};
189
190static struct axxia_pllclk clk_sm1_pll = {
191	.aclk.hw.init = &(struct clk_init_data){
192		.name = "clk_sm1_pll",
193		.parent_names = (const char *[]){
194			"clk_ref1"
195		},
196		.num_parents = 1,
197		.ops = &axxia_pllclk_ops,
198	},
199	.reg   = 0x03800,
200};
201
202/*
203 * Clock dividers
204 */
205
206static struct axxia_divclk clk_cpu0_div = {
207	.aclk.hw.init = &(struct clk_init_data){
208		.name = "clk_cpu0_div",
209		.parent_names = (const char *[]){
210			"clk_cpu_pll"
211		},
212		.num_parents = 1,
213		.ops = &axxia_divclk_ops,
214	},
215	.reg   = 0x10008,
216	.shift = 0,
217	.width = 4,
218};
219
220static struct axxia_divclk clk_cpu1_div = {
221	.aclk.hw.init = &(struct clk_init_data){
222		.name = "clk_cpu1_div",
223		.parent_names = (const char *[]){
224			"clk_cpu_pll"
225		},
226		.num_parents = 1,
227		.ops = &axxia_divclk_ops,
228	},
229	.reg   = 0x10008,
230	.shift = 4,
231	.width = 4,
232};
233
234static struct axxia_divclk clk_cpu2_div = {
235	.aclk.hw.init = &(struct clk_init_data){
236		.name = "clk_cpu2_div",
237		.parent_names = (const char *[]){
238			"clk_cpu_pll"
239		},
240		.num_parents = 1,
241		.ops = &axxia_divclk_ops,
242	},
243	.reg   = 0x10008,
244	.shift = 8,
245	.width = 4,
246};
247
248static struct axxia_divclk clk_cpu3_div = {
249	.aclk.hw.init = &(struct clk_init_data){
250		.name = "clk_cpu3_div",
251		.parent_names = (const char *[]){
252			"clk_cpu_pll"
253		},
254		.num_parents = 1,
255		.ops = &axxia_divclk_ops,
256	},
257	.reg   = 0x10008,
258	.shift = 12,
259	.width = 4,
260};
261
262static struct axxia_divclk clk_nrcp_div = {
263	.aclk.hw.init = &(struct clk_init_data){
264		.name = "clk_nrcp_div",
265		.parent_names = (const char *[]){
266			"clk_sys_pll"
267		},
268		.num_parents = 1,
269		.ops = &axxia_divclk_ops,
270	},
271	.reg   = 0x1000c,
272	.shift = 0,
273	.width = 4,
274};
275
276static struct axxia_divclk clk_sys_div = {
277	.aclk.hw.init = &(struct clk_init_data){
278		.name = "clk_sys_div",
279		.parent_names = (const char *[]){
280			"clk_sys_pll"
281		},
282		.num_parents = 1,
283		.ops = &axxia_divclk_ops,
284	},
285	.reg   = 0x1000c,
286	.shift = 4,
287	.width = 4,
288};
289
290static struct axxia_divclk clk_fab_div = {
291	.aclk.hw.init = &(struct clk_init_data){
292		.name = "clk_fab_div",
293		.parent_names = (const char *[]){
294			"clk_fab_pll"
295		},
296		.num_parents = 1,
297		.ops = &axxia_divclk_ops,
298	},
299	.reg   = 0x1000c,
300	.shift = 8,
301	.width = 4,
302};
303
304static struct axxia_divclk clk_per_div = {
305	.aclk.hw.init = &(struct clk_init_data){
306		.name = "clk_per_div",
307		.parent_names = (const char *[]){
308			"clk_sm1_pll"
309		},
310		.num_parents = 1,
311		.ops = &axxia_divclk_ops,
312	},
313	.reg   = 0x1000c,
314	.shift = 12,
315	.width = 4,
316};
317
318static struct axxia_divclk clk_mmc_div = {
319	.aclk.hw.init = &(struct clk_init_data){
320		.name = "clk_mmc_div",
321		.parent_names = (const char *[]){
322			"clk_sm1_pll"
323		},
324		.num_parents = 1,
325		.ops = &axxia_divclk_ops,
326	},
327	.reg   = 0x1000c,
328	.shift = 16,
329	.width = 4,
330};
331
332/*
333 * Clock MUXes
334 */
335
336static struct axxia_clkmux clk_cpu0_mux = {
337	.aclk.hw.init = &(struct clk_init_data){
338		.name = "clk_cpu0",
339		.parent_names = (const char *[]){
340			"clk_ref0",
341			"clk_cpu_pll",
342			"clk_cpu0_div",
343			"clk_cpu0_div"
344		},
345		.num_parents = 4,
346		.ops = &axxia_clkmux_ops,
347	},
348	.reg   = 0x10000,
349	.shift = 0,
350	.width = 2,
351};
352
353static struct axxia_clkmux clk_cpu1_mux = {
354	.aclk.hw.init = &(struct clk_init_data){
355		.name = "clk_cpu1",
356		.parent_names = (const char *[]){
357			"clk_ref0",
358			"clk_cpu_pll",
359			"clk_cpu1_div",
360			"clk_cpu1_div"
361		},
362		.num_parents = 4,
363		.ops = &axxia_clkmux_ops,
364	},
365	.reg   = 0x10000,
366	.shift = 2,
367	.width = 2,
368};
369
370static struct axxia_clkmux clk_cpu2_mux = {
371	.aclk.hw.init = &(struct clk_init_data){
372		.name = "clk_cpu2",
373		.parent_names = (const char *[]){
374			"clk_ref0",
375			"clk_cpu_pll",
376			"clk_cpu2_div",
377			"clk_cpu2_div"
378		},
379		.num_parents = 4,
380		.ops = &axxia_clkmux_ops,
381	},
382	.reg   = 0x10000,
383	.shift = 4,
384	.width = 2,
385};
386
387static struct axxia_clkmux clk_cpu3_mux = {
388	.aclk.hw.init = &(struct clk_init_data){
389		.name = "clk_cpu3",
390		.parent_names = (const char *[]){
391			"clk_ref0",
392			"clk_cpu_pll",
393			"clk_cpu3_div",
394			"clk_cpu3_div"
395		},
396		.num_parents = 4,
397		.ops = &axxia_clkmux_ops,
398	},
399	.reg   = 0x10000,
400	.shift = 6,
401	.width = 2,
402};
403
404static struct axxia_clkmux clk_nrcp_mux = {
405	.aclk.hw.init = &(struct clk_init_data){
406		.name = "clk_nrcp",
407		.parent_names = (const char *[]){
408			"clk_ref0",
409			"clk_sys_pll",
410			"clk_nrcp_div",
411			"clk_nrcp_div"
412		},
413		.num_parents = 4,
414		.ops = &axxia_clkmux_ops,
415	},
416	.reg   = 0x10004,
417	.shift = 0,
418	.width = 2,
419};
420
421static struct axxia_clkmux clk_sys_mux = {
422	.aclk.hw.init = &(struct clk_init_data){
423		.name = "clk_sys",
424		.parent_names = (const char *[]){
425			"clk_ref0",
426			"clk_sys_pll",
427			"clk_sys_div",
428			"clk_sys_div"
429		},
430		.num_parents = 4,
431		.ops = &axxia_clkmux_ops,
432	},
433	.reg   = 0x10004,
434	.shift = 2,
435	.width = 2,
436};
437
438static struct axxia_clkmux clk_fab_mux = {
439	.aclk.hw.init = &(struct clk_init_data){
440		.name = "clk_fab",
441		.parent_names = (const char *[]){
442			"clk_ref0",
443			"clk_fab_pll",
444			"clk_fab_div",
445			"clk_fab_div"
446		},
447		.num_parents = 4,
448		.ops = &axxia_clkmux_ops,
449	},
450	.reg   = 0x10004,
451	.shift = 4,
452	.width = 2,
453};
454
455static struct axxia_clkmux clk_per_mux = {
456	.aclk.hw.init = &(struct clk_init_data){
457		.name = "clk_per",
458		.parent_names = (const char *[]){
459			"clk_ref1",
460			"clk_per_div"
461		},
462		.num_parents = 2,
463		.ops = &axxia_clkmux_ops,
464	},
465	.reg   = 0x10004,
466	.shift = 6,
467	.width = 1,
468};
469
470static struct axxia_clkmux clk_mmc_mux = {
471	.aclk.hw.init = &(struct clk_init_data){
472		.name = "clk_mmc",
473		.parent_names = (const char *[]){
474			"clk_ref1",
475			"clk_mmc_div"
476		},
477		.num_parents = 2,
478		.ops = &axxia_clkmux_ops,
479	},
480	.reg   = 0x10004,
481	.shift = 9,
482	.width = 1,
483};
484
485/* Table of all supported clocks indexed by the clock identifiers from the
486 * device tree binding
487 */
488static struct axxia_clk *axmclk_clocks[] = {
489	[AXXIA_CLK_FAB_PLL]  = &clk_fab_pll.aclk,
490	[AXXIA_CLK_CPU_PLL]  = &clk_cpu_pll.aclk,
491	[AXXIA_CLK_SYS_PLL]  = &clk_sys_pll.aclk,
492	[AXXIA_CLK_SM0_PLL]  = &clk_sm0_pll.aclk,
493	[AXXIA_CLK_SM1_PLL]  = &clk_sm1_pll.aclk,
494	[AXXIA_CLK_FAB_DIV]  = &clk_fab_div.aclk,
495	[AXXIA_CLK_SYS_DIV]  = &clk_sys_div.aclk,
496	[AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,
497	[AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,
498	[AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,
499	[AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,
500	[AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,
501	[AXXIA_CLK_PER_DIV]  = &clk_per_div.aclk,
502	[AXXIA_CLK_MMC_DIV]  = &clk_mmc_div.aclk,
503	[AXXIA_CLK_FAB]      = &clk_fab_mux.aclk,
504	[AXXIA_CLK_SYS]      = &clk_sys_mux.aclk,
505	[AXXIA_CLK_NRCP]     = &clk_nrcp_mux.aclk,
506	[AXXIA_CLK_CPU0]     = &clk_cpu0_mux.aclk,
507	[AXXIA_CLK_CPU1]     = &clk_cpu1_mux.aclk,
508	[AXXIA_CLK_CPU2]     = &clk_cpu2_mux.aclk,
509	[AXXIA_CLK_CPU3]     = &clk_cpu3_mux.aclk,
510	[AXXIA_CLK_PER]      = &clk_per_mux.aclk,
511	[AXXIA_CLK_MMC]      = &clk_mmc_mux.aclk,
512};
513
514static struct clk_hw *
515of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)
516{
517	unsigned int idx = clkspec->args[0];
518
519	if (idx >= ARRAY_SIZE(axmclk_clocks)) {
520		pr_err("%s: invalid index %u\n", __func__, idx);
521		return ERR_PTR(-EINVAL);
522	}
523
524	return &axmclk_clocks[idx]->hw;
525}
526
527static const struct regmap_config axmclk_regmap_config = {
528	.reg_bits	= 32,
529	.reg_stride	= 4,
530	.val_bits	= 32,
531	.max_register	= 0x1fffc,
532	.fast_io	= true,
533};
534
535static const struct of_device_id axmclk_match_table[] = {
536	{ .compatible = "lsi,axm5516-clks" },
537	{ }
538};
539MODULE_DEVICE_TABLE(of, axmclk_match_table);
540
541static int axmclk_probe(struct platform_device *pdev)
542{
543	void __iomem *base;
544	int i, ret;
545	struct device *dev = &pdev->dev;
546	struct regmap *regmap;
547	size_t num_clks;
548
549	base = devm_platform_ioremap_resource(pdev, 0);
550	if (IS_ERR(base))
551		return PTR_ERR(base);
552
553	regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);
554	if (IS_ERR(regmap))
555		return PTR_ERR(regmap);
556
557	num_clks = ARRAY_SIZE(axmclk_clocks);
558	pr_info("axmclk: supporting %zu clocks\n", num_clks);
559
560	/* Update each entry with the allocated regmap and register the clock
561	 * with the common clock framework
562	 */
563	for (i = 0; i < num_clks; i++) {
564		axmclk_clocks[i]->regmap = regmap;
565		ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);
566		if (ret)
567			return ret;
568	}
569
570	return devm_of_clk_add_hw_provider(dev, of_clk_axmclk_get, NULL);
571}
572
573static struct platform_driver axmclk_driver = {
574	.probe		= axmclk_probe,
575	.driver		= {
576		.name	= "clk-axm5516",
577		.of_match_table = axmclk_match_table,
578	},
579};
580
581static int __init axmclk_init(void)
582{
583	return platform_driver_register(&axmclk_driver);
584}
585core_initcall(axmclk_init);
586
587static void __exit axmclk_exit(void)
588{
589	platform_driver_unregister(&axmclk_driver);
590}
591module_exit(axmclk_exit);
592
593MODULE_DESCRIPTION("AXM5516 clock driver");
594MODULE_LICENSE("GPL v2");
595MODULE_ALIAS("platform:clk-axm5516");
596