1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2014 Marvell Technology Group Ltd.
4  *
5  * Alexandre Belloni <alexandre.belloni@free-electrons.com>
6  * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
7  */
8 #include <linux/clk-provider.h>
9 #include <linux/io.h>
10 #include <linux/kernel.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/slab.h>
14 #include <asm/div64.h>
15 
16 #include "berlin2-div.h"
17 #include "berlin2-pll.h"
18 
19 struct berlin2_pll {
20 	struct clk_hw hw;
21 	void __iomem *base;
22 	struct berlin2_pll_map map;
23 };
24 
25 #define to_berlin2_pll(hw) container_of(hw, struct berlin2_pll, hw)
26 
27 #define SPLL_CTRL0	0x00
28 #define SPLL_CTRL1	0x04
29 #define SPLL_CTRL2	0x08
30 #define SPLL_CTRL3	0x0c
31 #define SPLL_CTRL4	0x10
32 
33 #define FBDIV_MASK	0x1ff
34 #define RFDIV_MASK	0x1f
35 #define DIVSEL_MASK	0xf
36 
37 /*
38  * The output frequency formula for the pll is:
39  * clkout = fbdiv / refdiv * parent / vcodiv
40  */
41 static unsigned long
berlin2_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)42 berlin2_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
43 {
44 	struct berlin2_pll *pll = to_berlin2_pll(hw);
45 	struct berlin2_pll_map *map = &pll->map;
46 	u32 val, fbdiv, rfdiv, vcodivsel, vcodiv;
47 	u64 rate = parent_rate;
48 
49 	val = readl_relaxed(pll->base + SPLL_CTRL0);
50 	fbdiv = (val >> map->fbdiv_shift) & FBDIV_MASK;
51 	rfdiv = (val >> map->rfdiv_shift) & RFDIV_MASK;
52 	if (rfdiv == 0) {
53 		pr_warn("%s has zero rfdiv\n", clk_hw_get_name(hw));
54 		rfdiv = 1;
55 	}
56 
57 	val = readl_relaxed(pll->base + SPLL_CTRL1);
58 	vcodivsel = (val >> map->divsel_shift) & DIVSEL_MASK;
59 	vcodiv = map->vcodiv[vcodivsel];
60 	if (vcodiv == 0) {
61 		pr_warn("%s has zero vcodiv (index %d)\n",
62 			clk_hw_get_name(hw), vcodivsel);
63 		vcodiv = 1;
64 	}
65 
66 	rate *= fbdiv * map->mult;
67 	do_div(rate, rfdiv * vcodiv);
68 
69 	return (unsigned long)rate;
70 }
71 
72 static const struct clk_ops berlin2_pll_ops = {
73 	.recalc_rate	= berlin2_pll_recalc_rate,
74 };
75 
76 int __init
berlin2_pll_register(const struct berlin2_pll_map * map,void __iomem * base,const char * name,const char * parent_name,unsigned long flags)77 berlin2_pll_register(const struct berlin2_pll_map *map,
78 		     void __iomem *base, const char *name,
79 		     const char *parent_name, unsigned long flags)
80 {
81 	struct clk_init_data init;
82 	struct berlin2_pll *pll;
83 
84 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
85 	if (!pll)
86 		return -ENOMEM;
87 
88 	/* copy pll_map to allow __initconst */
89 	memcpy(&pll->map, map, sizeof(*map));
90 	pll->base = base;
91 	pll->hw.init = &init;
92 	init.name = name;
93 	init.ops = &berlin2_pll_ops;
94 	init.parent_names = &parent_name;
95 	init.num_parents = 1;
96 	init.flags = flags;
97 
98 	return clk_hw_register(NULL, &pll->hw);
99 }
100