1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2011-2012 Calxeda, Inc. |
4 | * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> |
5 | * |
6 | * Based from clk-highbank.c |
7 | */ |
8 | #include <linux/slab.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/io.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_address.h> |
13 | |
14 | #include "clk.h" |
15 | |
16 | /* Clock bypass bits */ |
17 | #define MAINPLL_BYPASS (1<<0) |
18 | #define SDRAMPLL_BYPASS (1<<1) |
19 | #define SDRAMPLL_SRC_BYPASS (1<<2) |
20 | #define PERPLL_BYPASS (1<<3) |
21 | #define PERPLL_SRC_BYPASS (1<<4) |
22 | |
23 | #define SOCFPGA_PLL_BG_PWRDWN 0 |
24 | #define SOCFPGA_PLL_EXT_ENA 1 |
25 | #define SOCFPGA_PLL_PWR_DOWN 2 |
26 | #define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 |
27 | #define SOCFPGA_PLL_DIVF_SHIFT 3 |
28 | #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 |
29 | #define SOCFPGA_PLL_DIVQ_SHIFT 16 |
30 | |
31 | #define CLK_MGR_PLL_CLK_SRC_SHIFT 22 |
32 | #define CLK_MGR_PLL_CLK_SRC_MASK 0x3 |
33 | |
34 | #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) |
35 | |
36 | void __iomem *clk_mgr_base_addr; |
37 | |
38 | static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, |
39 | unsigned long parent_rate) |
40 | { |
41 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); |
42 | unsigned long divf, divq, reg; |
43 | unsigned long long vco_freq; |
44 | unsigned long bypass; |
45 | |
46 | reg = readl(addr: socfpgaclk->hw.reg); |
47 | bypass = readl(addr: clk_mgr_base_addr + CLKMGR_BYPASS); |
48 | if (bypass & MAINPLL_BYPASS) |
49 | return parent_rate; |
50 | |
51 | divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; |
52 | divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; |
53 | vco_freq = (unsigned long long)parent_rate * (divf + 1); |
54 | do_div(vco_freq, (1 + divq)); |
55 | return (unsigned long)vco_freq; |
56 | } |
57 | |
58 | static u8 clk_pll_get_parent(struct clk_hw *hwclk) |
59 | { |
60 | u32 pll_src; |
61 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); |
62 | |
63 | pll_src = readl(addr: socfpgaclk->hw.reg); |
64 | return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & |
65 | CLK_MGR_PLL_CLK_SRC_MASK; |
66 | } |
67 | |
68 | static const struct clk_ops clk_pll_ops = { |
69 | .recalc_rate = clk_pll_recalc_rate, |
70 | .get_parent = clk_pll_get_parent, |
71 | }; |
72 | |
73 | static void __init __socfpga_pll_init(struct device_node *node, |
74 | const struct clk_ops *ops) |
75 | { |
76 | u32 reg; |
77 | struct clk_hw *hw_clk; |
78 | struct socfpga_pll *pll_clk; |
79 | const char *clk_name = node->name; |
80 | const char *parent_name[SOCFPGA_MAX_PARENTS]; |
81 | struct clk_init_data init; |
82 | struct device_node *clkmgr_np; |
83 | int rc; |
84 | |
85 | of_property_read_u32(np: node, propname: "reg" , out_value: ®); |
86 | |
87 | pll_clk = kzalloc(size: sizeof(*pll_clk), GFP_KERNEL); |
88 | if (WARN_ON(!pll_clk)) |
89 | return; |
90 | |
91 | clkmgr_np = of_find_compatible_node(NULL, NULL, compat: "altr,clk-mgr" ); |
92 | clk_mgr_base_addr = of_iomap(node: clkmgr_np, index: 0); |
93 | of_node_put(node: clkmgr_np); |
94 | BUG_ON(!clk_mgr_base_addr); |
95 | pll_clk->hw.reg = clk_mgr_base_addr + reg; |
96 | |
97 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &clk_name); |
98 | |
99 | init.name = clk_name; |
100 | init.ops = ops; |
101 | init.flags = 0; |
102 | |
103 | init.num_parents = of_clk_parent_fill(np: node, parents: parent_name, SOCFPGA_MAX_PARENTS); |
104 | init.parent_names = parent_name; |
105 | pll_clk->hw.hw.init = &init; |
106 | |
107 | pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; |
108 | |
109 | hw_clk = &pll_clk->hw.hw; |
110 | |
111 | rc = clk_hw_register(NULL, hw: hw_clk); |
112 | if (rc) { |
113 | pr_err("Could not register clock:%s\n" , clk_name); |
114 | goto err_clk_hw_register; |
115 | } |
116 | |
117 | rc = of_clk_add_hw_provider(np: node, get: of_clk_hw_simple_get, data: hw_clk); |
118 | if (rc) { |
119 | pr_err("Could not register clock provider for node:%s\n" , |
120 | clk_name); |
121 | goto err_of_clk_add_hw_provider; |
122 | } |
123 | |
124 | return; |
125 | |
126 | err_of_clk_add_hw_provider: |
127 | clk_hw_unregister(hw: hw_clk); |
128 | err_clk_hw_register: |
129 | kfree(objp: pll_clk); |
130 | } |
131 | |
132 | void __init socfpga_pll_init(struct device_node *node) |
133 | { |
134 | __socfpga_pll_init(node, ops: &clk_pll_ops); |
135 | } |
136 | |