1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/clk/zynqmp.h>
20#include <linux/clk-provider.h>
21#include <linux/slab.h>
22#include <linux/io.h>
23
24
25
26
27
28
29
30
31
32struct zynqmp_pll {
33 struct clk_hw hw;
34 resource_size_t *pll_ctrl;
35 resource_size_t *pll_status;
36 u8 lockbit;
37};
38#define to_zynqmp_pll(_hw) container_of(_hw, struct zynqmp_pll, hw)
39
40
41#define PLLCTRL_FBDIV_MASK 0x7f00
42#define PLLCTRL_FBDIV_SHIFT 8
43#define PLLCTRL_BP_MASK (1 << 3)
44#define PLLCTRL_DIV2_MASK (1 << 16)
45#define PLLCTRL_RESET_MASK 1
46#define PLLCTRL_RESET_VAL 1
47#define PLL_STATUS_LOCKED 1
48#define PLLCTRL_RESET_SHIFT 0
49#define PLLCTRL_DIV2_SHIFT 16
50
51#define PLL_FBDIV_MIN 25
52#define PLL_FBDIV_MAX 125
53
54enum pll_mode {
55 PLL_MODE_FRAC,
56 PLL_MODE_INT,
57};
58
59#define FRAC_OFFSET 0x8
60#define PLLFCFG_FRAC_EN BIT(31)
61#define FRAC_DIV 0x10000
62
63static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw)
64{
65 struct zynqmp_pll *clk = to_zynqmp_pll(hw);
66 u32 reg;
67
68 reg = zynqmp_pm_mmio_readl(clk->pll_ctrl + FRAC_OFFSET);
69 reg = reg & PLLFCFG_FRAC_EN;
70 return reg ? PLL_MODE_FRAC : PLL_MODE_INT;
71}
72
73
74
75
76
77
78
79
80
81static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
82 unsigned long *prate)
83{
84 u32 fbdiv;
85 long rate_div, frac, m, f;
86
87 if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) {
88 rate_div = ((rate*100) / *prate);
89 m = rate_div / 100;
90 f = rate_div % 100;
91 m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
92 rate = *prate * m;
93 frac = (*prate * f) / 100;
94 return (rate + frac);
95 }
96
97 fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
98 fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
99 return *prate * fbdiv;
100}
101
102
103
104
105
106
107
108static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw,
109 unsigned long parent_rate)
110{
111 struct zynqmp_pll *clk = to_zynqmp_pll(hw);
112 u32 fbdiv, div2, data;
113 unsigned long rate, frac;
114
115
116
117
118
119 fbdiv = (zynqmp_pm_mmio_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
120 PLLCTRL_FBDIV_SHIFT;
121 div2 = (zynqmp_pm_mmio_readl(clk->pll_ctrl) & PLLCTRL_DIV2_MASK) >>
122 PLLCTRL_DIV2_SHIFT;
123 if (div2)
124 fbdiv = fbdiv * 2;
125
126 rate = parent_rate * fbdiv;
127 if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) {
128 data = (zynqmp_pm_mmio_readl(clk->pll_ctrl + FRAC_OFFSET) &
129 0xffff);
130 frac = (rate * data) / FRAC_DIV;
131 rate = rate + frac;
132 }
133 return rate;
134}
135
136
137
138
139
140
141
142static int zynqmp_pll_is_enabled(struct clk_hw *hw)
143{
144 u32 reg;
145 struct zynqmp_pll *clk = to_zynqmp_pll(hw);
146
147 reg = zynqmp_pm_mmio_readl(clk->pll_ctrl);
148
149 return !(reg & (PLLCTRL_RESET_MASK));
150}
151
152
153
154
155
156
157
158static int zynqmp_pll_enable(struct clk_hw *hw)
159{
160 u32 reg;
161 struct zynqmp_pll *clk = to_zynqmp_pll(hw);
162
163 if (zynqmp_pll_is_enabled(hw))
164 return 0;
165
166 pr_info("PLL: enable\n");
167
168 reg = zynqmp_pm_mmio_readl(clk->pll_ctrl);
169 reg &= ~(PLLCTRL_RESET_MASK);
170 zynqmp_pm_mmio_writel(reg, clk->pll_ctrl);
171 while (!(zynqmp_pm_mmio_readl(clk->pll_status) & (1 << clk->lockbit)))
172 cpu_relax();
173
174 return 0;
175}
176
177
178
179
180
181
182static void zynqmp_pll_disable(struct clk_hw *hw)
183{
184 struct zynqmp_pll *clk = to_zynqmp_pll(hw);
185
186 if (!zynqmp_pll_is_enabled(hw))
187 return;
188
189 pr_info("PLL: shutdown\n");
190
191
192 zynqmp_pm_mmio_write((u32)(ulong)clk->pll_ctrl, PLLCTRL_RESET_MASK,
193 PLLCTRL_RESET_VAL);
194}
195
196static const struct clk_ops zynqmp_pll_ops = {
197 .enable = zynqmp_pll_enable,
198 .disable = zynqmp_pll_disable,
199 .is_enabled = zynqmp_pll_is_enabled,
200 .round_rate = zynqmp_pll_round_rate,
201 .recalc_rate = zynqmp_pll_recalc_rate
202};
203
204
205
206
207
208
209
210
211
212
213
214
215struct clk *clk_register_zynqmp_pll(const char *name, const char *parent,
216 unsigned long flag, resource_size_t *pll_ctrl,
217 resource_size_t *pll_status, u8 lock_index)
218{
219 struct zynqmp_pll *pll;
220 struct clk *clk;
221 struct clk_init_data init;
222
223 init.name = name;
224 init.ops = &zynqmp_pll_ops;
225 init.flags = flag;
226 init.parent_names = &parent;
227 init.num_parents = 1;
228
229 pll = kmalloc(sizeof(*pll), GFP_KERNEL);
230 if (!pll)
231 return ERR_PTR(-ENOMEM);
232
233
234 pll->hw.init = &init;
235 pll->pll_ctrl = pll_ctrl;
236 pll->pll_status = pll_status;
237 pll->lockbit = lock_index;
238
239 clk = clk_register(NULL, &pll->hw);
240 if (WARN_ON(IS_ERR(clk)))
241 kfree(pll);
242
243 return clk;
244}
245