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