Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 47struct meson_clk_pll {
 48	struct clk_hw	hw;
 49	void __iomem	*base;
 50	struct pll_conf	*conf;
 51	unsigned int	rate_count;
 52	spinlock_t	*lock;
 53};
 54#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
 55
 56static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 57						unsigned long parent_rate)
 58{
 59	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
 60	struct parm *p;
 61	unsigned long parent_rate_mhz = parent_rate / 1000000;
 62	unsigned long rate_mhz;
 63	u16 n, m, od;
 64	u32 reg;
 65
 66	p = &pll->conf->n;
 67	reg = readl(pll->base + p->reg_off);
 68	n = PARM_GET(p->width, p->shift, reg);
 69
 70	p = &pll->conf->m;
 71	reg = readl(pll->base + p->reg_off);
 72	m = PARM_GET(p->width, p->shift, reg);
 73
 74	p = &pll->conf->od;
 75	reg = readl(pll->base + p->reg_off);
 76	od = PARM_GET(p->width, p->shift, reg);
 77
 78	rate_mhz = (parent_rate_mhz * m / n) >> od;
 79
 80	return rate_mhz * 1000000;
 81}
 82
 83static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 84				     unsigned long *parent_rate)
 85{
 86	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
 87	const struct pll_rate_table *rate_table = pll->conf->rate_table;
 88	int i;
 89
 90	for (i = 0; i < pll->rate_count; i++) {
 91		if (rate <= rate_table[i].rate)
 92			return rate_table[i].rate;
 93	}
 94
 95	/* else return the smallest value */
 96	return rate_table[0].rate;
 97}
 98
 99static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
100							       unsigned long rate)
101{
102	const struct pll_rate_table *rate_table = pll->conf->rate_table;
103	int i;
104
105	for (i = 0; i < pll->rate_count; i++) {
106		if (rate == rate_table[i].rate)
107			return &rate_table[i];
108	}
109	return NULL;
110}
111
112static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
113				   struct parm *p_n)
114{
115	int delay = 24000000;
116	u32 reg;
117
118	while (delay > 0) {
119		reg = readl(pll->base + p_n->reg_off);
120
121		if (reg & MESON_PLL_LOCK)
122			return 0;
123		delay--;
124	}
125	return -ETIMEDOUT;
126}
127
128static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
129				  unsigned long parent_rate)
130{
131	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
132	struct parm *p;
133	const struct pll_rate_table *rate_set;
134	unsigned long old_rate;
135	int ret = 0;
136	u32 reg;
137
138	if (parent_rate == 0 || rate == 0)
139		return -EINVAL;
140
141	old_rate = rate;
142
143	rate_set = meson_clk_get_pll_settings(pll, rate);
144	if (!rate_set)
145		return -EINVAL;
146
147	/* PLL reset */
148	p = &pll->conf->n;
149	reg = readl(pll->base + p->reg_off);
150	writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
151
152	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
153	writel(reg, pll->base + p->reg_off);
154
155	p = &pll->conf->m;
156	reg = readl(pll->base + p->reg_off);
157	reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
158	writel(reg, pll->base + p->reg_off);
159
160	p = &pll->conf->od;
161	reg = readl(pll->base + p->reg_off);
162	reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
163	writel(reg, pll->base + p->reg_off);
164
165	p = &pll->conf->n;
166	ret = meson_clk_pll_wait_lock(pll, p);
167	if (ret) {
168		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
169			__func__, old_rate);
170		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
171	}
172
173	return ret;
174}
175
176static const struct clk_ops meson_clk_pll_ops = {
177	.recalc_rate	= meson_clk_pll_recalc_rate,
178	.round_rate	= meson_clk_pll_round_rate,
179	.set_rate	= meson_clk_pll_set_rate,
180};
181
182static const struct clk_ops meson_clk_pll_ro_ops = {
183	.recalc_rate	= meson_clk_pll_recalc_rate,
184};
185
186struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
187				   void __iomem *reg_base,
188				   spinlock_t *lock)
189{
190	struct clk *clk;
191	struct meson_clk_pll *clk_pll;
192	struct clk_init_data init;
193
194	clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL);
195	if (!clk_pll)
196		return ERR_PTR(-ENOMEM);
197
198	clk_pll->base = reg_base + clk_conf->reg_off;
199	clk_pll->lock = lock;
200	clk_pll->conf = clk_conf->conf.pll;
201
202	init.name = clk_conf->clk_name;
203	init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
204
205	init.parent_names = &clk_conf->clks_parent[0];
206	init.num_parents = 1;
207	init.ops = &meson_clk_pll_ro_ops;
208
209	/* If no rate_table is specified we assume the PLL is read-only */
210	if (clk_pll->conf->rate_table) {
211		int len;
212
213		for (len = 0; clk_pll->conf->rate_table[len].rate != 0; )
214			len++;
215
216		 clk_pll->rate_count = len;
217		 init.ops = &meson_clk_pll_ops;
218	}
219
220	clk_pll->hw.init = &init;
221
222	clk = clk_register(NULL, &clk_pll->hw);
223	if (IS_ERR(clk))
224		kfree(clk_pll);
225
226	return clk;
227}