Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / drivers / clk / meson / clk-pll.c
1 /*
2  * Copyright (c) 2015 Endless Mobile, Inc.
3  * Author: Carlo Caione <carlo@endlessm.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * In the most basic form, a Meson PLL is composed as follows:
20  *
21  *                     PLL
22  *      +------------------------------+
23  *      |                              |
24  * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
25  *      |         ^        ^           |
26  *      +------------------------------+
27  *                |        |
28  *               FREF     VCO
29  *
30  * out = (in * M / N) >> OD
31  */
32
33 #include <linux/clk-provider.h>
34 #include <linux/delay.h>
35 #include <linux/err.h>
36 #include <linux/io.h>
37 #include <linux/module.h>
38 #include <linux/of_address.h>
39 #include <linux/slab.h>
40 #include <linux/string.h>
41
42 #include "clkc.h"
43
44 #define MESON_PLL_RESET                         BIT(29)
45 #define MESON_PLL_LOCK                          BIT(31)
46
47 #define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
48
49 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
50                                                 unsigned long parent_rate)
51 {
52         struct meson_clk_pll *pll = to_meson_clk_pll(hw);
53         struct parm *p;
54         unsigned long parent_rate_mhz = parent_rate / 1000000;
55         unsigned long rate_mhz;
56         u16 n, m, frac = 0, od, od2 = 0;
57         u32 reg;
58
59         p = &pll->n;
60         reg = readl(pll->base + p->reg_off);
61         n = PARM_GET(p->width, p->shift, reg);
62
63         p = &pll->m;
64         reg = readl(pll->base + p->reg_off);
65         m = PARM_GET(p->width, p->shift, reg);
66
67         p = &pll->od;
68         reg = readl(pll->base + p->reg_off);
69         od = PARM_GET(p->width, p->shift, reg);
70
71         p = &pll->od2;
72         if (p->width) {
73                 reg = readl(pll->base + p->reg_off);
74                 od2 = PARM_GET(p->width, p->shift, reg);
75         }
76
77         p = &pll->frac;
78         if (p->width) {
79                 reg = readl(pll->base + p->reg_off);
80                 frac = PARM_GET(p->width, p->shift, reg);
81                 rate_mhz = (parent_rate_mhz * m + \
82                                 (parent_rate_mhz * frac >> 12)) * 2 / n;
83                 rate_mhz = rate_mhz >> od >> od2;
84         } else
85                 rate_mhz = (parent_rate_mhz * m / n) >> od >> od2;
86
87         return rate_mhz * 1000000;
88 }
89
90 static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
91                                      unsigned long *parent_rate)
92 {
93         struct meson_clk_pll *pll = to_meson_clk_pll(hw);
94         const struct pll_rate_table *rate_table = pll->rate_table;
95         int i;
96
97         for (i = 0; i < pll->rate_count; i++) {
98                 if (rate <= rate_table[i].rate)
99                         return rate_table[i].rate;
100         }
101
102         /* else return the smallest value */
103         return rate_table[0].rate;
104 }
105
106 static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
107                                                                unsigned long rate)
108 {
109         const struct pll_rate_table *rate_table = pll->rate_table;
110         int i;
111
112         for (i = 0; i < pll->rate_count; i++) {
113                 if (rate == rate_table[i].rate)
114                         return &rate_table[i];
115         }
116         return NULL;
117 }
118
119 /* Specific wait loop for GXL/GXM GP0 PLL */
120 static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
121                                          struct parm *p_n)
122 {
123         int delay = 100;
124         u32 reg;
125
126         while (delay > 0) {
127                 reg = readl(pll->base + p_n->reg_off);
128                 writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
129                 udelay(10);
130                 writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);
131
132                 /* This delay comes from AMLogic tree clk-gp0-gxl driver */
133                 mdelay(1);
134
135                 reg = readl(pll->base + p_n->reg_off);
136                 if (reg & MESON_PLL_LOCK)
137                         return 0;
138                 delay--;
139         }
140         return -ETIMEDOUT;
141 }
142
143 static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
144                                    struct parm *p_n)
145 {
146         int delay = 24000000;
147         u32 reg;
148
149         while (delay > 0) {
150                 reg = readl(pll->base + p_n->reg_off);
151
152                 if (reg & MESON_PLL_LOCK)
153                         return 0;
154                 delay--;
155         }
156         return -ETIMEDOUT;
157 }
158
159 static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
160 {
161         int i;
162
163         for (i = 0 ; i < pll->params.params_count ; ++i)
164                 writel(pll->params.params_table[i].value,
165                        pll->base + pll->params.params_table[i].reg_off);
166 }
167
168 static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
169                                   unsigned long parent_rate)
170 {
171         struct meson_clk_pll *pll = to_meson_clk_pll(hw);
172         struct parm *p;
173         const struct pll_rate_table *rate_set;
174         unsigned long old_rate;
175         int ret = 0;
176         u32 reg;
177
178         if (parent_rate == 0 || rate == 0)
179                 return -EINVAL;
180
181         old_rate = rate;
182
183         rate_set = meson_clk_get_pll_settings(pll, rate);
184         if (!rate_set)
185                 return -EINVAL;
186
187         /* Initialize the PLL in a clean state if specified */
188         if (pll->params.params_count)
189                 meson_clk_pll_init_params(pll);
190
191         /* PLL reset */
192         p = &pll->n;
193         reg = readl(pll->base + p->reg_off);
194         /* If no_init_reset is provided, avoid resetting at this point */
195         if (!pll->params.no_init_reset)
196                 writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
197
198         reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
199         writel(reg, pll->base + p->reg_off);
200
201         p = &pll->m;
202         reg = readl(pll->base + p->reg_off);
203         reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
204         writel(reg, pll->base + p->reg_off);
205
206         p = &pll->od;
207         reg = readl(pll->base + p->reg_off);
208         reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
209         writel(reg, pll->base + p->reg_off);
210
211         p = &pll->od2;
212         if (p->width) {
213                 reg = readl(pll->base + p->reg_off);
214                 reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
215                 writel(reg, pll->base + p->reg_off);
216         }
217
218         p = &pll->frac;
219         if (p->width) {
220                 reg = readl(pll->base + p->reg_off);
221                 reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
222                 writel(reg, pll->base + p->reg_off);
223         }
224
225         p = &pll->n;
226         /* If clear_reset_for_lock is provided, remove the reset bit here */
227         if (pll->params.clear_reset_for_lock) {
228                 reg = readl(pll->base + p->reg_off);
229                 writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
230         }
231
232         /* If reset_lock_loop, use a special loop including resetting */
233         if (pll->params.reset_lock_loop)
234                 ret = meson_clk_pll_wait_lock_reset(pll, p);
235         else
236                 ret = meson_clk_pll_wait_lock(pll, p);
237         if (ret) {
238                 pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
239                         __func__, old_rate);
240                 meson_clk_pll_set_rate(hw, old_rate, parent_rate);
241         }
242
243         return ret;
244 }
245
246 const struct clk_ops meson_clk_pll_ops = {
247         .recalc_rate    = meson_clk_pll_recalc_rate,
248         .round_rate     = meson_clk_pll_round_rate,
249         .set_rate       = meson_clk_pll_set_rate,
250 };
251
252 const struct clk_ops meson_clk_pll_ro_ops = {
253         .recalc_rate    = meson_clk_pll_recalc_rate,
254 };