1*3504395fSJisheng 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