Blame view

drivers/clk/meson/clk-pll.c 5.14 KB
7a29a8694   Carlo Caione   clk: meson: Add s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  /*
   * Copyright (c) 2015 Endless Mobile, Inc.
   * Author: Carlo Caione <carlo@endlessm.com>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms and conditions of the GNU General Public License,
   * version 2, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   * more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  /*
   * In the most basic form, a Meson PLL is composed as follows:
   *
   *                     PLL
   *      +------------------------------+
   *      |                              |
   * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
   *      |         ^        ^           |
   *      +------------------------------+
   *                |        |
   *               FREF     VCO
   *
   * out = (in * M / N) >> OD
   */
  
  #include <linux/clk-provider.h>
  #include <linux/delay.h>
  #include <linux/err.h>
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/of_address.h>
  #include <linux/slab.h>
  #include <linux/string.h>
  
  #include "clkc.h"
  
  #define MESON_PLL_RESET				BIT(29)
  #define MESON_PLL_LOCK				BIT(31)
7a29a8694   Carlo Caione   clk: meson: Add s...
46
47
48
49
50
51
52
53
54
  #define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
  
  static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
  						unsigned long parent_rate)
  {
  	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
  	struct parm *p;
  	unsigned long parent_rate_mhz = parent_rate / 1000000;
  	unsigned long rate_mhz;
4a4729514   Michael Turquette   clk: meson: fract...
55
  	u16 n, m, frac = 0, od, od2 = 0;
7a29a8694   Carlo Caione   clk: meson: Add s...
56
  	u32 reg;
ec623f2a4   Michael Turquette   clk: meson8b: cle...
57
  	p = &pll->n;
7a29a8694   Carlo Caione   clk: meson: Add s...
58
59
  	reg = readl(pll->base + p->reg_off);
  	n = PARM_GET(p->width, p->shift, reg);
ec623f2a4   Michael Turquette   clk: meson8b: cle...
60
  	p = &pll->m;
7a29a8694   Carlo Caione   clk: meson: Add s...
61
62
  	reg = readl(pll->base + p->reg_off);
  	m = PARM_GET(p->width, p->shift, reg);
ec623f2a4   Michael Turquette   clk: meson8b: cle...
63
  	p = &pll->od;
7a29a8694   Carlo Caione   clk: meson: Add s...
64
65
  	reg = readl(pll->base + p->reg_off);
  	od = PARM_GET(p->width, p->shift, reg);
4a4729514   Michael Turquette   clk: meson: fract...
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  	p = &pll->od2;
  	if (p->width) {
  		reg = readl(pll->base + p->reg_off);
  		od2 = PARM_GET(p->width, p->shift, reg);
  	}
  
  	p = &pll->frac;
  	if (p->width) {
  		reg = readl(pll->base + p->reg_off);
  		frac = PARM_GET(p->width, p->shift, reg);
  		rate_mhz = (parent_rate_mhz * m + \
  				(parent_rate_mhz * frac >> 12)) * 2 / n;
  		rate_mhz = rate_mhz >> od >> od2;
  	} else
  		rate_mhz = (parent_rate_mhz * m / n) >> od >> od2;
7a29a8694   Carlo Caione   clk: meson: Add s...
81
82
83
84
85
86
87
88
  
  	return rate_mhz * 1000000;
  }
  
  static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
  				     unsigned long *parent_rate)
  {
  	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
ec623f2a4   Michael Turquette   clk: meson8b: cle...
89
  	const struct pll_rate_table *rate_table = pll->rate_table;
7a29a8694   Carlo Caione   clk: meson: Add s...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  	int i;
  
  	for (i = 0; i < pll->rate_count; i++) {
  		if (rate <= rate_table[i].rate)
  			return rate_table[i].rate;
  	}
  
  	/* else return the smallest value */
  	return rate_table[0].rate;
  }
  
  static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
  							       unsigned long rate)
  {
ec623f2a4   Michael Turquette   clk: meson8b: cle...
104
  	const struct pll_rate_table *rate_table = pll->rate_table;
7a29a8694   Carlo Caione   clk: meson: Add s...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  	int i;
  
  	for (i = 0; i < pll->rate_count; i++) {
  		if (rate == rate_table[i].rate)
  			return &rate_table[i];
  	}
  	return NULL;
  }
  
  static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
  				   struct parm *p_n)
  {
  	int delay = 24000000;
  	u32 reg;
  
  	while (delay > 0) {
  		reg = readl(pll->base + p_n->reg_off);
  
  		if (reg & MESON_PLL_LOCK)
  			return 0;
  		delay--;
  	}
  	return -ETIMEDOUT;
  }
  
  static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
  				  unsigned long parent_rate)
  {
  	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
  	struct parm *p;
  	const struct pll_rate_table *rate_set;
  	unsigned long old_rate;
  	int ret = 0;
  	u32 reg;
  
  	if (parent_rate == 0 || rate == 0)
  		return -EINVAL;
  
  	old_rate = rate;
  
  	rate_set = meson_clk_get_pll_settings(pll, rate);
  	if (!rate_set)
  		return -EINVAL;
  
  	/* PLL reset */
ec623f2a4   Michael Turquette   clk: meson8b: cle...
150
  	p = &pll->n;
7a29a8694   Carlo Caione   clk: meson: Add s...
151
152
153
154
155
  	reg = readl(pll->base + p->reg_off);
  	writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
  
  	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
  	writel(reg, pll->base + p->reg_off);
ec623f2a4   Michael Turquette   clk: meson8b: cle...
156
  	p = &pll->m;
7a29a8694   Carlo Caione   clk: meson: Add s...
157
158
159
  	reg = readl(pll->base + p->reg_off);
  	reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
  	writel(reg, pll->base + p->reg_off);
ec623f2a4   Michael Turquette   clk: meson8b: cle...
160
  	p = &pll->od;
7a29a8694   Carlo Caione   clk: meson: Add s...
161
162
163
  	reg = readl(pll->base + p->reg_off);
  	reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
  	writel(reg, pll->base + p->reg_off);
4a4729514   Michael Turquette   clk: meson: fract...
164
165
166
167
168
169
170
171
172
173
174
175
176
  	p = &pll->od2;
  	if (p->width) {
  		reg = readl(pll->base + p->reg_off);
  		reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
  		writel(reg, pll->base + p->reg_off);
  	}
  
  	p = &pll->frac;
  	if (p->width) {
  		reg = readl(pll->base + p->reg_off);
  		reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
  		writel(reg, pll->base + p->reg_off);
  	}
ec623f2a4   Michael Turquette   clk: meson8b: cle...
177
  	p = &pll->n;
7a29a8694   Carlo Caione   clk: meson: Add s...
178
179
180
181
182
183
184
185
186
187
  	ret = meson_clk_pll_wait_lock(pll, p);
  	if (ret) {
  		pr_warn("%s: pll did not lock, trying to restore old rate %lu
  ",
  			__func__, old_rate);
  		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
  	}
  
  	return ret;
  }
ec623f2a4   Michael Turquette   clk: meson8b: cle...
188
  const struct clk_ops meson_clk_pll_ops = {
7a29a8694   Carlo Caione   clk: meson: Add s...
189
190
191
192
  	.recalc_rate	= meson_clk_pll_recalc_rate,
  	.round_rate	= meson_clk_pll_round_rate,
  	.set_rate	= meson_clk_pll_set_rate,
  };
ec623f2a4   Michael Turquette   clk: meson8b: cle...
193
  const struct clk_ops meson_clk_pll_ro_ops = {
7a29a8694   Carlo Caione   clk: meson: Add s...
194
195
  	.recalc_rate	= meson_clk_pll_recalc_rate,
  };