GNU Linux-libre 4.9.333-gnu1
[releases.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 static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
120                                    struct parm *p_n)
121 {
122         int delay = 24000000;
123         u32 reg;
124
125         while (delay > 0) {
126                 reg = readl(pll->base + p_n->reg_off);
127
128                 if (reg & MESON_PLL_LOCK)
129                         return 0;
130                 delay--;
131         }
132         return -ETIMEDOUT;
133 }
134
135 static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
136                                   unsigned long parent_rate)
137 {
138         struct meson_clk_pll *pll = to_meson_clk_pll(hw);
139         struct parm *p;
140         const struct pll_rate_table *rate_set;
141         unsigned long old_rate;
142         int ret = 0;
143         u32 reg;
144
145         if (parent_rate == 0 || rate == 0)
146                 return -EINVAL;
147
148         old_rate = clk_hw_get_rate(hw);
149
150         rate_set = meson_clk_get_pll_settings(pll, rate);
151         if (!rate_set)
152                 return -EINVAL;
153
154         /* PLL reset */
155         p = &pll->n;
156         reg = readl(pll->base + p->reg_off);
157         writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
158
159         reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
160         writel(reg, pll->base + p->reg_off);
161
162         p = &pll->m;
163         reg = readl(pll->base + p->reg_off);
164         reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
165         writel(reg, pll->base + p->reg_off);
166
167         p = &pll->od;
168         reg = readl(pll->base + p->reg_off);
169         reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
170         writel(reg, pll->base + p->reg_off);
171
172         p = &pll->od2;
173         if (p->width) {
174                 reg = readl(pll->base + p->reg_off);
175                 reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
176                 writel(reg, pll->base + p->reg_off);
177         }
178
179         p = &pll->frac;
180         if (p->width) {
181                 reg = readl(pll->base + p->reg_off);
182                 reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
183                 writel(reg, pll->base + p->reg_off);
184         }
185
186         p = &pll->n;
187         ret = meson_clk_pll_wait_lock(pll, p);
188         if (ret) {
189                 pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
190                         __func__, old_rate);
191                 meson_clk_pll_set_rate(hw, old_rate, parent_rate);
192         }
193
194         return ret;
195 }
196
197 const struct clk_ops meson_clk_pll_ops = {
198         .recalc_rate    = meson_clk_pll_recalc_rate,
199         .round_rate     = meson_clk_pll_round_rate,
200         .set_rate       = meson_clk_pll_set_rate,
201 };
202
203 const struct clk_ops meson_clk_pll_ro_ops = {
204         .recalc_rate    = meson_clk_pll_recalc_rate,
205 };