GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / clk / qcom / clk-hfpll.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018, The Linux Foundation. All rights reserved.
3
4 #include <linux/kernel.h>
5 #include <linux/export.h>
6 #include <linux/regmap.h>
7 #include <linux/delay.h>
8 #include <linux/err.h>
9 #include <linux/clk-provider.h>
10 #include <linux/spinlock.h>
11
12 #include "clk-regmap.h"
13 #include "clk-hfpll.h"
14
15 #define PLL_OUTCTRL     BIT(0)
16 #define PLL_BYPASSNL    BIT(1)
17 #define PLL_RESET_N     BIT(2)
18
19 /* Initialize a HFPLL at a given rate and enable it. */
20 static void __clk_hfpll_init_once(struct clk_hw *hw)
21 {
22         struct clk_hfpll *h = to_clk_hfpll(hw);
23         struct hfpll_data const *hd = h->d;
24         struct regmap *regmap = h->clkr.regmap;
25
26         if (likely(h->init_done))
27                 return;
28
29         /* Configure PLL parameters for integer mode. */
30         if (hd->config_val)
31                 regmap_write(regmap, hd->config_reg, hd->config_val);
32         regmap_write(regmap, hd->m_reg, 0);
33         regmap_write(regmap, hd->n_reg, 1);
34
35         if (hd->user_reg) {
36                 u32 regval = hd->user_val;
37                 unsigned long rate;
38
39                 rate = clk_hw_get_rate(hw);
40
41                 /* Pick the right VCO. */
42                 if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
43                         regval |= hd->user_vco_mask;
44                 regmap_write(regmap, hd->user_reg, regval);
45         }
46
47         /* Write L_VAL from conf if it exist */
48         if (hd->l_val)
49                 regmap_write(regmap, hd->l_reg, hd->l_val);
50
51         if (hd->droop_reg)
52                 regmap_write(regmap, hd->droop_reg, hd->droop_val);
53
54         h->init_done = true;
55 }
56
57 static void __clk_hfpll_enable(struct clk_hw *hw)
58 {
59         struct clk_hfpll *h = to_clk_hfpll(hw);
60         struct hfpll_data const *hd = h->d;
61         struct regmap *regmap = h->clkr.regmap;
62         u32 val;
63
64         __clk_hfpll_init_once(hw);
65
66         /* Disable PLL bypass mode. */
67         regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
68
69         /*
70          * H/W requires a 5us delay between disabling the bypass and
71          * de-asserting the reset. Delay 10us just to be safe.
72          */
73         udelay(10);
74
75         /* De-assert active-low PLL reset. */
76         regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
77
78         /* Wait for PLL to lock. */
79         if (hd->status_reg)
80                 /*
81                  * Busy wait. Should never timeout, we add a timeout to
82                  * prevent any sort of stall.
83                  */
84                 regmap_read_poll_timeout(regmap, hd->status_reg, val,
85                                          !(val & BIT(hd->lock_bit)), 0,
86                                          100 * USEC_PER_MSEC);
87         else
88                 udelay(60);
89
90         /* Enable PLL output. */
91         regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
92 }
93
94 /* Enable an already-configured HFPLL. */
95 static int clk_hfpll_enable(struct clk_hw *hw)
96 {
97         unsigned long flags;
98         struct clk_hfpll *h = to_clk_hfpll(hw);
99         struct hfpll_data const *hd = h->d;
100         struct regmap *regmap = h->clkr.regmap;
101         u32 mode;
102
103         spin_lock_irqsave(&h->lock, flags);
104         regmap_read(regmap, hd->mode_reg, &mode);
105         if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
106                 __clk_hfpll_enable(hw);
107         spin_unlock_irqrestore(&h->lock, flags);
108
109         return 0;
110 }
111
112 static void __clk_hfpll_disable(struct clk_hfpll *h)
113 {
114         struct hfpll_data const *hd = h->d;
115         struct regmap *regmap = h->clkr.regmap;
116
117         /*
118          * Disable the PLL output, disable test mode, enable the bypass mode,
119          * and assert the reset.
120          */
121         regmap_update_bits(regmap, hd->mode_reg,
122                            PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
123 }
124
125 static void clk_hfpll_disable(struct clk_hw *hw)
126 {
127         struct clk_hfpll *h = to_clk_hfpll(hw);
128         unsigned long flags;
129
130         spin_lock_irqsave(&h->lock, flags);
131         __clk_hfpll_disable(h);
132         spin_unlock_irqrestore(&h->lock, flags);
133 }
134
135 static int clk_hfpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
136 {
137         struct clk_hfpll *h = to_clk_hfpll(hw);
138         struct hfpll_data const *hd = h->d;
139         unsigned long rrate;
140
141         req->rate = clamp(req->rate, hd->min_rate, hd->max_rate);
142
143         rrate = DIV_ROUND_UP(req->rate, req->best_parent_rate) * req->best_parent_rate;
144         if (rrate > hd->max_rate)
145                 rrate -= req->best_parent_rate;
146
147         req->rate = rrate;
148         return 0;
149 }
150
151 /*
152  * For optimization reasons, assumes no downstream clocks are actively using
153  * it.
154  */
155 static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
156                               unsigned long parent_rate)
157 {
158         struct clk_hfpll *h = to_clk_hfpll(hw);
159         struct hfpll_data const *hd = h->d;
160         struct regmap *regmap = h->clkr.regmap;
161         unsigned long flags;
162         u32 l_val, val;
163         bool enabled;
164
165         l_val = rate / parent_rate;
166
167         spin_lock_irqsave(&h->lock, flags);
168
169         enabled = __clk_is_enabled(hw->clk);
170         if (enabled)
171                 __clk_hfpll_disable(h);
172
173         /* Pick the right VCO. */
174         if (hd->user_reg && hd->user_vco_mask) {
175                 regmap_read(regmap, hd->user_reg, &val);
176                 if (rate <= hd->low_vco_max_rate)
177                         val &= ~hd->user_vco_mask;
178                 else
179                         val |= hd->user_vco_mask;
180                 regmap_write(regmap, hd->user_reg, val);
181         }
182
183         regmap_write(regmap, hd->l_reg, l_val);
184
185         if (enabled)
186                 __clk_hfpll_enable(hw);
187
188         spin_unlock_irqrestore(&h->lock, flags);
189
190         return 0;
191 }
192
193 static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
194                                            unsigned long parent_rate)
195 {
196         struct clk_hfpll *h = to_clk_hfpll(hw);
197         struct hfpll_data const *hd = h->d;
198         struct regmap *regmap = h->clkr.regmap;
199         u32 l_val;
200
201         regmap_read(regmap, hd->l_reg, &l_val);
202
203         return l_val * parent_rate;
204 }
205
206 static int clk_hfpll_init(struct clk_hw *hw)
207 {
208         struct clk_hfpll *h = to_clk_hfpll(hw);
209         struct hfpll_data const *hd = h->d;
210         struct regmap *regmap = h->clkr.regmap;
211         u32 mode, status;
212
213         regmap_read(regmap, hd->mode_reg, &mode);
214         if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
215                 __clk_hfpll_init_once(hw);
216                 return 0;
217         }
218
219         if (hd->status_reg) {
220                 regmap_read(regmap, hd->status_reg, &status);
221                 if (!(status & BIT(hd->lock_bit))) {
222                         WARN(1, "HFPLL %s is ON, but not locked!\n",
223                              __clk_get_name(hw->clk));
224                         clk_hfpll_disable(hw);
225                         __clk_hfpll_init_once(hw);
226                 }
227         }
228
229         return 0;
230 }
231
232 static int hfpll_is_enabled(struct clk_hw *hw)
233 {
234         struct clk_hfpll *h = to_clk_hfpll(hw);
235         struct hfpll_data const *hd = h->d;
236         struct regmap *regmap = h->clkr.regmap;
237         u32 mode;
238
239         regmap_read(regmap, hd->mode_reg, &mode);
240         mode &= 0x7;
241         return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
242 }
243
244 const struct clk_ops clk_ops_hfpll = {
245         .enable = clk_hfpll_enable,
246         .disable = clk_hfpll_disable,
247         .is_enabled = hfpll_is_enabled,
248         .determine_rate = clk_hfpll_determine_rate,
249         .set_rate = clk_hfpll_set_rate,
250         .recalc_rate = clk_hfpll_recalc_rate,
251         .init = clk_hfpll_init,
252 };
253 EXPORT_SYMBOL_GPL(clk_ops_hfpll);