107afb8dbSDinh Nguyen // SPDX-License-Identifier: GPL-2.0
207afb8dbSDinh Nguyen /*
307afb8dbSDinh Nguyen * Copyright (C) 2017, Intel Corporation
407afb8dbSDinh Nguyen */
507afb8dbSDinh Nguyen #include <linux/slab.h>
607afb8dbSDinh Nguyen #include <linux/clk-provider.h>
762e59c4eSStephen Boyd #include <linux/io.h>
807afb8dbSDinh Nguyen
907afb8dbSDinh Nguyen #include "stratix10-clk.h"
1007afb8dbSDinh Nguyen #include "clk.h"
1107afb8dbSDinh Nguyen
1207afb8dbSDinh Nguyen /* Clock Manager offsets */
1307afb8dbSDinh Nguyen #define CLK_MGR_PLL_CLK_SRC_SHIFT 16
1407afb8dbSDinh Nguyen #define CLK_MGR_PLL_CLK_SRC_MASK 0x3
1507afb8dbSDinh Nguyen
1607afb8dbSDinh Nguyen /* PLL Clock enable bits */
1707afb8dbSDinh Nguyen #define SOCFPGA_PLL_POWER 0
1807afb8dbSDinh Nguyen #define SOCFPGA_PLL_RESET_MASK 0x2
1907afb8dbSDinh Nguyen #define SOCFPGA_PLL_REFDIV_MASK 0x00003F00
2007afb8dbSDinh Nguyen #define SOCFPGA_PLL_REFDIV_SHIFT 8
2180c6b7a0SDinh Nguyen #define SOCFPGA_PLL_AREFDIV_MASK 0x00000F00
2280c6b7a0SDinh Nguyen #define SOCFPGA_PLL_DREFDIV_MASK 0x00003000
2380c6b7a0SDinh Nguyen #define SOCFPGA_PLL_DREFDIV_SHIFT 12
2407afb8dbSDinh Nguyen #define SOCFPGA_PLL_MDIV_MASK 0xFF000000
2507afb8dbSDinh Nguyen #define SOCFPGA_PLL_MDIV_SHIFT 24
2680c6b7a0SDinh Nguyen #define SOCFPGA_AGILEX_PLL_MDIV_MASK 0x000003FF
2707afb8dbSDinh Nguyen #define SWCTRLBTCLKSEL_MASK 0x200
2807afb8dbSDinh Nguyen #define SWCTRLBTCLKSEL_SHIFT 9
2907afb8dbSDinh Nguyen
30a0f9819cSDinh Nguyen #define SOCFPGA_N5X_PLLDIV_FDIV_MASK GENMASK(16, 8)
31a0f9819cSDinh Nguyen #define SOCFPGA_N5X_PLLDIV_FDIV_SHIFT 8
32a0f9819cSDinh Nguyen #define SOCFPGA_N5X_PLLDIV_RDIV_MASK GENMASK(5, 0)
33a0f9819cSDinh Nguyen #define SOCFPGA_N5X_PLLDIV_QDIV_MASK GENMASK(26, 24)
34a0f9819cSDinh Nguyen #define SOCFPGA_N5X_PLLDIV_QDIV_SHIFT 24
35a0f9819cSDinh Nguyen
3607afb8dbSDinh Nguyen #define SOCFPGA_BOOT_CLK "boot_clk"
3707afb8dbSDinh Nguyen
3807afb8dbSDinh Nguyen #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
3907afb8dbSDinh Nguyen
n5x_clk_pll_recalc_rate(struct clk_hw * hwclk,unsigned long parent_rate)40a0f9819cSDinh Nguyen static unsigned long n5x_clk_pll_recalc_rate(struct clk_hw *hwclk,
41a0f9819cSDinh Nguyen unsigned long parent_rate)
42a0f9819cSDinh Nguyen {
43a0f9819cSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
44a0f9819cSDinh Nguyen unsigned long fdiv, reg, rdiv, qdiv;
45a0f9819cSDinh Nguyen u32 power = 1;
46a0f9819cSDinh Nguyen
47a0f9819cSDinh Nguyen /* read VCO1 reg for numerator and denominator */
48a0f9819cSDinh Nguyen reg = readl(socfpgaclk->hw.reg + 0x8);
49a0f9819cSDinh Nguyen fdiv = (reg & SOCFPGA_N5X_PLLDIV_FDIV_MASK) >> SOCFPGA_N5X_PLLDIV_FDIV_SHIFT;
50a0f9819cSDinh Nguyen rdiv = (reg & SOCFPGA_N5X_PLLDIV_RDIV_MASK);
51a0f9819cSDinh Nguyen qdiv = (reg & SOCFPGA_N5X_PLLDIV_QDIV_MASK) >> SOCFPGA_N5X_PLLDIV_QDIV_SHIFT;
52a0f9819cSDinh Nguyen
53a0f9819cSDinh Nguyen while (qdiv) {
54a0f9819cSDinh Nguyen power *= 2;
55a0f9819cSDinh Nguyen qdiv--;
56a0f9819cSDinh Nguyen }
57a0f9819cSDinh Nguyen
58a0f9819cSDinh Nguyen return ((parent_rate * 2 * (fdiv + 1)) / ((rdiv + 1) * power));
59a0f9819cSDinh Nguyen }
60a0f9819cSDinh Nguyen
agilex_clk_pll_recalc_rate(struct clk_hw * hwclk,unsigned long parent_rate)6180c6b7a0SDinh Nguyen static unsigned long agilex_clk_pll_recalc_rate(struct clk_hw *hwclk,
6280c6b7a0SDinh Nguyen unsigned long parent_rate)
6380c6b7a0SDinh Nguyen {
6480c6b7a0SDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
6580c6b7a0SDinh Nguyen unsigned long arefdiv, reg, mdiv;
6680c6b7a0SDinh Nguyen unsigned long long vco_freq;
6780c6b7a0SDinh Nguyen
6880c6b7a0SDinh Nguyen /* read VCO1 reg for numerator and denominator */
6980c6b7a0SDinh Nguyen reg = readl(socfpgaclk->hw.reg);
7080c6b7a0SDinh Nguyen arefdiv = (reg & SOCFPGA_PLL_AREFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;
7180c6b7a0SDinh Nguyen
7280c6b7a0SDinh Nguyen vco_freq = (unsigned long long)parent_rate / arefdiv;
7380c6b7a0SDinh Nguyen
7480c6b7a0SDinh Nguyen /* Read mdiv and fdiv from the fdbck register */
7580c6b7a0SDinh Nguyen reg = readl(socfpgaclk->hw.reg + 0x24);
7680c6b7a0SDinh Nguyen mdiv = reg & SOCFPGA_AGILEX_PLL_MDIV_MASK;
7780c6b7a0SDinh Nguyen
7880c6b7a0SDinh Nguyen vco_freq = (unsigned long long)vco_freq * mdiv;
7980c6b7a0SDinh Nguyen return (unsigned long)vco_freq;
8080c6b7a0SDinh Nguyen }
8180c6b7a0SDinh Nguyen
clk_pll_recalc_rate(struct clk_hw * hwclk,unsigned long parent_rate)8207afb8dbSDinh Nguyen static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
8307afb8dbSDinh Nguyen unsigned long parent_rate)
8407afb8dbSDinh Nguyen {
8507afb8dbSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
86*0248bfb2SThorsten Blum u32 mdiv;
87*0248bfb2SThorsten Blum u32 refdiv;
88*0248bfb2SThorsten Blum u32 reg;
8907afb8dbSDinh Nguyen unsigned long long vco_freq;
9007afb8dbSDinh Nguyen
9107afb8dbSDinh Nguyen /* read VCO1 reg for numerator and denominator */
9207afb8dbSDinh Nguyen reg = readl(socfpgaclk->hw.reg);
9307afb8dbSDinh Nguyen refdiv = (reg & SOCFPGA_PLL_REFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;
94cc26ed7bSDinh Nguyen
95cc26ed7bSDinh Nguyen vco_freq = parent_rate;
96cc26ed7bSDinh Nguyen do_div(vco_freq, refdiv);
9707afb8dbSDinh Nguyen
9807afb8dbSDinh Nguyen /* Read mdiv and fdiv from the fdbck register */
9907afb8dbSDinh Nguyen reg = readl(socfpgaclk->hw.reg + 0x4);
10007afb8dbSDinh Nguyen mdiv = (reg & SOCFPGA_PLL_MDIV_MASK) >> SOCFPGA_PLL_MDIV_SHIFT;
101c0a636e4SDinh Nguyen vco_freq = (unsigned long long)vco_freq * (mdiv + 6);
10207afb8dbSDinh Nguyen
10307afb8dbSDinh Nguyen return (unsigned long)vco_freq;
10407afb8dbSDinh Nguyen }
10507afb8dbSDinh Nguyen
clk_boot_clk_recalc_rate(struct clk_hw * hwclk,unsigned long parent_rate)10607afb8dbSDinh Nguyen static unsigned long clk_boot_clk_recalc_rate(struct clk_hw *hwclk,
10707afb8dbSDinh Nguyen unsigned long parent_rate)
10807afb8dbSDinh Nguyen {
10907afb8dbSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
11052d1a8daSColin Ian King u32 div;
11107afb8dbSDinh Nguyen
11207afb8dbSDinh Nguyen div = ((readl(socfpgaclk->hw.reg) &
11307afb8dbSDinh Nguyen SWCTRLBTCLKSEL_MASK) >>
11407afb8dbSDinh Nguyen SWCTRLBTCLKSEL_SHIFT);
11507afb8dbSDinh Nguyen div += 1;
11608d92c7aSColin Ian King return parent_rate / div;
11707afb8dbSDinh Nguyen }
11807afb8dbSDinh Nguyen
11907afb8dbSDinh Nguyen
clk_pll_get_parent(struct clk_hw * hwclk)12007afb8dbSDinh Nguyen static u8 clk_pll_get_parent(struct clk_hw *hwclk)
12107afb8dbSDinh Nguyen {
12207afb8dbSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
12307afb8dbSDinh Nguyen u32 pll_src;
12407afb8dbSDinh Nguyen
12507afb8dbSDinh Nguyen pll_src = readl(socfpgaclk->hw.reg);
12607afb8dbSDinh Nguyen return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
12707afb8dbSDinh Nguyen CLK_MGR_PLL_CLK_SRC_MASK;
12807afb8dbSDinh Nguyen }
12907afb8dbSDinh Nguyen
clk_boot_get_parent(struct clk_hw * hwclk)13007afb8dbSDinh Nguyen static u8 clk_boot_get_parent(struct clk_hw *hwclk)
13107afb8dbSDinh Nguyen {
13207afb8dbSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
13307afb8dbSDinh Nguyen u32 pll_src;
13407afb8dbSDinh Nguyen
13507afb8dbSDinh Nguyen pll_src = readl(socfpgaclk->hw.reg);
13607afb8dbSDinh Nguyen return (pll_src >> SWCTRLBTCLKSEL_SHIFT) &
13707afb8dbSDinh Nguyen SWCTRLBTCLKSEL_MASK;
13807afb8dbSDinh Nguyen }
13907afb8dbSDinh Nguyen
clk_pll_prepare(struct clk_hw * hwclk)14007afb8dbSDinh Nguyen static int clk_pll_prepare(struct clk_hw *hwclk)
14107afb8dbSDinh Nguyen {
14207afb8dbSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
14307afb8dbSDinh Nguyen u32 reg;
14407afb8dbSDinh Nguyen
14507afb8dbSDinh Nguyen /* Bring PLL out of reset */
14607afb8dbSDinh Nguyen reg = readl(socfpgaclk->hw.reg);
14707afb8dbSDinh Nguyen reg |= SOCFPGA_PLL_RESET_MASK;
14807afb8dbSDinh Nguyen writel(reg, socfpgaclk->hw.reg);
14907afb8dbSDinh Nguyen
15007afb8dbSDinh Nguyen return 0;
15107afb8dbSDinh Nguyen }
15207afb8dbSDinh Nguyen
n5x_clk_pll_prepare(struct clk_hw * hwclk)153a0f9819cSDinh Nguyen static int n5x_clk_pll_prepare(struct clk_hw *hwclk)
154a0f9819cSDinh Nguyen {
155a0f9819cSDinh Nguyen struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
156a0f9819cSDinh Nguyen u32 reg;
157a0f9819cSDinh Nguyen
158a0f9819cSDinh Nguyen /* Bring PLL out of reset */
159a0f9819cSDinh Nguyen reg = readl(socfpgaclk->hw.reg + 0x4);
160a0f9819cSDinh Nguyen reg |= SOCFPGA_PLL_RESET_MASK;
161a0f9819cSDinh Nguyen writel(reg, socfpgaclk->hw.reg + 0x4);
162a0f9819cSDinh Nguyen
163a0f9819cSDinh Nguyen return 0;
164a0f9819cSDinh Nguyen }
165a0f9819cSDinh Nguyen
166a0f9819cSDinh Nguyen static const struct clk_ops n5x_clk_pll_ops = {
167a0f9819cSDinh Nguyen .recalc_rate = n5x_clk_pll_recalc_rate,
168a0f9819cSDinh Nguyen .get_parent = clk_pll_get_parent,
169a0f9819cSDinh Nguyen .prepare = n5x_clk_pll_prepare,
170a0f9819cSDinh Nguyen };
171a0f9819cSDinh Nguyen
17280c6b7a0SDinh Nguyen static const struct clk_ops agilex_clk_pll_ops = {
17380c6b7a0SDinh Nguyen .recalc_rate = agilex_clk_pll_recalc_rate,
17480c6b7a0SDinh Nguyen .get_parent = clk_pll_get_parent,
17580c6b7a0SDinh Nguyen .prepare = clk_pll_prepare,
17680c6b7a0SDinh Nguyen };
17780c6b7a0SDinh Nguyen
178d52579ceSDinh Nguyen static const struct clk_ops clk_pll_ops = {
17907afb8dbSDinh Nguyen .recalc_rate = clk_pll_recalc_rate,
18007afb8dbSDinh Nguyen .get_parent = clk_pll_get_parent,
18107afb8dbSDinh Nguyen .prepare = clk_pll_prepare,
18207afb8dbSDinh Nguyen };
18307afb8dbSDinh Nguyen
184d52579ceSDinh Nguyen static const struct clk_ops clk_boot_ops = {
18507afb8dbSDinh Nguyen .recalc_rate = clk_boot_clk_recalc_rate,
18607afb8dbSDinh Nguyen .get_parent = clk_boot_get_parent,
18707afb8dbSDinh Nguyen .prepare = clk_pll_prepare,
18807afb8dbSDinh Nguyen };
18907afb8dbSDinh Nguyen
s10_register_pll(const struct stratix10_pll_clock * clks,void __iomem * reg)190ba7e2584SDinh Nguyen struct clk_hw *s10_register_pll(const struct stratix10_pll_clock *clks,
1918c0e783dSDinh Nguyen void __iomem *reg)
19207afb8dbSDinh Nguyen {
193ba7e2584SDinh Nguyen struct clk_hw *hw_clk;
19407afb8dbSDinh Nguyen struct socfpga_pll *pll_clk;
19507afb8dbSDinh Nguyen struct clk_init_data init;
1968c0e783dSDinh Nguyen const char *name = clks->name;
197ba7e2584SDinh Nguyen int ret;
19807afb8dbSDinh Nguyen
19907afb8dbSDinh Nguyen pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
20007afb8dbSDinh Nguyen if (WARN_ON(!pll_clk))
20107afb8dbSDinh Nguyen return NULL;
20207afb8dbSDinh Nguyen
2038c0e783dSDinh Nguyen pll_clk->hw.reg = reg + clks->offset;
20407afb8dbSDinh Nguyen
20507afb8dbSDinh Nguyen if (streq(name, SOCFPGA_BOOT_CLK))
20607afb8dbSDinh Nguyen init.ops = &clk_boot_ops;
20707afb8dbSDinh Nguyen else
20807afb8dbSDinh Nguyen init.ops = &clk_pll_ops;
20907afb8dbSDinh Nguyen
21007afb8dbSDinh Nguyen init.name = name;
2118c0e783dSDinh Nguyen init.flags = clks->flags;
21207afb8dbSDinh Nguyen
2138c0e783dSDinh Nguyen init.num_parents = clks->num_parents;
214762d961aSDinh Nguyen init.parent_names = NULL;
215762d961aSDinh Nguyen init.parent_data = clks->parent_data;
21607afb8dbSDinh Nguyen pll_clk->hw.hw.init = &init;
21707afb8dbSDinh Nguyen
21807afb8dbSDinh Nguyen pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
21907afb8dbSDinh Nguyen
220ba7e2584SDinh Nguyen hw_clk = &pll_clk->hw.hw;
221ba7e2584SDinh Nguyen
222ba7e2584SDinh Nguyen ret = clk_hw_register(NULL, hw_clk);
223ba7e2584SDinh Nguyen if (ret) {
22407afb8dbSDinh Nguyen kfree(pll_clk);
225ba7e2584SDinh Nguyen return ERR_PTR(ret);
22607afb8dbSDinh Nguyen }
227ba7e2584SDinh Nguyen return hw_clk;
22807afb8dbSDinh Nguyen }
22980c6b7a0SDinh Nguyen
agilex_register_pll(const struct stratix10_pll_clock * clks,void __iomem * reg)230ba7e2584SDinh Nguyen struct clk_hw *agilex_register_pll(const struct stratix10_pll_clock *clks,
23180c6b7a0SDinh Nguyen void __iomem *reg)
23280c6b7a0SDinh Nguyen {
233ba7e2584SDinh Nguyen struct clk_hw *hw_clk;
23480c6b7a0SDinh Nguyen struct socfpga_pll *pll_clk;
23580c6b7a0SDinh Nguyen struct clk_init_data init;
23680c6b7a0SDinh Nguyen const char *name = clks->name;
237ba7e2584SDinh Nguyen int ret;
23880c6b7a0SDinh Nguyen
23980c6b7a0SDinh Nguyen pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
24080c6b7a0SDinh Nguyen if (WARN_ON(!pll_clk))
24180c6b7a0SDinh Nguyen return NULL;
24280c6b7a0SDinh Nguyen
24380c6b7a0SDinh Nguyen pll_clk->hw.reg = reg + clks->offset;
24480c6b7a0SDinh Nguyen
24580c6b7a0SDinh Nguyen if (streq(name, SOCFPGA_BOOT_CLK))
24680c6b7a0SDinh Nguyen init.ops = &clk_boot_ops;
24780c6b7a0SDinh Nguyen else
24880c6b7a0SDinh Nguyen init.ops = &agilex_clk_pll_ops;
24980c6b7a0SDinh Nguyen
25080c6b7a0SDinh Nguyen init.name = name;
25180c6b7a0SDinh Nguyen init.flags = clks->flags;
25280c6b7a0SDinh Nguyen
25380c6b7a0SDinh Nguyen init.num_parents = clks->num_parents;
25480c6b7a0SDinh Nguyen init.parent_names = NULL;
25580c6b7a0SDinh Nguyen init.parent_data = clks->parent_data;
25680c6b7a0SDinh Nguyen pll_clk->hw.hw.init = &init;
25780c6b7a0SDinh Nguyen
25880c6b7a0SDinh Nguyen pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
259ba7e2584SDinh Nguyen hw_clk = &pll_clk->hw.hw;
26080c6b7a0SDinh Nguyen
261ba7e2584SDinh Nguyen ret = clk_hw_register(NULL, hw_clk);
262ba7e2584SDinh Nguyen if (ret) {
26380c6b7a0SDinh Nguyen kfree(pll_clk);
264ba7e2584SDinh Nguyen return ERR_PTR(ret);
26580c6b7a0SDinh Nguyen }
266ba7e2584SDinh Nguyen return hw_clk;
26780c6b7a0SDinh Nguyen }
268a0f9819cSDinh Nguyen
n5x_register_pll(const struct stratix10_pll_clock * clks,void __iomem * reg)269ba7e2584SDinh Nguyen struct clk_hw *n5x_register_pll(const struct stratix10_pll_clock *clks,
270a0f9819cSDinh Nguyen void __iomem *reg)
271a0f9819cSDinh Nguyen {
272ba7e2584SDinh Nguyen struct clk_hw *hw_clk;
273a0f9819cSDinh Nguyen struct socfpga_pll *pll_clk;
274a0f9819cSDinh Nguyen struct clk_init_data init;
275a0f9819cSDinh Nguyen const char *name = clks->name;
276ba7e2584SDinh Nguyen int ret;
277a0f9819cSDinh Nguyen
278a0f9819cSDinh Nguyen pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
279a0f9819cSDinh Nguyen if (WARN_ON(!pll_clk))
280a0f9819cSDinh Nguyen return NULL;
281a0f9819cSDinh Nguyen
282a0f9819cSDinh Nguyen pll_clk->hw.reg = reg + clks->offset;
283a0f9819cSDinh Nguyen
284a0f9819cSDinh Nguyen if (streq(name, SOCFPGA_BOOT_CLK))
285a0f9819cSDinh Nguyen init.ops = &clk_boot_ops;
286a0f9819cSDinh Nguyen else
287a0f9819cSDinh Nguyen init.ops = &n5x_clk_pll_ops;
288a0f9819cSDinh Nguyen
289a0f9819cSDinh Nguyen init.name = name;
290a0f9819cSDinh Nguyen init.flags = clks->flags;
291a0f9819cSDinh Nguyen
292a0f9819cSDinh Nguyen init.num_parents = clks->num_parents;
293a0f9819cSDinh Nguyen init.parent_names = NULL;
294a0f9819cSDinh Nguyen init.parent_data = clks->parent_data;
295a0f9819cSDinh Nguyen pll_clk->hw.hw.init = &init;
296a0f9819cSDinh Nguyen
297a0f9819cSDinh Nguyen pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
298ba7e2584SDinh Nguyen hw_clk = &pll_clk->hw.hw;
299a0f9819cSDinh Nguyen
300ba7e2584SDinh Nguyen ret = clk_hw_register(NULL, hw_clk);
301ba7e2584SDinh Nguyen if (ret) {
302a0f9819cSDinh Nguyen kfree(pll_clk);
303ba7e2584SDinh Nguyen return ERR_PTR(ret);
304a0f9819cSDinh Nguyen }
305ba7e2584SDinh Nguyen return hw_clk;
306a0f9819cSDinh Nguyen }
307